Repository: reasonml-editor/reasonml-idea-plugin Branch: master Commit: c3df8ada78a9 Files: 873 Total size: 3.0 MB Directory structure: gitextract_px5e5xvk/ ├── .github/ │ ├── FUNDING.md │ ├── FUNDING.yml │ └── workflows/ │ ├── gradle.yml │ └── website-deploy.yml ├── .gitignore ├── CHANGELOG.md ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE.md ├── LICENSE ├── README.md ├── build.gradle ├── gradle/ │ └── wrapper/ │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.properties ├── gradlew ├── gradlew.bat ├── settings.gradle ├── src/ │ ├── main/ │ │ ├── java/ │ │ │ ├── com/ │ │ │ │ └── reason/ │ │ │ │ ├── FileHelper.java │ │ │ │ ├── comp/ │ │ │ │ │ ├── CliType.java │ │ │ │ │ ├── CompilerOutputAnalyzer.java │ │ │ │ │ ├── CompilerOutputListener.java │ │ │ │ │ ├── ORCompiler.java │ │ │ │ │ ├── ORCompilerConfigManager.java │ │ │ │ │ ├── ORCompilerManager.java │ │ │ │ │ ├── ORCompilerOutputAnalyzer.java │ │ │ │ │ ├── ORConstants.java │ │ │ │ │ ├── ORPlatform.java │ │ │ │ │ ├── ORResolvedCompiler.java │ │ │ │ │ ├── ProcessFinishedListener.java │ │ │ │ │ ├── bs/ │ │ │ │ │ │ ├── BsColoredProcessHandler.java │ │ │ │ │ │ ├── BsCompiler.java │ │ │ │ │ │ ├── BsConfig.java │ │ │ │ │ │ ├── BsConfigReader.java │ │ │ │ │ │ ├── BsFormatProcess.java │ │ │ │ │ │ ├── BsLineProcessor.java │ │ │ │ │ │ ├── BsNotification.java │ │ │ │ │ │ ├── BsPlatform.java │ │ │ │ │ │ ├── BsProcess.java │ │ │ │ │ │ ├── BsResolvedCompiler.java │ │ │ │ │ │ └── Ninja.java │ │ │ │ │ ├── dune/ │ │ │ │ │ │ ├── DuneCompiler.java │ │ │ │ │ │ ├── DuneOutputAnalyzer.java │ │ │ │ │ │ ├── DunePlatform.java │ │ │ │ │ │ └── DuneProcess.java │ │ │ │ │ ├── esy/ │ │ │ │ │ │ ├── Esy.java │ │ │ │ │ │ ├── EsyCompiler.java │ │ │ │ │ │ ├── EsyConstants.java │ │ │ │ │ │ ├── EsyNotification.java │ │ │ │ │ │ ├── EsyPackageJson.java │ │ │ │ │ │ ├── EsyPlatform.java │ │ │ │ │ │ └── EsyProcess.java │ │ │ │ │ ├── ocaml/ │ │ │ │ │ │ ├── OcamlFormatProcess.java │ │ │ │ │ │ ├── OpamCommandLine.java │ │ │ │ │ │ ├── OpamEnv.java │ │ │ │ │ │ └── OpamProcess.java │ │ │ │ │ └── rescript/ │ │ │ │ │ ├── ResCompiler.java │ │ │ │ │ ├── ResConfigReader.java │ │ │ │ │ ├── ResFormatProcess.java │ │ │ │ │ ├── ResPlatform.java │ │ │ │ │ ├── ResProcessHandler.java │ │ │ │ │ ├── ResResolvedCompiler.java │ │ │ │ │ └── RescriptOutputAnalyzer.java │ │ │ │ ├── hints/ │ │ │ │ │ ├── InsightManager.java │ │ │ │ │ ├── Rincewind.java │ │ │ │ │ ├── RincewindDownloader.java │ │ │ │ │ └── RincewindProcess.java │ │ │ │ ├── ide/ │ │ │ │ │ ├── CompileOnSave.java │ │ │ │ │ ├── EditorPosition.java │ │ │ │ │ ├── IconProvider.java │ │ │ │ │ ├── OREditorFactoryListener.java │ │ │ │ │ ├── OREditorTracker.java │ │ │ │ │ ├── ORFileDocumentListener.java │ │ │ │ │ ├── ORFileUtils.java │ │ │ │ │ ├── ORIcons.java │ │ │ │ │ ├── ORPostStartupActivity.java │ │ │ │ │ ├── ORVirtualFileListener.java │ │ │ │ │ ├── actions/ │ │ │ │ │ │ ├── ConvertAction.java │ │ │ │ │ │ └── TransformAction.java │ │ │ │ │ ├── annotations/ │ │ │ │ │ │ ├── BsErrorAnnotator.java │ │ │ │ │ │ ├── ErrorFileHighlighter.java │ │ │ │ │ │ ├── ErrorsManager.java │ │ │ │ │ │ ├── ORErrorAnnotator.java │ │ │ │ │ │ ├── OutputInfo.java │ │ │ │ │ │ └── ResErrorAnnotator.java │ │ │ │ │ ├── comment/ │ │ │ │ │ │ ├── DuneCommenter.java │ │ │ │ │ │ ├── MlyCommenter.java │ │ │ │ │ │ ├── OclCommenter.java │ │ │ │ │ │ └── RmlCommenter.java │ │ │ │ │ ├── console/ │ │ │ │ │ │ ├── ClearLogAction.java │ │ │ │ │ │ ├── CompilerAction.java │ │ │ │ │ │ ├── ORConsoleFilter.java │ │ │ │ │ │ ├── ORConsoleFilterProvider.java │ │ │ │ │ │ ├── ORToolWindowFactory.java │ │ │ │ │ │ ├── ORToolWindowManager.java │ │ │ │ │ │ ├── bs/ │ │ │ │ │ │ │ ├── BsConsoleView.java │ │ │ │ │ │ │ ├── BsMakeAction.java │ │ │ │ │ │ │ ├── BsMakeWorldAction.java │ │ │ │ │ │ │ └── BsToolWindowFactory.java │ │ │ │ │ │ ├── dune/ │ │ │ │ │ │ │ ├── DuneBuildAction.java │ │ │ │ │ │ │ ├── DuneCleanAction.java │ │ │ │ │ │ │ ├── DuneConsoleView.java │ │ │ │ │ │ │ ├── DuneToolWindowFactory.java │ │ │ │ │ │ │ └── OCamlConsoleFilter.java │ │ │ │ │ │ ├── esy/ │ │ │ │ │ │ │ ├── EsyBuildAction.java │ │ │ │ │ │ │ ├── EsyConsoleView.java │ │ │ │ │ │ │ └── EsyToolWindowFactory.java │ │ │ │ │ │ └── rescript/ │ │ │ │ │ │ ├── RescriptBuildAction.java │ │ │ │ │ │ ├── RescriptConsoleFilter.java │ │ │ │ │ │ ├── RescriptConsoleView.java │ │ │ │ │ │ ├── RescriptResetAction.java │ │ │ │ │ │ └── RescriptToolWindowFactory.java │ │ │ │ │ ├── debug/ │ │ │ │ │ │ ├── OCamlApplicationConfiguration.java │ │ │ │ │ │ ├── OCamlApplicationRunningState.java │ │ │ │ │ │ ├── OCamlDebugRunner.java │ │ │ │ │ │ ├── OCamlModuleBasedConfiguration.java │ │ │ │ │ │ ├── ORLineBreakpointProperties.java │ │ │ │ │ │ ├── ORLineBreakpointType.java │ │ │ │ │ │ └── OclDebuggerRunnerSettings.java │ │ │ │ │ ├── docs/ │ │ │ │ │ │ ├── DocFormatter.java │ │ │ │ │ │ └── ORDocumentationProvider.java │ │ │ │ │ ├── editors/ │ │ │ │ │ │ ├── CmtFileEditor.java │ │ │ │ │ │ ├── CmtFileEditorProvider.java │ │ │ │ │ │ └── CmtXmlComponent.java │ │ │ │ │ ├── files/ │ │ │ │ │ │ ├── CmtFileType.java │ │ │ │ │ │ ├── DuneFile.java │ │ │ │ │ │ ├── DuneFileType.java │ │ │ │ │ │ ├── FileBase.java │ │ │ │ │ │ ├── Ml4File.java │ │ │ │ │ │ ├── Ml4FileType.java │ │ │ │ │ │ ├── MlgFile.java │ │ │ │ │ │ ├── MlgFileType.java │ │ │ │ │ │ ├── MlgFileViewProvider.java │ │ │ │ │ │ ├── MlgFileViewProviderFactory.java │ │ │ │ │ │ ├── MllFile.java │ │ │ │ │ │ ├── MllFileType.java │ │ │ │ │ │ ├── MllFileViewProvider.java │ │ │ │ │ │ ├── MllFileViewProviderFactory.java │ │ │ │ │ │ ├── MlyFile.java │ │ │ │ │ │ ├── MlyFileType.java │ │ │ │ │ │ ├── MlyFileViewProvider.java │ │ │ │ │ │ ├── MlyFileViewProviderFactory.java │ │ │ │ │ │ ├── ORConfigJsonFileType.java │ │ │ │ │ │ ├── ORTargetElementEvaluator.java │ │ │ │ │ │ ├── OclFile.java │ │ │ │ │ │ ├── OclFileType.java │ │ │ │ │ │ ├── OclInterfaceFile.java │ │ │ │ │ │ ├── OclInterfaceFileType.java │ │ │ │ │ │ ├── ResFile.java │ │ │ │ │ │ ├── ResFileType.java │ │ │ │ │ │ ├── ResInterfaceFile.java │ │ │ │ │ │ ├── ResInterfaceFileType.java │ │ │ │ │ │ ├── RmlFile.java │ │ │ │ │ │ ├── RmlFileType.java │ │ │ │ │ │ ├── RmlInterfaceFile.java │ │ │ │ │ │ └── RmlInterfaceFileType.java │ │ │ │ │ ├── folding/ │ │ │ │ │ │ ├── DuneFoldingBuilder.java │ │ │ │ │ │ └── ORFoldingBuilder.java │ │ │ │ │ ├── format/ │ │ │ │ │ │ ├── FormatterProcessor.java │ │ │ │ │ │ ├── ORPostFormatProcessor.java │ │ │ │ │ │ └── ReformatOnSave.java │ │ │ │ │ ├── go/ │ │ │ │ │ │ ├── FileModuleDataModuleRendererFactory.java │ │ │ │ │ │ ├── ORLineMarkerProvider.java │ │ │ │ │ │ └── ORModuleContributor.java │ │ │ │ │ ├── handlers/ │ │ │ │ │ │ ├── ORTypedHandler.java │ │ │ │ │ │ ├── OclQuoteHandler.java │ │ │ │ │ │ ├── ResQuoteHandler.java │ │ │ │ │ │ └── RmlQuoteHandler.java │ │ │ │ │ ├── highlight/ │ │ │ │ │ │ ├── DuneSyntaxAnnotator.java │ │ │ │ │ │ ├── DuneSyntaxHighlighter.java │ │ │ │ │ │ ├── DuneSyntaxHighlighterFactory.java │ │ │ │ │ │ ├── ORSyntaxAnnotator.java │ │ │ │ │ │ ├── ORSyntaxHighlighter.java │ │ │ │ │ │ ├── OclGrammarEditorHighlighter.java │ │ │ │ │ │ ├── OclGrammarSyntaxHighlighterFactory.java │ │ │ │ │ │ ├── OclLexEditorHighlighter.java │ │ │ │ │ │ ├── OclLexSyntaxHighlighterFactory.java │ │ │ │ │ │ ├── OclSyntaxAnnotator.java │ │ │ │ │ │ ├── OclSyntaxHighlighterFactory.java │ │ │ │ │ │ ├── OclYaccEditorHighlighter.java │ │ │ │ │ │ ├── OclYaccSyntaxHighlighterFactory.java │ │ │ │ │ │ ├── ResSyntaxAnnotator.java │ │ │ │ │ │ ├── ResSyntaxHighlighterFactory.java │ │ │ │ │ │ ├── RmlSyntaxAnnotator.java │ │ │ │ │ │ └── RmlSyntaxHighlighterFactory.java │ │ │ │ │ ├── hints/ │ │ │ │ │ │ ├── CodeLens.java │ │ │ │ │ │ ├── InferredTypes.java │ │ │ │ │ │ ├── InferredTypesImplementation.java │ │ │ │ │ │ ├── InferredTypesService.java │ │ │ │ │ │ ├── OREditorLinePainter.java │ │ │ │ │ │ ├── ORParameterInfoHandler.java │ │ │ │ │ │ ├── OclParameterInfoHandler.java │ │ │ │ │ │ ├── ResParameterInfoHandler.java │ │ │ │ │ │ ├── RmlParameterInfoHandler.java │ │ │ │ │ │ ├── RmlTypeProvider.java │ │ │ │ │ │ └── SignatureProvider.java │ │ │ │ │ ├── importWizard/ │ │ │ │ │ │ ├── DuneExternalConstants.java │ │ │ │ │ │ ├── DuneProjectImportBuilder.java │ │ │ │ │ │ ├── DuneProjectImportProvider.java │ │ │ │ │ │ ├── DuneProjectOpenProcessor.java │ │ │ │ │ │ ├── DuneProjectRootStep.form │ │ │ │ │ │ ├── DuneProjectRootStep.java │ │ │ │ │ │ └── ImportedDuneBuild.java │ │ │ │ │ ├── insight/ │ │ │ │ │ │ ├── CompletionUtils.java │ │ │ │ │ │ ├── KeywordCompletionContributor.java │ │ │ │ │ │ ├── ORCompletionContributor.java │ │ │ │ │ │ ├── OclCompletionContributor.java │ │ │ │ │ │ ├── OclKeywordCompletionContributor.java │ │ │ │ │ │ ├── ResCompletionContributor.java │ │ │ │ │ │ ├── ResKeywordCompletionContributor.java │ │ │ │ │ │ ├── RmlCompletionContributor.java │ │ │ │ │ │ ├── RmlKeywordCompletionContributor.java │ │ │ │ │ │ ├── pattern/ │ │ │ │ │ │ │ └── ORElementPatternMatcher.java │ │ │ │ │ │ └── provider/ │ │ │ │ │ │ ├── DotExpressionCompletionProvider.java │ │ │ │ │ │ ├── FreeExpressionCompletionProvider.java │ │ │ │ │ │ ├── JsxAttributeCompletionProvider.java │ │ │ │ │ │ ├── JsxNameCompletionProvider.java │ │ │ │ │ │ ├── ModuleCompletionProvider.java │ │ │ │ │ │ └── ObjectCompletionProvider.java │ │ │ │ │ ├── intentions/ │ │ │ │ │ │ ├── ExpandLocalOpenIntention.java │ │ │ │ │ │ └── FunctionBracesIntention.java │ │ │ │ │ ├── js/ │ │ │ │ │ │ ├── JsIconProvider.java │ │ │ │ │ │ ├── JsInjector.java │ │ │ │ │ │ ├── ORIndexableFileNamesProvider.java │ │ │ │ │ │ └── ORJsLibraryManager.java │ │ │ │ │ ├── library/ │ │ │ │ │ │ ├── OclLibraryKind.java │ │ │ │ │ │ ├── OclLibraryRootProvider.java │ │ │ │ │ │ ├── OclLibraryRootsComponentDescriptor.java │ │ │ │ │ │ ├── OclLibraryType.java │ │ │ │ │ │ └── OclSdkSetupValidator.java │ │ │ │ │ ├── match/ │ │ │ │ │ │ ├── DunePairedBraceMatcher.java │ │ │ │ │ │ ├── OclPairedBraceMatcher.java │ │ │ │ │ │ ├── ResPairedBraceMatcher.java │ │ │ │ │ │ └── RmlPairedBraceMatcher.java │ │ │ │ │ ├── module/ │ │ │ │ │ │ ├── DuneModuleEditor.form │ │ │ │ │ │ ├── DuneModuleEditor.java │ │ │ │ │ │ └── DuneModuleEditorProvider.java │ │ │ │ │ ├── refactor/ │ │ │ │ │ │ └── ORRefactoringSupportProvider.java │ │ │ │ │ ├── repl/ │ │ │ │ │ │ ├── PromptConsole.java │ │ │ │ │ │ ├── PromptConsoleView.java │ │ │ │ │ │ ├── PromptHistory.java │ │ │ │ │ │ ├── ReplConfigurationFactory.java │ │ │ │ │ │ ├── ReplGenericState.java │ │ │ │ │ │ ├── ReplRunConfiguration.java │ │ │ │ │ │ ├── ReplRunConfigurationType.java │ │ │ │ │ │ ├── ReplRunSettingsEditor.form │ │ │ │ │ │ └── ReplRunSettingsEditor.java │ │ │ │ │ ├── search/ │ │ │ │ │ │ ├── FileModuleData.java │ │ │ │ │ │ ├── IndexedFileModule.java │ │ │ │ │ │ ├── ModuleIndexService.java │ │ │ │ │ │ ├── ORFindUsagesProvider.java │ │ │ │ │ │ ├── ORImplementationSearch.java │ │ │ │ │ │ ├── OclFindUsagesProvider.java │ │ │ │ │ │ ├── PsiTypeElementProvider.java │ │ │ │ │ │ ├── ResFindUsagesProvider.java │ │ │ │ │ │ ├── RmlFindUsagesProvider.java │ │ │ │ │ │ ├── index/ │ │ │ │ │ │ │ ├── ClassFqnIndex.java │ │ │ │ │ │ │ ├── ClassMethodFqnIndex.java │ │ │ │ │ │ │ ├── ExceptionFqnIndex.java │ │ │ │ │ │ │ ├── ExceptionIndex.java │ │ │ │ │ │ │ ├── ExternalFqnIndex.java │ │ │ │ │ │ │ ├── ExternalIndex.java │ │ │ │ │ │ │ ├── FileModuleIndex.java │ │ │ │ │ │ │ ├── FileModuleIndexService.java │ │ │ │ │ │ │ ├── IncludeIndex.java │ │ │ │ │ │ │ ├── IndexKeys.java │ │ │ │ │ │ │ ├── LetComponentFqnIndex.java │ │ │ │ │ │ │ ├── LetFqnIndex.java │ │ │ │ │ │ │ ├── ModuleFqnIndex.java │ │ │ │ │ │ │ ├── ModuleIndex.java │ │ │ │ │ │ │ ├── ModuleSignatureIndex.java │ │ │ │ │ │ │ ├── NamespaceIndex.java │ │ │ │ │ │ │ ├── ObjectFieldIndex.java │ │ │ │ │ │ │ ├── OpenIndex.java │ │ │ │ │ │ │ ├── ParameterFqnIndex.java │ │ │ │ │ │ │ ├── ParameterIndex.java │ │ │ │ │ │ │ ├── RecordFieldIndex.java │ │ │ │ │ │ │ ├── TypeFqnIndex.java │ │ │ │ │ │ │ ├── TypeIndex.java │ │ │ │ │ │ │ ├── ValFqnIndex.java │ │ │ │ │ │ │ └── VariantFqnIndex.java │ │ │ │ │ │ └── reference/ │ │ │ │ │ │ ├── ORFakeResolvedElement.java │ │ │ │ │ │ ├── ORModuleResolutionPsiGist.java │ │ │ │ │ │ ├── ORMultiSymbolReference.java │ │ │ │ │ │ ├── ORPsiLiteralStringReference.java │ │ │ │ │ │ ├── ORPsiLowerSymbolReference.java │ │ │ │ │ │ ├── ORPsiPropertyNameReference.java │ │ │ │ │ │ ├── ORPsiUpperSymbolReference.java │ │ │ │ │ │ └── ORReferenceAnalyzer.java │ │ │ │ │ ├── settings/ │ │ │ │ │ │ ├── DuneColorSettingsPage.java │ │ │ │ │ │ ├── ORColorSettingsPage.java │ │ │ │ │ │ ├── ORSettings.java │ │ │ │ │ │ ├── ORSettingsConfigurable.form │ │ │ │ │ │ ├── ORSettingsConfigurable.java │ │ │ │ │ │ ├── OpamConfigurationTab.form │ │ │ │ │ │ └── OpamConfigurationTab.java │ │ │ │ │ ├── spellcheckers/ │ │ │ │ │ │ ├── ORSpellCheckerStrategy.java │ │ │ │ │ │ ├── OclSpellCheckerStrategy.java │ │ │ │ │ │ ├── ResSpellCheckerStrategy.java │ │ │ │ │ │ └── RmlSpellCheckerStrategy.java │ │ │ │ │ ├── structure/ │ │ │ │ │ │ ├── NestedFunctionsFilter.java │ │ │ │ │ │ ├── ORStructureViewModel.java │ │ │ │ │ │ ├── ShowVariableFilter.java │ │ │ │ │ │ ├── StructureViewElement.java │ │ │ │ │ │ └── StructureViewFactory.java │ │ │ │ │ ├── template/ │ │ │ │ │ │ ├── OCamlBaseLiveTemplateContextType.java │ │ │ │ │ │ ├── OCamlCodeLiveTemplateContextType.java │ │ │ │ │ │ ├── OCamlCommentLiveTemplateContextType.java │ │ │ │ │ │ ├── ResBaseLiveTemplateContextType.java │ │ │ │ │ │ └── RmlBaseLiveTemplateContextType.java │ │ │ │ │ └── testAssistant/ │ │ │ │ │ ├── GotoTestDataAction.java │ │ │ │ │ └── TestDataNavigationHandler.java │ │ │ │ └── lang/ │ │ │ │ ├── CommonPsiParser.java │ │ │ │ ├── Marker.java │ │ │ │ ├── ModuleHelper.java │ │ │ │ ├── ORLanguageParser.java │ │ │ │ ├── ORLanguageProperties.java │ │ │ │ ├── ORParser.java │ │ │ │ ├── core/ │ │ │ │ │ ├── LiteralStringManipulator.java │ │ │ │ │ ├── ORCodeFactory.java │ │ │ │ │ ├── ORUtil.java │ │ │ │ │ ├── psi/ │ │ │ │ │ │ ├── RPsiClass.java │ │ │ │ │ │ ├── RPsiClassMethod.java │ │ │ │ │ │ ├── RPsiConditional.java │ │ │ │ │ │ ├── RPsiException.java │ │ │ │ │ │ ├── RPsiExternal.java │ │ │ │ │ │ ├── RPsiField.java │ │ │ │ │ │ ├── RPsiFunctor.java │ │ │ │ │ │ ├── RPsiInclude.java │ │ │ │ │ │ ├── RPsiInferredType.java │ │ │ │ │ │ ├── RPsiInnerModule.java │ │ │ │ │ │ ├── RPsiLanguageConverter.java │ │ │ │ │ │ ├── RPsiLet.java │ │ │ │ │ │ ├── RPsiModule.java │ │ │ │ │ │ ├── RPsiOCamlInjection.java │ │ │ │ │ │ ├── RPsiOpen.java │ │ │ │ │ │ ├── RPsiParameterDeclaration.java │ │ │ │ │ │ ├── RPsiQualifiedPathElement.java │ │ │ │ │ │ ├── RPsiRecordField.java │ │ │ │ │ │ ├── RPsiSignature.java │ │ │ │ │ │ ├── RPsiSignatureElement.java │ │ │ │ │ │ ├── RPsiSignatureItem.java │ │ │ │ │ │ ├── RPsiSignatureUtil.java │ │ │ │ │ │ ├── RPsiStructuredElement.java │ │ │ │ │ │ ├── RPsiType.java │ │ │ │ │ │ ├── RPsiVal.java │ │ │ │ │ │ ├── RPsiVar.java │ │ │ │ │ │ ├── impl/ │ │ │ │ │ │ │ ├── ORASTFactory.java │ │ │ │ │ │ │ ├── RPsiAnnotation.java │ │ │ │ │ │ │ ├── RPsiArray.java │ │ │ │ │ │ │ ├── RPsiAssert.java │ │ │ │ │ │ │ ├── RPsiBinaryCondition.java │ │ │ │ │ │ │ ├── RPsiClassConstructor.java │ │ │ │ │ │ │ ├── RPsiClassField.java │ │ │ │ │ │ │ ├── RPsiClassImpl.java │ │ │ │ │ │ │ ├── RPsiClassInitializer.java │ │ │ │ │ │ │ ├── RPsiClassMethodImpl.java │ │ │ │ │ │ │ ├── RPsiConstraints.java │ │ │ │ │ │ │ ├── RPsiDeconstruction.java │ │ │ │ │ │ │ ├── RPsiDefaultValue.java │ │ │ │ │ │ │ ├── RPsiDirective.java │ │ │ │ │ │ │ ├── RPsiDuneField.java │ │ │ │ │ │ │ ├── RPsiDuneFields.java │ │ │ │ │ │ │ ├── RPsiDuneSExpr.java │ │ │ │ │ │ │ ├── RPsiDuneStanza.java │ │ │ │ │ │ │ ├── RPsiDuneVar.java │ │ │ │ │ │ │ ├── RPsiExceptionImpl.java │ │ │ │ │ │ │ ├── RPsiExternalImpl.java │ │ │ │ │ │ │ ├── RPsiFieldValue.java │ │ │ │ │ │ │ ├── RPsiFirstClass.java │ │ │ │ │ │ │ ├── RPsiForLoop.java │ │ │ │ │ │ │ ├── RPsiFunSwitch.java │ │ │ │ │ │ │ ├── RPsiFunction.java │ │ │ │ │ │ │ ├── RPsiFunctionBody.java │ │ │ │ │ │ │ ├── RPsiFunctionCall.java │ │ │ │ │ │ │ ├── RPsiFunctorBinding.java │ │ │ │ │ │ │ ├── RPsiFunctorCall.java │ │ │ │ │ │ │ ├── RPsiFunctorImpl.java │ │ │ │ │ │ │ ├── RPsiFunctorResult.java │ │ │ │ │ │ │ ├── RPsiGuard.java │ │ │ │ │ │ │ ├── RPsiIfStatement.java │ │ │ │ │ │ │ ├── RPsiIncludeImpl.java │ │ │ │ │ │ │ ├── RPsiInherit.java │ │ │ │ │ │ │ ├── RPsiInnerModuleImpl.java │ │ │ │ │ │ │ ├── RPsiInterpolation.java │ │ │ │ │ │ │ ├── RPsiInterpolationReference.java │ │ │ │ │ │ │ ├── RPsiJsObject.java │ │ │ │ │ │ │ ├── RPsiLeafPropertyName.java │ │ │ │ │ │ │ ├── RPsiLetAttribute.java │ │ │ │ │ │ │ ├── RPsiLetBinding.java │ │ │ │ │ │ │ ├── RPsiLetImpl.java │ │ │ │ │ │ │ ├── RPsiLiteralString.java │ │ │ │ │ │ │ ├── RPsiLocalOpen.java │ │ │ │ │ │ │ ├── RPsiLowerName.java │ │ │ │ │ │ │ ├── RPsiLowerSymbol.java │ │ │ │ │ │ │ ├── RPsiMacro.java │ │ │ │ │ │ │ ├── RPsiMacroBody.java │ │ │ │ │ │ │ ├── RPsiMacroName.java │ │ │ │ │ │ │ ├── RPsiMethodCall.java │ │ │ │ │ │ │ ├── RPsiMixinField.java │ │ │ │ │ │ │ ├── RPsiModuleBinding.java │ │ │ │ │ │ │ ├── RPsiModuleSignature.java │ │ │ │ │ │ │ ├── RPsiMultiLineInterpolator.java │ │ │ │ │ │ │ ├── RPsiObject.java │ │ │ │ │ │ │ ├── RPsiObjectField.java │ │ │ │ │ │ │ ├── RPsiOpenImpl.java │ │ │ │ │ │ │ ├── RPsiOption.java │ │ │ │ │ │ │ ├── RPsiOptionValue.java │ │ │ │ │ │ │ ├── RPsiParameterDeclarationImpl.java │ │ │ │ │ │ │ ├── RPsiParameterReference.java │ │ │ │ │ │ │ ├── RPsiParameters.java │ │ │ │ │ │ │ ├── RPsiPatternMatch.java │ │ │ │ │ │ │ ├── RPsiPatternMatchBody.java │ │ │ │ │ │ │ ├── RPsiPolyVariantConstraint.java │ │ │ │ │ │ │ ├── RPsiRecord.java │ │ │ │ │ │ │ ├── RPsiRecordFieldImpl.java │ │ │ │ │ │ │ ├── RPsiScopedExpr.java │ │ │ │ │ │ │ ├── RPsiSignatureImpl.java │ │ │ │ │ │ │ ├── RPsiSignatureItemImpl.java │ │ │ │ │ │ │ ├── RPsiStruct.java │ │ │ │ │ │ │ ├── RPsiSwitch.java │ │ │ │ │ │ │ ├── RPsiSwitchBody.java │ │ │ │ │ │ │ ├── RPsiTag.java │ │ │ │ │ │ │ ├── RPsiTagBody.java │ │ │ │ │ │ │ ├── RPsiTagClose.java │ │ │ │ │ │ │ ├── RPsiTagProperty.java │ │ │ │ │ │ │ ├── RPsiTagPropertyValue.java │ │ │ │ │ │ │ ├── RPsiTagStart.java │ │ │ │ │ │ │ ├── RPsiTernary.java │ │ │ │ │ │ │ ├── RPsiTokenStub.java │ │ │ │ │ │ │ ├── RPsiTry.java │ │ │ │ │ │ │ ├── RPsiTryBody.java │ │ │ │ │ │ │ ├── RPsiTryHandler.java │ │ │ │ │ │ │ ├── RPsiTryHandlerBody.java │ │ │ │ │ │ │ ├── RPsiTuple.java │ │ │ │ │ │ │ ├── RPsiTypeBinding.java │ │ │ │ │ │ │ ├── RPsiTypeConstraint.java │ │ │ │ │ │ │ ├── RPsiTypeImpl.java │ │ │ │ │ │ │ ├── RPsiUnit.java │ │ │ │ │ │ │ ├── RPsiUnpack.java │ │ │ │ │ │ │ ├── RPsiUpperSymbol.java │ │ │ │ │ │ │ ├── RPsiUpperTagName.java │ │ │ │ │ │ │ ├── RPsiValImpl.java │ │ │ │ │ │ │ ├── RPsiVariantDeclaration.java │ │ │ │ │ │ │ └── RPsiWhile.java │ │ │ │ │ │ ├── ocamlgrammar/ │ │ │ │ │ │ │ ├── RPsiGrammarArgument.java │ │ │ │ │ │ │ ├── RPsiGrammarGrammar.java │ │ │ │ │ │ │ ├── RPsiGrammarTactic.java │ │ │ │ │ │ │ └── RPsiGrammarVernac.java │ │ │ │ │ │ ├── ocamllex/ │ │ │ │ │ │ │ ├── RPsiLexLet.java │ │ │ │ │ │ │ ├── RPsiLexPattern.java │ │ │ │ │ │ │ └── RPsiLexRule.java │ │ │ │ │ │ └── ocamlyacc/ │ │ │ │ │ │ ├── RPsiYaccDeclaration.java │ │ │ │ │ │ ├── RPsiYaccHeader.java │ │ │ │ │ │ ├── RPsiYaccRule.java │ │ │ │ │ │ └── RPsiYaccRuleBody.java │ │ │ │ │ ├── stub/ │ │ │ │ │ │ ├── OclFileStub.java │ │ │ │ │ │ ├── OclStubBasedElementTypes.java │ │ │ │ │ │ ├── PsiExceptionStub.java │ │ │ │ │ │ ├── PsiExternalStub.java │ │ │ │ │ │ ├── PsiIncludeStub.java │ │ │ │ │ │ ├── PsiLetStub.java │ │ │ │ │ │ ├── PsiModuleStub.java │ │ │ │ │ │ ├── PsiObjectFieldStub.java │ │ │ │ │ │ ├── PsiOpenStub.java │ │ │ │ │ │ ├── PsiParameterDeclarationStub.java │ │ │ │ │ │ ├── PsiQualifiedNameStub.java │ │ │ │ │ │ ├── PsiTypeStub.java │ │ │ │ │ │ ├── PsiValStub.java │ │ │ │ │ │ ├── PsiVariantDeclarationStub.java │ │ │ │ │ │ ├── ResFileStub.java │ │ │ │ │ │ ├── ResStubBasedElementTypes.java │ │ │ │ │ │ ├── RmlFileStub.java │ │ │ │ │ │ ├── RmlStubBasedElementTypes.java │ │ │ │ │ │ ├── RsiClassMethodStub.java │ │ │ │ │ │ ├── RsiClassStub.java │ │ │ │ │ │ ├── RsiRecordFieldStub.java │ │ │ │ │ │ └── type/ │ │ │ │ │ │ ├── ORStubElementType.java │ │ │ │ │ │ ├── ORStubVersions.java │ │ │ │ │ │ ├── OclFileStubElementType.java │ │ │ │ │ │ ├── PsiExceptionStubElementType.java │ │ │ │ │ │ ├── PsiExternalStubElementType.java │ │ │ │ │ │ ├── PsiFunctorModuleStubElementType.java │ │ │ │ │ │ ├── PsiIncludeStubElementType.java │ │ │ │ │ │ ├── PsiInnerModuleStubElementType.java │ │ │ │ │ │ ├── PsiLetStubElementType.java │ │ │ │ │ │ ├── PsiModuleStubElementType.java │ │ │ │ │ │ ├── PsiObjectFieldStubElementType.java │ │ │ │ │ │ ├── PsiOpenStubElementType.java │ │ │ │ │ │ ├── PsiParameterDeclarationStubElementType.java │ │ │ │ │ │ ├── PsiTypeStubElementType.java │ │ │ │ │ │ ├── PsiValStubElementType.java │ │ │ │ │ │ ├── PsiVariantStubElementType.java │ │ │ │ │ │ ├── RPsiClassMethodStubElementType.java │ │ │ │ │ │ ├── RPsiClassStubElementType.java │ │ │ │ │ │ ├── RPsiRecordFieldStubElementType.java │ │ │ │ │ │ ├── ResFileStubElementType.java │ │ │ │ │ │ ├── RmlFileStubElementType.java │ │ │ │ │ │ └── SerializerUtil.java │ │ │ │ │ └── type/ │ │ │ │ │ ├── ORCompositeElementType.java │ │ │ │ │ ├── ORCompositePsiElement.java │ │ │ │ │ ├── ORCompositeType.java │ │ │ │ │ ├── ORLangTypes.java │ │ │ │ │ ├── ORTokenElementType.java │ │ │ │ │ ├── ORTypes.java │ │ │ │ │ └── ORTypesUtil.java │ │ │ │ ├── doc/ │ │ │ │ │ ├── ORDocConverter.java │ │ │ │ │ ├── ocaml/ │ │ │ │ │ │ ├── ODocLexer.java │ │ │ │ │ │ ├── OclDocConverter.java │ │ │ │ │ │ ├── OclDocLanguage.java │ │ │ │ │ │ ├── OclDocTypes.java │ │ │ │ │ │ └── odoc.flex │ │ │ │ │ └── reason/ │ │ │ │ │ ├── RDocLexer.java │ │ │ │ │ ├── RmlDocConverter.java │ │ │ │ │ ├── RmlDocLanguage.java │ │ │ │ │ ├── RmlDocTypes.java │ │ │ │ │ └── rdoc.flex │ │ │ │ ├── dune/ │ │ │ │ │ ├── Dune.flex │ │ │ │ │ ├── DuneASTFactory.java │ │ │ │ │ ├── DuneLanguage.java │ │ │ │ │ ├── DuneLexer.java │ │ │ │ │ ├── DuneParser.java │ │ │ │ │ ├── DuneParserDefinition.java │ │ │ │ │ └── DuneTypes.java │ │ │ │ ├── extra/ │ │ │ │ │ ├── OclP4Language.java │ │ │ │ │ └── OclP4ParserDefinition.java │ │ │ │ ├── ocaml/ │ │ │ │ │ ├── OCaml.flex │ │ │ │ │ ├── OCamlLexer.java │ │ │ │ │ ├── OclASTFactory.java │ │ │ │ │ ├── OclLanguage.java │ │ │ │ │ ├── OclLexer.java │ │ │ │ │ ├── OclParser.java │ │ │ │ │ ├── OclParserDefinition.java │ │ │ │ │ ├── OclSafeParserDefinition.java │ │ │ │ │ └── OclTypes.java │ │ │ │ ├── ocamlgrammar/ │ │ │ │ │ ├── OclGrammarAstFactory.java │ │ │ │ │ ├── OclGrammarElementType.java │ │ │ │ │ ├── OclGrammarLanguage.java │ │ │ │ │ ├── OclGrammarLexer.java │ │ │ │ │ ├── OclGrammarParser.java │ │ │ │ │ ├── OclGrammarParserDefinition.java │ │ │ │ │ ├── OclGrammarTypes.java │ │ │ │ │ └── mlg.flex │ │ │ │ ├── ocamllex/ │ │ │ │ │ ├── OclLexAstFactory.java │ │ │ │ │ ├── OclLexElementType.java │ │ │ │ │ ├── OclLexLanguage.java │ │ │ │ │ ├── OclLexLexer.java │ │ │ │ │ ├── OclLexParser.java │ │ │ │ │ ├── OclLexParserDefinition.java │ │ │ │ │ ├── OclLexTypes.java │ │ │ │ │ └── mll.flex │ │ │ │ ├── ocamlyacc/ │ │ │ │ │ ├── OclYaccAstFactory.java │ │ │ │ │ ├── OclYaccElementType.java │ │ │ │ │ ├── OclYaccLanguage.java │ │ │ │ │ ├── OclYaccLexer.java │ │ │ │ │ ├── OclYaccParser.java │ │ │ │ │ ├── OclYaccParserDefinition.java │ │ │ │ │ ├── OclYaccTypes.java │ │ │ │ │ └── mly.flex │ │ │ │ ├── reason/ │ │ │ │ │ ├── ReasonML.flex │ │ │ │ │ ├── ReasonMLLexer.java │ │ │ │ │ ├── RmlASTFactory.java │ │ │ │ │ ├── RmlLanguage.java │ │ │ │ │ ├── RmlLexer.java │ │ │ │ │ ├── RmlParser.java │ │ │ │ │ ├── RmlParserDefinition.java │ │ │ │ │ ├── RmlSafeParserDefinition.java │ │ │ │ │ └── RmlTypes.java │ │ │ │ └── rescript/ │ │ │ │ ├── ResASTFactory.java │ │ │ │ ├── ResFlexLexer.java │ │ │ │ ├── ResLanguage.java │ │ │ │ ├── ResLexer.java │ │ │ │ ├── ResParser.java │ │ │ │ ├── ResParserDefinition.java │ │ │ │ ├── ResSafeParserDefinition.java │ │ │ │ ├── ResTypes.java │ │ │ │ └── Rescript.flex │ │ │ └── jpsplugin/ │ │ │ └── com/ │ │ │ └── reason/ │ │ │ ├── AutoDeletingTempFile.java │ │ │ ├── FileUtil.java │ │ │ ├── Interrupted.java │ │ │ ├── Joiner.java │ │ │ ├── Log.java │ │ │ ├── OCamlExecutable.java │ │ │ ├── OClSourcesOrderRootTypeUIFactory.java │ │ │ ├── ORNotification.java │ │ │ ├── ORProcessTerminated.java │ │ │ ├── OclSourcesOrderRootType.java │ │ │ ├── Platform.java │ │ │ ├── Streams.java │ │ │ ├── StringUtil.java │ │ │ ├── WGet.java │ │ │ ├── sdk/ │ │ │ │ └── SdkDownloader.java │ │ │ └── wizard/ │ │ │ ├── OCamlModuleWizardStep.java │ │ │ └── WizardStepForm.form │ │ └── resources/ │ │ ├── META-INF/ │ │ │ ├── java-deps.xml │ │ │ ├── js-deps.xml │ │ │ └── plugin.xml │ │ ├── fileTemplates/ │ │ │ ├── OCaml Interface.mli.ft │ │ │ ├── OCaml Module.ml.ft │ │ │ ├── Reason Interface.rei.ft │ │ │ ├── Reason Module.re.ft │ │ │ ├── Rescript Interface.resi.ft │ │ │ └── Rescript Module.res.ft │ │ ├── icons/ │ │ │ ├── reasonml-icon-file.xcf │ │ │ └── reasonml-icon.xcf │ │ ├── intentionDescriptions/ │ │ │ ├── ExpandLocalOpenIntention/ │ │ │ │ ├── after.re.template │ │ │ │ ├── before.re.template │ │ │ │ └── description.html │ │ │ └── FunctionBracesIntention/ │ │ │ ├── after.re.template │ │ │ ├── before.re.template │ │ │ └── description.html │ │ └── liveTemplates/ │ │ ├── OCaml.xml │ │ ├── Reason.xml │ │ └── Rescript.xml │ └── test/ │ ├── java/ │ │ └── com/ │ │ └── reason/ │ │ ├── FileHelperTest.java │ │ ├── comp/ │ │ │ ├── bs/ │ │ │ │ ├── BsConfigReaderTest.java │ │ │ │ ├── BsLineProcessorTest.java │ │ │ │ ├── NinjaTest.java │ │ │ │ └── ResConfigReaderTest.java │ │ │ ├── dune/ │ │ │ │ └── DuneOutputAnalyzerTest.java │ │ │ ├── esy/ │ │ │ │ └── EsyPackageJsonTest.java │ │ │ ├── ocaml/ │ │ │ │ └── OpamProcessTest.java │ │ │ └── rescript/ │ │ │ ├── RescriptOutputAnalyzerTest.java │ │ │ └── error-syntax.txt │ │ ├── hints/ │ │ │ └── RincewindTest.java │ │ ├── ide/ │ │ │ ├── ORBasePlatformTestCase.java │ │ │ ├── ORFileUtilsTest.java │ │ │ ├── RenameLowerTest.java │ │ │ ├── RenameUpper_OCL_Test.java │ │ │ ├── RenameUpper_RES_Test.java │ │ │ ├── RenameUpper_RML_Test.java │ │ │ ├── TypeConversionOclTest.java │ │ │ ├── comment/ │ │ │ │ └── OclCommenterTest.java │ │ │ ├── completion/ │ │ │ │ ├── CommentCompletion_OCL_Test.java │ │ │ │ ├── CommentCompletion_RES_Test.java │ │ │ │ ├── CommentCompletion_RML_Test.java │ │ │ │ ├── DotCompletion_OCL_Test.java │ │ │ │ ├── DotCompletion_RES_Test.java │ │ │ │ ├── DotCompletion_RML_Test.java │ │ │ │ ├── FreeCompletion_OCL_Test.java │ │ │ │ ├── FreeCompletion_RES_Test.java │ │ │ │ ├── FreeCompletion_RML_Test.java │ │ │ │ ├── JsObjectCompletion_RES_Test.java │ │ │ │ ├── JsObjectCompletion_RML_Test.java │ │ │ │ ├── Jsx3NameCompletion_RES_Test.java │ │ │ │ ├── Jsx3NameCompletion_RML_Test.java │ │ │ │ ├── Jsx3PropertyCompletion_RES_Test.java │ │ │ │ ├── Jsx3PropertyCompletion_RML_Test.java │ │ │ │ ├── Jsx4PropertyCompletion_RES_Test.java │ │ │ │ ├── KeywordCompletion_OCL_Test.java │ │ │ │ ├── KeywordCompletion_RES_Test.java │ │ │ │ ├── KeywordCompletion_RML_Test.java │ │ │ │ ├── ModuleCompletion_OCL_Test.java │ │ │ │ ├── ModuleCompletion_RES_Test.java │ │ │ │ ├── ModuleCompletion_RML_Test.java │ │ │ │ ├── RecordCompletion_OCL_Test.java │ │ │ │ ├── RecordCompletion_RES_Test.java │ │ │ │ └── RecordCompletion_RML_Test.java │ │ │ ├── console/ │ │ │ │ ├── OCamlMessages.java │ │ │ │ ├── ocaml/ │ │ │ │ │ └── OCamlConsoleFilterTest.java │ │ │ │ └── rescript/ │ │ │ │ └── RescriptConsoleFilterTest.java │ │ │ ├── docs/ │ │ │ │ ├── ORDocumentationProviderTest.java │ │ │ │ ├── QuickDocRESTest.java │ │ │ │ ├── QuickDocRMLTest.java │ │ │ │ ├── ShowDocOCLTest.java │ │ │ │ ├── ShowDocRESTest.java │ │ │ │ └── ShowDocRMLTest.java │ │ │ ├── go/ │ │ │ │ ├── GotoImplementationOCLTest.java │ │ │ │ ├── GotoImplementationRESTest.java │ │ │ │ ├── GotoImplementationRMLTest.java │ │ │ │ ├── LineMarkerProviderOCLTest.java │ │ │ │ ├── LineMarkerProviderRESTest.java │ │ │ │ └── LineMarkerProviderRMLTest.java │ │ │ ├── hints/ │ │ │ │ ├── OclParameterInfoHandlerTest.java │ │ │ │ ├── ResParameterInfoHandlerTest.java │ │ │ │ └── RmlParameterInfoHandlerTest.java │ │ │ ├── intention/ │ │ │ │ ├── ExpandLocalOpenIntentionRMLTest.java │ │ │ │ ├── FunctionBracesIntentionRESTest.java │ │ │ │ └── FunctionBracesIntentionRMLTest.java │ │ │ ├── search/ │ │ │ │ └── reference/ │ │ │ │ ├── FindLIdentUsagesOCLTest.java │ │ │ │ ├── FindLIdentUsagesRESTest.java │ │ │ │ ├── FindLIdentUsagesRMLTest.java │ │ │ │ ├── FindUIdentUsagesOCLTest.java │ │ │ │ ├── FindUIdentUsagesRESTest.java │ │ │ │ ├── FindUIdentUsagesRMLTest.java │ │ │ │ ├── ORModuleResolutionPsiGist_OCL_Test.java │ │ │ │ ├── ORModuleResolutionPsiGist_RES_Test.java │ │ │ │ ├── ORModuleResolutionPsiGist_RML_Test.java │ │ │ │ ├── ResolveJsxPropertyElementRESTest.java │ │ │ │ ├── ResolveJsxPropertyElementRMLTest.java │ │ │ │ ├── ResolveJsxTagElementRESTest.java │ │ │ │ ├── ResolveJsxTagElementRMLTest.java │ │ │ │ ├── ResolveLowerElement_OCL_Test.java │ │ │ │ ├── ResolveLowerElement_RES_Test.java │ │ │ │ ├── ResolveLowerElement_RML_Test.java │ │ │ │ ├── ResolveUpperElement_OCL_Test.java │ │ │ │ ├── ResolveUpperElement_RES_Test.java │ │ │ │ └── ResolveUpperElement_RML_Test.java │ │ │ └── structure/ │ │ │ ├── ProjectStructure_DUNE_Test.java │ │ │ ├── ProjectStructure_OCL_Test.java │ │ │ └── ValPresentation_OCL_Test.java │ │ └── lang/ │ │ ├── BaseParsingTestCase.java │ │ ├── core/ │ │ │ ├── FileBaseTest.java │ │ │ ├── ORSignatureTest.java │ │ │ └── ORUtilTest.java │ │ ├── doc/ │ │ │ ├── ocaml/ │ │ │ │ └── OclDocConverterTest.java │ │ │ └── reason/ │ │ │ └── RmlDocConverterTest.java │ │ ├── dune/ │ │ │ ├── CommentParsingTest.java │ │ │ ├── DuneParsingTestCase.java │ │ │ ├── DuneStanzaParsingTest.java │ │ │ ├── DuneVarParsingTest.java │ │ │ └── SExprParsingTest.java │ │ ├── ocaml/ │ │ │ ├── AndParsingTest.java │ │ │ ├── AnnotationParsingTest.java │ │ │ ├── AssertParsingTest.java │ │ │ ├── BeginParsingTest.java │ │ │ ├── ChainingParsingTest.java │ │ │ ├── ClassParsingTest.java │ │ │ ├── CommentParsingTest.java │ │ │ ├── DirectiveParsingTest.java │ │ │ ├── ExceptionParsingTest.java │ │ │ ├── ExpressionsParsingTest.java │ │ │ ├── ExternalParsingTest.java │ │ │ ├── FirstClassModuleParsingTest.java │ │ │ ├── FunctionCallParsingTest.java │ │ │ ├── FunctionParsingTest.java │ │ │ ├── FunctorCallParsingTest.java │ │ │ ├── FunctorParsingTest.java │ │ │ ├── IfParsingTest.java │ │ │ ├── ImmediateObjectParsingTest.java │ │ │ ├── IncludeParsingTest.java │ │ │ ├── LetParsingTest.java │ │ │ ├── LocalOpenParsingTest.java │ │ │ ├── LoopParsingTest.java │ │ │ ├── MatchParsingTest.java │ │ │ ├── ModuleParsingTest.java │ │ │ ├── OclParsingTestCase.java │ │ │ ├── OpenParsingTest.java │ │ │ ├── OperatorParsingTest.java │ │ │ ├── PolyVariantParsingTest.java │ │ │ ├── RecordParsingTest.java │ │ │ ├── SamplesParsingTest.java │ │ │ ├── SignatureParsingTest.java │ │ │ ├── TryWithParsingTest.java │ │ │ ├── TypeParsingTest.java │ │ │ ├── ValParsingTest.java │ │ │ ├── VariantCallParsingTest.java │ │ │ └── VariantDeclarationParsingTest.java │ │ ├── reason/ │ │ │ ├── AndParsingTest.java │ │ │ ├── AnnotationParsingTest.java │ │ │ ├── AssertParsingTest.java │ │ │ ├── ClassParsingTest.java │ │ │ ├── CommentTest.java │ │ │ ├── ComponentJsx3ParsingTest.java │ │ │ ├── ExceptionParsingTest.java │ │ │ ├── ExternalParsingTest.java │ │ │ ├── FirstClassModuleParsingTest.java │ │ │ ├── FunParsingTest.java │ │ │ ├── FunctionCallParsingTest.java │ │ │ ├── FunctionParsingTest.java │ │ │ ├── FunctorCallParsingTest.java │ │ │ ├── FunctorParsingTest.java │ │ │ ├── IfParsingTest.java │ │ │ ├── IncludeParsingTest.java │ │ │ ├── JsObjectParsingTest.java │ │ │ ├── JsxParsingTest.java │ │ │ ├── LetParsingTest.java │ │ │ ├── LocalOpenParsingTest.java │ │ │ ├── MacroParsingTest.java │ │ │ ├── ModuleParsingTest.java │ │ │ ├── OpenParsingTest.java │ │ │ ├── PolyVariantParsingTest.java │ │ │ ├── PolyVariantTest.java │ │ │ ├── RecordParsingTest.java │ │ │ ├── RmlParsingTestCase.java │ │ │ ├── SamplesParsingTest.java │ │ │ ├── SignatureParsingTest.java │ │ │ ├── StringTemplateParsingTest.java │ │ │ ├── SwitchParsingTest.java │ │ │ ├── TryParsingTest.java │ │ │ ├── TypeParsingTest.java │ │ │ ├── VariantCallParsingTest.java │ │ │ └── VariantDeclarationParsingTest.java │ │ └── rescript/ │ │ ├── AndParsingTest.java │ │ ├── AnnotationParsingTest.java │ │ ├── AssertParsingTest.java │ │ ├── ChainingParsingTest.java │ │ ├── CommentTest.java │ │ ├── ComponentJsx3ParsingTest.java │ │ ├── ExceptionParsingTest.java │ │ ├── ExpressionChainingParsingTest.java │ │ ├── ExternalParsingTest.java │ │ ├── FileFromJarTest.java │ │ ├── FirstClassModuleParsingTest.java │ │ ├── FunctionCallParsingTest.java │ │ ├── FunctionParsingTest.java │ │ ├── FunctorCallParsingTest.java │ │ ├── FunctorParsingTest.java │ │ ├── IfParsingTest.java │ │ ├── IncludeParsingTest.java │ │ ├── JsObjectParsingTest.java │ │ ├── JsxParsingTest.java │ │ ├── LetParsingTest.java │ │ ├── LocalOpenParsingTest.java │ │ ├── MacroParsingTest.java │ │ ├── ModuleParsingTest.java │ │ ├── OpenParsingTest.java │ │ ├── PolyVariantParsingTest.java │ │ ├── RecordParsingTest.java │ │ ├── ResParsingTestCase.java │ │ ├── SignatureParsingTest.java │ │ ├── StringTemplateParsingTest.java │ │ ├── SwitchParsingTest.java │ │ ├── TryParsingTest.java │ │ ├── TypeParsingTest.java │ │ ├── VariantCallParsingTest.java │ │ └── VariantDeclarationParsingTest.java │ └── testData/ │ ├── com/ │ │ └── reason/ │ │ ├── bs/ │ │ │ ├── bsplatform.json │ │ │ ├── comments.json │ │ │ ├── deps.json │ │ │ ├── issue_214.json │ │ │ ├── ninja-rescript.build │ │ │ ├── src_array.json │ │ │ ├── src_object.json │ │ │ └── src_string.json │ │ ├── esy/ │ │ │ └── package.json │ │ └── lang/ │ │ ├── ReactDOM.res │ │ ├── component/ │ │ │ ├── AnotherComp.re │ │ │ ├── CompMessage.res │ │ │ ├── Component.re │ │ │ ├── Inner.re │ │ │ └── Inner.res │ │ ├── jsxDOMU.res │ │ ├── pervasives.ml │ │ └── samples/ │ │ ├── belt_Map.ml │ │ ├── stream.ml │ │ └── toto.re │ ├── icu4j/ │ │ └── mt.res │ └── ns/ │ └── bsconfig.json └── website/ ├── .gitignore ├── README.md ├── babel.config.js ├── docs/ │ ├── Other/ │ │ ├── faq.md │ │ └── live-templates.md │ ├── build-tools/ │ │ ├── _category_.json │ │ ├── bucklescript.md │ │ ├── dune.md │ │ └── esy.md │ ├── contributing/ │ │ ├── _category_.json │ │ ├── how-to-contribute.md │ │ ├── plugin-architecture.md │ │ ├── plugin-development.md │ │ └── resources.md │ ├── intro.md │ ├── language-support/ │ │ ├── _category_.json │ │ ├── ocaml.md │ │ ├── reason.md │ │ └── rescript.md │ └── project-types.md ├── docusaurus.config.js ├── package.json ├── sidebars.js ├── src/ │ ├── components/ │ │ └── HomepageFeatures/ │ │ ├── index.js │ │ └── styles.module.css │ ├── css/ │ │ └── custom.css │ └── pages/ │ ├── index.js │ └── index.module.css └── static/ ├── .nojekyll └── img/ └── arch/ ├── fake_module.excalidraw ├── parser.excalidraw ├── psihier.excalidraw ├── stub_keys.excalidraw ├── tokens.xcf └── types.excalidraw ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/FUNDING.md ================================================ github: giraud custom: "https://www.paypal.com/paypalme/rvgiraud" ================================================ FILE: .github/FUNDING.yml ================================================ github: giraud custom: "https://www.paypal.com/paypalme/rvgiraud" ================================================ FILE: .github/workflows/gradle.yml ================================================ # This workflow will build a Java project with Gradle # For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle name: Build Status on: push: branches: [ master ] pull_request: branches: [ master ] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Set up JDK 17 uses: actions/setup-java@v2 with: distribution: 'zulu' java-version: '17' - name: Grant execute permission for gradlew run: chmod +x gradlew - name: Build with Gradle run: ./gradlew build - name: Test with Gradle run: ./gradlew test ================================================ FILE: .github/workflows/website-deploy.yml ================================================ name: Deploy Website on: pull_request: branches: [master] push: branches: [master] jobs: checks: if: github.event_name != 'push' runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 - uses: actions/setup-node@v1 with: node-version: '18.x' - name: Test Build run: | cd website if [ -e yarn.lock ]; then yarn install --frozen-lockfile elif [ -e package-lock.json ]; then npm ci else npm i fi npm run build gh-release: if: github.event_name != 'pull_request' runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 - uses: actions/setup-node@v1 with: node-version: '18.x' - name: Add key to allow access to repository env: SSH_AUTH_SOCK: /tmp/ssh_agent.sock run: | mkdir -p ~/.ssh ssh-keyscan github.com >> ~/.ssh/known_hosts echo "${{ secrets.GH_PAGES_DEPLOY }}" > ~/.ssh/id_rsa chmod 600 ~/.ssh/id_rsa cat <> ~/.ssh/config Host github.com HostName github.com IdentityFile ~/.ssh/id_rsa EOT - name: Release to GitHub Pages env: USE_SSH: true GIT_USER: git run: | git config --global user.email "actions@github.com" git config --global user.name "gh-actions" cd website if [ -e yarn.lock ]; then yarn install --frozen-lockfile elif [ -e package-lock.json ]; then npm ci else npm i fi npx docusaurus deploy ================================================ FILE: .gitignore ================================================ .gradle/ !.idea/ .idea/* .intellijPlatform/* !.idea/codeStyles/ !.idea/google-java-format.xml out/ build/ *.iml idea-flex*.skeleton jflex-*.jar jps-plugin/build jps-plugin/out ================================================ FILE: CHANGELOG.md ================================================ # Changelog > **Tags:** > - :boom: [Breaking Change] > - :rocket: [New Feature] > - :bug: [Bug Fix] > - :memo: [Documentation] > - :house: [Internal] > - :nail_care: [Polish] (_Tags are copied from [babel](https://github.com/babel/babel/blob/master/CHANGELOG.md)_) # unreleased **NOTE**: minimal supported version is **2024.3** # 0.131 - 2025/09/01 - :bug: [#511](https://github.com/giraud/reasonml-idea-plugin/issues/511) Fix infinite indexing (parameters) # 0.130 - 2025/07/10 - :bug: Fix lower element resolution with typed parameter - :bug: ![n] Fix variant declaration with @as - :bug: Parse warning message in output analyzer - :bug: Fix anonymous function declaration in option - :bug: Fix constraints of module signature - :bug: Fix log level # 0.129 - 2025/04/14 - :bug: [#504](https://github.com/giraud/reasonml-idea-plugin/issues/504) Fix StackOverflow exception - :bug: [#476](https://github.com/giraud/reasonml-idea-plugin/issues/476) Better indexing of recursive declarations # 0.128 - 2025/02/18 - :bug: [#499](https://github.com/giraud/reasonml-idea-plugin/issues/499) Find usages of destructured elements - :bug: [#498](https://github.com/giraud/reasonml-idea-plugin/issues/498) Parser error with patterns and parenthesis - :bug: [#497](https://github.com/giraud/reasonml-idea-plugin/issues/497) Fix include inlined module - :bug: [#496](https://github.com/giraud/reasonml-idea-plugin/issues/496) Improve free completion of poly variants - :house: Remove 'function' keyword in Rescript lexer # 0.127 - 2025/01/14 - :bug: [#494](https://github.com/giraud/reasonml-idea-plugin/issues/494) JsonFileType exception in 2024.3 - :bug: ![o] [#492](https://github.com/giraud/reasonml-idea-plugin/issues/492) Ocaml parser error when using #end - :bug: [#490](https://github.com/giraud/reasonml-idea-plugin/issues/490) Object representation in structure panel - :bug: [#246](https://github.com/giraud/reasonml-idea-plugin/issues/246) Input in priority in autocomplete list **NOTE**: minimal supported version is **2024.1** # 0.126 - 2024/11/19 - :rocket: [#485](https://github.com/giraud/reasonml-idea-plugin/issues/485) Gutter icons on module signatures - :bug: [#487](https://github.com/giraud/reasonml-idea-plugin/issues/487) Ocaml setup on Mac OS (same than 480) - :bug: [#480](https://github.com/giraud/reasonml-idea-plugin/issues/480) No Opam switch detection - :house: update platform-gradle-plugin to v2 # 0.125 - 2024/08/26 **NOTE**: Added support for version **2024.3** - :bug: [#475](https://github.com/giraud/reasonml-idea-plugin/issues/475) Stack overflow - :rocket: [#473](https://github.com/giraud/reasonml-idea-plugin/issues/473) Enhancement: Structure panel: provide a filter setting to select whether to show variables - :bug: ![o] [#469](https://github.com/giraud/reasonml-idea-plugin/issues/469) '"' in comments confuses comment recognition - :bug: [#465](https://github.com/giraud/reasonml-idea-plugin/issues/465) Exception for action 'add braces to blockless function' - :nail_care: Fix incorrect file icons # 0.124 - 2024/05/02 - parsers updates, resolution updates - Completion of js objects in Rescript - Remove violations fonud by plugin verifier # 0.123 - 2024/03/28 - :bug: [#461](https://github.com/giraud/reasonml-idea-plugin/issues/461) Parameter signature element incorrectly referenced - :bug: [#460](https://github.com/giraud/reasonml-idea-plugin/issues/460) Keywords used as identifier - :bug: [#459](https://github.com/giraud/reasonml-idea-plugin/issues/459) Convert to light services - :bug: ![n] [#458](https://github.com/giraud/reasonml-idea-plugin/issues/458) Pipe first operator chaining not correctly parsed in rescript - :bug: ![o] [#456](https://github.com/giraud/reasonml-idea-plugin/issues/456) OCaml function parameters incorrectly parsed if using tuples - :bug: [#455](https://github.com/giraud/reasonml-idea-plugin/issues/455) Color scheme improvements request for records and functions (OCaml) - :bug: [#453](https://github.com/giraud/reasonml-idea-plugin/issues/453) Completion of records when using mixin - :bug: [#450](https://github.com/giraud/reasonml-idea-plugin/issues/450) Can’t rename a module ## 0.122 - 2024/03/11 - :bug: [#454](https://github.com/giraud/reasonml-idea-plugin/issues/454) Duplicate definitions for globally opened modules in completion - :bug: [#452](https://github.com/giraud/reasonml-idea-plugin/issues/452) Resolve unpacked module - :bug: ![n] [#451](https://github.com/giraud/reasonml-idea-plugin/issues/451) Module incorrectly parsed as functor in Rescript - :bug: [#449](https://github.com/giraud/reasonml-idea-plugin/issues/449) Navigation icon in the gutter referencing wrong module ## 0.121 - 2024/02/02 - :bug: ![o] [#445](https://github.com/giraud/reasonml-idea-plugin/issues/445) Fix jump to implementation - :bug: ![o] [#442](https://github.com/giraud/reasonml-idea-plugin/issues/442) Incorrect parsing of 'IN' in pattern matching - :bug: ![o] [#441](https://github.com/giraud/reasonml-idea-plugin/issues/441) Incorrect parsing of pattern matching ## 0.120 - 2024/01/11 **NOTE**: minimal supported version is **2023.1** - :bug: ![o] [#444](https://github.com/giraud/reasonml-idea-plugin/issues/444) Fix OCaml parser for 'inherit' - :bug: ![o] [#440](https://github.com/giraud/reasonml-idea-plugin/issues/440) Incorrect parsing of record (OCaml) - :bug: [#439](https://github.com/giraud/reasonml-idea-plugin/issues/439) Do not display 'let _' in structure panel - :bug: ![n] [#438](https://github.com/giraud/reasonml-idea-plugin/issues/438) Resolution of js object field references in Rescript - :bug: [#415](https://github.com/giraud/reasonml-idea-plugin/issues/415) Remove one level deep in structure view ## 0.119 - 2023/12/06 **NOTE**: minimal supported version is **2022.3** - :bug: [#437](https://github.com/giraud/reasonml-idea-plugin/issues/437) Incorrect language injection in macro - :house: Removing some TODOs in code - :house: Fix cast exception in ORModuleContributor - :house: Fix some parser errors ## 0.118.2 - 2023/11/21 - :rocket: Support for JSX 4 - :bug: [#436](https://github.com/giraud/reasonml-idea-plugin/issues/436) Macro parsing error - :bug: [#434](https://github.com/giraud/reasonml-idea-plugin/issues/435) Rescript 11 deprecate bsconfig.json, wants rescript.json - :bug: [#434](https://github.com/giraud/reasonml-idea-plugin/issues/434) Update rescript keywords - :bug: [#433](https://github.com/giraud/reasonml-idea-plugin/issues/433) Gutter icon in ml file pointing to mli file disappears when it shouldn't - :bug: [#430](https://github.com/giraud/reasonml-idea-plugin/issues/430) No SDK created when importing a dune project - :bug: [#423](https://github.com/giraud/reasonml-idea-plugin/issues/423) Incorrect parsing when 'ref' is used as identifier ## 0.117.1 - 2023/09/11 - :bug: [#429](https://github.com/giraud/reasonml-idea-plugin/issues/429) Structure panel shows "Unknown presentation ..." for "module X : sig ..." - :bug: [#428](https://github.com/giraud/reasonml-idea-plugin/issues/428) Webstorm error: No display name specified in plugin descriptor... - :bug: [#427](https://github.com/giraud/reasonml-idea-plugin/issues/427) Preview not working in 'find in files' - :bug: [#425](https://github.com/giraud/reasonml-idea-plugin/issues/425) Structure panel Some variables have the icon indicating a function ## 0.116 - 2023/08/31 - :bug: [#426](https://github.com/giraud/reasonml-idea-plugin/issues/426) Module alias must be resolved for resolution (same file) - :bug: ![n] [#424](https://github.com/giraud/reasonml-idea-plugin/issues/424) Incorrect ternary parsing in Rescript when using newline - :bug: [#422](https://github.com/giraud/reasonml-idea-plugin/issues/422) Plugin "failed to save settings and has been disabled" in PyCharm - :bug: ![o] [#421](https://github.com/giraud/reasonml-idea-plugin/issues/421) End of comments containing quotes not always detected - :bug: ![r] ![n] [#418](https://github.com/giraud/reasonml-idea-plugin/issues/418) Globally opened modules don’t participate in autocomplete resolution ## 0.115 - 2023/08/22 **NOTE**: Added support for version **2023.2** - :house: [#416](https://github.com/giraud/reasonml-idea-plugin/issues/416) Excessive CPU use - :bug: ![o] [#409](https://github.com/giraud/reasonml-idea-plugin/issues/409) Wrong structure on let b = 1 = 1 in (OCaml) ## 0.114 - 2023/07/04 (!) Jfrog is no longer serving binaries for rincewind, URL is changed and you must upgrade your plugin to this revision if you want to continue using it (type annotations) - :rocket: ![o] [#118](https://github.com/giraud/reasonml-idea-plugin/issues/118) Much better parsing of mlg files (ocaml injection, highlighting, folding) ## 0.113 - 2023/05/29 - :bug: ![n] [#414](https://github.com/giraud/reasonml-idea-plugin/issues/414) Extensible variant incorrectly parsed in Rescript - :bug: ![n] [#413](https://github.com/giraud/reasonml-idea-plugin/issues/413) Unpack is a keyword in rescript - :bug: ![o] [#412](https://github.com/giraud/reasonml-idea-plugin/issues/412) Scope incorrectly parsed in mll file - :bug: ![o] [#411](https://github.com/giraud/reasonml-idea-plugin/issues/411) Block comment operation (Ctrl-Shift-/) on highlighted text should not add a newline - :bug: [#410](https://github.com/giraud/reasonml-idea-plugin/issues/410) Goto icons not shown between module and module type in same file - :bug: ![o] [#409](https://github.com/giraud/reasonml-idea-plugin/issues/409) Wrong structure on let b = 1 = 1 in ## 0.112 - 2023/05/05 **NOTE**: minimal supported version is **2022.2** - :bug: ![o] [#409](https://github.com/giraud/reasonml-idea-plugin/issues/409) Wrong structure on let b = 1 = 1 in - :bug: ![o] [#408](https://github.com/giraud/reasonml-idea-plugin/issues/408) let ... in not shown in let a = ... structure - :bug: ![o] [#407](https://github.com/giraud/reasonml-idea-plugin/issues/407) Support operator (non-alphanum) declarations - :bug: ![o] [#406](https://github.com/giraud/reasonml-idea-plugin/issues/406) Faulty let open ... in parsing ## 0.111.1 - 2023/05/03 - :house: New reference resolution algorithm - :bug: ![o] [#405](https://github.com/giraud/reasonml-idea-plugin/issues/405) Bad parsing of if in method - :bug: [#350](https://github.com/giraud/reasonml-idea-plugin/issues/350) Use declaration documentation if no local documentation - :bug: [#319](https://github.com/giraud/reasonml-idea-plugin/issues/319) "Comment with line comment" action incorrect for lines ending with "(" - :bug: ![o] [#174](https://github.com/giraud/reasonml-idea-plugin/issues/174) Compiler output listener needs a change for ocaml >= 4.09 ## 0.110 - 2023/03/01 - :bug: ![n] [#399](https://github.com/giraud/reasonml-idea-plugin/issues/399) Rescript: incorrectly parse element as tag - :bug: ![n] [#384](https://github.com/giraud/reasonml-idea-plugin/issues/384) Rescript compile errors not being displayed on code ## 0.109.2 - 2023/01/24 **NOTE**: minimal supported version is **2022.1** - :bug: [#398](https://github.com/giraud/reasonml-idea-plugin/issues/398) Rescript format doesn’t respect UTF-8 encoding - 0.109.1 - :bug: [#392](https://github.com/giraud/reasonml-idea-plugin/issues/392) Attempting to open editorconfig causes IDE exception - :bug: [#391](https://github.com/giraud/reasonml-idea-plugin/issues/391) Dune comments are broken (lexer) - :bug: [#390](https://github.com/giraud/reasonml-idea-plugin/issues/390) Stub mismatch exceptions fixed ## 0.108 - 2022/12/01 **NOTE**: minimal supported version is **2021.3** - :nail_care: Display dune-project fields in structure view - :bug: [#389](https://github.com/giraud/reasonml-idea-plugin/issues/389) java.util.EmptyStackException - :bug: [#358](https://github.com/giraud/reasonml-idea-plugin/issues/358) Incorrect resolution with module in pattern - :bug: [#323](https://github.com/giraud/reasonml-idea-plugin/issues/323) Method declarations in .ml files should link to their implementations - :house: ![o] Parse ternary if in OCaml - :house: ![o] Fix illegal characters in dune files ## 0.107 - 2022/09/13 - :nail_care: fix missing color for fragment tag - :bug: [#379](https://github.com/giraud/reasonml-idea-plugin/issues/379) Plugin crashes with NoClassDefFoundError in WebStorm during any attempt to autocomplete - :house: reworked the parsers. Some resources files (potentially in binary form) inside jars are using the .res extension, but there is no possibility to detect they are not rescript files. The plugin now do not index files inside a jar archive. ## 0.106.1 - 2022/05/02 - :bug: [#379](https://github.com/giraud/reasonml-idea-plugin/issues/379) Plugin crashes with NoClassDefFoundError in WebStorm during any attempt to autocomplete - :bug: [#378](https://github.com/giraud/reasonml-idea-plugin/issues/378) NPE in macro - :boom: :rocket: New opam configuration, should allow other IDE to use dune projects. Dune facet is removed. - :bug: [#364](https://github.com/giraud/reasonml-idea-plugin/issues/364) StringIndexOutOfBoundsException ## 0.105.1 - 2021/11/12 Logo changed: it was too similar to the jetbrains plugins - :bug: [#362](https://github.com/giraud/reasonml-idea-plugin/issues/362) Check invalid file before compile (rescript execution exception) - :bug: ![o] [#360](https://github.com/giraud/reasonml-idea-plugin/issues/360) Should not display annotation signature - :bug: [#359](https://github.com/giraud/reasonml-idea-plugin/issues/359) Doc not displayed for JSX component - :house: prevent multiple compiler run in parallel ## 0.104 - 2021/10/14 - :bug: [#357](https://github.com/giraud/reasonml-idea-plugin/issues/357) Read access is allowed from inside read-action - :bug: [#354](https://github.com/giraud/reasonml-idea-plugin/issues/354) Update spellCheck strategy - :bug: ![r] [#353](https://github.com/giraud/reasonml-idea-plugin/issues/353) Interpolation incorrectly parsed - :bug: [#352](https://github.com/giraud/reasonml-idea-plugin/issues/352) CMT viewer is broken - :bug: [#327](https://github.com/giraud/reasonml-idea-plugin/issues/327) Use resolver for completions ## 0.103 - 2021/09/21 - :rocket: [#344](https://github.com/giraud/reasonml-idea-plugin/pull/344) Enabling spellchecker [@QuentinRa](https://github.com/QuentinRa) - :rocket: ![o] [#343](https://github.com/giraud/reasonml-idea-plugin/pull/343) Adding live templates for OCaml by [@QuentinRa](https://github.com/QuentinRa) - :bug: ![o] [#325](https://github.com/giraud/reasonml-idea-plugin/issues/325) Structure panel missing "pp_misc" for "let misc, pp_misc = ...". Gutter icon missing for pp_misc in mli file - :bug: ![o] [#322](https://github.com/giraud/reasonml-idea-plugin/issues/322) Class types in .mli files should link to the corresponding definition in the .ml file - :house: [#351](https://github.com/giraud/reasonml-idea-plugin/issues/351) Error annotator use detected compiler, not file language - :house: update GitHub workflow to use Java 11 - :house: add qualified name index for classes - :house: add line markers for Rescript - :house: enable line markers in every IDE ## 0.102.2 - 2021/08/26 - :bug: ![o] [#340](https://github.com/giraud/reasonml-idea-plugin/issues/340) Items missing in structure panel after let ending with match (incorrect object parsing) - :bug: ![o] [#329](https://github.com/giraud/reasonml-idea-plugin/issues/329) IllegalArgumentException: Not an OCaml node: Element(OCAML_LAZY_NODE) - :house: remove jps-plugin.jar reference in plugin.xml 0.102.1 - :bug: [#333](https://github.com/giraud/reasonml-idea-plugin/issues/333) CLion crash, remove dependency to java only class - :bug: [#157](https://github.com/giraud/reasonml-idea-plugin/issues/157) Update parameter info handler, only show known types 0.102 - :house: [#328](https://github.com/giraud/reasonml-idea-plugin/issues/328) Update Rescript parser to latest syntax - :bug: [#326](https://github.com/giraud/reasonml-idea-plugin/issues/326) Structure panel shows "mutable" for mutable record fields rather than the field name - :bug: [#318](https://github.com/giraud/reasonml-idea-plugin/issues/318) Rescript integration ## 0.101 - 2021/05/27 - :bug: [#303](https://github.com/giraud/reasonml-idea-plugin/issues/303) Incorrect resolution for record field - :house: Reference resolution algorithm has been totally redesigned ## 0.100 - 2021/05/05 - :house: move to jfrog, bintray is no more available - :bug: [#317](https://github.com/giraud/reasonml-idea-plugin/issues/317) Belt.Array incorrectly resolved # 0.99 - 2021/04/08 Version 0.99 supports the 2021.1 release. - :bug: [#315](https://github.com/giraud/reasonml-idea-plugin/issues/315) ![r] insertion handler for JSX doesn't detect close tag - :bug: [#190](https://github.com/giraud/reasonml-idea-plugin/issues/190) show nested function definitions in structure panel - :bug: fix incorrect identification of optional parameter - :nail_care: ![r] order jsx attributes, mandatory first # 0.98.3 - 2021/04/06 - :bug: [#314](https://github.com/giraud/reasonml-idea-plugin/issues/314) missing item in structure panel (structural diff operator) - :bug: [#313](https://github.com/giraud/reasonml-idea-plugin/issues/313) alphabetize opened/included modules in the structure panel 0.98.2 - :bug: [#312](https://github.com/giraud/reasonml-idea-plugin/issues/312) ![o] switch with optional is not parsed correctly - :bug: [#311](https://github.com/giraud/reasonml-idea-plugin/issues/311) ![o] Missing icon in mli file - :bug: [#310](https://github.com/giraud/reasonml-idea-plugin/issues/310) ![o] Missing types in structure panel after class definition 0.98.1 - :bug: [#293](https://github.com/giraud/reasonml-idea-plugin/issues/293) ![o] Better 'and' parsing - :house: Better integration with yarn workspaces (v2?): search for node_modules in parent directory also 0.98 - :nail_care: ![o] highlight of ocaml annotation include arrobase - :bug: [#309](https://github.com/giraud/reasonml-idea-plugin/issues/309) incorrectly parse infix operators - :bug: [#261](https://github.com/giraud/reasonml-idea-plugin/issues/261) warning configuration in bsconfig.json is not being applied # 0.97 - 2021/02/04 - :bug: [#304](https://github.com/giraud/reasonml-idea-plugin/issues/304) ![r] Properties in component using external are not found - :bug: Keep caret at position when reformat - :house: Fix file pattern introduced in bs-platform 8.3 - :house: Fix reformatOnSave when multiple projects (different settings) are opened - :house: Update ErrorAnnotator to use new ninja format introduced since bs-platform 8.4 - :house: Update ppx error extraction ## 0.96 - 2021/01/08 - :bug: [#295](https://github.com/giraud/reasonml-idea-plugin/issues/295) ![o] Parser error: incorrect chaining module/type - :bug: [#294](https://github.com/giraud/reasonml-idea-plugin/issues/294) ![o] `let open in` incorrectly parsed - :bug: [#291](https://github.com/giraud/reasonml-idea-plugin/issues/291) ![o] 'function' not parsed as function - :house: Better usage of externalAnnotator to highlight compilation problems in editor by [@pooch](https://github.com/JohnPucciarelli) - :house: Better reformatOnSave implementation that should prevent infinite loop ## 0.95.1 - 2020/12/03 A brand new site created by John Pucciarelli (https://giraud.github.io/reasonml-idea-plugin/) 2020.3 support - :rocket: [#284](https://github.com/giraud/reasonml-idea-plugin/issues/284) ![o] Support for OCamlFormat - :rocket: [#110](https://github.com/giraud/reasonml-idea-plugin/issues/110) ![o]![r]![n] Language injection in strings - :bug: [#279](https://github.com/giraud/reasonml-idea-plugin/issues/279) ![n] Matched brace isn't highlighted in .res source file - :bug: [#278](https://github.com/giraud/reasonml-idea-plugin/issues/278) ![o] let (//) incorrectly parsed as comment - :bug: [#277](https://github.com/giraud/reasonml-idea-plugin/issues/277) Wrong module resolution for js object - :nail_care: New plugin icon by [@pooch](https://github.com/JohnPucciarelli) ## 0.94 - 2020/11/03 Dune integration has been updated, [documentation](https://github.com/giraud/reasonml-idea-plugin/tree/master/docs) is also up to date. - :bug: [#276](https://github.com/giraud/reasonml-idea-plugin/issues/276) Class JSFile not found - :bug: [#275](https://github.com/giraud/reasonml-idea-plugin/issues/275) Incorrect parsing of switch with signature - :bug: [#245](https://github.com/giraud/reasonml-idea-plugin/issues/245) Resolve property of JSX tag - :bug: [#124](https://github.com/giraud/reasonml-idea-plugin/issues/124) Repl freezes with Ocaml 4.07 - :nail_care: Fold switch/match - :house: Use [rincewind@0.9.1](https://github.com/giraud/rincewind) - :house: refactor Dune integration: works with native (linux), wsl (windows) and cygwin (windows) ## 0.93.1 - 2020/10/09 0.93.1 - :bug: [#272](https://github.com/giraud/reasonml-idea-plugin/issues/272) Another case: structure panel missing function definition with "and" - :nail_care: [#274](https://github.com/giraud/reasonml-idea-plugin/issues/274) Omit '()' in structure panel - :nail_care: [#273](https://github.com/giraud/reasonml-idea-plugin/issues/273) Some constructor names not highlighted in function/match patterns 0.93 - :rocket: [#119](https://github.com/giraud/reasonml-idea-plugin/issues/119) support for automatically closing quotes (double) - :nail_care: 'Goto class' use a custom presentation for inner modules - :bug: [#271](https://github.com/giraud/reasonml-idea-plugin/issues/271) Structure panel missing function defined with "and" - :bug: [#270](https://github.com/giraud/reasonml-idea-plugin/issues/270) Function definition with type variables not recognized as a function - :bug: [#91](https://github.com/giraud/reasonml-idea-plugin/issues/91) Jumping to some module-related symbol definitions from Structure Panel doesn't highlight symbol right away (incorrect module type detection) - :bug: Fix monorepo on windows ## 0.92 - 2020/09/18 0.92.1 - :bug: [#269](https://github.com/giraud/reasonml-idea-plugin/issues/269) Incorrect display of class methods names in structure panel - :nail_care: Better parsing of React fragment <> - :nail_care: Better presentation of inner modules in 'goto class' dialog 0.92 This new version is compatible with 2020.2. To support that version, the references have been redesigned. It might be needed to invalidate your caches and maybe restart project from scratch (or remove .idea folder). - :rocket: [#264](https://github.com/giraud/reasonml-idea-plugin/pull/264) a new unified language settings by [@pooch](https://github.com/JohnPucciarelli) - :rocket: [#81](https://github.com/giraud/reasonml-idea-plugin/issues/81) Support GoTo/Implementation(s) - :bug: [#266](https://github.com/giraud/reasonml-idea-plugin/issues/266) Incompatible with 2020.2 IDEs - :bug: [#268](https://github.com/giraud/reasonml-idea-plugin/issues/268) Regression: Structure panel should group methods under the class name - :bug: [#256](https://github.com/giraud/reasonml-idea-plugin/issues/256) Probable regression for jumping to a symbol - :house: Reworked parsers (try to remove some complexity) - rescript included [#260](https://github.com/giraud/reasonml-idea-plugin/issues/260) - :house: .res files are automatically compiled when saved, like .re ## 0.90.2 - 2020/07/02 0.90.1 This is a quick patch release to provide syntax highlighting to the new `.res` and `.resi` file types. Don't expect anything more than just highlighting. - :bug: Fix 'add braces' action when function has signature 0.90.0 - :memo: Reworked documentation, moved from wiki to the [docs](https://github.com/giraud/reasonml-idea-plugin/tree/master/docs) directory by [@pooch](https://github.com/JohnPucciarelli) - :memo: [#252](https://github.com/giraud/reasonml-idea-plugin/issues/252) Add an architecture documentation to explain some of the internal details - :bug: [#247](https://github.com/giraud/reasonml-idea-plugin/issues/247) Duplicated file templates - :bug: [#205](https://github.com/giraud/reasonml-idea-plugin/issues/205) Functors are now indexed (completion and resolution should work) - :bug: [#254](https://github.com/giraud/reasonml-idea-plugin/issues/254) ![r] Make a distinction between single-line and multi-line comments in parser - :bug: [#254](https://github.com/giraud/reasonml-idea-plugin/issues/254) ![r] Fold multiline JSX tags - :house: Add github action for CI (and badges) by [@pooch](https://github.com/JohnPucciarelli) - :house: [#253](https://github.com/giraud/reasonml-idea-plugin/pull/253) Breaking apart modules to avoid extra instances of IntelliJ when testing by [@pooch](https://github.com/JohnPucciarelli) - :house: [#257](https://github.com/giraud/reasonml-idea-plugin/pull/257) Fix monorepo executable by [@Coobaha](https://github.com/Coobaha) ## 0.89.0 - 2020/05/22 - :rocket: [#239](https://github.com/giraud/reasonml-idea-plugin/pull/239) Experimental: start of ESY support work by [@pooch](https://github.com/JohnPucciarelli) - :bug: [#249](https://github.com/giraud/reasonml-idea-plugin/issues/249) Too many warnings in log (bsconfig not found) - :bug: [#241](https://github.com/giraud/reasonml-idea-plugin/pull/241) Fix bug that prevent IDEA to keep correctly the source of SDK by [@kayl669](https://github.com/kayl669) - :bug: [#236](https://github.com/giraud/reasonml-idea-plugin/issues/236) Cannot find a module definition if namespace is used - :house: Find real bsc/bsb binaries if a symlink is found - :house: Fix gutter icons for navigation in OCaml files ## 0.88.1 - 2020/05/02 0.88.1 - :house: [#238](https://github.com/giraud/reasonml-idea-plugin/pull/238) ![o] ![r] Add build system info to action labels by [@Coobaha](https://github.com/Coobaha) - :house: [#234](https://github.com/giraud/reasonml-idea-plugin/issues/234) Display warning instead of exception 0.88 - :rocket: ![o] You can easily download OCaml SDK sources in SDK platform settings - :bug: [#231](https://github.com/giraud/reasonml-idea-plugin/issues/231) Fix ClassCastException when reading bsconfig.json - :nail_care: [#220](https://github.com/giraud/reasonml-idea-plugin/pull/220) ![o] ![r] Added BuckleScript file type and icon by [@pooch](https://github.com/JohnPucciarelli) - :nail_care: [#219](https://github.com/giraud/reasonml-idea-plugin/pull/219) ![o] ![r] Added file type support for Esy by [@pooch](https://github.com/JohnPucciarelli) - :nail_care: ![o] ![r] Poly-variants can have a different highlighting, color scheme updated - :nail_care: ![o] ![r] Use SVG icons - :house: [#224](https://github.com/giraud/reasonml-idea-plugin/pull/224) ![r] Use bsb.exe instead of node.js wrapper in monorepo by [@Coobaha](https://github.com/Coobaha) - :house: [#221](https://github.com/giraud/reasonml-idea-plugin/pull/221) ![o] ![r] Compilers have separate tool windows by [@pooch](https://github.com/JohnPucciarelli) - :house: Use rincewind@0.8 ## 0.87.1 - 2020/04/03 0.87.1 - :bug: [#214](https://github.com/giraud/reasonml-idea-plugin/issues/214) Infinite indexing 0.87 - :rocket: Compatible with bucklescript 7.2.2 - :rocket: Add OCaml 4.10 to the list of downloadable SDKs - :bug: [#211](https://github.com/giraud/reasonml-idea-plugin/issues/211) `let%private` support - :bug: [#209](https://github.com/giraud/reasonml-idea-plugin/issues/209) Deep dependencies are not indexed in IDE that have javascript functionality (ultimate/webstorm) - :house: Use rincewind 0.7 (support of OCaml 4.10) ## 0.86.1 - 2020/02/21 - :rocket: [#204](https://github.com/giraud/reasonml-idea-plugin/issues/204) ![r] New on-the-fly compilation to get faster inferred information - :bug: [#167](https://github.com/giraud/reasonml-idea-plugin/issues/167) Go-to definition is not working correctly for deconstructed tuples - :house: Compatible with bucklescript 7.2 ## 0.85 - 2020/02/04 - :rocket: File | New menu as OCaml and ReasonML templates - :rocket: Display cmt file in a custom editor - :rocket: [#200](https://github.com/giraud/reasonml-idea-plugin/issues/200) OCaml SDK can be set at the module level - :house: Fix bsc path - :house: New indexing of file modules - :house: Use Rincewind 0.6 - :house: [#199](https://github.com/giraud/reasonml-idea-plugin/issues/199) Prepare for dynamic plugins - :house: [#196](https://github.com/giraud/reasonml-idea-plugin/issues/196) Redesign content root finding ## 0.84 - 2019/12/10 - :bug: [#197](https://github.com/giraud/reasonml-idea-plugin/issues/197) java.lang.Throwable: Assertion failed: Undoable actions allowed inside commands only - :bug: [#192](https://github.com/giraud/reasonml-idea-plugin/issues/192) Undoing causes Following files have changes that cannot be undone: error - :house: Fix a stub exception for functor parameter ## 0.83 - 2019/12/03 New versioning scheme: each intellij release has its own plugin version. For example, 2019.3 can only use plugin 0.83-2019.3. This is much more work when developing the plugin but it is needed because API changes between releases can have many incompatibilities. The consequence is that I won't be able to maintain too many versions active. Older version of the plugin will be frozen and won't get any updates. ## [0.82] - 2019/11/15 - :rocket: Importing a Dune project automatically configure project structure : SDK, module and project. SDK can be automatically downloaded and indexed. - :rocket: Dune stanzas visible in the structure view - :rocket: Dune stanzas can be folded when they span on multiple lines - :bug: [#189](https://github.com/giraud/reasonml-idea-plugin/issues/189) ![o] Structure panel missing members/parse problem for `while` - :bug: [#175](https://github.com/giraud/reasonml-idea-plugin/issues/175) ![o] Structure panel missing top level functions defined after nested `let foo = function` - :house: Fix bs version extractor (format changed in bucklescript 6.2.1) - :house: Improved Dune parser ## [0.81] - 2019/10/17 - :bug: [#185](https://github.com/giraud/reasonml-idea-plugin/issues/185) `gradle verifyPlugin` fails - :bug: [#156](https://github.com/giraud/reasonml-idea-plugin/issues/156) Assigning any value to new variable confuses plugin - :bug: [#88](https://github.com/giraud/reasonml-idea-plugin/issues/88) ![r] ![o] GoTo/Declaration not looking for locally-defined symbol - :bug: [#83](https://github.com/giraud/reasonml-idea-plugin/issues/83) ![r] ![o] Support GoTo/Declaration for function-local symbols - :nail_care: Try to improve quick navigation doc - :house: ![o] Parsing directives (`#if`, `#else`, `#end`) - :house: Get inferred type from definition if not found in usage - :house: Some deduplication of pervasives expressions in free completion contributor - :house: Work on signature conversion (partial conversion) - :house: Better dot completion when module is an alias (ex: Belt.Map.String.>=" in the structure panel ## 0.58 - 2018/07/12 This release contains a big rewrite of how modules are referenced, it may break things that were working before (ex: find usages). - :bug: [#72](https://github.com/giraud/reasonml-idea-plugin/issues/72) Autocomplete not working in uncurried function ## 0.57.1 - 2018/07/10 - :rocket: Some record field completion - :bug: [#66](https://github.com/giraud/reasonml-idea-plugin/issues/66) Go To + module alias - :house: ![o] Better module path resolution - :house: Improved parsers ## 0.56 - :rocket: ![r] Go to symbol is working for external/let mainly - :rocket: ![r] ctrl-hover on a lower symbol display its type (still wip, not working for all symbols) - :bug: ![r] JSX attribute completion with reason-react 0.4.2 (from external to type) ## 0.55.1 * :house: fix editor freeze when too many cmt/i files are updated * :house: ![o] better completion ## 0.54 * :rocket: add "exposing" code lens to opens * :house: completion uses opens and local opens * :house: better path resolution for "go to" action * :house: rincewind-0.2 * :house: improve parsers ## 0.53 * :rocket: ![r] ![o] improve parsers * :rocket: ![r] improve braces intention * :bug: [#65](https://github.com/giraud/reasonml-idea-plugin/issues/65) refmt not working in .rei ## 0.52 * :rocket: ![o] Pair match in OCaml: struct/end and sig/end * :rocket: [#53](https://github.com/giraud/reasonml-idea-plugin/issues/53) ![r] Implemented intentions * :nail_care: Add bool/char/int/float/string as keywords * :nail_care: [#64](https://github.com/giraud/reasonml-idea-plugin/issues/53) Handle warning message from bsb * :house: Protect against concurrent run of bsb compilation process ## 0.51.1 * :bug: Fix [#61](https://github.com/giraud/reasonml-idea-plugin/issues/61) * :house: Slightly better bootstrap for type inference ## 0.51 * :bug: [#52](https://github.com/giraud/reasonml-idea-plugin/issues/52) Fix problem when parsing JSX tag names * :house: Type annotation is now using a native cmt extractor (Windows, Linux, OSX) ## 0.50 * :nail_care: [#55](https://github.com/giraud/reasonml-idea-plugin/issues/55) Sorry, no more facets: they can't be used outside Idea. Settings can be found in Project settings and they are per project ## 0.49 * :rocket: [#47](https://github.com/giraud/reasonml-idea-plugin/issues/47) No more JVM properties 'reasonBsb' and 'reasonRefmt', configuration is done via facet ## 0.48.1 * :bug: [#51](https://github.com/giraud/reasonml-idea-plugin/issues/51) Fix a NPE in OCaml ## 0.48 * :house: Fix folding code * :house: Better locate js.ml, belt.ml * :house: Add Pervasives to completion ## 0.47.1 * :bug: Fixed 2 bugs (stack and 'editor not disposed' exceptions) * :house: Fixed recursive type parsing problem with variants ## 0.47 * :rocket: Reason settings to change reformat column width * :bug: [#45](https://github.com/giraud/reasonml-idea-plugin/issues/45) (classes have been deleted) * :house: Better error extraction * :house: Reformat on save can be activated (see doc) ## 0.46 * :bug: Fix version 0.45 * :rocket: Better error annotation (includes warnings) ## 0.45 * :nail_care: Bucklescript window has an icon * :nail_care: File icons updated * :bug: Goto action use the same path resolution than completion * :rocket: Display signature for 'val' in completion popup * :house: Parsers improvement * :house: Reworked how to get the types from cmi files ## 0.44 * :bug: [#40](https://github.com/giraud/reasonml-idea-plugin/issues/40) * :house: Improve completion on expressions ## 0.43 * :bug: [#35](https://github.com/giraud/reasonml-idea-plugin/issues/35) Type annotations not working with bs namespace * :house: Improve completion on expressions ## 0.42 * :rocket: Add |. operator * :rocket: goto action on file module is working * :house: Improve completion on expressions * :bug: Color settings no more blocked on loading spinner ## 0.41 * :house: Much improved performances for code lens ## 0.40 * :rocket: JSX completion (tag/attribute) * :rocket: Variant names can be highlighted * :house: Improved parsers ## 0.39 * :house: Better JSX parsing/highlighting * :rocket: Code lens style is customisable * :bug: [#33](https://github.com/giraud/reasonml-idea-plugin/issues/33) * :rocket: Completion contributor for OCaml files (wip) ## 0.38 * :house: Better JSX parsing/highlighting * :house: Fix inferred type annotations * :house: Improved reason parser ## 0.37 * :bug: [#30](https://github.com/giraud/reasonml-idea-plugin/issues/30) * :rocket: add completion when starting typing * :house: Display types for val/external (completion popup) * :house: Use mli file when present (Pervasives) ## 0.36 * :bug: [#29](https://github.com/giraud/reasonml-idea-plugin/issues/29) * :house: Improve parsers * :rocket: add keyword completion (start expression) * :rocket: add module completion for open expression ## 0.35 * :bug: Fix an infinite loop in PsiModule * :house: Improve parsers ## 0.34 * :bug: Fix a NPE in the folder code when comments are too small * :bug: [#28](https://github.com/giraud/reasonml-idea-plugin/issues/28) IntelliJ error "Assertion failed: Too many element types registered. Out of (short) range." **maybe** * :rocket: [#25](https://github.com/giraud/reasonml-idea-plugin/issues/25) Support .ml4 files ## 0.33 * :bug: [#22](https://github.com/giraud/reasonml-idea-plugin/issues/22) Mishandling of "Comment with Block Comment" action (CTL-SHIFT-/) * :bug: [#21](https://github.com/giraud/reasonml-idea-plugin/issues/21) Error message: "startNotify called already" ## 0.32 * :nail_care: [#20](https://github.com/giraud/reasonml-idea-plugin/issues/20) Improved notification, add plugin name and link to github * :rocket: 'go to' for Open expression (ctrl+click) * :bug: [#19](https://github.com/giraud/reasonml-idea-plugin/issues/19) Character constant with an octal character not highlighted correctly ## 0.31 * :nail_care: val expressions (OCaml) are displayed in the structure view * :nail_care: Exceptions are displayed in the structure view * :bug: [#18](https://github.com/giraud/reasonml-idea-plugin/issues/18) Incorrect handling of characters in parser * :house: Improve OCaml parser ## 0.30 * :house: Fix Idea 15 compatibility * :house: Work done on indexing and referencing (module) ## 0.29 * :house: Redesigned the reason parser ## 0.28 * :rocket: Reformat action is working on ocaml source * :house: Working on stub indexing and first implementation of completion using static analysis ## 0.27 * :bug: Fixed an IllegalArgumentException in KillableColoredProcessHandler * :rocket: Structure view enabled for OCaml files * :rocket: Inferring types using bsc if no merlin found (wip) * :house: Using gradle for build * :house: Improving reason parser ## 0.26 * :house: Improving parsers ## 0.25 * :rocket: [#14](https://github.com/giraud/reasonml-idea-plugin/issues/14) Add a very lightweight support of OCaml, to have more syntax coloring and no incorrect parser errors ## 0.24 * :nail_care: [#13](https://github.com/giraud/reasonml-idea-plugin/issues/13) Add Number and Type argument in color settings ## 0.23 * :house: New default values for bsb and refmt. bsb=> node_modules/bs-platform/bin/bsb.exe, refmt=> node_modules/bs-platform/bin/refmt3.exe ## 0.22 * :rocket: Parse Bsb super errors and use them to annotate source code [r]: website/static/img/reason-file.png [o]: website/static/img/ocaml-file.png [n]: website/static/img/rescript-file.png ================================================ FILE: CONTRIBUTING.md ================================================ # Contributing Contributions are welcome and encouraged. Please see the [How to Contribute](https://giraud.github.io/reasonml-idea-plugin/docs/contributing/) page to get started. ================================================ FILE: ISSUE_TEMPLATE.md ================================================ plugin version: ### Description ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2017 Giraud Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # Reason IDEA Plugin **!!! THIS PROJECT IS IN MAINTENANCE MODE !!!** [**giraud.github.io/reasonml-idea-plugin**](https://giraud.github.io/reasonml-idea-plugin/) ReasonML language plugin for IDEA. Supports Reason, ReScript and OCaml. ![Build Status](https://github.com/giraud/reasonml-idea-plugin/workflows/Build%20Status/badge.svg) [![JetBrains IntelliJ plugins](https://img.shields.io/jetbrains/plugin/d/9440-reasonml.svg)](https://plugins.jetbrains.com/plugin/9440-reasonml) [![Discord](https://img.shields.io/discord/713777184996589580)](https://discord.gg/65fz5jb) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![All Contributors](https://img.shields.io/badge/all_contributors-13-orange.svg?style=flat-square)](#contributors) ![screenshot](screenshot.png) ## 🏁 Quick Start Download and install the [plugin from the JetBrains Plugin Marketplace](https://plugins.jetbrains.com/plugin/9440-reasonml). > Make sure `"namespace": false` is set in `bsconfig.json` to enable inferred types hints in the editor. ## 📔 Documentation Please see our [website](https://giraud.github.io/reasonml-idea-plugin/) for the latest documentation and more information on how to get started. ## 💬 Language Features | **Feature** | **OCaml (.ml)** | **Reason (.re, .rei)** | **ReScript (.res, .resi)** | |-----------------------|:---------------:|:----------------------:|:--------------------------:| | Syntax Support | ✅ | ✅ | ✅ | | Reformat Code | ✅ | ✅ | | | Structured View | ✅ | ✅ | ✅ | | Code Folding | ✅ | ✅ | ✅ | | JSX Support | ✅ | ✅ | ✅ | | JS Language Injection | ✅ | ✅ | ✅ | | Line & Block Comments | ✅ | ✅ | ✅ | | Type Annotations | | ✅ | ✅ | | Code Intentions | ✅ | ✅ | ✅ | | Pair Braces Matching | ✅ | ✅ | ✅ | ## 🛠 Build Tool Support | **Feature** | **BuckleScript** | **Dune** | **Esy** | |----------------------|:----------------:|:--------:|:-------:| | Install Dependencies | ✅ | ✅ | ✅ | | Build Project | ✅ | ✅ | ✅ | | Error Reporting | ✅ | ✅ | ✅ | ## 📝 Questions & Feedback Please see the [FAQ](https://giraud.github.io/reasonml-idea-plugin/docs/get-started/faq) page or [report](https://github.com/giraud/reasonml-idea-plugin/issues/new) an issue. Post any questions or feedback in [Discord](https://discord.gg/65fz5jb). ## 💁 How to Help - Be patient. - Give this project some love, star it or star the plugin page in the [JetBrains Plugin Marketplace](https://plugins.jetbrains.com/plugin/9440-reasonml-language-plugin). - Report any issues [here](https://github.com/giraud/reasonml-idea-plugin/issues/new). ### Donate Donate using PayPal Support my work with paypal-me. ## 🍻 Thanks To all the people who have donated, you are awesome !! Really, this is pure anonymous donation, and it blows my mind... I'm very grateful, and it's kinda stupid, but it keeps me motivated. So big thanks. Many thanks also to the [JetBrains Team](https://www.jetbrains.com/?from=reasonml-idea-plugin) who provide me an OSS licence for their product. ## 📄 License This project is [MIT licensed](https://github.com/giraud/reasonml-idea-plugin/blob/pooch/documentation/LICENSE). ================================================ FILE: build.gradle ================================================ import org.jetbrains.intellij.platform.gradle.TestFrameworkType plugins { id('java') id('org.jetbrains.intellij.platform') version('2.7.1') // IntelliJ Platform Gradle Plugin } // Configure project's dependencies repositories { mavenCentral() // IntelliJ Platform Gradle Plugin Repositories Extension - read more: https://plugins.jetbrains.com/docs/intellij/tools-intellij-platform-gradle-plugin-repositories-extension.html intellijPlatform { defaultRepositories() } } java.sourceCompatibility = 21.0 // Dependencies are managed with Gradle version catalog - read more: https://docs.gradle.org/current/userguide/platforms.html#sub:version-catalog dependencies { testRuntimeOnly('org.opentest4j:opentest4j:1.3.0') testImplementation('junit:junit:4.13.2') // IntelliJ Platform Gradle Plugin Dependencies Extension - read more: https://plugins.jetbrains.com/docs/intellij/tools-intellij-platform-gradle-plugin-dependencies-extension.html intellijPlatform { create(platformType, platformVersion) // Plugin Dependencies. Uses `platformBundledPlugins` property from the gradle.properties file for bundled IntelliJ Platform plugins. bundledPlugins(platformBundledPlugins.split(',').collect { it.trim() }) // Plugin Dependencies. Uses `platformPlugins` property from the gradle.properties file for plugin from JetBrains Marketplace. plugins(platformPlugins.split(',').collect { it.trim() }) instrumentationTools() pluginVerifier() testFramework(TestFrameworkType.Platform.INSTANCE) testFramework(TestFrameworkType.Plugin.Java.INSTANCE) } } group = pluginGroup version = pluginVersion + '-' + platformVersion intellijPlatform { pluginConfiguration { version = pluginVersion + '-' + platformVersion ideaVersion { sinceBuild = pluginSinceBuild untilBuild = pluginUntilBuild } } patchPluginXml { sinceBuild = pluginSinceBuild untilBuild = pluginUntilBuild changeNotes = """

Full change log...

To see how to integrate tools, go to the website.

""".stripIndent() } pluginVerification { ides { recommended() } } } runIde { systemProperty 'idea.is.internal', true jvmArgs '-Xmx2G' // MAVEN_CACHE C:\Users\hgiraud\scoop\persist\maven\.m2 // MAVEN_HOME C:\Users\hgiraud\scoop\apps\maven\current // MAVEN_REPOSITORY C:\Users\hgiraud\scoop\persist\maven\.m2\repository } ================================================ FILE: gradle/wrapper/gradle-wrapper.properties ================================================ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists ================================================ FILE: gradle.properties ================================================ org.gradle.jvmargs=-XX:MaxHeapSize=512m -Xms512m -Xmx1g pluginGroup = com.reason pluginName = reasonml-plugin-idea pluginVersion = 0.131 # http://www.jetbrains.org/intellij/sdk/docs/basics/getting_started/build_number_ranges.html pluginSinceBuild = 242.0 pluginUntilBuild = 242.* # IntelliJ Platform Properties -> https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html#configuration-intellij-extension # https://www.jetbrains.com/intellij-repository/releases # https://www.jetbrains.com/intellij-repository/snapshots platformType = IU platformVersion = 2024.2 # Plugin Dependencies -> https://plugins.jetbrains.com/docs/intellij/plugin-dependencies.html # https://plugins.jetbrains.com/plugin/13029-indices-viewer/versions # https://plugins.jetbrains.com/plugin/227-psiviewer/versions platformPlugins = com.jetbrains.hackathon.indices.viewer:1.28, PsiViewer:242.4697 platformBundledPlugins=com.intellij.java, JavaScript # Enable Gradle Configuration Cache -> https://docs.gradle.org/current/userguide/configuration_cache.html org.gradle.configuration-cache = true # Enable Gradle Build Cache -> https://docs.gradle.org/current/userguide/build_cache.html org.gradle.caching = true org.gradle.configuration-cache.parallel=true ================================================ FILE: gradlew ================================================ #!/bin/sh # # Copyright © 2015-2021 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 ' "$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 CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # 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" ) CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 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, 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" \ -classpath "$CLASSPATH" \ org.gradle.wrapper.GradleWrapperMain \ "$@" # 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" "$@" ================================================ FILE: gradlew.bat ================================================ @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 set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar @rem Execute Gradle "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* :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 ================================================ FILE: settings.gradle ================================================ rootProject.name = 'reasonml-idea-plugin' ================================================ FILE: src/main/java/com/reason/FileHelper.java ================================================ package com.reason; import com.intellij.json.*; import com.intellij.openapi.fileTypes.FileType; import com.intellij.openapi.project.*; import com.intellij.openapi.vfs.*; import com.intellij.psi.*; import com.reason.ide.files.*; import com.reason.ide.search.*; import com.reason.lang.core.psi.*; import jpsplugin.com.reason.Platform; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.nio.file.*; import static com.reason.comp.ORConstants.BS_CONFIG_FILENAME; import static com.reason.comp.ORConstants.RESCRIPT_CONFIG_FILENAME; public class FileHelper { private FileHelper() { } public static boolean isCompilable(@Nullable FileType fileType) { return isReason(fileType) || isRescript(fileType) || isOCaml(fileType) || isOCamlLexer(fileType) || isOCamlParser(fileType); } public static boolean isBsConfigJson(@Nullable VirtualFile file) { return file != null && BS_CONFIG_FILENAME.equals(file.getName()); } public static boolean isRescriptConfigJson(@Nullable VirtualFile file) { return file != null && RESCRIPT_CONFIG_FILENAME.equals(file.getName()); } public static boolean isCompilerConfigJson(@Nullable VirtualFile file) { if (file != null && file.getFileType() instanceof JsonFileType) { String fileName = file.getName(); return RESCRIPT_CONFIG_FILENAME.equals(fileName) || BS_CONFIG_FILENAME.equals(fileName); } return false; } public static boolean isReason(@Nullable FileType fileType) { return fileType instanceof RmlFileType || fileType instanceof RmlInterfaceFileType; } public static boolean isRescript(@Nullable FileType fileType) { return fileType instanceof ResFileType || fileType instanceof ResInterfaceFileType; } private static boolean isOCamlLexer(@Nullable FileType fileType) { return fileType instanceof MllFileType; } private static boolean isOCamlParser(@Nullable FileType fileType) { return fileType instanceof MlyFileType; } public static boolean isOCaml(@Nullable FileType fileType) { return fileType instanceof OclFileType || fileType instanceof OclInterfaceFileType; } public static boolean isNinja(@Nullable VirtualFile file) { return file != null && "build.ninja".equals(file.getName()); } public static boolean isInterface(@Nullable FileType fileType) { return fileType instanceof RmlInterfaceFileType || fileType instanceof ResInterfaceFileType || fileType instanceof OclInterfaceFileType; } @NotNull public static String shortLocation(@NotNull String path, @NotNull Project project) { String newPath = Platform.getRelativePathToModule(path, project); int nodeIndex = newPath.indexOf("node_modules"); if (0 <= nodeIndex) { newPath = newPath.substring(nodeIndex); } int pos = newPath.lastIndexOf("/"); return 0 < pos ? newPath.substring(0, pos) : newPath; } public static @Nullable RPsiModule getPsiModule(@Nullable FileModuleData data, @NotNull Project project) { // not working with unit tests VirtualFile vFile = data == null ? null : VirtualFileManager.getInstance().findFileByNioPath(Path.of(data.getPath())); PsiFile file = vFile == null ? null : PsiManager.getInstance(project).findFile(vFile); return file instanceof RPsiModule ? (RPsiModule) file : null; } } ================================================ FILE: src/main/java/com/reason/comp/CliType.java ================================================ package com.reason.comp; import com.reason.comp.ORCompiler.*; import org.jetbrains.annotations.*; public interface CliType { @NotNull CompilerType getCompilerType(); enum Bs implements CliType { MAKE, CLEAN_MAKE; @Override public @NotNull CompilerType getCompilerType() { return CompilerType.BS; } } enum Rescript implements CliType { MAKE, CLEAN; @Override public @NotNull CompilerType getCompilerType() { return CompilerType.RESCRIPT; } } enum Dune implements CliType { BUILD, CLEAN, INSTALL, VERSION; @Override public @NotNull CompilerType getCompilerType() { return CompilerType.DUNE; } } enum Esy implements CliType { INSTALL, BUILD, SHELL; @Override public @NotNull CompilerType getCompilerType() { return CompilerType.ESY; } } } ================================================ FILE: src/main/java/com/reason/comp/CompilerOutputAnalyzer.java ================================================ package com.reason.comp; import com.reason.ide.annotations.*; import org.jetbrains.annotations.*; import java.util.*; import java.util.regex.*; public interface CompilerOutputAnalyzer { Pattern FILE_LOCATION = Pattern.compile("\\s*File \"(.+)\", lines? (\\d+)(?:-(\\d+))?, characters (\\d+)-(\\d+):"); Pattern SYNTAX_LOCATION = Pattern.compile("\\s*(.+):(\\d+):(\\d+)(?:-(\\d+)(?::(\\d+))?)?"); @NotNull List getOutputInfo(); void onTextAvailable(@NotNull String line); } ================================================ FILE: src/main/java/com/reason/comp/CompilerOutputListener.java ================================================ package com.reason.comp; import com.intellij.execution.process.*; import com.intellij.openapi.application.*; import com.intellij.openapi.project.*; import com.intellij.openapi.util.*; import com.intellij.psi.*; import com.reason.ide.annotations.*; import com.reason.ide.hints.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; import java.util.*; public class CompilerOutputListener implements ProcessListener { private static final Log LOG = Log.create("output"); private final Project myProject; private final CompilerOutputAnalyzer myOutputAnalyzer; public CompilerOutputListener(@NotNull Project project, @NotNull CompilerOutputAnalyzer outputAnalyzer) { myProject = project; myOutputAnalyzer = outputAnalyzer; } @Override public void startNotified(@NotNull ProcessEvent event) { myProject.getService(ErrorsManager.class).clearErrors(); } @Override public void processTerminated(@NotNull ProcessEvent event) { List outputInfo = myOutputAnalyzer.getOutputInfo(); if (!outputInfo.isEmpty() && !myProject.isDisposed()) { LOG.debug("Update errors manager with output results"); myProject.getService(ErrorsManager.class).addAllInfo(outputInfo); } ApplicationManager.getApplication() .invokeLater( () -> { // When build is done, we need to refresh editors to be notified of the latest // modifications if (!myProject.isDisposed()) { LOG.debug("Compilation done: read new types"); PsiFile selectedFile = InferredTypesService.getPsiFile(myProject); if (selectedFile != null) { selectedFile.putUserData(SignatureProvider.SIGNATURES_CONTEXT, null); // reset InferredTypesService.queryTypes(myProject, selectedFile); } } }, ModalityState.nonModal()); } @Override public void onTextAvailable(@NotNull ProcessEvent event, @NotNull Key outputType) { String text = event.getText(); myOutputAnalyzer.onTextAvailable(text); } } ================================================ FILE: src/main/java/com/reason/comp/ORCompiler.java ================================================ package com.reason.comp; import com.intellij.openapi.project.*; import com.intellij.openapi.vfs.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; public interface ORCompiler { enum CompilerType { BS("BuckleScript"), RESCRIPT("Rescript"), DUNE("Dune"), ESY("Esy"); private final String myDisplayName; CompilerType(String displayName) { myDisplayName = displayName; } public @NotNull String displayName() { return myDisplayName; } } @NotNull CompilerType getType(); boolean isConfigured(@NotNull Project project); boolean isAvailable(@NotNull Project project); @NotNull String getFullVersion(@Nullable VirtualFile file); void runDefault(@NotNull VirtualFile file, @Nullable ORProcessTerminated onProcessTerminated); void run(@Nullable VirtualFile file, @NotNull CliType cliType, @Nullable ORProcessTerminated onProcessTerminated); boolean isAvailable(); } ================================================ FILE: src/main/java/com/reason/comp/ORCompilerConfigManager.java ================================================ package com.reason.comp; import com.intellij.openapi.components.*; import com.intellij.openapi.project.*; import com.intellij.openapi.vfs.*; import com.intellij.psi.*; import com.reason.*; import com.reason.comp.bs.*; import com.reason.comp.rescript.*; import com.reason.ide.*; import org.jetbrains.annotations.*; import java.util.*; import static com.reason.comp.ORConstants.*; /** * Caches BsConfig available for project. */ @Service(Service.Level.PROJECT) public final class ORCompilerConfigManager { private final Project myProject; private final Map myConfigs = new HashMap<>(); public ORCompilerConfigManager(@NotNull Project project) { myProject = project; } public void refresh(@NotNull VirtualFile configFile) { BsConfig config = FileHelper.isBsConfigJson(configFile) ? BsConfigReader.read(configFile) : ResConfigReader.read(configFile); myConfigs.put(configFile.getCanonicalPath(), config); } public @Nullable BsConfig getConfig(@Nullable VirtualFile configFile) { BsConfig config = null; String canonicalPath = configFile == null ? null : configFile.getCanonicalPath(); if (canonicalPath != null) { config = myConfigs.get(canonicalPath); if (config == null) { config = FileHelper.isBsConfigJson(configFile) ? BsConfigReader.read(configFile) : ResConfigReader.read(configFile); myConfigs.put(canonicalPath, config); } } return config; } /** * Finds the "nearest" bsconfig.json/rescript.json to a given file. * Searches up the file-system until one file is found or the project root is reached. * * @param sourceFile starting point for search * @return configuration file, if found */ public @Nullable VirtualFile findNearestConfigFile(@NotNull VirtualFile sourceFile) { return ORFileUtils.findOneOfAncestor(myProject, sourceFile, RESCRIPT_CONFIG_FILENAME, BS_CONFIG_FILENAME); } public @Nullable BsConfig getNearestConfig(@Nullable VirtualFile sourceFile) { VirtualFile configFile = sourceFile == null ? null : findNearestConfigFile(sourceFile); return getConfig(configFile); } public @Nullable BsConfig getNearestConfig(@Nullable PsiFile psiFile) { VirtualFile virtualFile = psiFile == null ? null : ORFileUtils.getVirtualFile(psiFile); return getNearestConfig(virtualFile); } } ================================================ FILE: src/main/java/com/reason/comp/ORCompilerManager.java ================================================ package com.reason.comp; import com.intellij.openapi.components.*; import com.intellij.openapi.project.*; import com.intellij.openapi.vfs.*; import com.reason.*; import com.reason.comp.ORCompiler.*; import com.reason.comp.bs.*; import com.reason.comp.dune.*; import com.reason.comp.esy.*; import com.reason.comp.rescript.*; import com.reason.ide.files.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; import java.util.*; @Service(Service.Level.PROJECT) public final class ORCompilerManager { private static final Log LOG = Log.create("compiler.manager"); private final @NotNull Project myProject; public ORCompilerManager(@NotNull Project project) { myProject = project; } public @Nullable ORCompiler getCompiler(@NotNull CliType cliType) { return getCompiler(cliType.getCompilerType()); } public @Nullable ORCompiler getCompiler(@NotNull CompilerType compilerType) { ORCompiler compiler = myProject.getService(getCompilerClass(compilerType)); return compiler != null && compiler.isConfigured(myProject) ? compiler : null; } public @Nullable T getCompiler(@NotNull Class clazz) { T compiler = myProject.getService(clazz); return compiler != null && compiler.isConfigured(myProject) ? compiler : null; } public @Nullable ORResolvedCompiler getCompiler(@Nullable VirtualFile editorFile) { boolean shouldTraverse = editorFile != null && (editorFile.isDirectory() || FileHelper.isCompilable(editorFile.getFileType())); return shouldTraverse ? traverseAncestorsForCompiler(editorFile.getParent(), new HashMap<>()) : null; } private @Nullable ORResolvedCompiler traverseAncestorsForCompiler(@Nullable VirtualFile currentDir, @NotNull Map visited) { // hit filesystem root, give up if (currentDir == null) { return null; } // we've already visited this directory, must have hit a symlink if (visited.get(currentDir.getPath()) != null) { return null; } visited.put(currentDir.getPath(), currentDir); // look for a compiler configuration file in the current directory CompilerVisitor compilerVisitor = new CompilerVisitor(); VfsUtil.visitChildrenRecursively(currentDir, compilerVisitor); if (compilerVisitor.myCompiler != null) { return compilerVisitor.myCompiler; } // we just checked the project root, we're done if (currentDir.getPath().equals(myProject.getBasePath())) { return null; } // move up a directory and try again return traverseAncestorsForCompiler(currentDir.getParent(), visited); } private static @NotNull Class getCompilerClass(@NotNull CompilerType compilerType) { return switch (compilerType) { case BS -> BsCompiler.class; case RESCRIPT -> ResCompiler.class; case DUNE -> DuneCompiler.class; case ESY -> EsyCompiler.class; }; } private class CompilerVisitor extends VirtualFileVisitor { ORResolvedCompiler myCompiler = null; CompilerVisitor() { super(SKIP_ROOT, NO_FOLLOW_SYMLINKS, ONE_LEVEL_DEEP); } @Override public boolean visitFile(@NotNull VirtualFile file) { if (myCompiler != null || file.isDirectory()) { return false; } if (EsyPackageJson.isEsyPackageJson(file)) { EsyCompiler compiler = getCompiler(EsyCompiler.class); myCompiler = compiler != null ? new ORResolvedCompiler<>(compiler, file, null) : null; } else if (DuneFileType.isDuneFile(file)) { // Will be empty if dune isn't configured, might be an esy project DuneCompiler compiler = getCompiler(DuneCompiler.class); myCompiler = compiler != null ? new ORResolvedCompiler<>(compiler, file, null) : null; } else if (FileHelper.isRescriptConfigJson(file)) { LOG.debug("Detected rescript(only) config", file); ResCompiler rescript = getCompiler(ResCompiler.class); if (rescript != null) { VirtualFile binFile = ResPlatform.findBscExecutable(myProject, file); myCompiler = binFile != null ? new ResResolvedCompiler(rescript, file, binFile) : null; } } else if (FileHelper.isBsConfigJson(file)) { // Could be either a Rescript or a Bucklescript installation ResCompiler rescript = getCompiler(ResCompiler.class); VirtualFile binFile = ResPlatform.findBscExecutable(myProject, file); if (rescript != null && binFile != null) { myCompiler = new ResResolvedCompiler(rescript, file, binFile); } else { BsCompiler bucklescript = getCompiler(BsCompiler.class); binFile = BsPlatform.findBscExecutable(myProject, file); if (bucklescript != null && binFile != null) { myCompiler = new BsResolvedCompiler(bucklescript, file, binFile); } } } return true; } } } ================================================ FILE: src/main/java/com/reason/comp/ORCompilerOutputAnalyzer.java ================================================ package com.reason.comp; import com.reason.ide.annotations.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; import java.util.*; import java.util.regex.*; import static java.lang.Integer.*; public abstract class ORCompilerOutputAnalyzer implements CompilerOutputAnalyzer { private final List myOutputInfo = new ArrayList<>(); protected @Nullable OutputInfo myCurrentInfo = null; @Override public @NotNull List getOutputInfo() { return myOutputInfo; } @Override public abstract void onTextAvailable(@NotNull String line); protected @Nullable OutputInfo extractExtendedFilePositions(@NotNull Log LOG, @NotNull String line) { Matcher matcher = FILE_LOCATION.matcher(line); if (matcher.matches()) { String path = matcher.group(1); String lineStart = matcher.group(2); String lineEnd = matcher.group(3); String colStart = matcher.group(4); String colEnd = matcher.group(5); OutputInfo info = addInfo(path, lineStart, lineEnd, colStart, colEnd); if (info.colStart < 0 || info.colEnd < 0) { LOG.error("Can't decode columns for [" + line.replace("\n", "") + "]"); return null; } return info; } return null; } // C:\bla\bla\src\InputTest.res:1:11(-12(:22)?)? protected @Nullable OutputInfo extractSyntaxErrorFilePosition(@NotNull Log LOG, @NotNull String line) { Matcher matcher = SYNTAX_LOCATION.matcher(line); if (matcher.matches()) { String path = matcher.group(1); String lineStart = matcher.group(2); String colStart = matcher.group(3); String lineEnd = null; String colEnd = null; String end1 = matcher.group(4); String end2 = matcher.group(5); if (end2 == null && end1 != null) { colEnd = end1; } else if (end2 != null) { lineEnd = end1; colEnd = end2; } OutputInfo info = addInfo(path, lineStart, lineEnd, colStart, colEnd); if (info.colStart < 0 || info.colEnd < 0) { LOG.error("Can't decode columns for [" + line.replace("\n", "") + "]"); return null; } return info; } return null; } protected @NotNull OutputInfo addInfo(@NotNull String path, @NotNull String lineStart, @Nullable String lineEnd, @NotNull String colStart, @Nullable String colEnd) { OutputInfo info = new OutputInfo(); info.path = path; info.lineStart = parseInt(lineStart); info.colStart = parseInt(colStart); info.lineEnd = lineEnd == null ? info.lineStart : parseInt(lineEnd); info.colEnd = colEnd == null ? info.colStart : parseInt(colEnd); if (info.colEnd == info.colStart) { info.colEnd += 1; } myOutputInfo.add(info); return info; } } ================================================ FILE: src/main/java/com/reason/comp/ORConstants.java ================================================ package com.reason.comp; public class ORConstants { public static final String RESCRIPT_EXE_NAME = "rescript"; public static final String RESCRIPT_CONFIG_FILENAME = "rescript.json"; public static final String RESCRIPT_DIR = "rescript"; public static final String BSB_EXE_NAME = "bsb"; public static final String BSC_EXE_NAME = "bsc"; public static final String REFMT_EXE_NAME = "refmt"; public static final String BS_CONFIG_FILENAME = "bsconfig.json"; public static final String BS_DIR = "bs-platform"; public static final String BS_JS_FILE_EXTENSION = "bs.js"; public static final String NODE_MODULES = "node_modules"; private ORConstants() { } } ================================================ FILE: src/main/java/com/reason/comp/ORPlatform.java ================================================ package com.reason.comp; import com.intellij.openapi.project.*; import com.intellij.openapi.util.*; import com.intellij.openapi.vfs.*; import com.reason.ide.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; import static com.reason.comp.ORConstants.*; import static jpsplugin.com.reason.Platform.*; public class ORPlatform { private static final Log LOG = Log.create("platform"); private ORPlatform() { } public static @Nullable VirtualFile findCompilerPathInNodeModules(@NotNull Project project, @Nullable VirtualFile configFile, @NotNull String dirName, @NotNull String binName) { if (configFile == null) { return null; } VirtualFile parentDir = configFile.getParent(); VirtualFile nodeModules = parentDir == null ? null : parentDir.findFileByRelativePath(NODE_MODULES); if (parentDir != null && nodeModules == null) { // In yarn workspaces, node_modules can be in a parent directory nodeModules = ORFileUtils.findAncestor(project, parentDir, NODE_MODULES); } VirtualFile binRootDir = nodeModules == null ? null : nodeModules.findFileByRelativePath(dirName); if (nodeModules != null && binRootDir == null) { VirtualFile binary = nodeModules.findFileByRelativePath(".bin/" + binName); if (binary != null) { // This must be a mono-repo, only the .bin is found VirtualFile canonicalFile = binary.getCanonicalFile(); if (canonicalFile != null) { if (binary.is(VFileProperty.SYMLINK)) { // Mac/Linux: .bin contains symlinks to real exe, must follow VirtualFile canonicalDirectory = canonicalFile.getParent(); while (canonicalDirectory != null && !dirName.equals(canonicalDirectory.getName())) { canonicalDirectory = canonicalDirectory.getParent(); } return canonicalDirectory; } else { // Windows: no symlinks, only bat files VirtualFile nodModules = ORFileUtils.findAncestor(project, nodeModules.getParent(), NODE_MODULES); return nodModules == null ? null : nodeModules.findFileByRelativePath(dirName); } } } } return binRootDir != null && binRootDir.isDirectory() ? binRootDir : null; } public static @Nullable VirtualFile findBinary(@NotNull VirtualFile binDir, @NotNull String exeName) { String os = getOsPrefix(); if (os == null) { LOG.warn("Unable to determine OS prefix"); return null; } VirtualFile executable; // first, try to find platform-specific binary executable = binDir.findFileByRelativePath(os + "/" + exeName + WINDOWS_EXECUTABLE_SUFFIX); if (executable == null) { // next, try to find platform-agnostic wrappers executable = binDir.findFileByRelativePath(exeName + getOsBinaryWrapperExtension()); if (executable == null) { // last, try old locations of binary executable = binDir.findFileByRelativePath("bin/" + exeName + WINDOWS_EXECUTABLE_SUFFIX); if (executable == null) { executable = binDir.findFileByRelativePath("lib/" + exeName + WINDOWS_EXECUTABLE_SUFFIX); } } } return executable; } @VisibleForTesting static @NotNull String getOsBinaryWrapperExtension() { return SystemInfo.isWindows ? ".cmd" : ""; } @VisibleForTesting static @Nullable String getOsPrefix() { if (SystemInfo.isWindows) { return "win32"; } if (SystemInfo.isLinux) { return "linux"; } if (SystemInfo.isMac) { return "darwin"; } return null; } } ================================================ FILE: src/main/java/com/reason/comp/ORResolvedCompiler.java ================================================ package com.reason.comp; import com.intellij.openapi.vfs.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; /** * When a content root file has been found, we can associate it to its corresponding compiler. * Use ORCompilerManager to instantiate this class. */ public class ORResolvedCompiler { protected final C myCompiler; protected final VirtualFile myConfigFile; // Compiler configuration file, like bsconfig.json or dune-project protected final VirtualFile myBinFile; public ORResolvedCompiler(@NotNull C compiler, @NotNull VirtualFile configFile, @Nullable VirtualFile binFile) { myCompiler = compiler; myConfigFile = configFile; myBinFile = binFile; } public @NotNull ORCompiler.CompilerType getType() { return myCompiler.getType(); } public void runDefault(@NotNull VirtualFile file, @Nullable ORProcessTerminated onProcessTerminated) { myCompiler.runDefault(file, onProcessTerminated); } public @NotNull String getFullVersion() { return myCompiler.getFullVersion(myConfigFile); } public @NotNull VirtualFile getConfigFile() { return myConfigFile; } public @Nullable VirtualFile getContentRoot() { return myConfigFile.getParent(); } public @NotNull String getPath() { return myBinFile == null ? "" : myBinFile.getPath(); } } ================================================ FILE: src/main/java/com/reason/comp/ProcessFinishedListener.java ================================================ package com.reason.comp; import com.intellij.execution.process.*; import org.jetbrains.annotations.*; public class ProcessFinishedListener extends ProcessAdapter { private final long m_start; public ProcessFinishedListener() { this(System.currentTimeMillis()); } public ProcessFinishedListener(long start) { m_start = start; } @Override public void processTerminated(@NotNull ProcessEvent event) { long end = System.currentTimeMillis(); Object source = event.getSource(); if (source instanceof ProcessHandler) { ((ProcessHandler) source).notifyTextAvailable("Process finished in " + formatBuildTime(end - m_start) + "\n\n", ProcessOutputTypes.SYSTEM); } } private static @NotNull String formatBuildTime(long milliSeconds) { if (milliSeconds < 1000) { return milliSeconds + "ms"; } long seconds = milliSeconds / 1000; final StringBuilder sb = new StringBuilder(); if (seconds >= 3600) { sb.append(seconds / 3600).append("h "); seconds %= 3600; } if (seconds >= 60 || !sb.isEmpty()) { sb.append(seconds / 60).append("m "); seconds %= 60; } if (seconds > 0 || !sb.isEmpty()) { sb.append(seconds).append("s"); } return sb.toString(); } } ================================================ FILE: src/main/java/com/reason/comp/bs/BsColoredProcessHandler.java ================================================ package com.reason.comp.bs; import com.intellij.execution.*; import com.intellij.execution.configurations.*; import com.intellij.execution.process.*; import com.intellij.openapi.util.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; class BsColoredProcessHandler extends KillableProcessHandler implements AnsiEscapeDecoder.ColoredTextAcceptor { private final @NotNull AnsiEscapeDecoder m_ansiEscapeDecoder = new AnsiEscapeDecoder(); private final @Nullable ORProcessTerminated m_onProcessTerminated; BsColoredProcessHandler(@NotNull GeneralCommandLine commandLine, @Nullable ORProcessTerminated onProcessTerminated) throws ExecutionException { super(commandLine); m_onProcessTerminated = onProcessTerminated; } @Override protected void onOSProcessTerminated(int exitCode) { super.onOSProcessTerminated(exitCode); if (m_onProcessTerminated != null) { m_onProcessTerminated.run(null); } } @Override public final void notifyTextAvailable(@NotNull final String text, @NotNull final Key outputType) { m_ansiEscapeDecoder.escapeText(text, outputType, super::notifyTextAvailable); } @Override public void coloredTextAvailable(@NotNull String text, @NotNull Key attributes) { super.notifyTextAvailable(text, attributes); } } ================================================ FILE: src/main/java/com/reason/comp/bs/BsCompiler.java ================================================ package com.reason.comp.bs; import com.intellij.codeInsight.daemon.*; import com.intellij.execution.process.*; import com.intellij.execution.ui.*; import com.intellij.notification.*; import com.intellij.openapi.components.*; import com.intellij.openapi.editor.*; import com.intellij.openapi.project.*; import com.intellij.openapi.vfs.*; import com.reason.comp.*; import com.reason.hints.*; import com.reason.ide.console.*; import com.reason.ide.console.bs.*; import com.reason.ide.settings.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; import java.util.concurrent.atomic.*; @Service(Service.Level.PROJECT) public final class BsCompiler implements ORCompiler { private static final Log LOG = Log.create("bs.compiler"); private final Project myProject; private final AtomicBoolean myRefreshNinjaIsNeeded = new AtomicBoolean(true); private final AtomicBoolean myProcessStarted = new AtomicBoolean(false); private Boolean myDisabled = null; // Never call directly, use isDisabled() private Ninja myNinja = new Ninja(null); private BsCompiler(@NotNull Project project) { myProject = project; } @Override public @NotNull CompilerType getType() { return CompilerType.BS; } public void refreshNinjaBuild() { myRefreshNinjaIsNeeded.compareAndSet(false, true); } @Override public @NotNull String getFullVersion(@Nullable VirtualFile file) { return new BsProcess(myProject).getFullVersion(file); } public @NotNull String getNamespace(@NotNull VirtualFile sourceFile) { BsConfig bsConfig = myProject.getService(ORCompilerConfigManager.class).getNearestConfig(sourceFile); return bsConfig == null ? "" : bsConfig.getNamespace(); } @Override public void runDefault(@NotNull VirtualFile file, @Nullable ORProcessTerminated onProcessTerminated) { run(file, CliType.Bs.MAKE, onProcessTerminated); } @Override public void run(@Nullable VirtualFile file, @NotNull CliType cliType, @Nullable ORProcessTerminated onProcessTerminated) { if (!isDisabled() && myProject.getService(ORSettings.class).isBsEnabled()) { VirtualFile configFile = BsPlatform.findConfigFile(myProject, file); BsConfig bsConfig = myProject.getService(ORCompilerConfigManager.class).getConfig(configFile); if (configFile == null || bsConfig == null) { return; } if (myProcessStarted.compareAndSet(false, true)) { ProcessHandler bscHandler = new BsProcess(myProject).create(configFile, cliType, onProcessTerminated); ConsoleView console = myProject.getService(ORToolWindowManager.class).getConsoleView(BsToolWindowFactory.ID); if (bscHandler != null && console != null) { long start = System.currentTimeMillis(); bscHandler.addProcessListener(new ProcessFinishedListener(start)); bscHandler.addProcessListener(new ProcessAdapter() { @Override public void processTerminated(@NotNull ProcessEvent event) { myProcessStarted.compareAndSet(true, false); LOG.debug("Compilation process terminated, restart daemon code analyzer for all edited files"); DaemonCodeAnalyzer.getInstance(myProject).restart(); } }); console.attachToProcess(bscHandler); bscHandler.startNotify(); myProject.getService(InsightManager.class).downloadRincewindIfNeeded(configFile); } } else { myProcessStarted.compareAndSet(true, false); } } } @Override public boolean isConfigured(@NotNull Project project) { // BuckleScript doesn't require any project-level configuration return true; } @Override public boolean isAvailable(@NotNull Project project) { return !BsPlatform.findConfigFiles(project).isEmpty(); } public @Nullable String convert(@Nullable VirtualFile virtualFile, boolean isInterface, @NotNull String fromFormat, @NotNull String toFormat, @NotNull Document document) { String result = null; if (virtualFile != null) { BsFormatProcess refmt = myProject.getService(BsFormatProcess.class); String oldText = document.getText(); String newText = refmt.convert(virtualFile, isInterface, fromFormat, toFormat, oldText); // additional protection result = oldText.isEmpty() || newText.isEmpty() ? null : newText; } return result; } public @NotNull Ninja readNinjaBuild(@Nullable VirtualFile contentRoot) { if (myRefreshNinjaIsNeeded.get() && contentRoot != null) { VirtualFile ninjaFile = contentRoot.findFileByRelativePath("lib/bs/build.ninja"); if (ninjaFile != null) { myNinja = new Ninja(FileUtil.readFileContent(ninjaFile)); } } return myNinja; } // endregion private boolean isDisabled() { if (myDisabled == null) { myDisabled = Boolean.getBoolean("reasonBsbDisabled"); if (myDisabled) { // Possible but you should NEVER do that Notifications.Bus.notify(new ORNotification("Bsb", "Bucklescript is disabled", NotificationType.WARNING)); } } return myDisabled; } @Override public boolean isAvailable() { return !myProcessStarted.get(); } } ================================================ FILE: src/main/java/com/reason/comp/bs/BsConfig.java ================================================ package com.reason.comp.bs; import org.jetbrains.annotations.*; import java.util.*; public class BsConfig { private final String myName; private final String myNamespace; private final String myJsxVersion; private final String myJsxMode; private final boolean myUncurried; private final Set myExternals = new HashSet<>(); private final Set mySources = new HashSet<>(); private final Set myDevSources = new HashSet<>(); private final Set myDeps = new HashSet<>(); private final Set myBscFlags = new HashSet<>(); private final Set myOpenedDeps = new HashSet<>(); private final String[] myPpx; private boolean myUseExternalAsSource = false; BsConfig(@NotNull String name, @Nullable String namespace, @Nullable Set sources, @Nullable Set devSources, @Nullable Set externals, @Nullable Set deps, @NotNull Set bscFlags, @Nullable List ppx, @Nullable String jsxVersion, @Nullable String jsxMode, boolean uncurried) { myName = name; myNamespace = namespace == null ? "" : namespace; myJsxVersion = jsxVersion; myJsxMode = jsxMode; myUncurried = uncurried; myPpx = ppx == null ? new String[0] : ppx.toArray(new String[0]); if (sources != null) { mySources.addAll(sources); } if (devSources != null) { myDevSources.addAll(devSources); } if (externals != null) { myExternals.addAll(externals); } if (deps != null) { myDeps.addAll(deps); } myBscFlags.addAll(bscFlags); for (String flag : myBscFlags) { if (flag.startsWith("-open ")) { String dependency = flag.substring(6).trim(); if (!dependency.isEmpty()) { myOpenedDeps.add(dependency); } } } } public @NotNull String getNamespace() { return myNamespace; } public boolean hasNamespace() { return !myNamespace.isEmpty(); } public @NotNull Set getSources() { return myUseExternalAsSource ? myExternals : mySources; } public @NotNull Set getDevSources() { return myDevSources; } public @NotNull String getName() { return myName; } public @NotNull Set getDependencies() { return myDeps; } public @NotNull Set getBscFlags() { return myBscFlags; } public @NotNull Set getOpenedDeps() { return myOpenedDeps; } public @Nullable String getJsxVersion() { return myJsxVersion; } public @Nullable String getJsxMode() { return myJsxMode; } public boolean isUncurried() { return myUncurried; } public @NotNull String[] getPpx() { return myPpx; } public void setUseExternalAsSource(boolean useExternalAsSource) { myUseExternalAsSource = useExternalAsSource; } } ================================================ FILE: src/main/java/com/reason/comp/bs/BsConfigReader.java ================================================ package com.reason.comp.bs; import static jpsplugin.com.reason.StringUtil.toFirstUpper; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParser; import com.google.gson.JsonPrimitive; import com.intellij.openapi.vfs.VirtualFile; import jpsplugin.com.reason.FileUtil; import java.util.*; import java.util.regex.*; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; public class BsConfigReader { private static final Pattern NORMALIZE = Pattern.compile(",$"); private BsConfigReader() { } @NotNull public static BsConfig read(@NotNull VirtualFile bsConfigFile) { return read(bsConfigFile, false); } @NotNull public static BsConfig read(@NotNull VirtualFile bsConfigFile, boolean useExternalAsSource) { BsConfig config = parse(FileUtil.readFileContent(bsConfigFile)); config.setUseExternalAsSource(useExternalAsSource); return config; } @NotNull static BsConfig parse(@NotNull String content) { String normalizedContent = NORMALIZE .matcher(content) .replaceAll("") .replaceAll(",[\\s\\n]*]", "]") .replaceAll(",[\\s\\n]*}", "}"); JsonElement topElement = JsonParser.parseString(normalizedContent); if (topElement.isJsonObject()) { JsonObject top = topElement.getAsJsonObject(); String name = ""; JsonPrimitive nameProp = top.getAsJsonPrimitive("name"); if (nameProp != null && nameProp.isString()) { name = nameProp.getAsString(); } String namespace = null; JsonPrimitive namespaceProp = top.getAsJsonPrimitive("namespace"); if (namespaceProp != null) { if (namespaceProp.isBoolean()) { boolean hasNamespace = namespaceProp.getAsBoolean(); namespace = hasNamespace ? toNamespace(name) : null; } else if (namespaceProp.isString()) { namespace = namespaceProp.getAsString(); } } // JSX String jsxVersion = null; String jsxMode = null; // BS config: {reason: {react-jsx: 3}} JsonElement reason = top.get("reason"); JsonObject reasonProp = reason != null && reason.isJsonObject() ? top.getAsJsonObject("reason") : null; if (reasonProp != null) { JsonPrimitive jsxProp = reasonProp.getAsJsonPrimitive("react-jsx"); if (jsxProp != null && jsxProp.isNumber()) { jsxVersion = Integer.toString(jsxProp.getAsInt()); } } else { JsonObject jsxProp = top.getAsJsonObject("jsx"); if (jsxProp != null) { JsonPrimitive jsxVersionProp = jsxProp.getAsJsonPrimitive("version"); if (jsxVersionProp != null && jsxVersionProp.isNumber()) { jsxVersion = Integer.toString(jsxVersionProp.getAsInt()); } JsonPrimitive jsxModeProp = jsxProp.getAsJsonPrimitive("mode"); if (jsxModeProp != null && jsxModeProp.isString()) { jsxMode = jsxModeProp.getAsString(); } } } // Uncurried boolean uncurried = true; // by default JsonPrimitive uncurriedProp = top.getAsJsonPrimitive("uncurried"); if (uncurriedProp != null && uncurriedProp.isBoolean()) { uncurried = uncurriedProp.getAsBoolean(); } // PPX List ppx = new ArrayList<>(); JsonArray ppxProp = top.getAsJsonArray("ppx-flags"); if (ppxProp != null) { for (JsonElement item : ppxProp) { if (item.isJsonPrimitive() && ((JsonPrimitive) item).isString()) { String itemValue = item.getAsString(); if (!itemValue.isEmpty()) { ppx.add(itemValue); } } } } // bs-platform/bsconfig.json Set externals = new HashSet<>(); JsonArray extIncludes = top.getAsJsonArray("bs-external-includes"); if (extIncludes != null) { for (JsonElement item : extIncludes) { if (item.isJsonPrimitive() && ((JsonPrimitive) item).isString()) { externals.add(item.getAsString()); } } } externals.add("lib/ocaml"); // Because of Belt ! Set sources = new HashSet<>(); Set devSources = new HashSet<>(); JsonElement srcProp = top.get("sources"); if (srcProp != null) { readSources(srcProp, "", sources); readSources(srcProp, "dev", devSources); } Set deps = new HashSet<>(); JsonArray depsProp = top.getAsJsonArray("bs-dependencies"); if (depsProp != null) { for (JsonElement item : depsProp) { if (item.isJsonPrimitive() && ((JsonPrimitive) item).isString()) { String itemValue = item.getAsString(); if (!itemValue.isEmpty()) { deps.add(itemValue); } } } } Set flags = new HashSet<>(); JsonElement bscProp = top.get("bsc-flags"); if (bscProp != null && bscProp.isJsonArray()) { for (JsonElement bscFlag : bscProp.getAsJsonArray()) { if (bscFlag.isJsonPrimitive() && ((JsonPrimitive) bscFlag).isString()) { String value = bscFlag.getAsString(); if (!value.isEmpty()) { flags.add(value); } } } } return new BsConfig(name, namespace, sources, devSources, externals, deps, flags, ppx, jsxVersion, jsxMode, uncurried); } throw new RuntimeException("Not a Bucklescript config"); } @NotNull private static String toNamespace(@NotNull String name) { StringBuilder result = new StringBuilder(name.replaceAll("_", "")); String[] tokens = result.toString().split("[-@/]"); result = new StringBuilder(toFirstUpper(tokens[0])); if (1 < tokens.length) { for (int i = 1; i < tokens.length; i++) { result.append(toFirstUpper(tokens[i])); } } return result.toString(); } private static void readSources( @NotNull JsonElement value, @NotNull String type, @NotNull Set sources) { if (value.isJsonPrimitive() && ((JsonPrimitive) value).isString()) { if (type.isEmpty()) { sources.add(value.getAsString()); } } else if (value.isJsonObject()) { assert value instanceof JsonObject; String src = parseSourceItem((JsonObject) value, type); if (src != null) { sources.add(src); } } else if (value.isJsonArray()) { for (JsonElement item : value.getAsJsonArray()) { if (item.isJsonPrimitive()) { if (((JsonPrimitive) item).isString() && type.isEmpty()) { sources.add(item.getAsString()); } } else if (item.isJsonObject()) { String src = parseSourceItem((JsonObject) item, type); if (src != null) { sources.add(src); } } } } } @Nullable private static String parseSourceItem(@NotNull JsonObject obj, @NotNull String type) { JsonPrimitive srcProp = obj.getAsJsonPrimitive("dir"); if (srcProp != null) { JsonPrimitive typeProp = obj.getAsJsonPrimitive("type"); String typeValue = typeProp != null && typeProp.isString() ? typeProp.getAsString() : ""; if (type.equals(typeValue)) { if (srcProp.isString()) { return srcProp.getAsString(); } } } return null; } } ================================================ FILE: src/main/java/com/reason/comp/bs/BsFormatProcess.java ================================================ package com.reason.comp.bs; import com.intellij.openapi.components.*; import com.intellij.openapi.editor.ex.*; import com.intellij.openapi.project.*; import com.intellij.openapi.vfs.*; import com.reason.comp.rescript.*; import com.reason.ide.settings.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; import java.io.*; import static jpsplugin.com.reason.Platform.*; @Service(Service.Level.PROJECT) public final class BsFormatProcess { private static final Log LOG = Log.create("format.refmt"); private final Project m_project; public BsFormatProcess(@NotNull Project project) { m_project = project; } @NotNull public String convert(@NotNull VirtualFile sourceFile, boolean isInterface, @NotNull String fromFormat, @NotNull String toFormat, @NotNull String code) { VirtualFile refmtDir = BsPlatform.findRefmtExecutable(m_project, sourceFile); if (refmtDir == null) { refmtDir = ResPlatform.findRefmtExecutable(m_project, sourceFile); if (refmtDir == null) { LOG.debug("No refmt binary found, reformat cancelled"); return code; } } String columnsWidth = m_project.getService(ORSettings.class).getFormatColumnWidth(); ProcessBuilder processBuilder = new ProcessBuilder(refmtDir.getPath(), "-i", Boolean.toString(isInterface), "--parse=" + fromFormat, "-p", toFormat, "-w", columnsWidth); if (LOG.isDebugEnabled()) { LOG.debug("Reformatting " + sourceFile.getPath() + " (" + fromFormat + " -> " + toFormat + ") using " + columnsWidth + " cols for project [" + m_project + "]"); } Process refmt = null; try { refmt = processBuilder.start(); BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(refmt.getOutputStream(), UTF8)); BufferedReader reader = new BufferedReader(new InputStreamReader(refmt.getInputStream(), UTF8)); BufferedReader errReader = new BufferedReader(new InputStreamReader(refmt.getErrorStream(), UTF8)); writer.write(code); writer.flush(); writer.close(); Streams.waitUntilReady(reader, errReader); StringBuilder msgBuffer = new StringBuilder(); if (!errReader.ready()) { final boolean[] empty = {true}; reader .lines() .forEach( line -> { if (empty[0]) { empty[0] = false; } else { msgBuffer.append('\n'); } msgBuffer.append(line); }); String newText = msgBuffer.toString(); if (!code.isEmpty() && !newText.isEmpty()) { // additional protection boolean ensureNewLineAtEOF = EditorSettingsExternalizable.getInstance().isEnsureNewLineAtEOF(); boolean addNewLine = ensureNewLineAtEOF && newText.charAt(newText.length() - 1) != '\n'; return addNewLine ? newText + '\n' : newText; } } } catch (IOException | RuntimeException e) { LOG.warn(e); } finally { if (refmt != null) { refmt.destroyForcibly(); } } // Something bad happened, do nothing return code; } } ================================================ FILE: src/main/java/com/reason/comp/bs/BsLineProcessor.java ================================================ package com.reason.comp.bs; import com.reason.comp.*; import com.reason.ide.annotations.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; import java.util.*; import java.util.regex.*; import static com.reason.comp.bs.BsLineProcessor.BuildStatus.*; import static java.lang.Integer.*; /** * Line processor is a state machine. */ public class BsLineProcessor implements CompilerOutputAnalyzer { private static final Pattern FILE_LOCATION = Pattern.compile("File \"(.+)\", line (\\d+), characters (\\d+)-(\\d+):\n?"); private static final Pattern POSITIONS = Pattern.compile("[\\s:]\\d+:\\d+(-\\d+(:\\d+)?)?$"); private final Log m_log; private final List m_bsbInfo = new ArrayList<>(); @Override public @NotNull List getOutputInfo() { return m_bsbInfo; } private @Nullable OutputInfo m_latestInfo = new OutputInfo(); private @NotNull BuildStatus m_status = BuildStatus.unknown; enum BuildStatus { unknown, // // warning steps warningDetected, warningLinePos, warningSourceExtract, warningMessage, // // error steps errorDetected, errorLinePos, errorSourceExtract, errorMessage, // // syntax error syntaxError } public BsLineProcessor(Log log) { m_log = log; } @Override public void onTextAvailable(@NotNull String text) { String trimmedText = text.trim(); if (m_log.isTraceEnabled()) { m_log.trace(trimmedText); } switch (m_status) { /* Warning */ case warningDetected: // Must contain warning location (file/position) // ...path\src\File.re 61:10 m_latestInfo = extractFilePositions(text); if (m_latestInfo != null) { m_latestInfo.isError = false; } m_status = warningLinePos; break; case warningLinePos: case errorLinePos: if (m_latestInfo != null && !trimmedText.isEmpty()) { m_status = m_latestInfo.isError ? errorSourceExtract : warningSourceExtract; } break; case warningSourceExtract: case errorSourceExtract: if (m_latestInfo != null && trimmedText.isEmpty()) { m_status = m_latestInfo.isError ? errorMessage : warningMessage; } break; case warningMessage: case errorMessage: if (trimmedText.isEmpty() || text.charAt(0) != ' ') { // create bsb info if (m_latestInfo != null) { m_latestInfo.message = m_latestInfo.message.trim(); } m_status = unknown; } else if (m_latestInfo != null) { m_latestInfo.message += text; } break; /* Error */ case errorDetected: // Must contain error location (file/position) // ...path\src\File.re 61:10-23 m_latestInfo = extractFilePositions(text); if (m_latestInfo != null) { m_latestInfo.isError = true; } m_status = errorLinePos; break; default: if (trimmedText.startsWith("Warning number")) { reset(); m_status = warningDetected; } else if (text.startsWith("Error:")) { // It's a one line message m_status = syntaxError; if (m_latestInfo != null) { m_latestInfo.message = text.substring(6).trim(); } } else if (trimmedText.startsWith("We've found a bug for you")) { if (m_status != syntaxError) { reset(); m_status = errorDetected; } } else if (m_latestInfo != null && trimmedText.startsWith("Hint:")) { m_latestInfo.message += ". " + text.trim(); } else if (trimmedText.startsWith("File")) { m_latestInfo = extractExtendedFilePositions(trimmedText); } } } public void reset() { m_status = unknown; m_latestInfo = null; } // File "...path/src/Source.re", line 111, characters 0-3: private @Nullable OutputInfo extractExtendedFilePositions(@Nullable String text) { if (text != null) { Matcher matcher = FILE_LOCATION.matcher(text); if (matcher.matches()) { String path = matcher.group(1); String line = matcher.group(2); String colStart = matcher.group(3); String colEnd = matcher.group(4); OutputInfo info = addInfo(path, line, colStart, colEnd); if (info.colStart < 0 || info.colEnd < 0) { m_log.error("Can't decode columns for [" + text + "]"); return null; } return info; } } return null; } // "...path/src/Source.re 111:21-112:22" or " ...path/src/Source.re:111:21-112:22" // "...path/src/Source.re 111:21-22" or "...path/src/Source.re:111:21-22" // "...path/src/Source.re 111:21" or "...path/src/Source.re:111:21" private @Nullable OutputInfo extractFilePositions(@Nullable String text) { if (text == null) { return null; } String trimmed = text.trim(); Matcher matcher = POSITIONS.matcher(trimmed); // extract path and positions if (matcher.find()) { String positions = matcher.group(); // remove positions from text to get path String path = text.replace(positions, ""); // remove leading space or colon from positions positions = positions.substring(1); // split "111:21-112:22" into ["111:21", "112:22"] String[] startAndEndPositions = positions.split("-"); // only start positions found, ["111:21"] String[] startLineAndCol = startAndEndPositions[0].split(":"); if (startAndEndPositions.length == 1) { return addInfo(path, startLineAndCol[0], startLineAndCol[1], null, null); } // both start and end positions present if (startAndEndPositions.length == 2) { String[] endLineAndCol = startAndEndPositions[1].split(":"); // "111:21-22" --> "111:21-111:22" if (endLineAndCol.length == 1) { return addInfo( path, startLineAndCol[0], startLineAndCol[1], startLineAndCol[0], endLineAndCol[0]); } // "111:21-112:22" if (endLineAndCol.length == 2) { return addInfo( path, startLineAndCol[0], startLineAndCol[1], endLineAndCol[0], endLineAndCol[1]); } } m_log.error("Can't decode columns for [" + text + "]"); } return null; } private @NotNull OutputInfo addInfo(@NotNull String path, @NotNull String lineStart, @NotNull String colStart, @Nullable String lineEnd, @Nullable String colEnd) { OutputInfo info = new OutputInfo(); info.path = path; info.isError = true; info.lineStart = parseInt(lineStart); info.colStart = parseInt(colStart); info.lineEnd = lineEnd == null ? info.lineStart : parseInt(lineEnd); info.colEnd = colEnd == null ? info.colStart + 1 : parseInt(colEnd) + 1; m_bsbInfo.add(info); return info; } private @NotNull OutputInfo addInfo(@NotNull String path, @NotNull String line, @NotNull String colStart, @NotNull String colEnd) { OutputInfo info = new OutputInfo(); info.path = path; info.isError = true; info.lineStart = parseInt(line); info.colStart = parseInt(colStart); info.lineEnd = info.lineStart; info.colEnd = parseInt(colEnd); if (info.colEnd == info.colStart) { info.colEnd += 1; } m_bsbInfo.add(info); return info; } } ================================================ FILE: src/main/java/com/reason/comp/bs/BsNotification.java ================================================ package com.reason.comp.bs; import com.intellij.ide.browsers.*; import com.intellij.notification.*; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.project.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; import java.net.*; import static com.intellij.notification.NotificationType.*; public class BsNotification { private BsNotification() { } @Nls public static void showBsbNotFound(String workingDirectory) { ORNotification notification = new ORNotification( "Bsb", "" + "Can't find bsb.\n" + "The working directory is '" + workingDirectory + "'.\n" + "Be sure that bsb is installed and reachable from that directory." + "", ERROR); notification.addAction(new DumbAwareAction("Open documentation") { @Override public void actionPerformed(@NotNull AnActionEvent e) { URI docURL = URI.create("https://giraud.github.io/reasonml-idea-plugin/docs/build-tools/bucklescript"); BrowserLauncher.getInstance().browse(docURL); } }); Notifications.Bus.notify(notification); } @Nls public static void showWorkingDirectoryNotFound() { Notifications.Bus.notify( new ORNotification( "BuckleScript", "Can't determine working directory.\nEnsure your project contains a bsconfig.json file.", ERROR)); } } ================================================ FILE: src/main/java/com/reason/comp/bs/BsPlatform.java ================================================ package com.reason.comp.bs; import com.intellij.openapi.project.*; import com.intellij.openapi.vfs.*; import com.intellij.psi.search.*; import com.reason.comp.*; import com.reason.ide.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; import java.util.*; import static com.reason.comp.ORConstants.*; public class BsPlatform { private static final Log LOG = Log.create("bs.platform"); private BsPlatform() { } public static @NotNull List findConfigFiles(@NotNull Project project) { GlobalSearchScope scope = GlobalSearchScope.projectScope(project); List validConfigs = FilenameIndex.getVirtualFilesByName(BS_CONFIG_FILENAME, scope).stream() .filter(bsConfigFile -> { VirtualFile bsbBin = ORPlatform.findCompilerPathInNodeModules(project, bsConfigFile, BS_DIR, BSC_EXE_NAME); VirtualFile resBin = ORPlatform.findCompilerPathInNodeModules(project, bsConfigFile, RESCRIPT_DIR, BSC_EXE_NAME); return bsbBin != null && resBin == null; }) .sorted(ORFileUtils.FILE_DEPTH_COMPARATOR) .toList(); if (LOG.isDebugEnabled()) { LOG.debug("Valid configs for project=\"" + project.getName() + "\": [" + Joiner.join(",", validConfigs) + "]"); } return validConfigs; } public static @Nullable VirtualFile findConfigFile(@NotNull Project project, @Nullable VirtualFile sourceFile) { return sourceFile != null ? ORFileUtils.findAncestor(project, sourceFile, BS_CONFIG_FILENAME) : findConfigFiles(project).stream().findFirst().orElse(null); } public static @Nullable VirtualFile findBsbExecutable(@NotNull Project project, @Nullable VirtualFile sourceFile) { VirtualFile configFile = findConfigFile(project, sourceFile); VirtualFile binDir = ORPlatform.findCompilerPathInNodeModules(project, configFile, BS_DIR, BSB_EXE_NAME); return binDir != null ? ORPlatform.findBinary(binDir, BSB_EXE_NAME) : null; } public static @Nullable VirtualFile findBscExecutable(@NotNull Project project, @Nullable VirtualFile sourceFile) { VirtualFile configFile = findConfigFile(project, sourceFile); VirtualFile binDir = ORPlatform.findCompilerPathInNodeModules(project, configFile, BS_DIR, BSC_EXE_NAME); return binDir != null ? ORPlatform.findBinary(binDir, BSC_EXE_NAME) : null; } public static @Nullable VirtualFile findRefmtExecutable(@NotNull Project project, @NotNull VirtualFile sourceFile) { VirtualFile bsConfigFile = ORFileUtils.findAncestor(project, sourceFile, BS_CONFIG_FILENAME); VirtualFile bsDir = ORPlatform.findCompilerPathInNodeModules(project, bsConfigFile, BS_DIR, BSC_EXE_NAME); if (bsDir == null) { return null; } VirtualFile binaryInBsPlatform; // first, try standard name binaryInBsPlatform = ORPlatform.findBinary(bsDir, REFMT_EXE_NAME); if (binaryInBsPlatform == null) { // next, try alternative names binaryInBsPlatform = ORPlatform.findBinary(bsDir, "refmt3"); if (binaryInBsPlatform == null) { binaryInBsPlatform = ORPlatform.findBinary(bsDir, "bsrefmt"); } } return binaryInBsPlatform; } } ================================================ FILE: src/main/java/com/reason/comp/bs/BsProcess.java ================================================ package com.reason.comp.bs; import com.intellij.codeInsight.daemon.*; import com.intellij.execution.*; import com.intellij.execution.configurations.*; import com.intellij.execution.process.*; import com.intellij.notification.*; import com.intellij.openapi.project.*; import com.intellij.openapi.vfs.*; import com.reason.comp.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; import java.io.*; import static com.intellij.notification.NotificationType.*; public final class BsProcess { private final @NotNull Project myProject; private @Nullable KillableProcessHandler myBsb; public BsProcess(@NotNull Project project) { myProject = project; } @Nullable ProcessHandler create(@NotNull VirtualFile source, @NotNull CliType cliType, @Nullable ORProcessTerminated onProcessTerminated) { try { if (cliType instanceof CliType.Bs) { return createProcessHandler(source, (CliType.Bs) cliType, onProcessTerminated); } else { Notifications.Bus.notify(new ORNotification("Bsb", "Invalid commandline type (" + cliType.getCompilerType() + ")", WARNING)); } } catch (ExecutionException e) { ORNotification.notifyError("Bsb", "Execution exception", e.getMessage()); } return null; } @Nullable private ProcessHandler createProcessHandler(@NotNull VirtualFile sourceFile, @NotNull CliType.Bs cliType, @Nullable ORProcessTerminated onProcessTerminated) throws ExecutionException { killIt(); GeneralCommandLine cli = getGeneralCommandLine(sourceFile, cliType); if (cli != null) { myBsb = new BsColoredProcessHandler(cli, (_none) -> { if (onProcessTerminated != null) { onProcessTerminated.run(null); } // When build is done, we need to refresh editors to be notified of the latest modifications DaemonCodeAnalyzer.getInstance(myProject).restart(); }); } return myBsb; } private void killIt() { if (myBsb != null) { myBsb.killProcess(); myBsb = null; } } @Nullable private GeneralCommandLine getGeneralCommandLine(@NotNull VirtualFile sourceFile, @NotNull CliType.Bs cliType) { VirtualFile bsConfigFile = BsPlatform.findConfigFile(myProject, sourceFile); VirtualFile bsConfigDir = bsConfigFile != null ? bsConfigFile.getParent() : null; if (bsConfigDir == null) { BsNotification.showWorkingDirectoryNotFound(); return null; } String bsConfigRootPath = bsConfigDir.getPath(); VirtualFile bsbExecutable = BsPlatform.findBsbExecutable(myProject, sourceFile); if (bsbExecutable == null) { BsNotification.showBsbNotFound(bsConfigRootPath); return null; } String bsbPath = bsbExecutable.getPath(); GeneralCommandLine cli = switch (cliType) { case MAKE -> new GeneralCommandLine(bsbPath, "-make-world"); case CLEAN_MAKE -> new GeneralCommandLine(bsbPath, "-clean-world", "-make-world"); }; cli.withWorkDirectory(bsConfigRootPath); cli.withEnvironment("NINJA_ANSI_FORCED", "1"); return cli; } public @NotNull String getFullVersion(@Nullable VirtualFile sourceFile) { VirtualFile bsc = BsPlatform.findBscExecutable(myProject, sourceFile); if (bsc != null) { String[] command = new String[]{bsc.getPath(), "-version"}; try (InputStream inputStream = Runtime.getRuntime().exec(command).getInputStream()) { BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); return reader.readLine(); } catch (IOException e) { return "error: " + e.getMessage(); } } return "unknown (bsc not found)"; } } ================================================ FILE: src/main/java/com/reason/comp/bs/BsResolvedCompiler.java ================================================ package com.reason.comp.bs; import com.intellij.openapi.vfs.*; import com.reason.comp.*; import org.jetbrains.annotations.*; public class BsResolvedCompiler extends ORResolvedCompiler { public BsResolvedCompiler(@NotNull BsCompiler compiler, @NotNull VirtualFile contentRootFile, @Nullable VirtualFile binFile) { super(compiler, contentRootFile, binFile); } public @Nullable Ninja readNinja() { return myCompiler.readNinjaBuild(getContentRoot()); } } ================================================ FILE: src/main/java/com/reason/comp/bs/Ninja.java ================================================ package com.reason.comp.bs; import org.jetbrains.annotations.*; import java.util.*; import java.util.stream.*; import static java.util.Collections.*; /** * REASON format (8.2.0): * ---------------------- *

* g_pkg_flg = -bs-package-name bs-basic * src_root_dir = U:\reason\projects\bs-basic * bsc = "U:\reason\projects\bs-basic\node_modules\bs-platform\win32\bsc.exe" * bsdep = "U:\reason\projects\bs-basic\node_modules\bs-platform\win32\bsb_helper.exe" * warnings = * bsc_flags = * ppx_flags = * g_dpkg_incls = * g_ns = * g_lib_incls = -I src -I "U:\reason\projects\bs-basic\node_modules\reason-react\lib\ocaml" * rule build_ast_from_re * command = $bsc $warnings -bs-jsx 3 $bsc_flags -o $out -bs-syntax-only -bs-binary-ast $in * description = Building ${out} * build src\Foo.reast : build_ast_from_re $src_root_dir\src\Foo.re * rule mk_deps * command = $bsdep -hash 20de222cbd4694f33717b5139235334c $g_ns $in * restat = 1 * description = Building ${out} * build src\Foo.d : mk_deps src\Foo.reast * rule ml_cmj_cmi * command = $bsc $g_pkg_flg $g_lib_incls $warnings $bsc_flags -o $out $in * dyndep = $in_e.d * restat = 1 * description = Building ${out} * build src\Foo.cmj | src\Foo.cmi $src_root_dir\lib\js\src\Foo.js : ml_cmj_cmi src\Foo.reast || src\Foo.d * g_pkg_flg = $g_pkg_flg -bs-package-output commonjs:lib\js\src *

*

* RESCRIPT format (8.4.0): * ---------------- *

* rescript = 1 * g_finger := U:\reason\projects\bs-basic\node_modules\reason-react\lib\ocaml\install.stamp * rule astj * command = "U:\reason\projects\bs-basic\node_modules\bs-platform\win32\bsc.exe" -bs-v 8.4.2 -bs-jsx 3 -absname -bs-ast -o $out $i * o src\Foo.ast : astj ..\..\src\Foo.re * rule deps * command = "U:\reason\projects\bs-basic\node_modules\bs-platform\win32\bsb_helper.exe" -hash 20de222cbd4694f33717b5139235334c $in * restat = 1 * o src\Foo.d : deps src\Foo.ast * rule mij * command = "U:\reason\projects\bs-basic\node_modules\bs-platform\win32\bsc.exe" -I src -I "U:\reason\projects\bs-basic\node_modules\reason-react\lib\ocaml" -bs-package-name bs-basic -bs-package-output commonjs:lib\js\$in_d:.js -bs-v $g_finger $i * dyndep = 1 * restat = 1 * o src\Foo.cmj src\Foo.cmi ..\js\src\Foo.js : mij src\Foo.ast */ public final class Ninja { private final @NotNull List m_includes; private final @NotNull List m_ppxIncludes; private final @NotNull List m_pkgFlags; private final @NotNull List m_bscFlags; private final @NotNull List m_args = new ArrayList<>(); private final @NotNull List m_argsDev = new ArrayList<>(); private final boolean m_isRescriptFormat; public Ninja(@Nullable String contents) { m_isRescriptFormat = contents != null && contents.startsWith("rescript"); m_includes = readIncludes(contents); m_ppxIncludes = readPpxIncludes(contents); m_pkgFlags = readPkgFlags(contents); m_bscFlags = readBscFlags(contents); if (m_isRescriptFormat) { List astj = extractRuleAstj(contents); m_args.addAll(astj); m_args.addAll(extractRuleMij(contents)); m_argsDev.addAll(astj); m_argsDev.addAll(extractRuleMijDev(contents)); } } private @NotNull List extractRuleAstj(@NotNull String contents) { List filteredTokens = new ArrayList<>(); int ruleMijPos = contents.indexOf("rule astj"); if (0 < ruleMijPos) { int commandPos = contents.indexOf("command", ruleMijPos); if (0 < commandPos) { int commandEolPos = contents.indexOf("\n", commandPos); String command = contents.substring(commandPos + 9, commandEolPos).trim(); String[] tokens = command.split(" "); for (int i = 0; i < tokens.length; i++) { String token = tokens[i]; if ("-ppx".equals(token)) { filteredTokens.add(token); filteredTokens.add(tokens[i + 1]); } } } } return filteredTokens; } private @NotNull List extractMijCommand(int rulePos, @NotNull String contents) { int commandPos = contents.indexOf("command", rulePos); if (0 < commandPos) { int commandEolPos = contents.indexOf("\n", commandPos); String command = contents.substring(commandPos + 9, commandEolPos).trim(); String[] tokens = command.split(" "); List filteredTokens = Arrays.stream(tokens).filter(s -> !s.isEmpty() && !"$g_finger".equals(s) && !"$i".equals(s) && !"-bs-v".equals(s) && !"-bs-package-output".equals(s) && !s.contains("$in_d:")).collect(Collectors.toList()); filteredTokens.remove(0); return filteredTokens; } return emptyList(); } private @NotNull List extractRuleMij(@NotNull String contents) { int ruleMijPos = contents.indexOf("rule mij"); if (0 < ruleMijPos) { String ruleValue = contents.substring(ruleMijPos, ruleMijPos + 12); if ("rule mij_dev".equals(ruleValue)) { // search again ruleMijPos = contents.indexOf("rule mij", ruleMijPos + 12); if (0 < ruleMijPos) { return extractMijCommand(ruleMijPos, contents); } } else { return extractMijCommand(ruleMijPos, contents); } } return emptyList(); } private @NotNull List extractRuleMijDev(@NotNull String contents) { int ruleMijPos = contents.indexOf("rule mij_dev"); if (0 < ruleMijPos) { return extractMijCommand(ruleMijPos, contents); } return emptyList(); } public @NotNull List getArgs() { return m_args; } public @NotNull List getArgsDev() { return m_argsDev; } private @NotNull List readIncludes(@Nullable String contents) { List result = new ArrayList<>(); if (contents == null) { return result; } int g_lib_incls = contents.indexOf("g_lib_incls"); if (g_lib_incls >= 0) { int end_of_g_lib_incls = contents.indexOf("\n", g_lib_incls); String lineContents = contents.substring(g_lib_incls + 13, end_of_g_lib_incls).trim(); for (String token : lineContents.split("-I\\s")) { String trimmedToken = token.trim(); if (!trimmedToken.isEmpty()) { if (trimmedToken.startsWith("\"")) { result.add(trimmedToken.substring(1, trimmedToken.length() - 1)); } else { result.add(trimmedToken); } } } } int g_dpkg_incls = contents.indexOf("g_dpkg_incls"); if (g_dpkg_incls >= 0) { int end_of_g_dpkg_incls = contents.indexOf("\n", g_dpkg_incls); String lineContents = contents.substring(g_dpkg_incls + 14, end_of_g_dpkg_incls).trim(); for (String token : lineContents.split("-I\\s")) { String trimmedToken = token.trim(); if (!trimmedToken.isEmpty()) { if (trimmedToken.startsWith("\"")) { result.add(trimmedToken.substring(1, trimmedToken.length() - 1)); } else { result.add(trimmedToken); } } } } return result; } public @NotNull List readPpxIncludes(@Nullable String contents) { List result = new ArrayList<>(); if (contents == null) { return result; } int ppx_flags = contents.indexOf("ppx_flags"); if (ppx_flags >= 0) { int end_of_ppx_flags = contents.indexOf("\n", ppx_flags); String ppxLine = contents.substring(ppx_flags + 11, end_of_ppx_flags).trim(); String[] split = ppxLine.split("-ppx\\s"); for (String include : split) { String trimmedInclude = include.trim(); if (!trimmedInclude.isEmpty()) { if (trimmedInclude.startsWith("\"")) { result.add(trimmedInclude.substring(1, trimmedInclude.length() - 1)); } else { result.add(trimmedInclude); } } } } return result; } private @NotNull List readPkgFlags(@Nullable String contents) { List result = new ArrayList<>(); if (contents == null) { return result; } int property = contents.indexOf("g_pkg_flg"); if (property >= 0) { int end_of_property = contents.indexOf("\n", property); String lineContents = contents.substring(property + 11, end_of_property).trim(); for (String tokens : lineContents.split("\\s")) { String trimmedToken = tokens.trim(); if (!trimmedToken.isEmpty()) { result.add(trimmedToken); } } } return result; } private @NotNull List readBscFlags(@Nullable String contents) { List result = new ArrayList<>(); if (contents == null) { return result; } int property = contents.indexOf("bsc_flags"); if (property >= 0) { int end_of_property = contents.indexOf("\n", property); String lineContents = contents.substring(property + 11, end_of_property).trim(); for (String token : lineContents.split("\\s")) { String trimmedToken = token.trim(); if (!trimmedToken.isEmpty()) { result.add(trimmedToken); } } } return result; } public void addInclude(@NotNull String source) { m_includes.add(source); } public @NotNull List getPkgFlags() { return m_pkgFlags; } public @NotNull List getBscFlags() { return m_bscFlags; } public @NotNull List getPpxIncludes() { return m_ppxIncludes; } public @NotNull List getIncludes() { return m_includes; } public boolean isRescriptFormat() { return m_isRescriptFormat; } } ================================================ FILE: src/main/java/com/reason/comp/dune/DuneCompiler.java ================================================ package com.reason.comp.dune; import com.intellij.execution.process.*; import com.intellij.openapi.components.*; import com.intellij.openapi.project.*; import com.intellij.openapi.vfs.*; import com.reason.comp.*; import com.reason.comp.esy.*; import com.reason.hints.*; import com.reason.ide.console.*; import com.reason.ide.console.dune.*; import com.reason.ide.settings.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; import java.util.concurrent.atomic.*; @Service(Service.Level.PROJECT) public final class DuneCompiler implements ORCompiler { private static final Log LOG = Log.create("dune.compiler"); private final @NotNull Project myProject; private final AtomicBoolean myProcessStarted = new AtomicBoolean(false); DuneCompiler(@NotNull Project project) { myProject = project; } @Override public @NotNull CompilerType getType() { return CompilerType.DUNE; } @Override public @NotNull String getFullVersion(@Nullable VirtualFile file) { /* Dune version, but we don't care in fact. We are interested in OCaml version. * GeneralCommandLine cli = new DuneProcess.DuneCommandLine(myProject, "dune") .addParameters(CliType.Dune.VERSION) .create(file); try (InputStream inputStream = Runtime.getRuntime().exec(cli.getCommandLineString(), new String[]{}, cli.getWorkDirectory()).getInputStream()) { BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); return reader.readLine(); } catch (IOException e) { return "error: " + e.getMessage(); } /* String version = "unknown "; version += " - not implemented yet"; //DuneFacet duneFacet = DunePlatform.getFacet(myProject, file); //if (duneFacet == null) { // return version + "(dune facet not found)"; //} else { // Sdk odk = duneFacet.getODK(); // SdkTypeId sdkType = odk == null ? null : odk.getSdkType(); // if (sdkType == null) { // return version + "(SDK not found)"; // } // version = sdkType.getVersionString(odk); //} return "Dune ( OCaml:" + version + " )"; */ ORSettings settings = myProject.getService(ORSettings.class); return "Dune (OCaml:" + settings.getSwitchName() + ")"; } @Override public boolean isConfigured(@NotNull Project project) { ORSettings settings = project.getService(ORSettings.class); return !settings.getOpamLocation().isEmpty() && settings.getSwitchName() != null; } @Override public boolean isAvailable(@NotNull Project project) { return !EsyPlatform.isEsyProject(project) && !DunePlatform.findConfigFiles(project).isEmpty(); } @Override public void runDefault(@NotNull VirtualFile file, @Nullable ORProcessTerminated onProcessTerminated) { run(file, CliType.Dune.BUILD, onProcessTerminated); } @Override public void run(@Nullable VirtualFile file, @NotNull CliType cliType, @Nullable ORProcessTerminated onProcessTerminated) { if (!(cliType instanceof CliType.Dune)) { LOG.error("Invalid cliType for dune compiler. cliType = " + cliType); return; } if (myProject.isDisposed()) { return; } if (myProcessStarted.compareAndSet(false, true)) { VirtualFile sourceFile = DunePlatform.findConfigFiles(myProject).stream().findFirst().orElse(null); DuneConsoleView console = (DuneConsoleView) myProject.getService(ORToolWindowManager.class).getConsoleView(DuneToolWindowFactory.ID); DuneProcess process = new DuneProcess(myProject); ProcessHandler processHandler = sourceFile == null ? null : process.create(sourceFile, cliType, onProcessTerminated); if (processHandler != null && console != null) { processHandler.addProcessListener(new CompilerOutputListener(myProject, new DuneOutputAnalyzer())); processHandler.addProcessListener(new ProcessFinishedListener()); processHandler.addProcessListener(new ProcessAdapter() { @Override public void processTerminated(@NotNull ProcessEvent event) { myProcessStarted.compareAndSet(true, false); } }); console.attachToProcess(processHandler); process.startNotify(); myProject.getService(InsightManager.class).downloadRincewindIfNeeded(sourceFile); } else { myProcessStarted.compareAndSet(true, false); } } } @Override public boolean isAvailable() { return !myProcessStarted.get(); } } ================================================ FILE: src/main/java/com/reason/comp/dune/DuneOutputAnalyzer.java ================================================ package com.reason.comp.dune; import com.reason.comp.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; // See tests for error messages patterns // Need some version aware analyzer ? see https://github.com/giraud/reasonml-idea-plugin/issues/174 public class DuneOutputAnalyzer extends ORCompilerOutputAnalyzer { private static final Log LOG = Log.create("dune.output"); /* unknown -> fileLocation fileLocation -> message -> sourceCode sourceCode -> message */ enum OutputState { unknown, fileLocation, sourceCode, message, } private @NotNull OutputState myState = OutputState.unknown; @Override public void onTextAvailable(@NotNull String line) { // State transition: unknown -> fileLocation if (line.startsWith("File") && myState == OutputState.unknown) { myCurrentInfo = extractExtendedFilePositions(LOG, line); myState = OutputState.fileLocation; } // State transition: fileLocation|sourceCode -> message [ERROR] else if (line.startsWith("Error:") && (myState == OutputState.fileLocation || myState == OutputState.sourceCode)) { myState = OutputState.message; if (myCurrentInfo != null) { myCurrentInfo.isError = true; myCurrentInfo.message = line.substring(6).trim(); } } // Error message might be on multiple lines else if (myState == OutputState.message && myCurrentInfo != null && myCurrentInfo.isError && line.startsWith(" ")) { boolean endMessage = true; String trimmedLine = line.trim(); if (!trimmedLine.isEmpty() && !trimmedLine.startsWith("File")) { myCurrentInfo.message += " " + trimmedLine; endMessage = trimmedLine.endsWith("."); } if (endMessage) { myState = OutputState.unknown; myCurrentInfo = null; } } // State transition: fileLocation|sourceCode -> message [WARNING] else if (line.startsWith("Warning") && (myState == OutputState.fileLocation || myState == OutputState.sourceCode)) { myState = OutputState.message; if (myCurrentInfo != null) { myCurrentInfo.isError = false; int pos = line.indexOf(":"); myCurrentInfo.message = line.substring(pos + 1).trim(); } } // State transition: fileLocation -> sourceCode else if (myState == OutputState.fileLocation) { myState = OutputState.sourceCode; } // Fallback else if (myState != OutputState.sourceCode && myState != OutputState.unknown) { myState = OutputState.unknown; myCurrentInfo = null; } } } ================================================ FILE: src/main/java/com/reason/comp/dune/DunePlatform.java ================================================ package com.reason.comp.dune; import com.intellij.openapi.module.Module; import com.intellij.openapi.project.*; import com.intellij.openapi.roots.*; import com.intellij.openapi.vfs.*; import com.intellij.psi.search.*; import com.reason.ide.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; import java.util.*; public class DunePlatform { public static final String DUNE_EXECUTABLE_NAME = "dune"; public static final String DUNE_PROJECT_FILENAME = "dune-project"; public static final String DUNE_FILENAME = "dune"; public static final String LEGACY_JBUILDER_FILENAME = "jbuild"; private DunePlatform() { } public static @NotNull List findConfigFiles(@NotNull Project project) { GlobalSearchScope scope = GlobalSearchScope.projectScope(project); return FilenameIndex.getVirtualFilesByName(DUNE_PROJECT_FILENAME, scope).stream() .sorted(ORFileUtils.FILE_DEPTH_COMPARATOR) .toList(); } public static @Nullable VirtualFile findContentRoot(@NotNull Project project, @NotNull VirtualFile sourceFile) { Module module = Platform.getModule(project, sourceFile); ModuleRootManager rootManager = module != null ? ModuleRootManager.getInstance(module) : null; VirtualFile[] contentRoots = rootManager != null ? rootManager.getContentRoots() : null; return contentRoots != null ? contentRoots[0] : null; } } ================================================ FILE: src/main/java/com/reason/comp/dune/DuneProcess.java ================================================ package com.reason.comp.dune; import com.intellij.execution.*; import com.intellij.execution.configurations.*; import com.intellij.execution.process.*; import com.intellij.openapi.project.*; import com.intellij.openapi.vfs.*; import com.reason.comp.*; import com.reason.comp.ocaml.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; import java.util.*; public final class DuneProcess { private final @NotNull Project myProject; private @Nullable KillableColoredProcessHandler myProcessHandler; DuneProcess(@NotNull Project project) { myProject = project; } // Wait for the tool window to be ready before starting the process public void startNotify() { if (myProcessHandler != null && !myProcessHandler.isStartNotified()) { try { myProcessHandler.startNotify(); } catch (Throwable e) { // already done ? } } } public @Nullable ProcessHandler create(@NotNull VirtualFile source, @NotNull CliType cliType, @Nullable ORProcessTerminated onProcessTerminated) { try { killIt(); GeneralCommandLine cli = new DuneCommandLine(myProject, DunePlatform.DUNE_EXECUTABLE_NAME) .addParameters((CliType.Dune) cliType) .create(source); if (cli != null) { myProcessHandler = new KillableColoredProcessHandler(cli); if (onProcessTerminated != null) { myProcessHandler.addProcessListener(new ProcessAdapter() { @Override public void processTerminated(@NotNull ProcessEvent event) { onProcessTerminated.run(null); } }); } } return myProcessHandler; } catch (ExecutionException e) { ORNotification.notifyError("Dune", "Execution exception", e.getMessage()); return null; } } private void killIt() { if (myProcessHandler != null) { myProcessHandler.killProcess(); myProcessHandler = null; } } static class DuneCommandLine extends OpamCommandLine { private final List m_parameters = new ArrayList<>(); DuneCommandLine(@NotNull Project project, @NotNull String binary) { super(project, binary); } @Override protected @NotNull List getParameters() { return m_parameters; } @NotNull DuneCommandLine addParameters(@NotNull CliType.Dune cliType) { switch (cliType) { case VERSION: m_parameters.add("--version"); case CLEAN: m_parameters.add("clean"); break; case BUILD: default: m_parameters.add("build"); } m_parameters.add("--root=."); return this; } } } ================================================ FILE: src/main/java/com/reason/comp/esy/Esy.java ================================================ package com.reason.comp.esy; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import jpsplugin.com.reason.Platform; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Optional; import org.jetbrains.annotations.NotNull; public class Esy { private Esy() {} public static Optional findEsyExecutable() { Optional esyExecutablePath = findEsyExecutableInPath(); if (esyExecutablePath.isPresent()) { LocalFileSystem fileSystem = LocalFileSystem.getInstance(); return Optional.ofNullable(fileSystem.findFileByPath(esyExecutablePath.get().toString())); } return findEsyExecutableInstalledWithN(); } private static Optional findEsyExecutableInstalledWithN() { String homeDirectory = System.getProperty("user.home"); LocalFileSystem fileSystem = LocalFileSystem.getInstance(); Path executablePath = Paths.get(homeDirectory, ".n", "bin", EsyConstants.ESY_EXECUTABLE_NAME); return Optional.ofNullable(fileSystem.findFileByPath(executablePath.toString())); } private static @NotNull Optional findEsyExecutableInPath() { String systemPath = System.getenv("PATH"); return Platform.findExecutableInPath(EsyConstants.ESY_EXECUTABLE_NAME, systemPath); } } ================================================ FILE: src/main/java/com/reason/comp/esy/EsyCompiler.java ================================================ package com.reason.comp.esy; import com.intellij.execution.process.*; import com.intellij.execution.ui.*; import com.intellij.openapi.components.*; import com.intellij.openapi.project.*; import com.intellij.openapi.vfs.*; import com.reason.comp.*; import com.reason.comp.dune.*; import com.reason.hints.*; import com.reason.ide.console.*; import com.reason.ide.console.esy.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; import java.util.concurrent.atomic.*; @Service(Service.Level.PROJECT) public final class EsyCompiler implements ORCompiler { private static final Log LOG = Log.create("compiler.esy"); private final @NotNull Project myProject; private final @NotNull AtomicBoolean myProcessStarted = new AtomicBoolean(false); EsyCompiler(@NotNull Project project) { myProject = project; } @Override public @NotNull CompilerType getType() { return CompilerType.ESY; } @Override public @NotNull String getFullVersion(@Nullable VirtualFile file) { return "unknown"; } @Override public void runDefault(@NotNull VirtualFile file, @Nullable ORProcessTerminated onProcessTerminated) { run(file, CliType.Esy.BUILD, onProcessTerminated); } @Override public void run(@Nullable VirtualFile file, @NotNull CliType cliType, @Nullable ORProcessTerminated onProcessTerminated) { if (!(cliType instanceof CliType.Esy)) { LOG.error("Invalid cliType for esy compiler. cliType = " + cliType); return; } if (myProject.isDisposed()) { return; } if (myProcessStarted.compareAndSet(false, true)) { VirtualFile sourceFile = file != null ? file : EsyPlatform.findConfigFiles(myProject).stream().findFirst().orElse(null); ConsoleView console = myProject.getService(ORToolWindowManager.class).getConsoleView(EsyToolWindowFactory.ID); if (sourceFile != null && console != null) { EsyProcess process = new EsyProcess(myProject); ProcessHandler processHandler = process.create(sourceFile, cliType, onProcessTerminated); if (processHandler != null) { processHandler.addProcessListener(new CompilerOutputListener(myProject, new DuneOutputAnalyzer())); processHandler.addProcessListener(new ProcessFinishedListener()); processHandler.addProcessListener(new ProcessAdapter() { @Override public void processTerminated(@NotNull ProcessEvent event) { myProcessStarted.compareAndSet(true, false); } }); console.attachToProcess(processHandler); process.startNotify(); myProject.getService(InsightManager.class).downloadRincewindIfNeeded(sourceFile); } else { myProcessStarted.compareAndSet(true, false); } } } } @Override public boolean isConfigured(@NotNull Project project) { // Esy compiler doesn't require any project-level configuration return true; } @Override public boolean isAvailable(@NotNull Project project) { return EsyPlatform.isEsyProject(myProject); } @Override public boolean isAvailable() { return !myProcessStarted.get(); } } ================================================ FILE: src/main/java/com/reason/comp/esy/EsyConstants.java ================================================ package com.reason.comp.esy; public class EsyConstants { public static final String ESY_CONFIG_FILENAME = "package.json"; public static final String ESY_EXECUTABLE_NAME = "esy"; private EsyConstants() {} } ================================================ FILE: src/main/java/com/reason/comp/esy/EsyNotification.java ================================================ package com.reason.comp.esy; import static com.intellij.notification.NotificationType.ERROR; import com.intellij.notification.Notifications; import jpsplugin.com.reason.ORNotification; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; public class EsyNotification { private EsyNotification() { } @Nls public static void showEsyProjectNotFound() { Notifications.Bus.notify( new ORNotification("Esy Project Not Found", "Unable to find esy project. Have you run esy yet?", ERROR)); } @Nls public static void showExecutionException(@NotNull Exception e) { Notifications.Bus.notify( new ORNotification("Esy Exception", "Failed to execute esy command.\n" + e.getMessage(), ERROR)); } } ================================================ FILE: src/main/java/com/reason/comp/esy/EsyPackageJson.java ================================================ package com.reason.comp.esy; import com.google.gson.*; import com.intellij.framework.detection.*; import com.intellij.openapi.vfs.*; import com.intellij.patterns.*; import com.intellij.util.*; import com.intellij.util.indexing.*; import org.jetbrains.annotations.*; import java.io.*; import static com.reason.comp.esy.EsyConstants.*; import static java.nio.charset.StandardCharsets.*; public class EsyPackageJson { private EsyPackageJson() { } /* detects any "package.json" with a top-level "esy" property */ public static boolean isEsyPackageJson(@Nullable VirtualFile file) { if (file != null && "json".equals(file.getExtension())) { try { return FileContentPattern.fileContent() .withName(ESY_CONFIG_FILENAME) .with(new FileContentPatternCondition()) .accepts(FileContentImpl.createByFile(file)); } catch (IOException e) { return false; } } return false; } private static class FileContentPatternCondition extends PatternCondition { public FileContentPatternCondition() { super("esyPackageJsonPattern"); } // No PSI here, not allowed @Override public boolean accepts(@NotNull FileContent fileContent, ProcessingContext context) { try { JsonElement jsonContent = JsonParser.parseString(new String(fileContent.getContent(), UTF_8)); if (jsonContent.isJsonObject()) { JsonObject top = jsonContent.getAsJsonObject(); return top.has("esy"); } } catch (JsonSyntaxException e) { return false; } return false; } } } ================================================ FILE: src/main/java/com/reason/comp/esy/EsyPlatform.java ================================================ package com.reason.comp.esy; import com.intellij.openapi.project.*; import com.intellij.openapi.vfs.*; import com.intellij.psi.search.*; import com.reason.comp.bs.*; import com.reason.ide.*; import com.reason.ide.settings.*; import org.jetbrains.annotations.*; import java.util.*; import static com.reason.comp.esy.EsyConstants.*; public class EsyPlatform { private EsyPlatform() { } public static @NotNull List findConfigFiles(@NotNull Project project) { return BsPlatform.findConfigFiles(project); } public static boolean isEsyProject(@NotNull Project project) { return !findEsyConfigurationFiles(project).isEmpty(); } public static List findEsyConfigurationFiles(@NotNull Project project) { return FilenameIndex.getVirtualFilesByName(ESY_CONFIG_FILENAME, GlobalSearchScope.projectScope(project)) .stream() .filter(EsyPackageJson::isEsyPackageJson) .sorted(ORFileUtils.FILE_DEPTH_COMPARATOR) .toList(); } public static List findEsyContentRoots(@NotNull Project project) { return findEsyConfigurationFiles(project) .stream() .map(VirtualFile::getParent) .toList(); } public static Optional findEsyExecutable(@NotNull Project project) { String esyExecutable = project.getService(ORSettings.class).getEsyExecutable(); if (esyExecutable.isEmpty()) { return Esy.findEsyExecutable(); } return Optional.ofNullable(LocalFileSystem.getInstance().findFileByPath(esyExecutable)); } } ================================================ FILE: src/main/java/com/reason/comp/esy/EsyProcess.java ================================================ package com.reason.comp.esy; import com.intellij.execution.*; import com.intellij.execution.configurations.*; import com.intellij.execution.process.*; import com.intellij.openapi.project.*; import com.intellij.openapi.vfs.*; import com.reason.comp.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; import java.util.*; import java.util.function.*; public class EsyProcess { private static final Log LOG = Log.create("process.esy"); private final @NotNull Project myProject; @Nullable private KillableColoredProcessHandler processHandler; EsyProcess(@NotNull Project project) { myProject = project; } public void startNotify() { if (processHandler != null && !processHandler.isStartNotified()) { try { processHandler.startNotify(); } catch (Exception e) { LOG.error("Exception when calling 'startNotify'.", e); } } } @Nullable public ProcessHandler create(@NotNull VirtualFile source, @NotNull CliType cliType, @Nullable ORProcessTerminated onProcessTerminated) { killIt(); VirtualFile workingDir = findWorkingDirectory(myProject); if (workingDir == null) { return null; } Optional esyExecutableOptional = EsyPlatform.findEsyExecutable(myProject); if (esyExecutableOptional.isEmpty()) { return null; } VirtualFile esyExecutable = esyExecutableOptional.get(); GeneralCommandLine cli = newCommandLine(esyExecutable, workingDir, (CliType.Esy) cliType); try { processHandler = new KillableColoredProcessHandler(cli); } catch (ExecutionException e) { EsyNotification.showExecutionException(e); return null; } if (onProcessTerminated != null) { processHandler.addProcessListener(processTerminatedListener.apply(onProcessTerminated)); } return processHandler; } private void killIt() { if (processHandler == null || processHandler.isProcessTerminating() || processHandler.isProcessTerminated()) { return; } processHandler.killProcess(); processHandler = null; } private static GeneralCommandLine newCommandLine(@NotNull VirtualFile esyExecutable, @NotNull VirtualFile workingDir, @NotNull CliType.Esy cliType) { GeneralCommandLine commandLine; commandLine = new GeneralCommandLine(esyExecutable.getPath()); commandLine.setWorkDirectory(workingDir.getPath()); commandLine.setRedirectErrorStream(true); commandLine.addParameter(getCommand(cliType)); // 'esy + command' must be a single parameter return commandLine; } private static @NotNull String getCommand(@NotNull CliType.Esy cliType) { return switch (cliType) { case INSTALL -> "install"; case BUILD -> "build"; case SHELL -> "shell"; }; } private static @Nullable VirtualFile findWorkingDirectory(@NotNull Project project) { VirtualFile contentRoot = EsyPlatform.findEsyContentRoots(project).stream().findFirst().orElse(null); if (contentRoot == null) { EsyNotification.showEsyProjectNotFound(); } return contentRoot; } private static final Function, ProcessListener> processTerminatedListener = (onProcessTerminated) -> new ProcessAdapter() { @Override public void processTerminated(@NotNull ProcessEvent event) { onProcessTerminated.run(null); } }; } ================================================ FILE: src/main/java/com/reason/comp/ocaml/OcamlFormatProcess.java ================================================ package com.reason.comp.ocaml; import com.intellij.execution.*; import com.intellij.execution.configurations.*; import com.intellij.openapi.components.*; import com.intellij.openapi.project.*; import com.intellij.openapi.vfs.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; import java.io.*; import java.util.*; import static jpsplugin.com.reason.Platform.*; @Service(Service.Level.PROJECT) public final class OcamlFormatProcess { private static final Log LOG = Log.create("format.ocaml.process"); private final Project myProject; public OcamlFormatProcess(Project project) { myProject = project; } public @NotNull String format(@NotNull VirtualFile file, @NotNull String textToFormat) { GeneralCommandLine commandLine = new OCamlFormatCommandLine(myProject, "ocamlformat").addParameters(file).create(file); if (commandLine != null && !textToFormat.isEmpty()) { Process fmt = null; try { fmt = commandLine.createProcess(); BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(fmt.getOutputStream(), UTF8)); BufferedReader reader = new BufferedReader(new InputStreamReader(fmt.getInputStream(), UTF8)); BufferedReader errReader = new BufferedReader(new InputStreamReader(fmt.getErrorStream(), UTF8)); writer.write(textToFormat); writer.flush(); writer.close(); Streams.waitUntilReady(reader, errReader); StringBuilder msgBuffer = new StringBuilder(); if (!errReader.ready()) { final boolean[] empty = {true}; reader .lines() .forEach(line -> { if (empty[0]) { empty[0] = false; } else { msgBuffer.append('\n'); } msgBuffer.append(line); }); String newText = msgBuffer.toString(); if (!newText.isEmpty()) { // additional protection return newText; } } else { errReader.lines().forEach(line -> msgBuffer.append(line).append('\n')); LOG.warn(StringUtil.trimLastCR(msgBuffer.toString())); } } catch (IOException | RuntimeException | ExecutionException e) { LOG.warn(e); } finally { if (fmt != null) { fmt.destroyForcibly(); } } } return textToFormat; } static class OCamlFormatCommandLine extends OpamCommandLine { private final List m_parameters = new ArrayList<>(); OCamlFormatCommandLine(@NotNull Project project, @NotNull String binary) { super(project, binary, false); } @NotNull OCamlFormatCommandLine addParameters(@NotNull VirtualFile file) { m_parameters.add("-"); // use stdin m_parameters.add("--name"); m_parameters.add(file.getName()); return this; } @Override protected @NotNull List getParameters() { return m_parameters; } } } ================================================ FILE: src/main/java/com/reason/comp/ocaml/OpamCommandLine.java ================================================ package com.reason.comp.ocaml; import com.intellij.execution.configurations.*; import com.intellij.openapi.module.Module; import com.intellij.openapi.project.*; import com.intellij.openapi.roots.*; import com.intellij.openapi.vfs.*; import com.intellij.util.containers.*; import com.reason.ide.settings.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; import java.util.*; import static com.intellij.openapi.application.ApplicationManager.*; public abstract class OpamCommandLine { private static final Log LOG = Log.create("ocaml.opam"); public static final VirtualFile[] EMPTY_VFILES = new VirtualFile[0]; private final Project myProject; private final String myBinary; private final boolean myRedirectErrorStream; OpamCommandLine(@NotNull Project project, @NotNull String binary, boolean redirectErrorStream) { myProject = project; myBinary = binary; myRedirectErrorStream = redirectErrorStream; } protected OpamCommandLine(@NotNull Project project, @NotNull String binary) { this(project, binary, true); } protected abstract @NotNull List getParameters(); public @Nullable GeneralCommandLine create(@NotNull VirtualFile source) { ORSettings settings = myProject.getService(ORSettings.class); String opamLocation = settings.getOpamLocation(); if (!opamLocation.isEmpty()) { String switchLocation = opamLocation + "/" + settings.getSwitchName(); String binPath = switchLocation + "/bin"; Module module = Platform.getModule(myProject, source); VirtualFile[] contentRoots = module == null ? EMPTY_VFILES : ModuleRootManager.getInstance(module).getContentRoots(); if (contentRoots.length > 0) { GeneralCommandLine cli = new GeneralCommandLine(ContainerUtil.prepend(getParameters(), myBinary)); cli.withParentEnvironmentType(GeneralCommandLine.ParentEnvironmentType.CONSOLE); cli.setWorkDirectory(contentRoots[0].getPath()); cli.setRedirectErrorStream(myRedirectErrorStream); Map env = getApplication().getService(OpamEnv.class).getEnv(settings.getSwitchName()); if (env != null) { for (Map.Entry entry : env.entrySet()) { cli.withEnvironment(entry.getKey(), entry.getValue()); } } OCamlExecutable executable = OCamlExecutable.getExecutable(opamLocation, settings.getCygwinBash()); return executable.patchCommandLine(cli, null, false); } else { LOG.debug("Content roots", contentRoots); LOG.debug("Binary directory", binPath); } } return null; } } ================================================ FILE: src/main/java/com/reason/comp/ocaml/OpamEnv.java ================================================ package com.reason.comp.ocaml; import com.intellij.openapi.application.*; import com.intellij.openapi.components.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; import java.util.*; @Service(Service.Level.APP) public final class OpamEnv { private final Map> myEnvs = new HashMap<>(); public @Nullable Map getEnv(@Nullable String switchName) { return switchName == null ? null : myEnvs.get(switchName); } public void computeEnv(@Nullable String opamLocation, @Nullable String switchName, @Nullable String cygwinBash, @Nullable ORProcessTerminated> onEnvTerminated) { if (opamLocation != null && switchName != null) { ApplicationManager.getApplication() .getService(OpamProcess.class) .env(opamLocation, switchName, cygwinBash, data -> { myEnvs.put(switchName, data); if (onEnvTerminated != null) { onEnvTerminated.run(data); } }); } } } ================================================ FILE: src/main/java/com/reason/comp/ocaml/OpamProcess.java ================================================ package com.reason.comp.ocaml; import com.intellij.execution.*; import com.intellij.execution.configurations.*; import com.intellij.execution.process.*; import com.intellij.openapi.components.*; import com.intellij.openapi.util.*; import com.intellij.openapi.util.text.StringUtil; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; import java.io.*; import java.util.*; import java.util.regex.*; @Service(Service.Level.APP) public final class OpamProcess { private static final Pattern SEXP = Pattern.compile("\\(\"([^\"]+)\" \"([^\"]+)\"\\)"); public static final ProcessHandler NULL_HANDLER = new ProcessHandler() { @Override protected void destroyProcessImpl() { } @Override protected void detachProcessImpl() { } @Override public boolean detachIsDefault() { return false; } @Nullable @Override public OutputStream getProcessInput() { return null; } }; public void list(@NotNull String opamLocation, @NotNull String version, @Nullable String cygwinBash, @NotNull ORProcessTerminated> onProcessTerminated) { ArrayList installedLibs = new ArrayList<>(); if (StringUtil.isEmpty(opamLocation) || StringUtil.isEmpty(version)) { onProcessTerminated.run(installedLibs); return; } GeneralCommandLine cli = new GeneralCommandLine("opam", "list", "--installed", "--safe", "--color=never", "--switch=" + version); cli.setRedirectErrorStream(true); OCamlExecutable executable = OCamlExecutable.getExecutable(opamLocation, cygwinBash); executable.patchCommandLine(cli, null, true); KillableProcessHandler processHandler; try { processHandler = new KillableProcessHandler(cli); processHandler.addProcessListener(new ProcessListener() { @Override public void processTerminated(@NotNull ProcessEvent event) { onProcessTerminated.run(installedLibs); } @Override public void onTextAvailable(@NotNull ProcessEvent event, @NotNull Key outputType) { if (ProcessOutputType.isStdout(outputType)) { String text = event.getText().trim(); if (text.charAt(0) != '#') { String[] split = text.split("\\s+", 3); installedLibs.add(new String[]{split[0].trim(), split.length >= 2 ? split[1].trim() : "unknown", split.length >= 3 ? split[2].trim() : ""}); } } } }); processHandler.startNotify(); } catch (ExecutionException e) { ORNotification.notifyError("Opam", "Can't list libraries", e.getMessage()); installedLibs.add(new String[]{"Error", e.getMessage()}); onProcessTerminated.run(installedLibs); } } public void env(@Nullable String opamLocation, @Nullable String version, @Nullable String cygwinBash, @NotNull ORProcessTerminated> onProcessTerminated) { Map result = new HashMap<>(); if (StringUtil.isEmpty(opamLocation) || StringUtil.isEmpty(version)) { result.put("Incorrect setting", "Setup SDK in project settings"); onProcessTerminated.run(result); return; } GeneralCommandLine cli = new GeneralCommandLine("opam", "config", "env", "--sexp", "--switch=" + version); cli.setRedirectErrorStream(true); OCamlExecutable executable = OCamlExecutable.getExecutable(opamLocation, cygwinBash); executable.patchCommandLine(cli, null, true); KillableProcessHandler processHandler; try { processHandler = new KillableProcessHandler(cli); processHandler.addProcessListener(new ProcessListener() { @Override public void processTerminated(@NotNull ProcessEvent event) { onProcessTerminated.run(result); } @Override public void onTextAvailable(@NotNull ProcessEvent event, @NotNull Key outputType) { if (ProcessOutputType.isStdout(outputType)) { String text = event.getText().trim(); Matcher matcher = SEXP.matcher(text); if (matcher.matches()) { String key = matcher.group(1); String value = matcher.group(2); result.put(key, value); } } } }); processHandler.startNotify(); } catch (ExecutionException e) { ORNotification.notifyError("Opam", "Can't read opam env", e.getMessage()); onProcessTerminated.run(result); } } public void listSwitch(@NotNull String opamRootPath, @Nullable String cygwinBash, @NotNull ORProcessTerminated> onProcessTerminated) { ProcessListener processListener = new ListProcessListener(onProcessTerminated); OCamlExecutable executable = OCamlExecutable.getExecutable(opamRootPath, cygwinBash); GeneralCommandLine cli = new GeneralCommandLine("opam", "switch", "list", "--color=never"); executable.patchCommandLine(cli, null, true); KillableProcessHandler processHandler; try { processHandler = new KillableProcessHandler(cli); processHandler.addProcessListener(processListener); processHandler.startNotify(); } catch (ExecutionException e) { ORNotification.notifyError("Dune", "Can't run opam", e.getMessage()); processListener.processTerminated(new ProcessEvent(NULL_HANDLER)); } } public record OpamSwitch(boolean isSelected, String name) { @Override public String toString() { return (isSelected ? ">" : "") + name; } } static class ListProcessListener implements ProcessListener { private final ORProcessTerminated> myOnProcessTerminated; private final List myResult = new ArrayList<>(); private boolean myIsHeader = true; private boolean myIsFooter = false; public ListProcessListener(@NotNull ORProcessTerminated> onProcessTerminated) { myOnProcessTerminated = onProcessTerminated; } @Override public void processTerminated(@NotNull ProcessEvent event) { myOnProcessTerminated.run(myResult); } @Override public void onTextAvailable(@NotNull ProcessEvent event, @NotNull Key outputType) { if (ProcessOutputType.isStdout(outputType)) { String text = event.getText(); if (myIsHeader) { myIsHeader = false; } else if (text.startsWith("[WARNING]")) { myIsFooter = true; } else if (!myIsFooter) { String[] tokens = text.split("\\s+"); if (tokens.length >= 4) { myResult.add(new OpamSwitch(!tokens[0].isEmpty(), tokens[1])); } } } } } } ================================================ FILE: src/main/java/com/reason/comp/rescript/ResCompiler.java ================================================ package com.reason.comp.rescript; import com.intellij.codeInsight.daemon.*; import com.intellij.execution.*; import com.intellij.execution.configurations.*; import com.intellij.execution.process.*; import com.intellij.execution.ui.*; import com.intellij.openapi.components.*; import com.intellij.openapi.project.*; import com.intellij.openapi.vfs.*; import com.reason.comp.*; import com.reason.comp.bs.*; import com.reason.hints.*; import com.reason.ide.console.*; import com.reason.ide.console.rescript.*; import com.reason.ide.settings.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; import java.io.*; import java.util.concurrent.atomic.*; import static com.reason.comp.CliType.Rescript.*; @Service(Service.Level.PROJECT) public final class ResCompiler implements ORCompiler { private static final Log LOG = Log.create("compiler.rescript"); private final Project myProject; private final AtomicBoolean myProcessStarted = new AtomicBoolean(false); private ResCompiler(@NotNull Project project) { myProject = project; } @Override public @NotNull CompilerType getType() { return CompilerType.RESCRIPT; } @Override public @NotNull String getFullVersion(@Nullable VirtualFile file) { VirtualFile bscExecutable = ResPlatform.findBscExecutable(myProject, file); if (bscExecutable != null) { String[] command = new String[]{bscExecutable.getPath(), "-version"}; try (InputStream inputStream = Runtime.getRuntime().exec(command).getInputStream()) { BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); return reader.readLine(); } catch (IOException e) { return "error: " + e.getMessage(); } } return "unknown (bsc not found)"; } @Override public void runDefault(@NotNull VirtualFile file, @Nullable ORProcessTerminated onProcessTerminated) { run(file, MAKE, onProcessTerminated); } @Override public void run(@Nullable VirtualFile sourceFile, @NotNull CliType cliType, @Nullable ORProcessTerminated onProcessTerminated) { LOG.debug("Run compiler"); ORSettings settings = myProject.getService(ORSettings.class); if (settings.isBsEnabled()) { if (sourceFile != null) { myProject.getService(InsightManager.class).downloadRincewindIfNeeded(sourceFile); } VirtualFile configFile = ResPlatform.findConfigFile(myProject, sourceFile); VirtualFile bin = ResPlatform.findRescriptExecutable(myProject, configFile); ConsoleView console = myProject.getService(ORToolWindowManager.class).getConsoleView(RescriptToolWindowFactory.ID); if (configFile != null && bin != null && console != null) { try { if (myProcessStarted.compareAndSet(false, true)) { GeneralCommandLine cli = getCommandLine(bin.getPath(), (CliType.Rescript) cliType); VirtualFile configParent = configFile.getParent(); if (configParent != null) { cli.withWorkDirectory(configParent.getPath()); } cli.withEnvironment("NINJA_ANSI_FORCED", "1"); if (!settings.isUseSuperErrors()) { cli.withEnvironment("BS_VSCODE", "1"); } ResProcessHandler processHandler = new ResProcessHandler(cli); processHandler.addProcessListener(new ProcessFinishedListener(System.currentTimeMillis())); processHandler.addProcessListener(new ProcessAdapter() { @Override public void processTerminated(@NotNull ProcessEvent event) { if (onProcessTerminated != null) { onProcessTerminated.run(null); } // When build is done, we need to refresh editors to be notified of the latest modifications LOG.debug("Compilation process terminated, restart daemon code analyzer for all edited files"); DaemonCodeAnalyzer.getInstance(myProject).restart(); myProcessStarted.compareAndSet(true, false); } }); console.attachToProcess(processHandler); processHandler.startNotify(); } } catch (ExecutionException e) { ORNotification.notifyError("Rescript", "Execution exception", e.getMessage()); myProcessStarted.compareAndSet(true, false); } } } } private @NotNull GeneralCommandLine getCommandLine(@NotNull String binPath, @NotNull CliType.Rescript cliType) { return switch (cliType) { case MAKE -> new GeneralCommandLine(binPath, "build"); case CLEAN -> new GeneralCommandLine(binPath, "clean"); }; } public @NotNull Ninja readNinjaBuild(@Nullable VirtualFile contentRoot) { @NotNull Ninja ninja = new Ninja(null); if (/*m_refreshNinjaIsNeeded.get() &&*/ contentRoot != null) { VirtualFile ninjaFile = contentRoot.findFileByRelativePath("lib/bs/build.ninja"); if (ninjaFile != null) { ninja = new Ninja(FileUtil.readFileContent(ninjaFile)); } } return ninja; } @Override public boolean isConfigured(@NotNull Project project) { return true; // No project-level configuration } @Override public boolean isAvailable(@NotNull Project project) { VirtualFile bin = ResPlatform.findBscExecutable(project, null); return bin != null; } @Override public boolean isAvailable() { return !myProcessStarted.get(); } } ================================================ FILE: src/main/java/com/reason/comp/rescript/ResConfigReader.java ================================================ package com.reason.comp.rescript; import com.intellij.openapi.vfs.*; import com.reason.comp.bs.*; import org.jetbrains.annotations.*; public class ResConfigReader { private ResConfigReader() { } public static @NotNull BsConfig read(@NotNull VirtualFile bsConfigFile) { return read(bsConfigFile, false); } public static @NotNull BsConfig read(@NotNull VirtualFile configFile, boolean useExternalAsSource) { // For now, the same as bsconfig return BsConfigReader.read(configFile, useExternalAsSource); } } ================================================ FILE: src/main/java/com/reason/comp/rescript/ResFormatProcess.java ================================================ package com.reason.comp.rescript; import com.intellij.openapi.components.*; import com.intellij.openapi.editor.ex.*; import com.intellij.openapi.project.*; import com.intellij.openapi.vfs.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; import java.io.*; import static jpsplugin.com.reason.Platform.*; @Service(Service.Level.PROJECT) public final class ResFormatProcess { private static final Log LOG = Log.create("format.rescript"); private final Project m_project; public ResFormatProcess(@NotNull Project project) { m_project = project; } @NotNull public String format(@NotNull VirtualFile sourceFile, boolean isInterface, @NotNull String code) { VirtualFile fmtBin = ResPlatform.findBscExecutable(m_project, sourceFile); if (fmtBin == null) { LOG.debug("No rescript binary found, format cancelled"); return code; } if (LOG.isDebugEnabled()) { LOG.debug("Formatting " + sourceFile.getPath() + " for project [" + m_project + "]"); } try (AutoDeletingTempFile tempFile = new AutoDeletingTempFile("fileToFormat", isInterface ? ".resi" : ".res")) { tempFile.write(code, UTF8); ProcessBuilder processBuilder = new ProcessBuilder(fmtBin.getPath(), "-format", tempFile.getPath()); Process fmt = null; try { fmt = processBuilder.start(); BufferedReader reader = new BufferedReader(new InputStreamReader(fmt.getInputStream(), UTF8)); BufferedReader errReader = new BufferedReader(new InputStreamReader(fmt.getErrorStream(), UTF8)); Streams.waitUntilReady(reader, errReader); final boolean[] empty = {true}; StringBuilder msgBuffer = new StringBuilder(); if (!errReader.ready()) { reader .lines() .forEach( line -> { if (empty[0]) { empty[0] = false; } else { msgBuffer.append('\n'); } msgBuffer.append(line); }); String newText = msgBuffer.toString(); if (!code.isEmpty() && !newText.isEmpty()) { // additional protection boolean ensureNewLineAtEOF = EditorSettingsExternalizable.getInstance().isEnsureNewLineAtEOF(); boolean addNewLine = ensureNewLineAtEOF && newText.charAt(newText.length() - 1) != '\n'; return addNewLine ? newText + '\n' : newText; } } } catch (IOException | RuntimeException e) { LOG.warn(e); } finally { if (fmt != null) { fmt.destroyForcibly(); } } } catch (IOException e) { LOG.error(e.getMessage()); } // Something bad happened, do nothing return code; } } ================================================ FILE: src/main/java/com/reason/comp/rescript/ResPlatform.java ================================================ package com.reason.comp.rescript; import com.intellij.openapi.project.*; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.*; import com.intellij.psi.search.*; import com.reason.comp.*; import com.reason.comp.bs.*; import com.reason.ide.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; import java.util.*; import static com.reason.comp.ORConstants.*; public class ResPlatform { private static final Log LOG = Log.create("rescript.platform"); private ResPlatform() { } public static @NotNull List findConfigFiles(@NotNull Project project) { GlobalSearchScope scope = GlobalSearchScope.projectScope(project); Collection configs = FilenameIndex.getVirtualFilesByName(RESCRIPT_CONFIG_FILENAME, scope); configs.addAll(FilenameIndex.getVirtualFilesByName(BS_CONFIG_FILENAME, scope)); List validConfigs = configs.stream() .filter(configFile -> ORPlatform.findCompilerPathInNodeModules(project, configFile, RESCRIPT_DIR, BSC_EXE_NAME) != null) .sorted(ORFileUtils.FILE_DEPTH_COMPARATOR) .toList(); if (LOG.isDebugEnabled()) { LOG.debug("Valid configs for project=\"" + project.getName() + "\": [" + Joiner.join(",", validConfigs) + "]"); } return validConfigs; } static @Nullable VirtualFile findConfigFile(@NotNull Project project, @Nullable VirtualFile sourceFile) { return sourceFile != null ? ORFileUtils.findOneOfAncestor(project, sourceFile, RESCRIPT_CONFIG_FILENAME, BS_CONFIG_FILENAME) : findConfigFiles(project).stream().findFirst().orElse(null); } public static @Nullable VirtualFile findBscExecutable(@NotNull Project project, @Nullable VirtualFile sourceFile) { VirtualFile configFile = findConfigFile(project, sourceFile); VirtualFile binDir = ORPlatform.findCompilerPathInNodeModules(project, configFile, RESCRIPT_DIR, BSC_EXE_NAME); return binDir != null ? ORPlatform.findBinary(binDir, BSC_EXE_NAME) : null; } public static @Nullable VirtualFile findRescriptExecutable(@NotNull Project project, @Nullable VirtualFile sourceFile) { VirtualFile configFile = findConfigFile(project, sourceFile); VirtualFile binDir = ORPlatform.findCompilerPathInNodeModules(project, configFile, RESCRIPT_DIR, RESCRIPT_EXE_NAME); return binDir != null ? ORPlatform.findBinary(binDir, RESCRIPT_EXE_NAME) : null; } public static @Nullable VirtualFile findRefmtExecutable(@NotNull Project project, @NotNull VirtualFile sourceFile) { VirtualFile configFile = ORFileUtils.findOneOfAncestor(project, sourceFile, RESCRIPT_CONFIG_FILENAME, BS_CONFIG_FILENAME); VirtualFile binDir = ORPlatform.findCompilerPathInNodeModules(project, configFile, RESCRIPT_DIR, BSC_EXE_NAME); return binDir != null ? ORPlatform.findBinary(binDir, REFMT_EXE_NAME) : null; } public static boolean isDevSource(@NotNull VirtualFile sourceFile, @NotNull VirtualFile contentRoot, @NotNull BsConfig config) { for (String devSource : config.getDevSources()) { VirtualFile devFile = contentRoot.findFileByRelativePath(devSource); if (devFile != null && FileUtil.isAncestor(devFile.getPath(), sourceFile.getPath(), true)) { return true; } } return false; } } ================================================ FILE: src/main/java/com/reason/comp/rescript/ResProcessHandler.java ================================================ package com.reason.comp.rescript; import com.intellij.execution.*; import com.intellij.execution.configurations.*; import com.intellij.execution.process.*; import com.intellij.openapi.util.*; import org.jetbrains.annotations.*; public class ResProcessHandler extends KillableProcessHandler implements AnsiEscapeDecoder.ColoredTextAcceptor { private final @NotNull AnsiEscapeDecoder myAnsiEscapeDecoder = new AnsiEscapeDecoder(); public ResProcessHandler(@NotNull GeneralCommandLine commandLine) throws ExecutionException { super(commandLine); } @Override public final void notifyTextAvailable(@NotNull final String text, @NotNull final Key outputType) { myAnsiEscapeDecoder.escapeText(text, outputType, super::notifyTextAvailable); } @Override public void coloredTextAvailable(@NotNull String text, @NotNull Key attributes) { super.notifyTextAvailable(text, attributes); } } ================================================ FILE: src/main/java/com/reason/comp/rescript/ResResolvedCompiler.java ================================================ package com.reason.comp.rescript; import com.intellij.openapi.vfs.*; import com.reason.comp.*; import com.reason.comp.bs.*; import org.jetbrains.annotations.*; public class ResResolvedCompiler extends ORResolvedCompiler { public ResResolvedCompiler(@NotNull ResCompiler compiler, @NotNull VirtualFile contentRootFile, @Nullable VirtualFile binFile) { super(compiler, contentRootFile, binFile); } public @Nullable Ninja readNinjaBuild() { return myCompiler.readNinjaBuild(getContentRoot()); } } ================================================ FILE: src/main/java/com/reason/comp/rescript/RescriptOutputAnalyzer.java ================================================ package com.reason.comp.rescript; import com.reason.comp.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; // See tests for error messages patterns public class RescriptOutputAnalyzer extends ORCompilerOutputAnalyzer { private static final Log LOG = Log.create("rescript.output"); enum OutputState { unknown, // fileLocation, errorMessage, warningMessage, // syntaxError, syntaxErrorLocation, syntaxErrorSourceCode, syntaxErrorMessage } private @NotNull OutputState myCurrentState = OutputState.unknown; @Override public void onTextAvailable(@NotNull String line) { // FAILED: src/NotGood.cmj // -> Reset the state and info, starts a new analysis if (line.startsWith("FAILED:")) { myCurrentState = OutputState.unknown; myCurrentInfo = null; } // Syntax error || We've found a bug for you // -> Error level, next line is the location else if (myCurrentState == OutputState.unknown && (line.startsWith(" Syntax error") || line.startsWith(" We've found a bug") || line.startsWith(" Warning number"))) { myCurrentState = OutputState.syntaxError; } // -> Multiple syntax errors can be displayed, reset message else if (myCurrentState == OutputState.syntaxErrorMessage && line.startsWith(" Syntax error")) { myCurrentState = OutputState.syntaxError; myCurrentInfo = null; } // else if (line.startsWith("File") && (myCurrentState == OutputState.unknown || myCurrentState == OutputState.errorMessage || myCurrentState == OutputState.warningMessage)) { myCurrentInfo = extractExtendedFilePositions(LOG, line); myCurrentState = OutputState.fileLocation; } else if (line.startsWith("Error") && myCurrentState == OutputState.fileLocation) { if (myCurrentInfo != null) { myCurrentInfo.isError = line.startsWith("Error:"); // else Error (warning xx): int pos = line.indexOf(':'); myCurrentInfo.message = pos > 0 ? line.substring(pos + 1).trim() : ""; } myCurrentState = OutputState.errorMessage; } else if (line.startsWith("Warning") && myCurrentState == OutputState.fileLocation) { if (myCurrentInfo != null) { myCurrentInfo.isError = false; int pos = line.indexOf(':'); myCurrentInfo.message = pos > 0 ? line.substring(pos + 1).trim() : ""; } myCurrentState = OutputState.warningMessage; } else if (myCurrentState == OutputState.syntaxError) { myCurrentInfo = extractSyntaxErrorFilePosition(LOG, line); if (myCurrentInfo != null) { myCurrentInfo.isError = true; } myCurrentState = OutputState.syntaxErrorLocation; } else if (line.startsWith(" ") && myCurrentState == OutputState.errorMessage) { if (myCurrentInfo != null) { myCurrentInfo.message += " " + line.trim(); } } else if (line.startsWith(" ") && myCurrentState == OutputState.syntaxErrorMessage) { if (myCurrentInfo != null) { myCurrentInfo.message += (myCurrentInfo.message.isEmpty() ? "" : " ") + line.trim(); } } else if (line.isEmpty() && myCurrentState == OutputState.syntaxErrorLocation) { myCurrentState = OutputState.syntaxErrorSourceCode; } else if (line.isEmpty() && myCurrentState == OutputState.syntaxErrorSourceCode) { myCurrentState = OutputState.syntaxErrorMessage; } } } ================================================ FILE: src/main/java/com/reason/hints/InsightManager.java ================================================ package com.reason.hints; import com.intellij.openapi.application.*; import com.intellij.openapi.components.*; import com.intellij.openapi.progress.*; import com.intellij.openapi.project.*; import com.intellij.openapi.vfs.*; import com.reason.comp.*; import com.reason.ide.hints.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; import java.io.*; import java.nio.file.*; import java.util.*; import java.util.concurrent.atomic.*; import static jpsplugin.com.reason.Platform.*; @Service(Service.Level.PROJECT) public final class InsightManager { private static final Log LOG = Log.create("hints"); final @NotNull AtomicBoolean isDownloading = new AtomicBoolean(false); private final @NotNull Project myProject; InsightManager(@NotNull Project project) { myProject = project; } public void downloadRincewindIfNeeded(@NotNull VirtualFile sourceFile) { VirtualFile parentFile = sourceFile.getParent(); if (parentFile == null) { LOG.debug("Can't get parent file", sourceFile); return; } String rincewindName = ReadAction.compute(() -> getRincewindFilename(parentFile, "")); if (rincewindName == null) { LOG.debug("No rincewind version found, abort downloading"); return; } File targetFile = getRincewindTarget(rincewindName); if (targetFile != null && !targetFile.exists()) { ProgressManager.getInstance().run(new RincewindDownloader(myProject, targetFile)); } } public void queryTypes(@Nullable VirtualFile sourceFile, @NotNull Path cmtPath, @NotNull ORProcessTerminated runAfter) { VirtualFile sourceParentFile = sourceFile != null ? sourceFile.getParent() : null; String rincewindName = getRincewindFilename(sourceParentFile, ""); File rincewindFile = getRincewindTarget(rincewindName); if (sourceFile != null && rincewindFile != null) { myProject.getService(RincewindProcess.class).types(sourceFile, rincewindFile.getPath(), cmtPath.toString(), runAfter); } } public @NotNull List dumpMeta(@NotNull VirtualFile cmtFile) { String rincewindName = getRincewindFilename(cmtFile.getParent(), "0.4"); File rincewindFile = rincewindName == null ? null : getRincewindTarget(rincewindName); return rincewindFile == null ? Collections.emptyList() : myProject.getService(RincewindProcess.class).dumpMeta(rincewindFile.getPath(), cmtFile); } public @NotNull String dumpTree(@NotNull VirtualFile cmtFile) { String rincewindName = getRincewindFilename(cmtFile.getParent(), ""); File rincewindFile = rincewindName == null ? null : getRincewindTarget(rincewindName); return rincewindFile == null ? "\n rincewindFile not found\n " + cmtFile.getPath() + "\n" : myProject.getService(RincewindProcess.class).dumpTree(cmtFile, rincewindFile.getPath()); } public @NotNull List dumpInferredTypes(@NotNull VirtualFile cmtFile) { String rincewindName = getRincewindFilename(cmtFile.getParent(), ""); File rincewindFile = rincewindName == null ? null : getRincewindTarget(rincewindName); return rincewindFile == null ? Collections.emptyList() : myProject.getService(RincewindProcess.class).dumpTypes(rincewindFile.getPath(), cmtFile); } @Nullable File getRincewindTarget(@Nullable String filename) { Path pluginLocation = getPluginLocation(); String pluginPath = pluginLocation == null ? System.getProperty("java.io.tmpdir") : pluginLocation.toFile().getPath(); if (LOG.isTraceEnabled()) { LOG.trace("Rincewind filename: " + filename + " at " + pluginPath); } return filename == null ? null : new File(pluginPath, filename); } @Nullable String getRincewindFilename(@Nullable VirtualFile sourceFile, @NotNull String excludedVersion) { if (sourceFile != null) { ORCompilerManager compilerManager = myProject.getService(ORCompilerManager.class); ORResolvedCompiler compiler = compilerManager.getCompiler(sourceFile); String fullVersion = compiler != null ? compiler.getFullVersion() : null; String ocamlVersion = Rincewind.extractOcamlVersion(fullVersion); String rincewindVersion = Rincewind.getLatestVersion(ocamlVersion); // ocaml version default - opam -> use ocaml -version ?? // opam switch different from default // opam settings set correctly (not default) if (ocamlVersion != null && !rincewindVersion.equals(excludedVersion)) { return "rincewind_" + getOsPrefix() + ocamlVersion + "-" + rincewindVersion + ".exe"; } } return null; } } ================================================ FILE: src/main/java/com/reason/hints/Rincewind.java ================================================ package com.reason.hints; import org.jetbrains.annotations.*; import java.util.regex.*; public class Rincewind { private static final Pattern OCAML_VERSION_REGEXP = Pattern.compile(".*OCaml[:]?(\\d\\.\\d+).\\d+.*\\)"); private Rincewind() { } static @Nullable String extractOcamlVersion(@Nullable String fullVersion) { if (fullVersion != null) { Matcher matcher = OCAML_VERSION_REGEXP.matcher(fullVersion); if (matcher.matches()) { return matcher.group(1); } if (fullVersion.startsWith("ReScript")) { return "4.06"; } } return null; } static @NotNull String getLatestVersion(@Nullable String ocamlVersion) { if ("4.02".equals(ocamlVersion)) { return "0.4"; } return "0.10"; } } ================================================ FILE: src/main/java/com/reason/hints/RincewindDownloader.java ================================================ package com.reason.hints; import com.intellij.openapi.application.*; import com.intellij.openapi.fileEditor.*; import com.intellij.openapi.progress.*; import com.intellij.openapi.project.*; import com.intellij.openapi.vfs.*; import com.intellij.psi.*; import com.reason.ide.*; import com.reason.ide.files.*; import com.reason.ide.hints.*; import com.reason.lang.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; import java.io.*; import java.nio.file.*; public class RincewindDownloader extends Task.Backgroundable { private static final Log LOG = Log.create("hints"); private static final double TOTAL_BYTES = 10_000_000.0; private static final String DOWNLOAD_DEFAULT_URL = "https://s3.eu-west-2.wasabisys.com/rincewind/"; private final File myRincewindTarget; private final String myDownloadURL; RincewindDownloader(@Nullable Project project, @NotNull File rincewindTarget) { super(project, "Downloading Rincewind binary"); myRincewindTarget = rincewindTarget; // URL has changed several times since the beginning of the project, might be better to let the user overrides it if needed String systemDownloadURL = System.getProperties().getProperty("rincewindURL"); myDownloadURL = systemDownloadURL == null ? DOWNLOAD_DEFAULT_URL : systemDownloadURL; } @Override public void run(@NotNull ProgressIndicator indicator) { if (myProject == null || myProject.isDisposed()) { LOG.debug("Project is disposed, can't download rincewind"); return; } InsightManager insightManager = myProject.getService(InsightManager.class); if (!insightManager.isDownloading.compareAndSet(false, true)) { // We are already in the process of downloading LOG.debug("Already downloading, abort"); return; } try { String rincewindFilename = myRincewindTarget.getName(); LOG.info("Downloading " + rincewindFilename + "..."); indicator.setIndeterminate(false); indicator.setFraction(0.0); boolean downloaded = WGet.apply(myDownloadURL + rincewindFilename, myRincewindTarget, indicator, TOTAL_BYTES); if (downloaded) { Application application = ApplicationManager.getApplication(); application.executeOnPooledThread(() -> { DumbService dumbService = DumbService.getInstance(myProject); dumbService.runReadActionInSmartMode(() -> { LOG.info("Rincewind downloaded, query types for opened files"); PsiManager psiManager = PsiManager.getInstance(myProject); VirtualFile[] openedFiles = FileEditorManager.getInstance(myProject).getOpenFiles(); for (VirtualFile openedFile : openedFiles) { // Query types and update psi cache VirtualFile cmtFile = ORFileUtils.findCmtFileFromSource(myProject, openedFile.getNameWithoutExtension(), null); if (cmtFile != null) { Path cmtPath = FileSystems.getDefault().getPath(cmtFile.getPath()); application.invokeLater(() -> application.runReadAction(() -> { PsiFile psiFile = psiManager.findFile(openedFile); if (psiFile instanceof FileBase) { LOG.debug("Query types for " + openedFile); insightManager.queryTypes(openedFile, cmtPath, inferredTypes -> InferredTypesService.annotatePsiFile( myProject, ORLanguageProperties.cast(psiFile.getLanguage()), openedFile, inferredTypes)); } })); } } }); }); } indicator.setFraction(1.0); } finally { insightManager.isDownloading.set(false); } } } ================================================ FILE: src/main/java/com/reason/hints/RincewindProcess.java ================================================ package com.reason.hints; import com.intellij.notification.*; import com.intellij.openapi.components.*; import com.intellij.openapi.editor.*; import com.intellij.openapi.progress.*; import com.intellij.openapi.project.*; import com.intellij.openapi.vfs.*; import com.reason.comp.bs.*; import com.reason.comp.dune.*; import com.reason.ide.hints.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; import java.io.*; import java.nio.file.*; import java.util.*; @Service(Service.Level.PROJECT) public final class RincewindProcess { private static final Log LOG = Log.create("hints.rincewind"); private final Project myProject; RincewindProcess(@NotNull Project project) { myProject = project; } public void types(@NotNull VirtualFile sourceFile, @NotNull String rincewindBinary, @NotNull String cmtPath, @NotNull ORProcessTerminated runAfter) { if (!new File(rincewindBinary).exists()) { return; } LOG.debug("Looking for types for file", sourceFile); VirtualFile processDir = DunePlatform.findContentRoot(myProject, sourceFile); if (processDir == null) { VirtualFile configFile = BsPlatform.findConfigFile(myProject, sourceFile); processDir = configFile != null ? configFile.getParent() : null; } if (processDir == null) { LOG.trace("no content root found"); return; } ProcessBuilder processBuilder = new ProcessBuilder(rincewindBinary, cmtPath); processBuilder.directory(new File(processDir.getPath())); Process rincewind = null; try { rincewind = processBuilder.start(); BufferedReader reader = new BufferedReader(new InputStreamReader(rincewind.getInputStream())); BufferedReader errReader = new BufferedReader(new InputStreamReader(rincewind.getErrorStream())); Streams.waitUntilReady(reader, errReader); StringBuilder msgBuffer = new StringBuilder(); if (errReader.ready()) { errReader.lines().forEach(line -> msgBuffer.append(line).append(System.lineSeparator())); LOG.warn("Error when processing types"); LOG.warn(msgBuffer.toString()); } else { final InferredTypesImplementation types = new InferredTypesImplementation(); reader.lines().forEach(line -> { if (!line.isEmpty()) { LOG.trace(line); int entryPos = line.indexOf("|"); String entry = entryPos > 0 ? line.substring(0, entryPos) : line; if (!"__".equals(entry)) { int locPos = line.indexOf("|", entryPos + 1); if (locPos > entryPos) { String[] loc = line.substring(entryPos + 1, locPos).split(","); types.add( myProject, entry, decodePosition(loc[0]), decodePosition(loc[1]), line.substring(locPos + 1)); } } } }); runAfter.run(types); } } catch (ProcessCanceledException e) { // Control-flow exceptions (like ProcessCanceledException) should never be logged: // ignore for explicitly started processes or rethrow to handle on the outer process level throw e; } catch (Exception e) { LOG.error("An error occurred when reading types", e); } finally { if (rincewind != null) { rincewind.destroy(); } } } public @NotNull String dumpTree(@NotNull VirtualFile cmtFile, @NotNull String rincewindBinary) { LOG.debug("Dumping tree", cmtFile); final StringBuilder dump = new StringBuilder(); dumper(rincewindBinary, cmtFile, "-d", dump::append); return dump.toString(); } public @NotNull List dumpMeta(@NotNull String rincewindBinary, @NotNull VirtualFile cmtFile) { LOG.debug("Dumping meta", cmtFile); List dump = new ArrayList<>(); dumper(rincewindBinary, cmtFile, "-m", dump::add); return dump; } public @NotNull List dumpTypes(@NotNull String rincewindBinary, @NotNull VirtualFile cmtFile) { LOG.debug("Dumping types", cmtFile); final List dump = new ArrayList<>(); dumper(rincewindBinary, cmtFile, null, dump::add); return dump; } public interface DumpVisitor { void visitLine(String line); } public void dumper(@NotNull String rincewindBinary, @NotNull VirtualFile cmtFile, @Nullable String arg, @NotNull DumpVisitor visitor) { if (!new File(rincewindBinary).exists()) { LOG.debug("File doesn't exists", rincewindBinary); return; } VirtualFile contentRoot = DunePlatform.findContentRoot(myProject, cmtFile); if (contentRoot == null) { VirtualFile configFile = BsPlatform.findConfigFile(myProject, cmtFile); contentRoot = configFile != null ? configFile.getParent() : null; } if (contentRoot != null) { Path cmtPath = FileSystems.getDefault().getPath(cmtFile.getPath()); ProcessBuilder processBuilder = arg == null ? new ProcessBuilder(rincewindBinary, cmtPath.toString()) : new ProcessBuilder(rincewindBinary, arg, cmtPath.toString()); processBuilder.directory(new File(contentRoot.getPath())); if (LOG.isTraceEnabled()) { LOG.trace(Joiner.join(" ", processBuilder.command())); } Process rincewind = null; try { rincewind = processBuilder.start(); BufferedReader reader = new BufferedReader(new InputStreamReader(rincewind.getInputStream())); BufferedReader errReader = new BufferedReader(new InputStreamReader(rincewind.getErrorStream())); Streams.waitUntilReady(reader, errReader); if (errReader.ready()) { StringBuilder msgBuffer = new StringBuilder(); errReader.lines().forEach(line -> msgBuffer.append(line).append("\n")); Notifications.Bus.notify(new ORNotification("Rincewind", msgBuffer.toString(), NotificationType.ERROR)); } else { reader.lines().forEach(line -> visitor.visitLine(line + "\n")); } } catch (Exception e) { LOG.error("An error occurred when dumping cmt file", e); } finally { if (rincewind != null) { rincewind.destroy(); } } } } private @NotNull LogicalPosition decodePosition(@NotNull String location) { String[] pos = location.split("\\."); int line = Integer.parseInt(pos[0]) - 1; int column = Integer.parseInt(pos[1]); return new LogicalPosition(Math.max(line, 0), Math.max(column, 0)); } } ================================================ FILE: src/main/java/com/reason/ide/CompileOnSave.java ================================================ package com.reason.ide; import com.intellij.openapi.application.ex.*; import com.intellij.openapi.project.*; import com.intellij.openapi.vfs.*; import com.reason.comp.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; import java.beans.*; public class CompileOnSave implements PropertyChangeListener { private static final Log LOG = Log.create("compileOnSaveListener"); private final @NotNull Project myProject; private final @NotNull VirtualFile myFile; public CompileOnSave(@NotNull Project project, @NotNull VirtualFile file) { myProject = project; myFile = file; } // If document is modified, but there is no new value, it means it has been saved to disk. // When document is saved, run the compiler !! @Override public void propertyChange(@NotNull PropertyChangeEvent evt) { if ("modified".equals(evt.getPropertyName()) && evt.getNewValue() == Boolean.FALSE) { ORCompilerManager compilerManager = myProject.getService(ORCompilerManager.class); ORResolvedCompiler compiler = compilerManager == null ? null : compilerManager.getCompiler(myFile); if (compiler == null) { LOG.debug("No compiler found", myFile); } else { // We invokeLater because compiler needs access to index files and can't do it in the event thread ApplicationManagerEx.getApplicationEx() .invokeLater(() -> { if (!myProject.isDisposed()) { compiler.runDefault(myFile, null); } }); } } } } ================================================ FILE: src/main/java/com/reason/ide/EditorPosition.java ================================================ package com.reason.ide; import com.intellij.openapi.editor.*; import org.jetbrains.annotations.*; /** * Offset <-> Position converter */ public class EditorPosition { private final int[] m_lineLengths; /** * Extract each line length, needed to compute an offset without a ref to the editor. */ public EditorPosition(@NotNull String[] lines) { m_lineLengths = new int[lines.length]; for (int i = 0; i < lines.length; i++) { String line = lines[i]; m_lineLengths[i] = line.length() + 1 /*line feed*/; } } /** * Transform an offset to a logical position (This is an approximation because we don't have * access to the editor) */ @Nullable public LogicalPosition getPositionFromOffset(int textOffset) { int currentLine = 0; int totalOffset = 0; for (int lineLength : m_lineLengths) { if (totalOffset < textOffset && textOffset <= totalOffset + lineLength) { break; } currentLine++; totalOffset += lineLength; } int currentPos = textOffset - totalOffset; return currentLine < 0 || currentPos < 0 ? null : new LogicalPosition(currentLine, currentPos); } } ================================================ FILE: src/main/java/com/reason/ide/IconProvider.java ================================================ package com.reason.ide; import com.intellij.json.psi.*; import com.intellij.psi.*; import com.reason.comp.esy.*; import com.reason.ide.files.*; import com.reason.ide.search.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import org.jetbrains.annotations.*; import javax.swing.*; public class IconProvider extends com.intellij.ide.IconProvider { @Nullable @Override public Icon getIcon(@NotNull PsiElement element, int flags) { if (element instanceof PsiFile) { if (element instanceof OclFile) { return ORIcons.OCL_FILE; } if (element instanceof OclInterfaceFile) { return ORIcons.OCL_INTERFACE_FILE; } if (element instanceof RmlFile) { return ORIcons.RML_FILE; } if (element instanceof RmlInterfaceFile) { return ORIcons.RML_INTERFACE_FILE; } if (element instanceof ResFile) { return ORIcons.RES_FILE; } if (element instanceof ResInterfaceFile) { return ORIcons.RES_INTERFACE_FILE; } if (isEsyPackageJson((PsiFile) element)) { return ORIcons.ESY_FILE; } } else if (element instanceof RPsiException) { return ORIcons.EXCEPTION; } else if (element instanceof RPsiInnerModule innerModule) { return innerModule.isModuleType() ? ORIcons.INNER_MODULE_INTF : ORIcons.INNER_MODULE; } else if (element instanceof RPsiModuleSignature) { return ORIcons.MODULE_TYPE; } else if (element instanceof RPsiFunctor) { return ORIcons.FUNCTOR; } else if (element instanceof RPsiType) { return ORIcons.TYPE; } else if (element instanceof RPsiVariantDeclaration) { return ORIcons.VARIANT; } else if (element instanceof RPsiLet let) { return let.isRecord() ? ORIcons.OBJECT : (let.isFunction() ? ORIcons.FUNCTION : ORIcons.LET); } else if (element instanceof RPsiExternal) { return ORIcons.EXTERNAL; } else if (element instanceof RPsiVal val) { return val.isFunction() ? ORIcons.FUNCTION : ORIcons.VAL; } return null; } public static @NotNull Icon getDataModuleIcon(@NotNull FileModuleData element) { boolean isInterface = element.isInterface(); if (element.isOCaml()) { return isInterface ? ORIcons.OCL_FILE_MODULE_INTERFACE : ORIcons.OCL_FILE_MODULE; } else if (element.isRescript()) { return isInterface ? ORIcons.RES_FILE_MODULE_INTERFACE : ORIcons.RES_FILE_MODULE; } else { return isInterface ? ORIcons.RML_FILE_MODULE_INTERFACE : ORIcons.RML_FILE_MODULE; } } public static @NotNull Icon getDataModuleFileIcon(@NotNull FileModuleData element) { boolean isInterface = element.isInterface(); if (element.isOCaml()) { return isInterface ? ORIcons.OCL_INTERFACE_FILE : ORIcons.OCL_FILE; } else if (element.isRescript()) { return isInterface ? ORIcons.RES_INTERFACE_FILE : ORIcons.RES_FILE; } else { return isInterface ? ORIcons.RML_INTERFACE_FILE : ORIcons.RML_FILE; } } private boolean isEsyPackageJson(@Nullable PsiFile element) { return element instanceof JsonFile && EsyPackageJson.isEsyPackageJson(ORFileUtils.getVirtualFile(element)); } } ================================================ FILE: src/main/java/com/reason/ide/OREditorFactoryListener.java ================================================ package com.reason.ide; import com.intellij.openapi.editor.event.*; import com.intellij.openapi.project.*; import org.jetbrains.annotations.*; public class OREditorFactoryListener implements EditorFactoryListener { @Override public void editorCreated(@NotNull EditorFactoryEvent event) { // Ensure that editor tracker is created and loaded Project project = event.getEditor().getProject(); if (project != null) { project.getService(OREditorTracker.class); } } } ================================================ FILE: src/main/java/com/reason/ide/OREditorTracker.java ================================================ package com.reason.ide; import com.intellij.openapi.*; import com.intellij.openapi.components.*; import com.intellij.openapi.editor.*; import com.intellij.openapi.editor.event.*; import com.intellij.openapi.fileEditor.*; import com.intellij.openapi.fileTypes.*; import com.intellij.openapi.project.*; import com.intellij.openapi.util.*; import com.intellij.openapi.vfs.*; import com.intellij.psi.*; import com.intellij.util.messages.*; import com.reason.*; import com.reason.hints.*; import com.reason.ide.hints.*; import org.jetbrains.annotations.*; import static com.intellij.openapi.fileEditor.FileEditorManagerListener.*; @Service(Service.Level.PROJECT) public final class OREditorTracker implements Disposable { private final MessageBusConnection m_messageBusConnection; public OREditorTracker(@NotNull Project project) { m_messageBusConnection = project.getMessageBus().connect(this); m_messageBusConnection.subscribe(FILE_EDITOR_MANAGER, new FileEditorManagerListener() { // When a file is opened inside an editor, we check that rincewind is present // we also register document listeners to detect changes @Override public void fileOpened(@NotNull FileEditorManager source, @NotNull VirtualFile file) { InsightManager insightManager = project.getService(InsightManager.class); if (insightManager != null) { insightManager.downloadRincewindIfNeeded(file); } FileType fileType = file.getFileType(); if (FileHelper.isCompilable(fileType)) { FileEditor selectedEditor = source.getSelectedEditor(file); Document document = FileDocumentManager.getInstance().getDocument(file); if (selectedEditor instanceof TextEditor && document != null) { document.addDocumentListener(new ORDocumentEventListener((TextEditor) selectedEditor), selectedEditor); CompileOnSave saveListener = new CompileOnSave(project, file); selectedEditor.addPropertyChangeListener(saveListener); Disposer.register(selectedEditor, () -> selectedEditor.removePropertyChangeListener(saveListener)); } } } // On tab change, we refresh inferred types @Override public void selectionChanged(@NotNull FileEditorManagerEvent event) { VirtualFile newFile = event.getNewFile(); FileType fileType = newFile == null ? null : newFile.getFileType(); if (FileHelper.isCompilable(fileType)) { PsiFile psiFile = InferredTypesService.getPsiFile(project); if (psiFile != null) { InferredTypesService.queryTypes(project, psiFile); } } } }); } @Override public void dispose() { m_messageBusConnection.disconnect(); } static class ORDocumentEventListener implements DocumentListener { private final TextEditor m_textEditor; private int m_oldLinesCount; public ORDocumentEventListener(TextEditor textEditor) { m_textEditor = textEditor; } @Override public void beforeDocumentChange(@NotNull DocumentEvent event) { Document document = event.getDocument(); m_oldLinesCount = document.getLineCount(); } // When document lines count change, we need to move the type annotations for line painter @Override public void documentChanged(@NotNull DocumentEvent event) { Document document = event.getDocument(); int newLineCount = document.getLineCount(); if (newLineCount != m_oldLinesCount) { VirtualFile file = FileDocumentManager.getInstance().getFile(document); CodeLens codeLens = file == null ? null : file.getUserData(CodeLens.CODE_LENS); if (codeLens != null) { LogicalPosition cursorPosition = m_textEditor.getEditor().offsetToLogicalPosition(event.getOffset()); int direction = newLineCount - m_oldLinesCount; codeLens.move(cursorPosition, direction); } } } } } ================================================ FILE: src/main/java/com/reason/ide/ORFileDocumentListener.java ================================================ package com.reason.ide; import com.intellij.openapi.*; import com.intellij.openapi.components.*; import com.intellij.openapi.editor.*; import com.intellij.openapi.fileEditor.*; import com.intellij.openapi.project.*; import com.intellij.util.messages.*; import com.reason.ide.format.*; import org.jetbrains.annotations.*; @Service(Service.Level.PROJECT) public final class ORFileDocumentListener implements Disposable { private final @NotNull MessageBusConnection m_messageBusConnection; public static void ensureSubscribed(@NotNull Project project) { project.getService(ORFileDocumentListener.class); } private ORFileDocumentListener(@NotNull Project project) { m_messageBusConnection = project.getMessageBus().connect(this); m_messageBusConnection.subscribe( FileDocumentManagerListener.TOPIC, new FileDocumentManagerListener() { @Override public void beforeDocumentSaving(@NotNull Document document) { ReformatOnSave.apply(project, document); } }); } @Override public void dispose() { m_messageBusConnection.disconnect(); } } ================================================ FILE: src/main/java/com/reason/ide/ORFileUtils.java ================================================ package com.reason.ide; import com.intellij.openapi.project.*; import com.intellij.openapi.vfs.*; import com.intellij.psi.*; import com.intellij.psi.search.*; import com.reason.comp.bs.*; import jpsplugin.com.reason.*; import org.apache.commons.lang3.*; import org.jetbrains.annotations.*; import java.nio.file.*; import java.util.*; public class ORFileUtils { public static final Comparator FILE_DEPTH_COMPARATOR = Comparator.comparingInt(file -> StringUtils.countMatches(file.getPath(), '/')); private static final Log LOG = Log.create("file"); private ORFileUtils() { } public static VirtualFile findCmtFileFromSource(@NotNull Project project, @NotNull String filenameWithoutExtension, @Nullable String namespace) { if (!DumbService.isDumb(project)) { GlobalSearchScope scope = GlobalSearchScope.allScope(project); String filename = (namespace == null ? "" : namespace) + filenameWithoutExtension + ".cmt"; Collection cmtFiles = FilenameIndex.getVirtualFilesByName(filename, scope); if (cmtFiles.isEmpty()) { LOG.debug("File module NOT FOUND", filename); return null; } VirtualFile firstFile = cmtFiles.iterator().next(); if (LOG.isDebugEnabled()) { LOG.debug("Found cmt " + filename + " (" + firstFile.getPath() + ")"); } return firstFile; } else { LOG.info("Cant find cmt while reindexing"); } return null; } public static @NotNull String toRelativeSourceName(@NotNull Project project, @NotNull VirtualFile sourceFile, @NotNull Path relativePath) { String sourcePath = relativePath.toString(); String namespace = project.getService(BsCompiler.class).getNamespace(sourceFile); if (!namespace.isEmpty()) { sourcePath = sourcePath.replace("-" + StringUtil.toFirstUpper(namespace), ""); } int dotPos = sourcePath.lastIndexOf("."); return 0 <= dotPos ? sourcePath.substring(0, dotPos) + ".re" : sourcePath; } /** * Given a `start` directory (or file), searches that directory for `target`, continuing up * directories until either `target` is found or the project base path is reached. * * @param project project to use * @param start starting directory (or file) to begin searching for `target` * @param target file being searched for * @return found target file */ public static @Nullable VirtualFile findAncestor(@NotNull Project project, @Nullable VirtualFile start, @NotNull String target) { if (start == null) { return null; } // start must be a directory, should only happen on first iteration if (!start.isDirectory()) { if (target.equals(start.getName())) { return start; } start = start.getParent(); // parent is null, done if (start == null) { return null; } } // target found, done VirtualFile foundTarget = start.findChild(target); if (foundTarget != null) { return foundTarget; } // just checked project root, done if (start.getPath().equals(project.getBasePath())) { return null; } // parent is null, we're done VirtualFile parent = start.getParent(); if (parent == null) { return null; } return findAncestor(project, parent, target); } public static @Nullable VirtualFile findOneOfAncestor(@NotNull Project project, @Nullable VirtualFile start, @NotNull String target1, @NotNull String target2) { if (start == null) { return null; } // start must be a directory, should only happen on first iteration if (!start.isDirectory()) { String startName = start.getName(); if (target1.equals(startName) || target2.equals(startName)) { return start; } return oneOfAncestor(project.getBasePath(), start.getParent(), target1, target2); } return oneOfAncestor(project.getBasePath(), start, target1, target2); } public static @Nullable VirtualFile oneOfAncestor(@Nullable String basePath, @Nullable VirtualFile start, @NotNull String target1, @NotNull String target2) { if (start == null) { return null; } // target found, done VirtualFile foundTarget1 = start.findChild(target1); if (foundTarget1 != null) { return foundTarget1; } VirtualFile foundTarget2 = start.findChild(target2); if (foundTarget2 != null) { return foundTarget2; } // just checked project root, done if (start.getPath().equals(basePath)) { return null; } // Continue return oneOfAncestor(basePath, start.getParent(), target1, target2); } // RELEASE: check no direct getVirtualFile() in code // Because psiFile.getVirtualFile() is not annotated with @Nullable ! public static @Nullable VirtualFile getVirtualFile(@Nullable PsiFile psiFile) { VirtualFile virtualFile = psiFile == null ? null : psiFile.getVirtualFile(); // If file is memory only, use original file if (psiFile != null && virtualFile == null) { virtualFile = psiFile.getOriginalFile().getVirtualFile(); } return virtualFile; } public static @Nullable VirtualFile getVirtualFile(@Nullable PsiElement psiElement) { return psiElement != null ? getVirtualFile(psiElement.getContainingFile()) : null; } public static @NotNull String getParentPath(@Nullable VirtualFile virtualFile) { VirtualFile parent = virtualFile != null ? virtualFile.getParent() : null; return parent != null ? parent.getPath() : ""; } public static String getParentPath(@Nullable PsiFile file) { return getParentPath(getVirtualFile(file)); } } ================================================ FILE: src/main/java/com/reason/ide/ORIcons.java ================================================ package com.reason.ide; import com.intellij.icons.*; import javax.swing.*; import static com.intellij.openapi.util.IconLoader.*; /* * https://www.jetbrains.org/intellij/sdk/docs/reference_guide/work_with_icons_and_images.html * https://jetbrains.design/intellij/principles/icons/ * https://jetbrains.design/intellij/resources/icons_list/ * * Node, action, filetype : 16x16 * Tool window : 13x13 * Editor gutter : 12x12 * Font : Gotham */ public class ORIcons { // TOOL WINDOW public static final Icon BUCKLESCRIPT_TOOL = getIcon("/icons/bucklescriptTool.svg", ORIcons.class); public static final Icon RESCRIPT_TOOL = getIcon("/icons/rescriptTool.svg", ORIcons.class); public static final Icon DUNE_TOOL = getIcon("/icons/duneTool.svg", ORIcons.class); public static final Icon ESY_TOOL = getIcon("/icons/esyTool.svg", ORIcons.class); // GUTTER public static final Icon IMPLEMENTED = AllIcons.Gutter.ImplementedMethod; public static final Icon IMPLEMENTING = AllIcons.Gutter.ImplementingMethod; // OTHER public static final Icon OCAML = getIcon("/icons/ocamlLogo.svg", ORIcons.class); public static final Icon DUNE = getIcon("/icons/duneLogo.svg", ORIcons.class); //public static final Icon ESY = getIcon("/icons/esy.svg", ORIcons.class); public static final Icon BS_FILE = getIcon("/icons/bsFile.svg", ORIcons.class); public static final Icon ESY_FILE = getIcon("/icons/esyFile.svg", ORIcons.class); public static final Icon RML_FILE = getIcon("/icons/reFile.svg", ORIcons.class); public static final Icon RML_INTERFACE_FILE = getIcon("/icons/reiFile.svg", ORIcons.class); public static final Icon RES_FILE = getIcon("/icons/resFile.svg", ORIcons.class); public static final Icon RES_INTERFACE_FILE = getIcon("/icons/resiFile.svg", ORIcons.class); public static final Icon OCL_FILE = getIcon("/icons/mlFile.svg", ORIcons.class); public static final Icon OCL_INTERFACE_FILE = getIcon("/icons/mliFile.svg", ORIcons.class); public static final Icon DUNE_FILE = getIcon("/icons/duneFile.svg", ORIcons.class); public static final Icon OCL_FILE_MODULE = getIcon("/icons/top-module-ocaml.svg", ORIcons.class); public static final Icon RML_FILE_MODULE = getIcon("/icons/top-module-reasonml.svg", ORIcons.class); public static final Icon RES_FILE_MODULE = getIcon("/icons/top-module-rescript.svg", ORIcons.class); public static final Icon OCL_FILE_MODULE_INTERFACE = getIcon("/icons/top-module-interface-ocaml.svg", ORIcons.class); public static final Icon RML_FILE_MODULE_INTERFACE = getIcon("/icons/top-module-interface-reasonml.svg", ORIcons.class); public static final Icon RES_FILE_MODULE_INTERFACE = getIcon("/icons/top-module-interface-rescript.svg", ORIcons.class); public static final Icon RML_BLUE = getIcon("/icons/top-module-interface-reasonml.svg", ORIcons.class); public static final Icon RML_YELLOW = getIcon("/icons/reasonYellow.svg", ORIcons.class); public static final Icon OCL_MODULE = getIcon("/icons/ocamlModule.svg", ORIcons.class); public static final Icon OCL_SDK = getIcon("/icons/ocamlSdk.svg", ORIcons.class); public static final Icon OCL_GREEN_FILE = getIcon("/icons/ocamlGreen.svg", ORIcons.class); public static final Icon TYPE = getIcon("/icons/type.svg", ORIcons.class); public static final Icon VARIANT = getIcon("/icons/variant.svg", ORIcons.class); //public static final Icon ANNOTATION = AllIcons.Nodes.Annotationtype; public static final Icon INNER_MODULE = getIcon("/icons/innerModule.svg", ORIcons.class); public static final Icon INNER_MODULE_INTF = getIcon("/icons/innerModuleIntf.svg", ORIcons.class); public static final Icon MODULE_TYPE = getIcon("/icons/javaModuleType.svg", ORIcons.class); public static final Icon FUNCTOR = AllIcons.Nodes.Artifact; public static final Icon LET = AllIcons.Nodes.Variable; public static final Icon VAL = AllIcons.Nodes.Variable; public static final Icon ATTRIBUTE = AllIcons.Nodes.Property; public static final Icon FUNCTION = AllIcons.Nodes.Function; public static final Icon METHOD = AllIcons.Nodes.Method; public static final Icon CLASS = AllIcons.Nodes.Class; public static final Icon EXCEPTION = AllIcons.Nodes.ExceptionClass; public static final Icon EXTERNAL = AllIcons.Nodes.Enum; public static final Icon OBJECT = AllIcons.Json.Object; public static final Icon VIRTUAL_NAMESPACE = AllIcons.Actions.GroupByPackage; public static final Icon OPEN = AllIcons.Actions.GroupByModule; public static final Icon INCLUDE = AllIcons.Actions.GroupByModule; public static final Icon OVERLAY_MANDATORY = AllIcons.Ide.ErrorPoint; public static final Icon OVERLAY_EXECUTE = AllIcons.Nodes.RunnableMark; public static final Icon RESET = getIcon("/icons/reset.svg", ORIcons.class); // REPL public static final Icon REPL = AllIcons.Nodes.Console; private ORIcons() { } } ================================================ FILE: src/main/java/com/reason/ide/ORPostStartupActivity.java ================================================ package com.reason.ide; import com.intellij.openapi.project.*; import com.intellij.openapi.startup.*; import com.reason.comp.ocaml.*; import com.reason.ide.console.*; import com.reason.ide.settings.*; import jpsplugin.com.reason.*; import kotlin.*; import kotlin.coroutines.*; import org.jetbrains.annotations.*; import static com.intellij.openapi.application.ApplicationManager.*; /** * Ensure all services have started after the startup. */ public class ORPostStartupActivity implements ProjectActivity, DumbAware { private static final Log LOG = Log.create("activity.startup"); @Override public @Nullable Object execute(@NotNull Project project, @NotNull Continuation continuation) { ORFileDocumentListener.ensureSubscribed(project); LOG.debug("Subscribed project and document listeners."); DumbService.getInstance(project).smartInvokeLater(() -> { ORSettings settings = project.getService(ORSettings.class); OpamEnv opamEnv = getApplication().getService(OpamEnv.class); opamEnv.computeEnv(settings.getOpamLocation(), settings.getSwitchName(), settings.getCygwinBash(), data -> LOG.debug("Computed opam env for " + settings.getSwitchName())); project.getService(ORToolWindowManager.class).shouldShowToolWindows(); }); return null; } } ================================================ FILE: src/main/java/com/reason/ide/ORVirtualFileListener.java ================================================ package com.reason.ide; import com.intellij.openapi.application.*; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.*; import com.intellij.openapi.project.*; import com.intellij.openapi.vfs.*; import com.intellij.openapi.vfs.newvfs.events.*; import com.reason.*; import com.reason.comp.*; import com.reason.comp.bs.*; import com.reason.comp.esy.*; import com.reason.hints.*; import com.reason.ide.console.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; import java.util.*; import static com.reason.comp.ORConstants.*; /** * Listener that detects all modifications on files. * This class receives events from all projects. */ class ORVirtualFileListener implements AsyncFileListener { private static final Log LOG = Log.create("VFSListener"); @Override public @Nullable ChangeApplier prepareChange(@NotNull List events) { return ORChangeApplier.apply(events); } private static class ORChangeApplier implements ChangeApplier { private final @NotNull List m_events; private ORChangeApplier(@NotNull List events) { m_events = events; } public static @NotNull ChangeApplier apply(@NotNull List events) { return new ORChangeApplier(events); } @Override public void afterVfsChange() { m_events.forEach(ORChangeApplier::handleEvent); } private static void handleEvent(@NotNull E event) { if (event instanceof VFileContentChangeEvent) { handleFileContentChangeEvent((VFileContentChangeEvent) event); showHideToolWindowsForConfigurationFiles(event); } else if (event instanceof VFileCreateEvent || event instanceof VFileDeleteEvent) { showHideToolWindowsForConfigurationFiles(event); } else if (event instanceof VFilePropertyChangeEvent && ((VFilePropertyChangeEvent) event).isRename()) { showHideToolWindowsForConfigurationFiles(event); } } private static void showHideToolWindowsForConfigurationFiles(@NotNull E event) { VirtualFile modifiedFile = event.getFile(); if (modifiedFile == null) { return; } String fileName = modifiedFile.getName(); boolean potentialToolWindowUpdate = NODE_MODULES.equals(fileName) || RESCRIPT_DIR.equals(fileName) || BS_DIR.equals(fileName) || FileHelper.isCompilerConfigJson(modifiedFile) || EsyPackageJson.isEsyPackageJson(modifiedFile); if (potentialToolWindowUpdate) { LOG.info("Update tool windows visibility"); for (Project project : ProjectManager.getInstance().getOpenProjects()) { ORToolWindowManager toolWindowManager = project.getService(ORToolWindowManager.class); ApplicationManager.getApplication().invokeLater(toolWindowManager::shouldShowToolWindows); } } } private static void handleFileContentChangeEvent(@NotNull VFileContentChangeEvent event) { VirtualFile file = event.getFile(); if (FileHelper.isCompilerConfigJson(file)) { handleCompilerConfigContentChange(file); } else if (FileHelper.isNinja(file)) { LOG.debug("Refresh ninja build", file); for (Project project : ProjectManager.getInstance().getOpenProjects()) { project.getService(BsCompiler.class).refreshNinjaBuild(); } } } private static void handleCompilerConfigContentChange(@NotNull VirtualFile compilerConfigFile) { LOG.debug("CompilerConfig content change", compilerConfigFile); for (Project project : ProjectManager.getInstance().getOpenProjects()) { project.getService(ORCompilerConfigManager.class).refresh(compilerConfigFile); Module module = ModuleUtil.findModuleForFile(compilerConfigFile, project); if (module != null) { project.getService(InsightManager.class).downloadRincewindIfNeeded(compilerConfigFile); } } } } } ================================================ FILE: src/main/java/com/reason/ide/actions/ConvertAction.java ================================================ package com.reason.ide.actions; import com.intellij.notification.*; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.application.*; import com.intellij.openapi.command.*; import com.intellij.openapi.editor.*; import com.intellij.openapi.fileEditor.*; import com.intellij.openapi.fileTypes.*; import com.intellij.openapi.project.*; import com.intellij.openapi.vfs.*; import com.intellij.psi.*; import com.reason.*; import com.reason.comp.bs.*; import com.reason.ide.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; import java.io.*; import static com.intellij.openapi.actionSystem.CommonDataKeys.*; import static com.reason.FileHelper.*; public class ConvertAction extends AnAction { @Override public void actionPerformed(@NotNull AnActionEvent e) { PsiFile file = e.getData(PSI_FILE); Project project = e.getProject(); if (project != null && file != null) { apply(project, file, false); } } protected void apply(@NotNull Project project, @NotNull PsiFile file, boolean isNewFile) { BsCompiler bucklescript = project.getService(BsCompiler.class); FileType fileType = file.getFileType(); final Document document = PsiDocumentManager.getInstance(project).getDocument(file); if (document != null) { String convertedText; String toFormat; VirtualFile sourceFile = ORFileUtils.getVirtualFile(file); boolean isInterface = isInterface(fileType); if (FileHelper.isReason(fileType)) { // convert ReasonML to OCaml toFormat = isInterface ? "mli" : "ml"; convertedText = bucklescript.convert(sourceFile, isInterface, "re", "ml", document); } else if (FileHelper.isOCaml(fileType)) { // convert OCaml to ReasonML toFormat = isInterface ? "rei" : "re"; convertedText = bucklescript.convert(sourceFile, isInterface, "ml", "re", document); } else { toFormat = null; convertedText = null; } if (sourceFile != null && convertedText != null) { CommandProcessor.getInstance() .runUndoTransparentAction( () -> WriteAction.run( () -> { String newFilename = sourceFile.getNameWithoutExtension() + "." + toFormat; if (isNewFile) { try { VirtualFile newSourceFile = VfsUtilCore.copyFile(this, sourceFile, sourceFile.getParent(), newFilename); PsiFile newPsiFile = PsiManager.getInstance(project).findFile(newSourceFile); Document newDocument = newPsiFile == null ? null : PsiDocumentManager.getInstance(project) .getDocument(newPsiFile); if (newDocument != null) { newDocument.setText(convertedText); FileDocumentManager.getInstance().saveDocument(newDocument); } VirtualFileManager.getInstance().syncRefresh(); FileEditorManager.getInstance(project).openFile(newSourceFile, true); } catch (IOException ex) { Notifications.Bus.notify( new ORNotification( "Convert", "File creation failed\n" + ex.getMessage(), NotificationType.ERROR)); } } else { document.setText(convertedText); FileDocumentManager.getInstance().saveDocument(document); try { sourceFile.rename(this, newFilename); } catch (IOException ex) { Notifications.Bus.notify( new ORNotification( "Convert", "File renaming failed\n" + ex.getMessage(), NotificationType.ERROR)); } } })); } } } } ================================================ FILE: src/main/java/com/reason/ide/actions/TransformAction.java ================================================ package com.reason.ide.actions; import static com.intellij.openapi.actionSystem.CommonDataKeys.PSI_FILE; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.project.Project; import com.intellij.psi.PsiFile; import org.jetbrains.annotations.NotNull; public class TransformAction extends ConvertAction { @Override public void actionPerformed(@NotNull AnActionEvent e) { PsiFile file = e.getData(PSI_FILE); Project project = e.getProject(); if (project != null && file != null) { apply(project, file, true); } } } ================================================ FILE: src/main/java/com/reason/ide/annotations/BsErrorAnnotator.java ================================================ package com.reason.ide.annotations; import com.intellij.execution.process.*; import com.intellij.openapi.editor.*; import com.intellij.openapi.project.*; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.*; import com.intellij.psi.*; import com.reason.comp.*; import com.reason.comp.bs.*; import com.reason.ide.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; import java.io.*; import java.util.*; import static com.reason.comp.rescript.ResPlatform.*; import static com.reason.ide.annotations.ORErrorAnnotator.*; import static java.util.Collections.*; public class BsErrorAnnotator { protected static final Log LOG = Log.create("annotator.bucklescript"); private BsErrorAnnotator() { } public static @Nullable ORErrorAnnotator.InitialInfo collectInformation(@NotNull BsResolvedCompiler compiler, @NotNull Editor editor, @NotNull PsiFile psiFile) { BsConfig config = psiFile.getProject().getService(ORCompilerConfigManager.class).getConfig(compiler.getConfigFile()); if (config == null) { LOG.info("No config file found for: " + compiler.getConfigFile()); return null; } VirtualFile contentRoot = compiler.getConfigFile(); VirtualFile libRoot = contentRoot.findFileByRelativePath("lib/bs"); if (libRoot == null) { LOG.info("Unable to find BuckleScript lib root."); return null; } Project project = psiFile.getProject(); Ninja ninja = compiler.readNinja(); VirtualFile sourceFile = ORFileUtils.getVirtualFile(psiFile); if (sourceFile == null) { return null; } if (ninja != null && ninja.isRescriptFormat()) { List args = isDevSource(sourceFile, contentRoot, config) ? ninja.getArgsDev() : ninja.getArgs(); return new ORErrorAnnotator.InitialInfo<>(compiler, psiFile, libRoot, null, editor, args, config.getJsxVersion(), config.getJsxMode(), config.isUncurried()); } // create temporary compilation directory File tempCompilationDirectory = ORErrorAnnotator.getOrCreateTempDirectory(project); cleanTempDirectory(tempCompilationDirectory, sourceFile.getNameWithoutExtension()); String jsxVersion = config.getJsxVersion(); String namespace = config.getNamespace(); // If a directory is marked as dev-only, it won't be built and exposed to other "dev" // directories in the same project // https://bucklescript.github.io/docs/en/build-configuration#sources for (String devSource : config.getDevSources()) { VirtualFile devFile = contentRoot.findFileByRelativePath(devSource); if (ninja != null && devFile != null && FileUtil.isAncestor(devFile.getPath(), sourceFile.getPath(), true)) { ninja.addInclude(devSource); } } // Creates a temporary file on disk with a copy of the current document. // It'll be used by bsc for a temporary compilation File sourceTempFile = ORErrorAnnotator.copyToTempFile(tempCompilationDirectory, psiFile, sourceFile.getNameWithoutExtension()); if (sourceTempFile == null || ninja == null) { return null; } LOG.trace("Wrote contents to temporary file", sourceTempFile); String tempNameWithoutExtension = FileUtil.getNameWithoutExtension(sourceTempFile); File cmtFile = new File(sourceTempFile.getParent(), tempNameWithoutExtension + ".cmt"); List arguments = new ArrayList<>(); arguments.add("-bs-super-errors"); arguments.add("-color"); arguments.add("never"); arguments.addAll(ninja.getPkgFlags()); arguments.addAll(ninja.getBscFlags()); for (String ppxPath : ninja.getPpxIncludes()) { arguments.add("-ppx"); arguments.add(ppxPath); } if (!namespace.isEmpty()) { arguments.add("-bs-ns"); arguments.add(namespace); } if (jsxVersion != null) { arguments.add("-bs-jsx"); arguments.add(jsxVersion); } for (String bscInclude : ninja.getIncludes()) { arguments.add("-I"); arguments.add(bscInclude); } arguments.add("-bin-annot"); arguments.add("-o"); arguments.add(cmtFile.getPath()); arguments.add(sourceTempFile.getPath()); return new ORErrorAnnotator.InitialInfo<>(compiler, psiFile, libRoot, sourceTempFile, editor, arguments, jsxVersion, null, false); } public static @Nullable AnnotationResult doAnnotate(@NotNull InitialInfo> initialInfo) { File cmtFile; List info; // https://github.com/giraud/reasonml-idea-plugin/issues/362 // lib directory can be invalidated after a clean if (!initialInfo.libRoot.isValid()) { LOG.debug("lib directory is invalid, skip compilation"); return null; } if (initialInfo.oldFormat) { assert initialInfo.tempFile != null; File baseFile = new File(initialInfo.tempFile.getParent(), FileUtil.getNameWithoutExtension(initialInfo.tempFile)); cmtFile = new File(baseFile + ".cmt"); long compilationStartTime = System.currentTimeMillis(); info = compile((BsResolvedCompiler) initialInfo.compiler, initialInfo.arguments, initialInfo.libRoot); if (LOG.isTraceEnabled()) { LOG.trace("Compilation done in " + (System.currentTimeMillis() - compilationStartTime) + "ms"); } LOG.debug("Clear temporary files from base", baseFile); FileUtil.delete(initialInfo.tempFile); FileUtil.delete(new File(baseFile.getPath() + ".cmi")); FileUtil.delete(new File(baseFile.getPath() + ".cmj")); // cmt is never deleted } else { Project project = initialInfo.sourcePsiFile.getProject(); VirtualFile sourceFile = ORFileUtils.getVirtualFile(initialInfo.sourcePsiFile); if (sourceFile == null) { return null; } // new info format String nameWithoutExtension = sourceFile.getNameWithoutExtension(); // Create and clean temporary compilation directory File tempCompilationDirectory = getOrCreateTempDirectory(project); cleanTempDirectory(tempCompilationDirectory, nameWithoutExtension); // Creates a temporary file on disk with a copy of the current document. // It'll be used by bsc for a temporary compilation File sourceTempFile = copyToTempFile(tempCompilationDirectory, initialInfo.sourcePsiFile, nameWithoutExtension); if (sourceTempFile == null) { return null; } LOG.trace("Wrote contents to temporary file", sourceTempFile); VirtualFile contentRoot = initialInfo.libRoot.getParent().getParent(); VirtualFile bsConfigFile = contentRoot.findFileByRelativePath(ORConstants.BS_CONFIG_FILENAME); BsConfig config = bsConfigFile == null ? null : BsConfigReader.read(bsConfigFile); if (config == null) { LOG.info("No bsconfig.json found for content root: " + contentRoot); return null; } cmtFile = new File(tempCompilationDirectory, nameWithoutExtension + ".cmt"); ArrayList arguments = new ArrayList<>(initialInfo.arguments); String jsxVersion = initialInfo.jsxVersion; if (jsxVersion != null) { arguments.add("-bs-jsx"); arguments.add(jsxVersion); } arguments.add("-bin-annot"); arguments.add("-o"); arguments.add(cmtFile.getPath()); arguments.add(sourceTempFile.getPath()); info = compile((BsResolvedCompiler) initialInfo.compiler, arguments, initialInfo.libRoot); File baseFile = new File(sourceTempFile.getParent(), FileUtil.getNameWithoutExtension(sourceTempFile)); LOG.debug("Clear temporary files from base", baseFile); FileUtil.delete(sourceTempFile); FileUtil.delete(new File(baseFile.getPath() + ".cmi")); FileUtil.delete(new File(baseFile.getPath() + ".cmj")); // cmt is never deleted } LOG.debug("Found info", info); return new ORErrorAnnotator.AnnotationResult(info, initialInfo.editor, cmtFile); } public static @NotNull List compile(@NotNull BsResolvedCompiler compiler, @NotNull List arguments, @NotNull VirtualFile workDir) { List command = new ArrayList<>(); command.add(compiler.getPath()); command.addAll(arguments); if (LOG.isTraceEnabled()) { LOG.trace(Joiner.join(" ", command.toArray(new String[0]))); LOG.trace(" work dir", workDir); } Process process = null; try { ORErrorAnnotator.CompilerProcessListener processListener = new ORErrorAnnotator.CompilerProcessListener(new BsLineProcessor(LOG)); ProcessBuilder builder = new ProcessBuilder(command); builder.directory(new File(workDir.getPath())); process = builder.start(); try (BufferedReader stdErr = new BufferedReader(new InputStreamReader(process.getErrorStream()))) { String line; while ((line = stdErr.readLine()) != null) { processListener.onTextAvailable(line, ProcessOutputTypes.STDERR); } } return processListener.getOutputInfo(); } catch (Exception e) { LOG.info("Execution error", e); } finally { if (process != null) { process.destroyForcibly(); } } return emptyList(); } } ================================================ FILE: src/main/java/com/reason/ide/annotations/ErrorFileHighlighter.java ================================================ package com.reason.ide.annotations; import com.intellij.openapi.fileTypes.FileType; import com.intellij.openapi.util.Condition; import com.intellij.openapi.vfs.VirtualFile; import com.reason.FileHelper; import org.jetbrains.annotations.NotNull; public class ErrorFileHighlighter implements Condition { @Override public boolean value(@NotNull VirtualFile file) { FileType fileType = file.getFileType(); return FileHelper.isOCaml(fileType) || FileHelper.isReason(fileType) || FileHelper.isRescript(fileType); } } ================================================ FILE: src/main/java/com/reason/ide/annotations/ErrorsManager.java ================================================ package com.reason.ide.annotations; import com.intellij.openapi.components.*; import com.intellij.util.containers.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; import java.util.HashMap; import java.util.*; import java.util.concurrent.*; @Service(Service.Level.PROJECT) public final class ErrorsManager { private static final Log LOG = Log.create("errors"); private final Map> m_errorsByFile = new ConcurrentHashMap<>(); private final MultiMap m_warningsByFile = MultiMap.createConcurrent(); public void addAllInfo(@NotNull Collection bsbInfo) { LOG.debug("Adding errors/warnings", bsbInfo); for (OutputInfo info : bsbInfo) { LOG.trace(" -> " + bsbInfo); if (info != null && info.path != null && !info.path.isEmpty()) { if (info.isError) { m_errorsByFile.compute( info.path, (k, v) -> { if (v == null) { Map byLine = new HashMap<>(); byLine.put(info.lineStart, info); return byLine; } v.put(info.lineStart, info); return v; }); if (LOG.isTraceEnabled()) { LOG.trace(" -> error: " + info); } } else { m_warningsByFile.putValue(info.path, info); if (LOG.isTraceEnabled()) { LOG.trace(" -> warning: " + info); } } } } if (LOG.isDebugEnabled()) { LOG.debug(" -> errors: [" + Joiner.join(", ", m_errorsByFile.keySet()) + "]"); LOG.debug(" -> warnings: [" + Joiner.join(", ", m_warningsByFile.keySet()) + "]"); } } public void clearErrors() { m_errorsByFile.clear(); m_warningsByFile.clear(); } } ================================================ FILE: src/main/java/com/reason/ide/annotations/ORErrorAnnotator.java ================================================ package com.reason.ide.annotations; import com.intellij.execution.process.*; import com.intellij.lang.annotation.*; import com.intellij.openapi.application.*; import com.intellij.openapi.editor.*; import com.intellij.openapi.editor.impl.*; import com.intellij.openapi.project.*; import com.intellij.openapi.util.*; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.*; import com.intellij.problems.*; import com.intellij.psi.*; import com.reason.*; import com.reason.comp.*; import com.reason.comp.bs.*; import com.reason.comp.rescript.*; import com.reason.hints.*; import com.reason.ide.*; import com.reason.ide.hints.*; import com.reason.lang.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; import java.io.*; import java.util.*; import static com.reason.ide.annotations.ORErrorAnnotator.*; public class ORErrorAnnotator extends ExternalAnnotator>, AnnotationResult> implements DumbAware { protected static final Log LOG = Log.create("annotator"); @Override public @Nullable InitialInfo collectInformation(@NotNull PsiFile psiFile, @NotNull Editor editor, boolean hasErrors) { if (hasErrors) { LOG.info("Annotator was initialized with errors. This isn't supported."); } else { ORCompilerManager compilerManager = psiFile.getProject().getService(ORCompilerManager.class); ORResolvedCompiler compiler = compilerManager.getCompiler(ORFileUtils.getVirtualFile(psiFile)); if (compiler != null) { return switch (compiler.getType()) { case BS -> BsErrorAnnotator.collectInformation((BsResolvedCompiler) compiler, editor, psiFile); case RESCRIPT -> ResErrorAnnotator.collectInformation((ResResolvedCompiler) compiler, editor, psiFile); default -> null; }; } } return null; } @Override public @Nullable AnnotationResult doAnnotate(@Nullable InitialInfo> initialInfo) { long compilationStartTime = System.currentTimeMillis(); AnnotationResult result = initialInfo != null ? switch (initialInfo.compiler.getType()) { case BS -> BsErrorAnnotator.doAnnotate(initialInfo); case RESCRIPT -> ResErrorAnnotator.doAnnotate(initialInfo); default -> null; } : null; if (LOG.isTraceEnabled()) { LOG.trace("Annotation done in " + (System.currentTimeMillis() - compilationStartTime) + "ms"); } return result; } @Override public void apply(@NotNull PsiFile sourcePsiFile, @Nullable AnnotationResult annotationResult, @NotNull AnnotationHolder holder) { VirtualFile sourceFile = ORFileUtils.getVirtualFile(sourcePsiFile); if (sourceFile == null) { return; } if (annotationResult == null) { return; } Project project = sourcePsiFile.getProject(); Editor editor = annotationResult.editor; File cmtFile = annotationResult.cmtFile; List annotations = annotationResult.outputInfo.stream() .map(info -> makeAnnotation(info, editor)) .filter(Objects::nonNull).toList(); WolfTheProblemSolver problemSolver = WolfTheProblemSolver.getInstance(project); if (annotations.isEmpty()) { LOG.trace("Clear problems", sourceFile); problemSolver.clearProblems(sourceFile); // Call rincewind on the generated cmt file ! updateCodeLens(project, ORLanguageProperties.cast(sourcePsiFile.getLanguage()), sourceFile, cmtFile); } else { Collection problems = new ArrayList<>(); for (Annotation annotation : annotations) { if (annotation.isError) { holder.newAnnotation(HighlightSeverity.ERROR, annotation.message) .range(annotation.range) .create(); // mark error in Project View problems.add(problemSolver.convertToProblem(sourceFile, annotation.startPos.line, annotation.startPos.column, new String[]{annotation.message})); // Remove hint with corresponding line ApplicationManager.getApplication().invokeLater( () -> ReadAction.run( () -> InferredTypesService.getSignatures(sourceFile).remove(annotation.startPos.line))); } else { holder .newAnnotation(HighlightSeverity.WARNING, annotation.message) .range(annotation.range) .create(); } } problemSolver.reportProblems(sourceFile, problems); } } private void updateCodeLens(@NotNull Project project, @Nullable ORLanguageProperties lang, @NotNull VirtualFile sourceFile, @NotNull File cmtFile) { InsightManager insightManager = project.getService(InsightManager.class); if (insightManager != null && !FileHelper.isInterface(sourceFile.getFileType())) { insightManager.queryTypes(sourceFile, cmtFile.toPath(), types -> { if (types != null) { LOG.debug("Updating signatures in user data cache for file", sourceFile); InferredTypesService.getSignatures(sourceFile).putAll(types.signaturesByLines(lang)); } }); } } private static @Nullable Annotation makeAnnotation(@NotNull OutputInfo info, @NotNull Editor editor) { int colStart = info.colStart; int colEnd = info.colEnd; int lineStart = info.lineStart; int lineEnd = info.lineEnd; LogicalPosition start = new LogicalPosition(lineStart < 1 ? 0 : lineStart - 1, colStart < 1 ? 0 : colStart - 1); LogicalPosition end = new LogicalPosition(lineEnd < 1 ? 0 : lineEnd - 1, colEnd < 1 ? 0 : colEnd - 1); int startOffset = editor.isDisposed() ? 0 : editor.logicalPositionToOffset(start); int endOffset = editor.isDisposed() ? 0 : editor.logicalPositionToOffset(end); if (0 < startOffset && 0 < endOffset && startOffset <= endOffset) { TextRangeInterval range = new TextRangeInterval(startOffset, endOffset); String message = info.message.replace('\n', ' ').replaceAll("\\s+", " ").trim(); if (LOG.isDebugEnabled()) { LOG.debug("annotate " + startOffset + ":" + endOffset + " '" + message + "'"); } return new Annotation(info.isError, message, range, start); } if (LOG.isDebugEnabled()) { LOG.debug(info.message.replace('\n', ' ').replaceAll("\\s+", " ").trim()); LOG.debug("Failed to locate info: " + start + "->" + end + ", offsets " + startOffset + "->" + endOffset + ", info " + info); } return null; } static @NotNull File getOrCreateTempDirectory(@NotNull Project project) { File result; String directoryName = "BS_" + project.getName().replaceAll(" ", "_"); String tempDirectory = FileUtil.getTempDirectory(); result = new File(tempDirectory, directoryName); if (!result.exists()) { try { result = FileUtil.createTempDirectory(directoryName, null, true); LOG.trace("Created temporary annotator directory", result); } catch (IOException e) { LOG.error("Failed to create temporary directory", e); throw new RuntimeException(e); } } return result; } // Annotator functions are called asynchronously and can be interrupted, leaving files on disk if operation is // aborted. -> Clean current temp directory, but for the current file only: avoid erasing files when concurrent // compilation happens. static void cleanTempDirectory(@NotNull File directory, @NotNull String fileName) { File[] files = directory.listFiles((dir, name) -> name.startsWith(fileName) && !name.endsWith(".cmt")); if (files != null) { Arrays.stream(files).parallel().forEach(FileUtil::asyncDelete); } } static @Nullable File copyToTempFile(@NotNull File tempCompilationDirectory, @NotNull PsiFile psiFile, @NotNull String nameWithoutExtension) { File sourceTempFile; try { VirtualFile virtualFile = ORFileUtils.getVirtualFile(psiFile); if (virtualFile == null) { return null; } sourceTempFile = FileUtil.createTempFile(tempCompilationDirectory, nameWithoutExtension, "." + virtualFile.getExtension()); } catch (IOException e) { LOG.info("Temporary file creation failed", e); // log error but do not show it in UI return null; } try { String psiText = ReadAction.compute(psiFile::getText); FileUtil.writeToFile(sourceTempFile, psiText.getBytes(Platform.UTF8)); } catch (IOException e) { // Sometimes, file is locked by another process, not a big deal, skip it LOG.trace("Write failed: " + e.getLocalizedMessage()); return null; } return sourceTempFile; } static class CompilerProcessListener { private final AnsiEscapeDecoder myAnsiEscapeDecoder = new AnsiEscapeDecoder(); private final CompilerOutputAnalyzer myLineProcessor; CompilerProcessListener(CompilerOutputAnalyzer lineProcessor) { myLineProcessor = lineProcessor; } public void onTextAvailable(@NotNull String line, @NotNull Key outputType) { StringBuilder sb = new StringBuilder(); myAnsiEscapeDecoder.escapeText(line, outputType, (chunk, attributes) -> sb.append(chunk)); myLineProcessor.onTextAvailable(sb.toString()); } public @NotNull List getOutputInfo() { return myLineProcessor.getOutputInfo(); } } static public class InitialInfo> { final R compiler; final PsiFile sourcePsiFile; final VirtualFile libRoot; final File tempFile; final Editor editor; final List arguments; final boolean oldFormat; final String jsxVersion; final String jsxMode; // classic or automatic final boolean uncurried; InitialInfo(R compiler, @NotNull PsiFile sourcePsiFile, @NotNull VirtualFile libRoot, @Nullable File tempFile, @NotNull Editor editor, @NotNull List arguments, @Nullable String jsxVersion, @Nullable String jsxMode, boolean uncurried) { this.compiler = compiler; this.sourcePsiFile = sourcePsiFile; this.libRoot = libRoot; this.tempFile = tempFile; this.editor = editor; this.arguments = arguments; this.oldFormat = tempFile != null; this.jsxVersion = jsxVersion; this.jsxMode = jsxMode; this.uncurried = uncurried; } } public record AnnotationResult(List outputInfo, Editor editor, File cmtFile) { public AnnotationResult(@NotNull List outputInfo, @NotNull Editor editor, @NotNull File cmtFile) { this.outputInfo = outputInfo; this.editor = editor; this.cmtFile = cmtFile; } } record Annotation(boolean isError, String message, TextRangeInterval range, LogicalPosition startPos) { Annotation(boolean isError, @NotNull String message, @NotNull TextRangeInterval range, @NotNull LogicalPosition startPos) { this.isError = isError; this.message = message; this.range = range; this.startPos = startPos; } } } ================================================ FILE: src/main/java/com/reason/ide/annotations/OutputInfo.java ================================================ package com.reason.ide.annotations; import org.jetbrains.annotations.*; public class OutputInfo { public boolean isError = false; public int lineStart = -1; public int colStart = -1; public int lineEnd = -1; public int colEnd = -1; @NotNull public String message = ""; @Nullable public String path = ""; @Override public @NotNull String toString() { return "OutputInfo." + (isError ? "ERR" : "WARN") + " -> " + lineStart + ":" + colStart + "-" + lineEnd + ":" + colEnd + (message.isEmpty() ? " «empty message»" : " / " + message); } } ================================================ FILE: src/main/java/com/reason/ide/annotations/ResErrorAnnotator.java ================================================ package com.reason.ide.annotations; import com.intellij.execution.process.*; import com.intellij.openapi.editor.*; import com.intellij.openapi.project.*; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.*; import com.intellij.psi.*; import com.reason.comp.*; import com.reason.comp.bs.*; import com.reason.comp.rescript.*; import com.reason.ide.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; import java.io.*; import java.util.*; import static com.reason.ide.annotations.ORErrorAnnotator.*; import static java.util.Collections.*; public class ResErrorAnnotator { protected static final Log LOG = Log.create("annotator.rescript"); private ResErrorAnnotator() { } public static @Nullable ORErrorAnnotator.InitialInfo collectInformation(@NotNull ResResolvedCompiler compiler, @NotNull Editor editor, @NotNull PsiFile psiFile) { BsConfig config = psiFile.getProject().getService(ORCompilerConfigManager.class).getConfig(compiler.getConfigFile()); VirtualFile contentRoot = compiler.getContentRoot(); VirtualFile libRoot = contentRoot != null ? contentRoot.findFileByRelativePath("lib/bs") : null; Ninja ninja = contentRoot != null ? compiler.readNinjaBuild() : null; if (ninja != null && config != null && libRoot != null) { VirtualFile virtualFile = ORFileUtils.getVirtualFile(psiFile); if (virtualFile != null) { List args = ResPlatform.isDevSource(virtualFile, contentRoot, config) ? ninja.getArgsDev() : ninja.getArgs(); return new ORErrorAnnotator.InitialInfo<>(compiler, psiFile, libRoot, null, editor, args, config.getJsxVersion(), config.getJsxMode(), config.isUncurried()); } } return null; } public static @Nullable ORErrorAnnotator.AnnotationResult doAnnotate(@NotNull InitialInfo> initialInfo) { PsiFile sourcePsiFile = initialInfo.sourcePsiFile; Project project = sourcePsiFile.getProject(); VirtualFile sourceFile = ORFileUtils.getVirtualFile(sourcePsiFile); if (sourceFile == null) { return null; } String nameWithoutExtension = sourceFile.getNameWithoutExtension(); // Create and clean temporary compilation directory File tempCompilationDirectory = getOrCreateTempDirectory(project); cleanTempDirectory(tempCompilationDirectory, nameWithoutExtension); // Creates a temporary file on disk with a copy of the current document. // It'll be used by bsc for a temporary compilation File sourceTempFile = copyToTempFile(tempCompilationDirectory, sourcePsiFile, nameWithoutExtension); if (sourceTempFile == null) { return null; } LOG.trace("Wrote contents to temporary file", sourceTempFile); File cmtFile = new File(tempCompilationDirectory, nameWithoutExtension + ".cmt"); List arguments = new ArrayList<>(initialInfo.arguments); // React/jsx flags String jsxVersion = initialInfo.jsxVersion; if (jsxVersion != null) { arguments.add("-bs-jsx"); arguments.add(jsxVersion); } String jsxMode = initialInfo.jsxMode; if (jsxMode != null) { arguments.add("-bs-jsx-mode"); arguments.add(jsxMode); } arguments.add("-bin-annot"); arguments.add("-o"); arguments.add(cmtFile.getPath()); arguments.add(sourceTempFile.getPath()); long compilationStartTime = System.currentTimeMillis(); // https://github.com/giraud/reasonml-idea-plugin/issues/362 // lib directory can be invalidated after a clean if (!initialInfo.libRoot.isValid()) { LOG.debug("lib directory is invalid, skip compilation"); return null; } List info = compile((ResResolvedCompiler) initialInfo.compiler, arguments, initialInfo.libRoot); if (LOG.isTraceEnabled()) { LOG.trace("Compilation done in " + (System.currentTimeMillis() - compilationStartTime) + "ms"); } LOG.debug("Found info", info); File baseFile = new File(sourceTempFile.getParent(), com.intellij.openapi.util.io.FileUtil.getNameWithoutExtension(sourceTempFile)); LOG.debug("Clear temporary files from base", baseFile); com.intellij.openapi.util.io.FileUtil.delete(sourceTempFile); com.intellij.openapi.util.io.FileUtil.delete(new File(baseFile.getPath() + ".cmi")); FileUtil.delete(new File(baseFile.getPath() + ".cmj")); // cmt is never deleted return new AnnotationResult(info, initialInfo.editor, cmtFile); } static @NotNull List compile(@NotNull ResResolvedCompiler compiler, @NotNull List arguments, @NotNull VirtualFile workDir) { List command = new ArrayList<>(); command.add(compiler.getPath()); command.addAll(arguments.stream().filter(s -> !"-bin-annot".equals(s)).toList()); if (LOG.isTraceEnabled()) { LOG.trace(Joiner.join(" ", command.toArray(new String[0]))); LOG.trace(" work dir" + (workDir.isValid() ? " [valid]" : " [not valid]"), workDir); } Process process = null; try { CompilerProcessListener processListener = new CompilerProcessListener(new RescriptOutputAnalyzer()); ProcessBuilder builder = new ProcessBuilder(command); builder.directory(new File(workDir.getPath())); builder.environment().put("BS_VSCODE", "1"); process = builder.start(); try (BufferedReader stdErr = new BufferedReader(new InputStreamReader(process.getErrorStream()))) { String line; while ((line = stdErr.readLine()) != null) { processListener.onTextAvailable(line, ProcessOutputTypes.STDERR); } } return processListener.getOutputInfo(); } catch (Exception e) { LOG.info("Execution error", e); } finally { if (process != null) { process.destroy(); } } return emptyList(); } } ================================================ FILE: src/main/java/com/reason/ide/comment/DuneCommenter.java ================================================ package com.reason.ide.comment; import com.intellij.lang.Commenter; import org.jetbrains.annotations.Nullable; public class DuneCommenter implements Commenter { @Nullable @Override public String getLineCommentPrefix() { return ";"; } @Nullable @Override public String getBlockCommentPrefix() { return "#|"; } @Nullable @Override public String getBlockCommentSuffix() { return "|#"; } @Nullable @Override public String getCommentedBlockCommentPrefix() { return "#|"; } @Nullable @Override public String getCommentedBlockCommentSuffix() { return "|#"; } } ================================================ FILE: src/main/java/com/reason/ide/comment/MlyCommenter.java ================================================ package com.reason.ide.comment; import com.intellij.lang.Commenter; import org.jetbrains.annotations.Nullable; public class MlyCommenter implements Commenter { @Nullable @Override public String getLineCommentPrefix() { return "//"; } @Nullable @Override public String getBlockCommentPrefix() { return "/*"; } @Nullable @Override public String getBlockCommentSuffix() { return "*/"; } @Nullable @Override public String getCommentedBlockCommentPrefix() { return "/*"; } @Nullable @Override public String getCommentedBlockCommentSuffix() { return "*/"; } } ================================================ FILE: src/main/java/com/reason/ide/comment/OclCommenter.java ================================================ package com.reason.ide.comment; import com.intellij.codeInsight.generation.*; import com.intellij.lang.*; import com.intellij.openapi.editor.*; import com.intellij.openapi.util.*; import com.intellij.openapi.util.text.*; import com.intellij.psi.*; import com.intellij.util.text.*; import org.jetbrains.annotations.*; import java.util.*; public class OclCommenter implements SelfManagingCommenter, Commenter, CommenterWithLineSuffix, CustomUncommenter { @Override public @NotNull String getLineCommentPrefix() { return "(*"; } @Override public @NotNull String getLineCommentSuffix() { return "*)"; } @Override public @NotNull String getBlockCommentPrefix() { return "(*"; } @Override public @NotNull String getBlockCommentSuffix() { return "*)"; } @Override public @NotNull String getCommentedBlockCommentPrefix() { return "(*"; } @Override public @NotNull String getCommentedBlockCommentSuffix() { return "*)"; } @Override public @NotNull CommenterData createLineCommentingState(int startLine, int endLine, @NotNull Document document, @NotNull PsiFile file) { return new CommenterData(startLine, endLine); } @Override public @NotNull CommenterData createBlockCommentingState(int selectionStart, int selectionEnd, @NotNull Document document, @NotNull PsiFile file) { int startLine = StringUtil.offsetToLineNumber(document.getCharsSequence(), selectionStart); int endLine = StringUtil.offsetToLineNumber(document.getCharsSequence(), selectionEnd); return new CommenterData(startLine, endLine); } @Override public void commentLine(int line, int offset, @NotNull Document document, @NotNull CommenterData data) { int lineEndOffset = document.getLineEndOffset(line); CharSequence chars = document.getCharsSequence(); boolean startWithSpace = chars.charAt(offset) == ' '; boolean endsWithSpace = chars.charAt(lineEndOffset - 1) == ' '; SelfManagingCommenterUtil.insertBlockComment(offset, lineEndOffset, document, startWithSpace ? "(*" : "(* ", endsWithSpace ? "*)" : " *)"); } @Override public void uncommentLine(int line, int offset, @NotNull Document document, @NotNull CommenterData data) { CharSequence chars = document.getCharsSequence(); int lineEndOffset = document.getLineEndOffset(line); int textEndOffset = CharArrayUtil.shiftBackward(chars, lineEndOffset - 1, " \t"); SelfManagingCommenterUtil.uncommentBlockComment(offset, textEndOffset + 1, document, "(*", "*)"); } @Override public boolean isLineCommented(int line, int offset, @NotNull Document document, @NotNull CommenterData data) { CharSequence charSequence = document.getCharsSequence().subSequence(offset, offset + 2); return charSequence.toString().equals(getBlockCommentPrefix()); } @Override public @NotNull String getCommentPrefix(int line, @NotNull Document document, @NotNull CommenterData data) { return "(*"; } @Override public @Nullable TextRange getBlockCommentRange(int selectionStart, int selectionEnd, @NotNull Document document, @NotNull CommenterData data) { return SelfManagingCommenterUtil.getBlockCommentRange(selectionStart, selectionEnd, document, "(*", "*)"); } @Override public @NotNull String getBlockCommentPrefix(int selectionStart, @NotNull Document document, @NotNull CommenterData data) { return "(*"; } @Override public @NotNull String getBlockCommentSuffix(int selectionEnd, @NotNull Document document, @NotNull CommenterData data) { return "*)"; } @Override public void uncommentBlockComment(int startOffset, int endOffset, Document document, CommenterData data) { CharSequence chars = document.getCharsSequence(); char startChar = chars.charAt(startOffset + 2); boolean startHasLF = startChar == '\n'; boolean startHasSpace = startChar == ' '; char endChar = chars.charAt(endOffset - 1 - 2); boolean endHasLF = endChar == '\n'; boolean endHasSpace = endChar == ' '; String start = startHasLF ? "(*\n" : (startHasSpace ? "(* " : "(*"); String end = endHasLF ? "\n*)" : (endHasSpace ? " *)" : "*)"); SelfManagingCommenterUtil.uncommentBlockComment(startOffset, endOffset, document, start, end); } @Override public @Nullable TextRange insertBlockComment(int startOffset, int endOffset, Document document, CommenterData data) { CharSequence chars = document.getCharsSequence(); boolean startHasLF = chars.charAt(startOffset) == '\n'; boolean endHasLF = chars.charAt(endOffset - 1) == '\n'; boolean hasLF = data.getEndLine() > data.getStartLine(); String startComment = startHasLF ? "(*" : (hasLF ? "(*\n" : "(* "); String endComment = endHasLF ? "*)\n" : " *)"; return SelfManagingCommenterUtil.insertBlockComment(startOffset, endOffset, document, startComment, endComment); } @Override public @Nullable TextRange findMaximumCommentedRange(@NotNull CharSequence text) { TextRange commentedRange = null; // trim start & end int selectionStart = 0; selectionStart = CharArrayUtil.shiftForward(text, selectionStart, " \t\n"); int selectionEnd = text.length() - 1; selectionEnd = CharArrayUtil.shiftBackward(text, selectionEnd, " \t\n") + 1; // Find how many distinct comments in text boolean commentStart = CharArrayUtil.regionMatches(text, selectionStart, "(*"); if (commentStart) { int commentCount = 0; int nestedComment = 0; for (int i = selectionStart; i < selectionEnd; i++) { char c = text.charAt(i); if (c == '(') { char c2 = text.charAt(i + 1); if (c2 == '*') { nestedComment++; } } else if (c == '*') { char c2 = text.charAt(i + 1); if (c2 == ')') { nestedComment--; if (nestedComment == 0) { commentCount++; } } } } if (commentCount == 1 && selectionEnd - selectionStart >= 2 + 2 && CharArrayUtil.regionMatches(text, selectionEnd - 2, "*)")) { commentedRange = new TextRange(selectionStart, selectionEnd); } } return commentedRange; } @Override public @NotNull Collection> getCommentRangesToDelete(@NotNull CharSequence text) { Collection> ranges = new ArrayList<>(); // should use nearest after all pairs (* *) int start = getNearest((String) text); TextRange prefixRange = expandRange(text, start, start + 2); int end = ((String) text).lastIndexOf("*)"); TextRange suffixRange = expandRange(text, end, end + 2); ranges.add(Couple.of(prefixRange, suffixRange)); return ranges; } private @NotNull TextRange expandRange(@NotNull CharSequence chars, int delOffset1, int delOffset2) { int offset1 = CharArrayUtil.shiftBackward(chars, delOffset1 - 1, " \t"); if (offset1 < 0 || chars.charAt(offset1) == '\n' || chars.charAt(offset1) == '\r') { int offset2 = CharArrayUtil.shiftForward(chars, delOffset2, " \t"); if (offset2 == chars.length() || chars.charAt(offset2) == '\r' || chars.charAt(offset2) == '\n') { delOffset1 = (offset1 < 0) ? offset1 + 1 : offset1; if (offset2 < chars.length()) { delOffset2 = offset2 + 1; if (chars.charAt(offset2) == '\r' && offset2 + 1 < chars.length() && chars.charAt(offset2 + 1) == '\n') { delOffset2++; } } } } return new TextRange(delOffset1, delOffset2); } private static int getNearest(@NotNull String text) { int result = text.indexOf("(*"); return result == -1 ? text.length() : result; } public static class CommenterData extends CommenterDataHolder { private final int myStartLine; private final int myEndLine; public CommenterData(int startLine, int endLine) { myStartLine = startLine; myEndLine = endLine; } public int getStartLine() { return myStartLine; } public int getEndLine() { return myEndLine; } } } ================================================ FILE: src/main/java/com/reason/ide/comment/RmlCommenter.java ================================================ package com.reason.ide.comment; import com.intellij.lang.Commenter; import org.jetbrains.annotations.Nullable; public class RmlCommenter implements Commenter { @Nullable @Override public String getLineCommentPrefix() { return "//"; } @Nullable @Override public String getBlockCommentPrefix() { return "/*"; } @Nullable @Override public String getBlockCommentSuffix() { return "*/"; } @Nullable @Override public String getCommentedBlockCommentPrefix() { return "/*"; } @Nullable @Override public String getCommentedBlockCommentSuffix() { return "*/"; } } ================================================ FILE: src/main/java/com/reason/ide/console/ClearLogAction.java ================================================ package com.reason.ide.console; import com.intellij.execution.ui.*; import com.intellij.icons.*; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.editor.*; import com.intellij.openapi.project.*; import org.jetbrains.annotations.*; public class ClearLogAction extends DumbAwareAction { private final ConsoleView myConsoleView; public ClearLogAction(ConsoleView consoleView) { super("Clear All", "Clear the contents of the logs", AllIcons.Actions.GC); myConsoleView = consoleView; } @Override public void update(@NotNull AnActionEvent e) { Editor editor = e.getData(CommonDataKeys.EDITOR); e.getPresentation().setEnabled(editor != null && editor.getDocument().getTextLength() > 0); } @Override public void actionPerformed(@NotNull AnActionEvent e) { myConsoleView.clear(); } @Override public @NotNull ActionUpdateThread getActionUpdateThread() { return ActionUpdateThread.BGT; } } ================================================ FILE: src/main/java/com/reason/ide/console/CompilerAction.java ================================================ package com.reason.ide.console; import com.intellij.openapi.editor.*; import com.intellij.openapi.fileEditor.*; import com.intellij.openapi.project.*; import com.intellij.psi.*; import com.reason.comp.*; import com.reason.ide.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; import javax.swing.*; public abstract class CompilerAction extends DumbAwareAction { protected CompilerAction(@NotNull String text, @NotNull String description, @NotNull Icon icon) { super(text, description, icon); } protected void doAction(@NotNull Project project, @NotNull CliType cliType, @Nullable ORProcessTerminated onProcessTerminated) { Editor editor = getActiveEditor(project); if (editor == null) { compileDirectory(project, cliType, onProcessTerminated); } else { compileFile(project, editor, cliType, onProcessTerminated); } } private static void compileDirectory(@NotNull Project project, @NotNull CliType cliType, @Nullable ORProcessTerminated onProcessTerminated) { ORCompiler compiler = project.getService(ORCompilerManager.class).getCompiler(cliType); if (compiler != null) { compiler.run(null, cliType, onProcessTerminated); } } private static void compileFile(@NotNull Project project, @NotNull Editor editor, @NotNull CliType cliType, @Nullable ORProcessTerminated onProcessTerminated) { ORCompiler compiler = project.getService(ORCompilerManager.class).getCompiler(cliType); PsiFile activeFile = getActiveFile(project, editor); // We always use the opened file to detect the config file if (activeFile != null && compiler != null) { compiler.run(ORFileUtils.getVirtualFile(activeFile), cliType, onProcessTerminated); } } private static @Nullable PsiFile getActiveFile(@NotNull Project project, @NotNull Editor editor) { PsiDocumentManager documentManager = PsiDocumentManager.getInstance(project); return documentManager == null ? null : documentManager.getPsiFile(editor.getDocument()); } private static @Nullable Editor getActiveEditor(@NotNull Project project) { FileEditorManager editorManager = FileEditorManager.getInstance(project); return editorManager == null ? null : editorManager.getSelectedTextEditor(); } } ================================================ FILE: src/main/java/com/reason/ide/console/ORConsoleFilter.java ================================================ package com.reason.ide.console; import com.intellij.execution.filters.*; import com.intellij.openapi.project.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; public abstract class ORConsoleFilter implements Filter { protected static final Log LOG = Log.create("console"); protected final Project myProject; protected ORConsoleFilter(@NotNull Project project) { myProject = project; } protected abstract @Nullable OpenFileHyperlinkInfo getHyperlinkInfo(String filePath, int documentLine, int documentColumn); } ================================================ FILE: src/main/java/com/reason/ide/console/ORConsoleFilterProvider.java ================================================ package com.reason.ide.console; import com.intellij.execution.filters.*; import com.intellij.execution.ui.*; import com.intellij.openapi.project.*; import com.intellij.psi.search.*; import com.reason.ide.console.dune.*; import com.reason.ide.console.esy.*; import com.reason.ide.console.rescript.*; import org.jetbrains.annotations.*; /** * Provide a filter for console output, for ex to extract specific file location for linking. * Default implementation extract basic url, like C:\xxx\source.re:row:col <- GenericFileFilter ? */ public class ORConsoleFilterProvider extends ConsoleDependentFilterProvider { @Override public @NotNull Filter[] getDefaultFilters(@NotNull ConsoleView consoleView, @NotNull Project project, @NotNull GlobalSearchScope scope) { if (consoleView instanceof RescriptConsoleView) { return ((RescriptConsoleView) consoleView).getFilters(); } if (consoleView instanceof DuneConsoleView) { return ((DuneConsoleView) consoleView).getFilters(); } if (consoleView instanceof EsyConsoleView) { return ((EsyConsoleView) consoleView).getFilters(); } return super.getDefaultFilters(project); } } ================================================ FILE: src/main/java/com/reason/ide/console/ORToolWindowFactory.java ================================================ package com.reason.ide.console; import com.intellij.openapi.project.*; import com.intellij.openapi.wm.*; import org.jetbrains.annotations.*; public abstract class ORToolWindowFactory implements ToolWindowFactory, DumbAware { /* id provided in plugin.xml. this is displayed in the UI */ public abstract @NotNull String getId(); public abstract @NotNull String getStripeTitle(); /** * Initially hide all tool windows until indexing has completed. See {@link com.reason.ide.ORPostStartupActivity} */ @Override public boolean shouldBeAvailable(@NotNull Project project) { return false; } @Override public abstract void createToolWindowContent(@NotNull final Project project, @NotNull ToolWindow window); @Override public void init(@NotNull ToolWindow window) { window.setTitle("Process"); window.setStripeTitle(getStripeTitle()); } } ================================================ FILE: src/main/java/com/reason/ide/console/ORToolWindowManager.java ================================================ package com.reason.ide.console; import com.intellij.execution.ui.*; import com.intellij.openapi.application.*; import com.intellij.openapi.components.*; import com.intellij.openapi.project.*; import com.intellij.openapi.ui.*; import com.intellij.openapi.wm.*; import com.intellij.ui.content.*; import com.intellij.util.concurrency.*; import com.reason.comp.*; import com.reason.comp.ORCompiler.*; import com.reason.ide.console.bs.*; import com.reason.ide.console.dune.*; import com.reason.ide.console.esy.*; import com.reason.ide.console.rescript.*; import org.jetbrains.annotations.*; import javax.swing.*; import java.util.concurrent.*; @Service(Service.Level.PROJECT) public final class ORToolWindowManager { private final @NotNull Project myProject; private ORToolWindowManager(@NotNull Project project) { myProject = project; } public @Nullable ConsoleView getConsoleView(@NotNull String id) { ToolWindow toolWindow = ToolWindowManager.getInstance(myProject).getToolWindow(id); Content windowContent = toolWindow == null ? null : toolWindow.getContentManager().getContent(0); JComponent component = windowContent == null ? null : windowContent.getComponent(); JComponent panel = (component instanceof SimpleToolWindowPanel) ? ((SimpleToolWindowPanel) component).getComponent() : null; return panel == null ? null : (ConsoleView) panel.getComponent(0); } public void shouldShowToolWindows() { ExecutorService executorService = AppExecutorUtil.getAppExecutorService(); ModalityState modalityState = ModalityState.defaultModalityState(); ReadAction.nonBlocking(() -> { ORCompiler[] compilers = new ORCompiler[4]; compilers[0] = getAvailableCompiler(CompilerType.RESCRIPT); compilers[1] = getAvailableCompiler(CompilerType.BS); compilers[2] = getAvailableCompiler(CompilerType.DUNE); compilers[3] = getAvailableCompiler(CompilerType.ESY); return compilers; }) .finishOnUiThread(modalityState, compilers -> { setToolWindowAvailable(RescriptToolWindowFactory.ID, compilers[0]); setToolWindowAvailable(BsToolWindowFactory.ID, compilers[1]); setToolWindowAvailable(DuneToolWindowFactory.ID, compilers[2]); setToolWindowAvailable(EsyToolWindowFactory.ID, compilers[3]); }) .coalesceBy(this) .submit(executorService); } private void setToolWindowAvailable(@NotNull String id, @Nullable ORCompiler compiler) { ToolWindow toolWindow = ToolWindowManager.getInstance(myProject).getToolWindow(id); if (toolWindow != null) { toolWindow.setAvailable(compiler != null, () -> { ConsoleView consoleView = getConsoleView(id); if (consoleView != null && compiler != null) { ReadAction.nonBlocking(() -> compiler.getFullVersion(null)) // slow operation not allowed on UI thread .finishOnUiThread(ModalityState.defaultModalityState(), version -> consoleView.print("Detected compiler: " + version + "\n", ConsoleViewContentType.NORMAL_OUTPUT)) .coalesceBy(compiler) .submit(AppExecutorUtil.getAppExecutorService()); } }); } } private @Nullable ORCompiler getAvailableCompiler(@NotNull CompilerType compilerType) { ORCompilerManager compilerService = myProject.isDisposed() ? null : myProject.getService(ORCompilerManager.class); ORCompiler compiler = compilerService != null ? compilerService.getCompiler(compilerType) : null; return compiler != null && compiler.isAvailable(myProject) ? compiler : null; } } ================================================ FILE: src/main/java/com/reason/ide/console/bs/BsConsoleView.java ================================================ package com.reason.ide.console.bs; import com.intellij.execution.impl.*; import com.intellij.openapi.project.*; import org.jetbrains.annotations.*; public class BsConsoleView extends ConsoleViewImpl { public BsConsoleView(@NotNull Project project) { super(project, true); } } ================================================ FILE: src/main/java/com/reason/ide/console/bs/BsMakeAction.java ================================================ package com.reason.ide.console.bs; import com.intellij.icons.*; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.project.*; import com.reason.comp.*; import com.reason.comp.bs.*; import com.reason.ide.console.*; import org.jetbrains.annotations.*; public class BsMakeAction extends CompilerAction { public BsMakeAction() { super("Make bsb", "Make bsb", AllIcons.Actions.Compile); } @Override public void update(@NotNull AnActionEvent e) { Project project = e.getProject(); BsCompiler compiler = project == null ? null : project.getService(BsCompiler.class); if (compiler != null) { e.getPresentation().setEnabled(compiler.isAvailable()); } } @Override public void actionPerformed(@NotNull AnActionEvent e) { Project project = e.getProject(); BsCompiler compiler = project == null ? null : project.getService(BsCompiler.class); if (compiler != null) { doAction(project, CliType.Bs.MAKE, (_void) -> e.getPresentation().setEnabled(compiler.isAvailable())); } } @Override public @NotNull ActionUpdateThread getActionUpdateThread() { return ActionUpdateThread.BGT; } } ================================================ FILE: src/main/java/com/reason/ide/console/bs/BsMakeWorldAction.java ================================================ package com.reason.ide.console.bs; import com.intellij.icons.*; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.project.*; import com.reason.comp.*; import com.reason.comp.bs.*; import com.reason.ide.console.*; import org.jetbrains.annotations.*; public class BsMakeWorldAction extends CompilerAction { public BsMakeWorldAction() { super("Clean and make world bsb", "Clean and make world bsb", AllIcons.General.Web); } @Override public void update(@NotNull AnActionEvent e) { Project project = e.getProject(); BsCompiler compiler = project == null ? null : project.getService(BsCompiler.class); if (compiler != null) { e.getPresentation().setEnabled(compiler.isAvailable()); } } @Override public void actionPerformed(@NotNull AnActionEvent e) { Project project = e.getProject(); BsCompiler compiler = project == null ? null : project.getService(BsCompiler.class); if (compiler != null) { doAction(project, CliType.Bs.CLEAN_MAKE, (_void) -> e.getPresentation().setEnabled(compiler.isAvailable())); } } @Override public @NotNull ActionUpdateThread getActionUpdateThread() { return ActionUpdateThread.BGT; } } ================================================ FILE: src/main/java/com/reason/ide/console/bs/BsToolWindowFactory.java ================================================ package com.reason.ide.console.bs; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.editor.actions.*; import com.intellij.openapi.project.*; import com.intellij.openapi.ui.*; import com.intellij.openapi.util.*; import com.intellij.openapi.wm.*; import com.intellij.ui.content.*; import com.reason.ide.console.*; import org.jetbrains.annotations.*; public class BsToolWindowFactory extends ORToolWindowFactory { public static final String ID = "BuckleScript:"; @Override public @NotNull String getId() { return ID; } @Override public @NotNull String getStripeTitle() { return "BuckleScript"; } @Override public void createToolWindowContent(@NotNull final Project project, @NotNull ToolWindow window) { SimpleToolWindowPanel panel = new SimpleToolWindowPanel(false, true); BsConsoleView console = new BsConsoleView(project); panel.setContent(console.getComponent()); ActionToolbar toolbar = createToolbar(console); panel.setToolbar(toolbar.getComponent()); Content content = ContentFactory.getInstance().createContent(panel, "", true); window.getContentManager().addContent(content); Disposer.register(window.getDisposable(), console); } private @NotNull ActionToolbar createToolbar(@NotNull BsConsoleView console) { DefaultActionGroup group = new DefaultActionGroup(); group.add(new ScrollToTheEndToolbarAction(console.getEditor())); group.add(new ClearLogAction(console)); group.add(new BsMakeAction()); group.add(new BsMakeWorldAction()); ActionToolbar toolbar = ActionManager.getInstance().createActionToolbar("left", group, false); toolbar.setTargetComponent(console.getComponent()); return toolbar; } } ================================================ FILE: src/main/java/com/reason/ide/console/dune/DuneBuildAction.java ================================================ package com.reason.ide.console.dune; import com.intellij.icons.*; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.project.*; import com.reason.comp.*; import com.reason.comp.dune.*; import com.reason.ide.console.*; import org.jetbrains.annotations.*; public class DuneBuildAction extends CompilerAction { public DuneBuildAction() { super("Build dune", "Build dune", AllIcons.Actions.Compile); } @Override public void update(@NotNull AnActionEvent e) { Project project = e.getProject(); ORCompiler compiler = project == null ? null : project.getService(DuneCompiler.class); if (compiler != null) { e.getPresentation().setEnabled(compiler.isAvailable()); } } @Override public void actionPerformed(@NotNull AnActionEvent e) { Project project = e.getProject(); ORCompiler compiler = project == null ? null : project.getService(DuneCompiler.class); if (compiler != null) { doAction(project, CliType.Dune.BUILD, (_void) -> e.getPresentation().setEnabled(compiler.isAvailable())); } } @Override public @NotNull ActionUpdateThread getActionUpdateThread() { return ActionUpdateThread.BGT; } } ================================================ FILE: src/main/java/com/reason/ide/console/dune/DuneCleanAction.java ================================================ package com.reason.ide.console.dune; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.project.*; import com.reason.comp.*; import com.reason.comp.dune.*; import com.reason.ide.*; import com.reason.ide.console.*; import org.jetbrains.annotations.*; public class DuneCleanAction extends CompilerAction { public DuneCleanAction() { super("Clean dune", "Clean dune", ORIcons.RESET); } @Override public void update(@NotNull AnActionEvent e) { Project project = e.getProject(); DuneCompiler compiler = project == null ? null : project.getService(DuneCompiler.class); if (compiler != null) { e.getPresentation().setEnabled(compiler.isAvailable()); } } @Override public void actionPerformed(@NotNull AnActionEvent e) { Project project = e.getProject(); DuneCompiler compiler = project == null ? null : project.getService(DuneCompiler.class); if (compiler != null) { doAction(project, CliType.Dune.CLEAN, (_void) -> e.getPresentation().setEnabled(compiler.isAvailable())); } } @Override public @NotNull ActionUpdateThread getActionUpdateThread() { return ActionUpdateThread.BGT; } } ================================================ FILE: src/main/java/com/reason/ide/console/dune/DuneConsoleView.java ================================================ package com.reason.ide.console.dune; import com.intellij.execution.filters.*; import com.intellij.execution.impl.*; import com.intellij.openapi.project.*; import org.jetbrains.annotations.*; public class DuneConsoleView extends ConsoleViewImpl { public DuneConsoleView(@NotNull Project project) { super(project, true); } public @NotNull Filter[] getFilters() { return new Filter[]{new OCamlConsoleFilter(getProject())}; } } ================================================ FILE: src/main/java/com/reason/ide/console/dune/DuneToolWindowFactory.java ================================================ package com.reason.ide.console.dune; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.editor.actions.*; import com.intellij.openapi.project.*; import com.intellij.openapi.ui.*; import com.intellij.openapi.util.*; import com.intellij.openapi.wm.*; import com.intellij.ui.content.*; import com.reason.ide.console.*; import org.jetbrains.annotations.*; public class DuneToolWindowFactory extends ORToolWindowFactory { public static final String ID = "Dune:"; @Override public @NotNull String getId() { return ID; } @Override public @NotNull String getStripeTitle() { return "Dune"; } @Override public void createToolWindowContent(@NotNull final Project project, @NotNull ToolWindow window) { SimpleToolWindowPanel panel = new SimpleToolWindowPanel(false, true); DuneConsoleView console = new DuneConsoleView(project); panel.setContent(console.getComponent()); ActionToolbar toolbar = createToolbar(console); panel.setToolbar(toolbar.getComponent()); Content content = ContentFactory.getInstance().createContent(panel, "", true); window.getContentManager().addContent(content); Disposer.register(window.getDisposable(), console); } @NotNull private ActionToolbar createToolbar(@NotNull DuneConsoleView console) { DefaultActionGroup group = new DefaultActionGroup(); group.add(new ScrollToTheEndToolbarAction(console.getEditor())); group.add(new ClearLogAction(console)); group.add(new DuneBuildAction()); group.add(new DuneCleanAction()); ActionToolbar toolbar = ActionManager.getInstance().createActionToolbar("left", group, false); toolbar.setTargetComponent(console.getComponent()); return toolbar; } } ================================================ FILE: src/main/java/com/reason/ide/console/dune/OCamlConsoleFilter.java ================================================ package com.reason.ide.console.dune; import com.intellij.execution.filters.*; import com.intellij.openapi.project.*; import com.reason.comp.dune.*; import com.reason.ide.console.*; import org.jetbrains.annotations.*; import java.util.regex.*; import static java.lang.Integer.*; /** * Filter consoles output to add hyperlink to file reference. *

* OCaml (from https://github.com/Chris00/tuareg/blob/master/compilation.txt) * File "xxx.ml", line x, characters x-y: * File "xxx.ml", lines x-y, characters x-y: */ public class OCamlConsoleFilter extends ORConsoleFilter { private static final Pattern OCAML_PATTERN = Pattern.compile("^File \"(.+\\.(?:ml|re)i?)\", lines? (\\d+)(?:-(\\d+))?, characters (\\d+)-(\\d+):$"); public OCamlConsoleFilter(@NotNull Project project) { super(project); } @Override public @Nullable Result applyFilter(@NotNull String line, int entireLength) { Matcher matcher = OCAML_PATTERN.matcher(line); if (matcher.find()) { try { boolean multiline = matcher.groupCount() == 5; int documentLine = parseInt(matcher.group(2)) - 1; int documentColumn = parseInt(matcher.group(multiline ? 4 : 3)); OpenFileHyperlinkInfo hyperlinkInfo = getHyperlinkInfo(matcher.group(1), documentLine, documentColumn); int startPoint = entireLength - line.length(); int highlightStartOffset = startPoint + matcher.start(1); int highlightEndOffset = startPoint + matcher.end(1); return new Result(highlightStartOffset, highlightEndOffset, hyperlinkInfo); } catch (NumberFormatException e) { LOG.error("Format exception for line [" + line + "]", e); } } return null; } @Override protected @Nullable OpenFileHyperlinkInfo getHyperlinkInfo(String filePath, int documentLine, int documentColumn) { return DunePlatform.findConfigFiles(myProject).stream() .findFirst() .map(configFile -> configFile.getParent().findFileByRelativePath(filePath)) .map(virtualFile -> new OpenFileHyperlinkInfo(myProject, virtualFile, documentLine, documentColumn)) .orElse(null); } } ================================================ FILE: src/main/java/com/reason/ide/console/esy/EsyBuildAction.java ================================================ package com.reason.ide.console.esy; import com.intellij.icons.*; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.project.*; import com.reason.comp.*; import com.reason.comp.esy.*; import com.reason.ide.console.*; import org.jetbrains.annotations.*; public class EsyBuildAction extends CompilerAction { public EsyBuildAction() { super("Build esy", "Build esy", AllIcons.Actions.Compile); } @Override public void update(@NotNull AnActionEvent e) { Project project = e.getProject(); ORCompiler compiler = project == null ? null : project.getService(EsyCompiler.class); if (compiler != null) { e.getPresentation().setEnabled(compiler.isAvailable()); } } @Override public void actionPerformed(@NotNull AnActionEvent e) { Project project = e.getProject(); ORCompiler compiler = project == null ? null : project.getService(EsyCompiler.class); if (compiler != null) { doAction(project, CliType.Esy.BUILD, (_void) -> e.getPresentation().setEnabled(compiler.isAvailable())); } } @Override public @NotNull ActionUpdateThread getActionUpdateThread() { return ActionUpdateThread.BGT; } } ================================================ FILE: src/main/java/com/reason/ide/console/esy/EsyConsoleView.java ================================================ package com.reason.ide.console.esy; import com.intellij.execution.filters.*; import com.intellij.execution.impl.*; import com.intellij.openapi.project.*; import com.intellij.openapi.vfs.*; import com.reason.comp.dune.*; import org.jetbrains.annotations.*; import java.util.regex.*; import static java.lang.Integer.*; public class EsyConsoleView extends ConsoleViewImpl { private static final Pattern OCAML_PATTERN = Pattern.compile("^File \"(.+\\.mli?)\", lines? (\\d+)(?:-(\\d+))?, characters (\\d+)-(\\d+):$"); public EsyConsoleView(@NotNull Project project) { super(project, true); } public @NotNull Filter[] getFilters() { return new Filter[]{new DuneConsoleFilter()}; } /** * Filter consoles output to add hyperlink to file reference. *

* OCaml (from https://github.com/Chris00/tuareg/blob/master/compilation.txt) * File "xxx.ml", line x, characters x-y: * File "xxx.ml", lines x-y, characters x-y: */ class DuneConsoleFilter implements Filter { @Override public @Nullable Result applyFilter(@NotNull String line, int entireLength) { Matcher matcher = OCAML_PATTERN.matcher(line); if (matcher.find()) { String filePath = matcher.group(1); VirtualFile virtualFile = DunePlatform.findConfigFiles(getProject()).stream() .findFirst() .map(configFile -> configFile.getParent().findFileByRelativePath(filePath)) .orElse(null); if (virtualFile != null) { boolean multiline = matcher.groupCount() == 5; int startPoint = entireLength - line.length(); int documentLine = parseInt(matcher.group(2)) - 1; int documentColumn = parseInt(matcher.group(multiline ? 4 : 3)); return new Result(startPoint + matcher.start(1), startPoint + matcher.end(1), new OpenFileHyperlinkInfo(getProject(), virtualFile, documentLine, documentColumn)); } } return null; } } } ================================================ FILE: src/main/java/com/reason/ide/console/esy/EsyToolWindowFactory.java ================================================ package com.reason.ide.console.esy; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.editor.actions.*; import com.intellij.openapi.project.*; import com.intellij.openapi.ui.*; import com.intellij.openapi.util.*; import com.intellij.openapi.wm.*; import com.intellij.ui.content.*; import com.reason.ide.console.*; import org.jetbrains.annotations.*; public class EsyToolWindowFactory extends ORToolWindowFactory { public static final String ID = "Esy:"; @Override public @NotNull String getId() { return ID; } @Override public @NotNull String getStripeTitle() { return "Esy"; } @Override public void createToolWindowContent(@NotNull final Project project, @NotNull ToolWindow window) { SimpleToolWindowPanel panel = new SimpleToolWindowPanel(false, true); EsyConsoleView consoleView = new EsyConsoleView(project); panel.setContent(consoleView.getComponent()); ActionToolbar toolbar = createToolbar(consoleView); panel.setToolbar(toolbar.getComponent()); Content content = ContentFactory.getInstance().createContent(panel, "", true); window.getContentManager().addContent(content); Disposer.register(window.getDisposable(), consoleView); } @NotNull private ActionToolbar createToolbar(@NotNull EsyConsoleView console) { DefaultActionGroup group = new DefaultActionGroup(); group.add(new ScrollToTheEndToolbarAction(console.getEditor())); group.add(new ClearLogAction(console)); group.add(new EsyBuildAction()); ActionToolbar toolbar = ActionManager.getInstance().createActionToolbar("left", group, false); toolbar.setTargetComponent(console.getComponent()); return toolbar; } } ================================================ FILE: src/main/java/com/reason/ide/console/rescript/RescriptBuildAction.java ================================================ package com.reason.ide.console.rescript; import com.intellij.icons.*; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.project.*; import com.reason.comp.*; import com.reason.comp.rescript.*; import com.reason.ide.console.*; import org.jetbrains.annotations.*; public class RescriptBuildAction extends CompilerAction { public RescriptBuildAction() { super("Build", "Build", AllIcons.Actions.Compile); } @Override public void update(@NotNull AnActionEvent e) { Project project = e.getProject(); ORCompiler compiler = project == null ? null : project.getService(ResCompiler.class); if (compiler != null) { e.getPresentation().setEnabled(compiler.isAvailable()); } } @Override public void actionPerformed(@NotNull AnActionEvent e) { Project project = e.getProject(); ORCompiler compiler = project == null ? null : project.getService(ResCompiler.class); if (compiler != null) { doAction(project, CliType.Rescript.MAKE, (_void) -> e.getPresentation().setEnabled(compiler.isAvailable())); } } @Override public @NotNull ActionUpdateThread getActionUpdateThread() { return ActionUpdateThread.BGT; } } ================================================ FILE: src/main/java/com/reason/ide/console/rescript/RescriptConsoleFilter.java ================================================ package com.reason.ide.console.rescript; import com.intellij.execution.filters.*; import com.intellij.openapi.project.*; import com.intellij.openapi.vfs.*; import com.reason.ide.console.*; import org.jetbrains.annotations.*; import java.util.regex.*; import static com.reason.comp.CompilerOutputAnalyzer.*; import static java.lang.Integer.*; /** * Filter consoles output to add hyperlink to file reference. *

* OCaml (from https://github.com/Chris00/tuareg/blob/master/compilation.txt) * File "xxx.ml", line x, characters x-y: * File "xxx.ml", lines x-y, characters x-y: */ public class RescriptConsoleFilter extends ORConsoleFilter { public RescriptConsoleFilter(@NotNull Project project) { super(project); } @Override public @Nullable Result applyFilter(@NotNull String line, int entireLength) { Pattern pattern = line.trim().startsWith("File") ? FILE_LOCATION : SYNTAX_LOCATION; Matcher matcher = pattern.matcher(line); if (matcher.find()) { try { boolean multiline = matcher.groupCount() >= 5; int startPoint = entireLength - line.length(); int documentLine = parseInt(matcher.group(2)) - 1; String multilineGroup = matcher.group(multiline ? 4 : 3); int documentColumn = multilineGroup != null ? parseInt(multilineGroup) : 0; OpenFileHyperlinkInfo hyperlinkInfo = getHyperlinkInfo(matcher.group(1), documentLine, documentColumn); return new Result(startPoint + matcher.start(1), startPoint + matcher.end(1), hyperlinkInfo); } catch (NumberFormatException e) { LOG.error("Format exception for line [" + line + "]", e); } } return null; } @Override protected @Nullable OpenFileHyperlinkInfo getHyperlinkInfo(String filePath, int documentLine, int documentColumn) { OpenFileHyperlinkInfo hyperlinkInfo = null; VirtualFile sourceFile = LocalFileSystem.getInstance().findFileByPath(filePath); if (sourceFile != null) { hyperlinkInfo = new OpenFileHyperlinkInfo(myProject, sourceFile, documentLine, documentColumn); } return hyperlinkInfo; } } ================================================ FILE: src/main/java/com/reason/ide/console/rescript/RescriptConsoleView.java ================================================ package com.reason.ide.console.rescript; import com.intellij.execution.filters.*; import com.intellij.execution.impl.*; import com.intellij.openapi.project.*; import org.jetbrains.annotations.*; public class RescriptConsoleView extends ConsoleViewImpl { public RescriptConsoleView(@NotNull Project project) { super(project, true); } public @NotNull Filter[] getFilters() { return new Filter[]{new RescriptConsoleFilter(getProject())}; } } ================================================ FILE: src/main/java/com/reason/ide/console/rescript/RescriptResetAction.java ================================================ package com.reason.ide.console.rescript; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.project.*; import com.reason.comp.*; import com.reason.comp.rescript.*; import com.reason.ide.*; import com.reason.ide.console.*; import org.jetbrains.annotations.*; public class RescriptResetAction extends CompilerAction { public RescriptResetAction() { super("Clean", "Clean", ORIcons.RESET); } @Override public void update(@NotNull AnActionEvent e) { Project project = e.getProject(); ResCompiler compiler = project == null ? null : project.getService(ResCompiler.class); if (compiler != null) { e.getPresentation().setEnabled(compiler.isAvailable()); } } @Override public void actionPerformed(@NotNull AnActionEvent e) { Project project = e.getProject(); ResCompiler compiler = project != null ? project.getService(ResCompiler.class) : null; if (compiler != null) { doAction(project, CliType.Rescript.CLEAN, (_void) -> e.getPresentation().setEnabled(compiler.isAvailable())); } } @Override public @NotNull ActionUpdateThread getActionUpdateThread() { return ActionUpdateThread.BGT; } } ================================================ FILE: src/main/java/com/reason/ide/console/rescript/RescriptToolWindowFactory.java ================================================ package com.reason.ide.console.rescript; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.editor.actions.*; import com.intellij.openapi.project.*; import com.intellij.openapi.ui.*; import com.intellij.openapi.util.*; import com.intellij.openapi.wm.*; import com.intellij.ui.content.*; import com.reason.ide.console.*; import org.jetbrains.annotations.*; public class RescriptToolWindowFactory extends ORToolWindowFactory { private static final String TITLE = "Rescript"; public static final String ID = TITLE + ":"; @Override public @NotNull String getId() { return ID; } @Override public @NotNull String getStripeTitle() { return TITLE; } @Override public void createToolWindowContent(@NotNull final Project project, @NotNull ToolWindow window) { SimpleToolWindowPanel panel = new SimpleToolWindowPanel(false, true); RescriptConsoleView console = new RescriptConsoleView(project); panel.setContent(console.getComponent()); ActionToolbar toolbar = createToolbar(console); panel.setToolbar(toolbar.getComponent()); Content content = ContentFactory.getInstance().createContent(panel, "", true); window.getContentManager().addContent(content); Disposer.register(window.getDisposable(), console); } private @NotNull ActionToolbar createToolbar(@NotNull RescriptConsoleView console) { DefaultActionGroup group = new DefaultActionGroup(); group.add(new ScrollToTheEndToolbarAction(console.getEditor())); group.add(new ClearLogAction(console)); group.add(new RescriptBuildAction()); group.add(new RescriptResetAction()); ActionToolbar toolbar = ActionManager.getInstance().createActionToolbar("left", group, false); toolbar.setTargetComponent(console.getComponent()); return toolbar; } } ================================================ FILE: src/main/java/com/reason/ide/debug/OCamlApplicationConfiguration.java ================================================ package com.reason.ide.debug; import com.intellij.execution.*; import com.intellij.execution.configurations.*; import com.intellij.execution.runners.*; import com.intellij.openapi.module.Module; import com.intellij.openapi.options.*; import com.reason.lang.core.psi.*; import org.jetbrains.annotations.*; import javax.swing.*; import java.util.*; public class OCamlApplicationConfiguration extends ModuleBasedConfiguration { public OCamlApplicationConfiguration(String name, @NotNull OCamlModuleBasedConfiguration configurationModule, @NotNull ConfigurationFactory factory) { super(name, configurationModule, factory); } @Override public @Nullable Collection getValidModules() { return null; } @Override public @NotNull SettingsEditor getConfigurationEditor() { return new CompositeSettingsEditor<>() { @Override public @NotNull CompositeSettingsBuilder getBuilder() { return new CompositeSettingsBuilder<>() { @Override public @NotNull Collection> getEditors() { return Collections.emptyList(); } @Override public @NotNull JComponent createCompoundEditor() { return new JPanel(); } }; } }; } @Override public @Nullable RunProfileState getState(@NotNull Executor executor, @NotNull ExecutionEnvironment environment) { return null; } } ================================================ FILE: src/main/java/com/reason/ide/debug/OCamlApplicationRunningState.java ================================================ package com.reason.ide.debug; import com.intellij.execution.ExecutionException; import com.intellij.execution.configurations.CommandLineState; import com.intellij.execution.configurations.GeneralCommandLine; import com.intellij.execution.filters.TextConsoleBuilder; import com.intellij.execution.filters.TextConsoleBuilderFactory; import com.intellij.execution.process.OSProcessHandler; import com.intellij.execution.process.ProcessHandler; import com.intellij.execution.runners.ExecutionEnvironment; import com.intellij.openapi.module.Module; import org.jetbrains.annotations.NotNull; public class OCamlApplicationRunningState extends CommandLineState { private final Module m_module; protected OCamlApplicationRunningState(ExecutionEnvironment environment, Module module) { super(environment); m_module = module; } @NotNull @Override protected ProcessHandler startProcess() throws ExecutionException { GeneralCommandLine commandLine = getCommand(); return new OSProcessHandler(commandLine.createProcess(), commandLine.getCommandLineString()); } @NotNull private GeneralCommandLine getCommand() { GeneralCommandLine commandLine = new GeneralCommandLine(); // set exe/working dir/... TextConsoleBuilder consoleBuilder = TextConsoleBuilderFactory.getInstance().createBuilder(m_module.getProject()); setConsoleBuilder(consoleBuilder); return commandLine; } } ================================================ FILE: src/main/java/com/reason/ide/debug/OCamlDebugRunner.java ================================================ package com.reason.ide.debug; import com.intellij.execution.configurations.*; import com.intellij.execution.executors.*; import com.intellij.execution.runners.*; import org.jetbrains.annotations.*; public class OCamlDebugRunner extends GenericProgramRunner { private static final String OCAML_DEBUG_RUNNER_ID = "OcamlDebugRunner"; @Override public @NotNull String getRunnerId() { return OCAML_DEBUG_RUNNER_ID; } @Override public boolean canRun(@NotNull String executorId, @NotNull RunProfile profile) { return DefaultDebugExecutor.EXECUTOR_ID.equals(executorId) && (profile instanceof OCamlApplicationConfiguration); } } ================================================ FILE: src/main/java/com/reason/ide/debug/OCamlModuleBasedConfiguration.java ================================================ package com.reason.ide.debug; import com.intellij.execution.configurations.RunConfigurationModule; import com.intellij.openapi.project.Project; import org.jetbrains.annotations.NotNull; public class OCamlModuleBasedConfiguration extends RunConfigurationModule { public OCamlModuleBasedConfiguration(@NotNull Project project) { super(project); } } ================================================ FILE: src/main/java/com/reason/ide/debug/ORLineBreakpointProperties.java ================================================ package com.reason.ide.debug; import com.intellij.xdebugger.breakpoints.XBreakpointProperties; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; public class ORLineBreakpointProperties extends XBreakpointProperties { @Nullable @Override public ORLineBreakpointProperties getState() { return this; } @Override public void loadState(@NotNull ORLineBreakpointProperties state) {} } ================================================ FILE: src/main/java/com/reason/ide/debug/ORLineBreakpointType.java ================================================ package com.reason.ide.debug; import com.intellij.openapi.editor.Document; import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.fileTypes.FileType; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiElement; import com.intellij.psi.tree.IElementType; import com.intellij.util.Processor; import com.intellij.xdebugger.XDebuggerUtil; import com.intellij.xdebugger.breakpoints.XLineBreakpointType; import com.reason.FileHelper; import com.reason.lang.core.type.ORLangTypes; import com.reason.lang.ocaml.OclTypes; import com.reason.lang.reason.RmlTypes; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; public class ORLineBreakpointType extends XLineBreakpointType { private static final String ID = "OCamlLineBreakpoint"; private static final String NAME = "Line breakpoint"; protected ORLineBreakpointType() { super(ID, NAME); } @Nullable @Override public ORLineBreakpointProperties createBreakpointProperties( @NotNull VirtualFile file, int line) { return new ORLineBreakpointProperties(); } @Override public boolean canPutAt(@NotNull VirtualFile file, int line, @NotNull Project project) { FileType fileType = file.getFileType(); if (FileHelper.isOCaml(fileType) || FileHelper.isReason(fileType)) { Document document = FileDocumentManager.getInstance().getDocument(file); if (document != null) { LineBreakpointAvailabilityProcessor canPutAtChecker = new LineBreakpointAvailabilityProcessor( FileHelper.isOCaml(fileType) ? OclTypes.INSTANCE : RmlTypes.INSTANCE); XDebuggerUtil.getInstance().iterateLine(project, document, line, canPutAtChecker); return canPutAtChecker.isLineBreakpointAvailable(); } } return false; } private static final class LineBreakpointAvailabilityProcessor implements Processor { private final ORLangTypes myTypes; private boolean myIsLineBreakpointAvailable; LineBreakpointAvailabilityProcessor(ORLangTypes types) { myTypes = types; } @Override public boolean process(@NotNull PsiElement element) { IElementType elementType = element.getNode().getElementType(); if (elementType.equals(myTypes.WHITE_SPACE) || elementType.equals(myTypes.SINGLE_COMMENT) || elementType.equals(myTypes.MULTI_COMMENT)) { return true; } if (elementType.equals(myTypes.LET)) { myIsLineBreakpointAvailable = true; return false; } return true; } boolean isLineBreakpointAvailable() { return myIsLineBreakpointAvailable; } } } ================================================ FILE: src/main/java/com/reason/ide/debug/OclDebuggerRunnerSettings.java ================================================ package com.reason.ide.debug; import com.intellij.execution.configurations.*; import com.intellij.openapi.util.*; import org.jdom.*; public class OclDebuggerRunnerSettings implements RunnerSettings { @Override public void readExternal(Element element) throws InvalidDataException { } @Override public void writeExternal(Element element) throws WriteExternalException { } } ================================================ FILE: src/main/java/com/reason/ide/docs/DocFormatter.java ================================================ package com.reason.ide.docs; import com.intellij.lang.documentation.*; import com.intellij.openapi.fileTypes.*; import com.intellij.openapi.util.text.*; import com.intellij.psi.*; import com.reason.*; import com.reason.ide.files.*; import com.reason.lang.*; import com.reason.lang.core.psi.*; import com.reason.lang.doc.*; import com.reason.lang.doc.ocaml.*; import com.reason.lang.doc.reason.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; /** * See {@link com.intellij.codeInsight.documentation.DocumentationManagerProtocol} for link protocol. */ class DocFormatter { private static final Log LOG = Log.create("doc.formatter"); private DocFormatter() { } static @NotNull String format(@NotNull PsiFile file, @NotNull PsiElement element, @Nullable ORLanguageProperties lang, @NotNull String text) { if (file instanceof FileBase source) { // Definition HtmlBuilder definitionBuilder = new HtmlBuilder(); String path = source.getModuleName(); if (element instanceof RPsiQualifiedPathElement) { path = Joiner.join(".", ((RPsiQualifiedPathElement) element).getPath()); } definitionBuilder.append(HtmlChunk.text(path).bold()); if (element instanceof PsiNamedElement) { String className = element.getClass().getSimpleName().substring(4).replace("Impl", "").toLowerCase(); String name = ((PsiNamedElement) element).getName(); if (name != null) { definitionBuilder.append(HtmlChunk.raw("

")); definitionBuilder.append(HtmlChunk.text(className + " " + name)); if (element instanceof RPsiSignatureElement) { RPsiSignature signature = ((RPsiSignatureElement) element).getSignature(); if (signature != null) { definitionBuilder.append(HtmlChunk.text(" : ")).append(HtmlChunk.text(signature.asText(lang)).wrapWith("code")); } } } definitionBuilder.append(HtmlChunk.raw("

")); } // Content HtmlBuilder contentBuilder = new HtmlBuilder(); FileType fileType = source.getFileType(); boolean isReasonLikeComment = FileHelper.isReason(fileType) || FileHelper.isRescript(fileType); ORDocConverter converter = isReasonLikeComment ? new RmlDocConverter() : new OclDocConverter(); contentBuilder.append(converter.convert(element, text)); // final render HtmlBuilder builder = new HtmlBuilder(); builder.append(definitionBuilder.wrapWith(DocumentationMarkup.DEFINITION_ELEMENT)); builder.append(contentBuilder.wrapWith(DocumentationMarkup.CONTENT_ELEMENT)); if (LOG.isDebugEnabled()) { LOG.debug(builder.toString()); } return builder.toString(); } return text; } static @NotNull String escapeCodeForHtml(@Nullable PsiElement code) { if (code == null) { return ""; } return escapeCodeForHtml(code.getText()); } @Nullable public static String escapeCodeForHtml(@Nullable String code) { return code == null ? null : code. replaceAll("<", "<"). replaceAll(">", ">"); } } ================================================ FILE: src/main/java/com/reason/ide/docs/ORDocumentationProvider.java ================================================ package com.reason.ide.docs; import com.intellij.lang.*; import com.intellij.lang.documentation.*; import com.intellij.openapi.editor.*; import com.intellij.openapi.project.*; import com.intellij.psi.*; import com.intellij.psi.search.*; import com.intellij.psi.util.*; import com.reason.ide.files.*; import com.reason.ide.hints.*; import com.reason.ide.search.*; import com.reason.ide.search.index.*; import com.reason.ide.search.reference.*; import com.reason.lang.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import com.reason.lang.ocaml.*; import com.reason.lang.reason.*; import com.reason.lang.rescript.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; import java.util.*; import java.util.function.*; public class ORDocumentationProvider extends AbstractDocumentationProvider { private static final Log LOG = Log.create("doc"); private static final @NotNull Predicate PSI_INTF_PREDICATE = psiElement -> ((FileBase) psiElement.getContainingFile()).isInterface(); @Override public @Nullable String generateDoc(PsiElement resolvedElement, @Nullable PsiElement originalElement) { ORLanguageProperties languageProperties = ORLanguageProperties.cast(originalElement == null ? null : originalElement.getLanguage()); PsiElement docElement = resolvedElement; if (resolvedElement instanceof RPsiModule module && module.isComponent()) { PsiElement make = module.getMakeFunction(); if (make != null) { docElement = make; } } else if (resolvedElement instanceof FileBase) { PsiElement child = resolvedElement.getFirstChild(); String text = ""; PsiElement nextSibling = child; while (nextSibling instanceof PsiComment) { if (isSpecialComment(nextSibling)) { text = nextSibling.getText(); nextSibling = null; } else { // Not a special comment, try with next child until no more comments found nextSibling = PsiTreeUtil.nextVisibleLeaf(nextSibling); } } if (!text.isEmpty()) { return DocFormatter.format((PsiFile) resolvedElement, resolvedElement, languageProperties, text); } } // If it's an alias, resolve to the alias if (docElement instanceof RPsiLet let) { String alias = let.getAlias(); if (alias != null) { PsiElement binding = let.getBinding(); RPsiLowerSymbol lSymbol = binding == null ? null : ORUtil.findImmediateLastChildOfClass(binding, RPsiLowerSymbol.class); ORPsiLowerSymbolReference lReference = lSymbol == null ? null : lSymbol.getReference(); PsiElement resolvedAlias = lReference == null ? null : lReference.resolveInterface(); if (resolvedAlias != null) { docElement = resolvedAlias; } } } PsiElement comment = findComment(docElement, docElement.getLanguage()); // Nothing found, try to find a comment in the interface if any if (comment == null && originalElement instanceof RPsiLowerSymbol && docElement instanceof RPsiQualifiedPathElement) { Project project = docElement.getProject(); GlobalSearchScope scope = GlobalSearchScope.allScope(project); String elementQName = ((RPsiQualifiedPathElement) docElement).getQualifiedName(); if (elementQName != null) { Collection vals = ValFqnIndex.getElements(elementQName, project, scope); if (!vals.isEmpty()) { RPsiVal next = vals.iterator().next(); comment = findComment(next, next.getLanguage()); } else { Collection lets = LetFqnIndex.getElements(elementQName, project, scope); RPsiLet letIntf = lets.stream() .filter(PSI_INTF_PREDICATE) .findFirst().orElse(null); if (letIntf != null) { comment = findComment(letIntf, letIntf.getLanguage()); } } } } if (comment != null) { if (comment instanceof RPsiAnnotation) { PsiElement value = ((RPsiAnnotation) comment).getValue(); String text = value == null ? null : value.getText(); return text == null ? null : text.substring(1, text.length() - 1); } return isSpecialComment(comment) ? DocFormatter.format(docElement.getContainingFile(), docElement, languageProperties, comment.getText()) : comment.getText(); } //} return null; } @Override public @Nullable String getQuickNavigateInfo(@NotNull PsiElement resolvedElement, @NotNull PsiElement originalElement) { String quickDoc = null; ORLanguageProperties languageProperties = ORLanguageProperties.cast(originalElement.getLanguage()); if (resolvedElement instanceof ORFakeResolvedElement) { // A fake element, used to query inferred types quickDoc = "Show usages of fake element '" + resolvedElement.getText() + "'"; } else if (resolvedElement instanceof FileBase resolvedFile) { LOG.debug("Quickdoc of topModule", resolvedElement); String relative_path = Platform.getRelativePathToModule(resolvedFile); quickDoc = "
" + relative_path + "
" + "Module " //+ DocFormatter.NAME_START + resolvedFile.getModuleName(); //+ DocFormatter.NAME_END; } else { LOG.trace("Resolved element", resolvedElement); if (resolvedElement instanceof RPsiType type) { String[] path = ORUtil.getQualifiedPath(type); String typeBinding = type.isAbstract() ? "This is an abstract type" : DocFormatter.escapeCodeForHtml(type.getBinding()); return createQuickDocTemplate(path, "type", type.getName(), typeBinding); } if (resolvedElement instanceof RPsiSignatureElement) { RPsiSignature signature = ((RPsiSignatureElement) resolvedElement).getSignature(); if (signature != null) { String sig = DocFormatter.escapeCodeForHtml(signature.asText(languageProperties)); if (resolvedElement instanceof RPsiQualifiedPathElement qualifiedElement) { String elementType = PsiTypeElementProvider.getType(resolvedElement); return createQuickDocTemplate(qualifiedElement.getPath(), elementType, qualifiedElement.getName(), sig); } return sig; } } // No signature found, but resolved if (resolvedElement instanceof PsiQualifiedNamedElement) { LOG.debug("Quickdoc resolved to ", resolvedElement); String elementType = PsiTypeElementProvider.getType(resolvedElement); String desc = ((PsiQualifiedNamedElement) resolvedElement).getName(); String[] path = ORUtil.getQualifiedPath(resolvedElement); PsiFile psiFile = originalElement.getContainingFile(); String inferredType = getInferredSignature(originalElement, psiFile, languageProperties); if (inferredType == null) { // Can't find type in the usage, try to get type from the definition inferredType = getInferredSignature(resolvedElement, resolvedElement.getContainingFile(), languageProperties); } String sig = inferredType == null ? null : DocFormatter.escapeCodeForHtml(inferredType); if (resolvedElement instanceof RPsiVariantDeclaration) { RPsiType type = PsiTreeUtil.getParentOfType(resolvedElement, RPsiType.class); sig = "type " + (type == null ? "unknown" : type.getName()); } return createQuickDocTemplate(path, elementType, desc, resolvedElement instanceof RPsiModule ? null : sig); } } return quickDoc; } @Override public @Nullable PsiElement getCustomDocumentationElement(@NotNull Editor editor, @NotNull PsiFile file, @Nullable PsiElement contextElement, int targetOffset) { PsiElement parent = contextElement == null ? null : contextElement.getParent(); // When quick doc inside empty parenthesis, we want to display the function doc (GitHub #155) // functionName() ==> functionName() if (contextElement != null && parent instanceof RPsiParameters) { Language contextLanguage = contextElement.getLanguage(); if (contextLanguage == RmlLanguage.INSTANCE || contextLanguage == ResLanguage.INSTANCE) { PsiElement prevSibling = parent.getPrevSibling(); if (prevSibling != null) { PsiReference reference = prevSibling.getReference(); if (reference != null) { return reference.resolve(); } } } } if (contextElement != null && parent instanceof RPsiLowerSymbol lowerParent) { ORPsiLowerSymbolReference reference = lowerParent.getReference(); ResolveResult[] resolveResults = reference.multiResolve(false); if (0 < resolveResults.length) { Arrays.sort(resolveResults, (rr1, rr2) -> ((ORPsiLowerSymbolReference.LowerResolveResult) rr1).inInterface() ? -1 : (((ORPsiLowerSymbolReference.LowerResolveResult) rr2).inInterface() ? 1 : 0)); return resolveResults[0].getElement(); } } return null; } private @Nullable PsiElement findComment(@Nullable PsiElement resolvedElement, @NotNull Language lang) { // Try to find a comment just below (OCaml only) if (lang == OclLanguage.INSTANCE) { PsiElement belowComment = findBelowComment(resolvedElement); if (belowComment != null) { return belowComment; } } // Else try to find a comment just above return findAboveComment(resolvedElement); } private @Nullable PsiElement findAboveComment(@Nullable PsiElement element) { if (element == null) { return null; } PsiElement commentElement = null; // search for a comment above boolean search = true; PsiElement prevSibling = element.getPrevSibling(); while (search) { if (prevSibling instanceof PsiComment) { search = false; commentElement = prevSibling; } else if (prevSibling instanceof PsiWhiteSpace) { prevSibling = prevSibling.getPrevSibling(); } else if (prevSibling instanceof RPsiAnnotation annotation) { if ("@ocaml.doc".equals(annotation.getName())) { search = false; commentElement = annotation; } else { prevSibling = prevSibling.getPrevSibling(); } } else { search = false; } } return commentElement; } private @Nullable PsiElement findBelowComment(@Nullable PsiElement element) { if (element != null) { PsiElement nextSibling = element.getNextSibling(); PsiElement nextNextSibling = nextSibling == null ? null : nextSibling.getNextSibling(); if (nextNextSibling instanceof PsiComment && nextSibling instanceof PsiWhiteSpace && nextSibling.getText().replaceAll("[ \t]", "").length() == 1) { return nextNextSibling; } } return null; } private @Nullable String getInferredSignature(@NotNull PsiElement element, @NotNull PsiFile psiFile, @Nullable ORLanguageProperties language) { SignatureProvider.InferredTypesWithLines signaturesContext = psiFile.getUserData(SignatureProvider.SIGNATURES_CONTEXT); if (signaturesContext != null) { RPsiSignature elementSignature = signaturesContext.getSignatureByOffset(element.getTextOffset()); if (elementSignature != null) { return elementSignature.asText(language); } } return null; } private @NotNull String createQuickDocTemplate(@Nullable String[] path, @Nullable String type, @Nullable String name, @Nullable String signature) { return Joiner.join(".", path) + "
" + (type == null ? "" : type) + (" " + name + "") + (signature == null ? "" : "
" + signature); } public static boolean isSpecialComment(@Nullable PsiElement element) { if (element == null) { return false; } String nextText = element.getText(); return (nextText.startsWith("(**") || nextText.startsWith("/**")) && nextText.charAt(3) != '*'; } } ================================================ FILE: src/main/java/com/reason/ide/editors/CmtFileEditor.java ================================================ package com.reason.ide.editors; import com.intellij.icons.*; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.fileEditor.*; import com.intellij.openapi.project.*; import com.intellij.openapi.util.*; import com.intellij.openapi.vfs.*; import com.intellij.ui.*; import com.intellij.ui.components.*; import com.intellij.ui.table.*; import com.reason.hints.*; import org.jetbrains.annotations.*; import javax.swing.*; import javax.swing.table.*; import java.beans.*; import java.util.*; public class CmtFileEditor extends UserDataHolderBase implements FileEditor { private final Project myProject; private final VirtualFile myFile; private TabbedPaneWrapper myRootTabbedPane; public CmtFileEditor(@NotNull Project project, @NotNull VirtualFile file) { myProject = project; myFile = file; } @Override public @NotNull VirtualFile getFile() { return myFile; } @Override public @NotNull JComponent getComponent() { // using workaround found at https://youtrack.jetbrains.com/issue/IDEA-272890 // myRootTabbedPane = new JBTabbedPane(JTabbedPane.TOP, JTabbedPane.SCROLL_TAB_LAYOUT); PrevNextActionsDescriptor descriptor = new PrevNextActionsDescriptor(IdeActions.ACTION_NEXT_EDITOR_TAB, IdeActions.ACTION_PREVIOUS_EDITOR_TAB); myRootTabbedPane = TabbedPaneWrapper.createJbTabs(myProject, SwingConstants.TOP, descriptor, this); InsightManager insightManager = myProject.getService(InsightManager.class); List meta = insightManager.dumpMeta(myFile); String xmlSource = insightManager.dumpTree(myFile); List types = insightManager.dumpInferredTypes(myFile); myRootTabbedPane.addTab("Meta", AllIcons.Nodes.DataTables, new JBScrollPane(Table.create(new MetaTableModel(meta))), "Meta information"); //noinspection DialogTitleCapitalization myRootTabbedPane.addTab("AST", AllIcons.FileTypes.Xml, new CmtXmlComponent(myProject, myRootTabbedPane, xmlSource), "Abstract Syntax Tree"); myRootTabbedPane.addTab("Inferred", AllIcons.Nodes.DataSchema, new JBScrollPane(Table.create(new InferredTableModel(types))), "Inferred types from AST"); return myRootTabbedPane.getComponent(); } @Override public @Nullable JComponent getPreferredFocusedComponent() { return myRootTabbedPane.getComponent(); } @Override public @NotNull String getName() { return "CMT Editor"; } @Override public boolean isModified() { return false; } @Override public boolean isValid() { return true; } @Override public void dispose() { Disposer.dispose(this); } @Override public void setState(@NotNull FileEditorState state) { } @Override public void addPropertyChangeListener(@NotNull PropertyChangeListener listener) { } @Override public void removePropertyChangeListener(@NotNull PropertyChangeListener listener) { } static class Table extends JBTable { private Table(TableModel model) { super(model); } public static @NotNull Table create(TableModel model) { Table table = new Table(model); table.setTableHeader(table.createDefaultTableHeader()); table.invalidate(); return table; } } static class MetaTableModel extends AbstractTableModel { private final List m_entries = new ArrayList<>(); public MetaTableModel(@NotNull List entries) { for (String entry : entries) { m_entries.add(entry.split("\\|", 2)); } } @Override public int getRowCount() { return m_entries.size(); } @Override public int getColumnCount() { return 2; } @Override public @NotNull String getColumnName(int col) { return col == 0 ? "Key" : "Value"; } @Override public @NotNull Class getColumnClass(int col) { return String.class; } @Override public Object getValueAt(int row, int col) { String[] values = m_entries.get(row); if (values != null && col < values.length) { return values[col]; } return ""; } } static class InferredTableModel extends AbstractTableModel { private final List m_types = new ArrayList<>(); public InferredTableModel(@NotNull List types) { for (String type : types) { m_types.add(type.split("\\|")); } } @Override public int getRowCount() { return m_types.size() - 1; } @Override public int getColumnCount() { return 5; } @Override public @NotNull String getColumnName(int col) { return switch (col) { case 0 -> "Kind"; case 1 -> "Position"; case 2 -> "Name"; case 3 -> "QName"; case 4 -> "Type"; default -> " "; }; } @Override public @NotNull Class getColumnClass(int col) { return String.class; } @Override public Object getValueAt(int row, int col) { String[] values = m_types.get(row + 1); if (values != null && col < values.length) { return values[col]; } return ""; } } } ================================================ FILE: src/main/java/com/reason/ide/editors/CmtFileEditorProvider.java ================================================ package com.reason.ide.editors; import com.intellij.openapi.fileEditor.*; import com.intellij.openapi.project.*; import com.intellij.openapi.vfs.*; import com.reason.ide.files.*; import org.jetbrains.annotations.*; public class CmtFileEditorProvider implements com.intellij.openapi.fileEditor.FileEditorProvider { @Override public boolean accept(@NotNull Project project, @NotNull VirtualFile file) { return file.getFileType() == CmtFileType.INSTANCE; } @Override public @NotNull FileEditor createEditor(@NotNull Project project, @NotNull VirtualFile file) { return new CmtFileEditor(project, file); } @Override public @NotNull String getEditorTypeId() { return "CMT"; } @Override public @NotNull FileEditorPolicy getPolicy() { return FileEditorPolicy.PLACE_AFTER_DEFAULT_EDITOR; } } ================================================ FILE: src/main/java/com/reason/ide/editors/CmtXmlComponent.java ================================================ package com.reason.ide.editors; import com.intellij.ide.highlighter.*; import com.intellij.lang.xhtml.*; import com.intellij.openapi.editor.*; import com.intellij.openapi.project.*; import com.intellij.psi.*; import com.intellij.ui.*; import com.intellij.util.ui.components.*; import org.jetbrains.annotations.*; import javax.swing.event.*; public class CmtXmlComponent extends BorderLayoutPanel implements ChangeListener { private final Project myProject; private final String myXmlDump; private boolean myChildrenAdded = false; public CmtXmlComponent(@NotNull Project project, @NotNull TabbedPaneWrapper rootTabbedPane, @NotNull String xmlDump) { myProject = project; myXmlDump = xmlDump; rootTabbedPane.addChangeListener(this); } @Override public void stateChanged(@NotNull ChangeEvent changeEvent) { if (!myChildrenAdded) { addChildren(); myChildrenAdded = true; } } private void addChildren() { PsiFile psiFile = PsiFileFactory.getInstance(myProject).createFileFromText(XHTMLLanguage.INSTANCE, myXmlDump); Document document = PsiDocumentManager.getInstance(myProject).getDocument(psiFile); if (document != null) { Editor editor = EditorFactory.getInstance().createEditor(document, myProject, HtmlFileType.INSTANCE, true); addToCenter(editor.getComponent()); } } } ================================================ FILE: src/main/java/com/reason/ide/files/CmtFileType.java ================================================ package com.reason.ide.files; import com.intellij.openapi.fileTypes.*; import com.intellij.openapi.vfs.*; import org.jetbrains.annotations.*; import javax.swing.*; public class CmtFileType implements FileType { public static final CmtFileType INSTANCE = new CmtFileType(); @NotNull @Override public String getName() { return "CMT"; } @NotNull @Override public String getDescription() { return "Cmt"; } @NotNull @Override public String getDefaultExtension() { return "cmt"; } @Nullable @Override public Icon getIcon() { return UnknownFileType.INSTANCE.getIcon(); } @Override public boolean isBinary() { return true; } @Override public boolean isReadOnly() { return true; } @Override public @Nullable String getCharset(@NotNull VirtualFile file, byte[] content) { return null; } } ================================================ FILE: src/main/java/com/reason/ide/files/DuneFile.java ================================================ package com.reason.ide.files; import com.intellij.extapi.psi.*; import com.intellij.openapi.fileTypes.*; import com.intellij.psi.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.impl.*; import com.reason.lang.dune.*; import org.jetbrains.annotations.*; public class DuneFile extends PsiFileBase { public DuneFile(@NotNull FileViewProvider viewProvider) { super(viewProvider, DuneLanguage.INSTANCE); } @Override public @NotNull FileType getFileType() { return DuneFileType.INSTANCE; } public @Nullable RPsiDuneStanza getStanza(@NotNull String name) { for (RPsiDuneStanza stanza : ORUtil.findImmediateChildrenOfClass(this, RPsiDuneStanza.class)) { if (name.equals(stanza.getName())) { return stanza; } } return null; } } ================================================ FILE: src/main/java/com/reason/ide/files/DuneFileType.java ================================================ package com.reason.ide.files; import com.google.common.collect.*; import com.intellij.openapi.fileTypes.*; import com.intellij.openapi.vfs.*; import com.reason.ide.*; import com.reason.lang.dune.*; import org.jetbrains.annotations.*; import javax.swing.*; import java.util.*; import static com.reason.comp.dune.DunePlatform.*; public class DuneFileType extends LanguageFileType { public static final FileType INSTANCE = new DuneFileType(); public static Set getDefaultFilenames() { return ImmutableSet.of(DUNE_FILENAME, DUNE_PROJECT_FILENAME, LEGACY_JBUILDER_FILENAME); } public static boolean isDuneFile(@NotNull VirtualFile file) { return getDefaultFilenames().stream().anyMatch(filename -> filename.equals(file.getName())); } private DuneFileType() { super(DuneLanguage.INSTANCE); } @NotNull @Override public String getName() { return "DUNE"; } @NotNull @Override public String getDescription() { return "Dune configuration file"; } @NotNull @Override public String getDefaultExtension() { return ""; } @Nullable @Override public Icon getIcon() { return ORIcons.DUNE_FILE; } } ================================================ FILE: src/main/java/com/reason/ide/files/FileBase.java ================================================ package com.reason.ide.files; import com.intellij.extapi.psi.*; import com.intellij.lang.*; import com.intellij.pom.*; import com.intellij.psi.*; import com.intellij.psi.util.*; import com.reason.*; import com.reason.lang.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import org.jetbrains.annotations.*; import java.util.*; public abstract class FileBase extends PsiFileBase implements RPsiModule, Navigatable { private final @NotNull String m_moduleName; FileBase(@NotNull FileViewProvider viewProvider, @NotNull Language language) { super(viewProvider, language); m_moduleName = ORUtil.fileNameToModuleName(getName()); } public @NotNull String getModuleName() { return m_moduleName; } //region PsiQualifiedName @Override public @Nullable String[] getPath() { return null; } @Override public @NotNull String getQualifiedName() { return getModuleName(); } //endregion @Override public @Nullable PsiElement getBody() { return this; } public boolean isComponent() { if (FileHelper.isOCaml(getFileType())) { return false; } return ModuleHelper.isComponent(this); } @Override public @Nullable PsiElement getMakeFunction() { PsiElement make = ORUtil.findImmediateNamedChildOfClass(this, RPsiLet.class, "make"); if (make == null) { make = ORUtil.findImmediateNamedChildOfClass(this, RPsiExternal.class, "make"); } return make; } @SafeVarargs public @NotNull final List getQualifiedExpressions(@Nullable String name, @NotNull Class... clazz) { List result = new ArrayList<>(); if (name != null) { Collection children = PsiTreeUtil.findChildrenOfAnyType(this, clazz); for (T child : children) { if (name.equals(child.getQualifiedName())) { result.add(child); } } } return result; } public boolean isInterface() { return FileHelper.isInterface(getFileType()); } @Override public boolean equals(@Nullable Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } FileBase fileBase = (FileBase) o; return m_moduleName.equals(fileBase.m_moduleName) && isInterface() == fileBase.isInterface(); } @Override public int hashCode() { return Objects.hash(m_moduleName, isInterface()); } } ================================================ FILE: src/main/java/com/reason/ide/files/Ml4File.java ================================================ package com.reason.ide.files; import com.intellij.extapi.psi.*; import com.intellij.openapi.fileTypes.FileType; import com.intellij.psi.FileViewProvider; import com.reason.lang.extra.OclP4Language; import org.jetbrains.annotations.NotNull; public class Ml4File extends PsiFileBase { public Ml4File(@NotNull FileViewProvider viewProvider) { super(viewProvider, OclP4Language.INSTANCE); } @NotNull @Override public FileType getFileType() { return Ml4FileType.INSTANCE; } @NotNull @Override public String toString() { return Ml4FileType.INSTANCE.getDescription(); } } ================================================ FILE: src/main/java/com/reason/ide/files/Ml4FileType.java ================================================ package com.reason.ide.files; import com.intellij.openapi.fileTypes.LanguageFileType; import com.reason.lang.extra.OclP4Language; import com.reason.ide.ORIcons; import javax.swing.*; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; public class Ml4FileType extends LanguageFileType { public static final Ml4FileType INSTANCE = new Ml4FileType(); private Ml4FileType() { super(OclP4Language.INSTANCE); } @NotNull @Override public String getName() { return "OCamlP4 file"; } @NotNull @Override public String getDescription() { return "OCaml preprocessor file"; } @NotNull @Override public String getDefaultExtension() { return "ml4"; } @Nullable @Override public Icon getIcon() { return ORIcons.OCL_GREEN_FILE; } } ================================================ FILE: src/main/java/com/reason/ide/files/MlgFile.java ================================================ package com.reason.ide.files; import com.intellij.extapi.psi.*; import com.intellij.openapi.fileTypes.FileType; import com.intellij.psi.FileViewProvider; import com.reason.lang.ocamlgrammar.*; import org.jetbrains.annotations.NotNull; public class MlgFile extends PsiFileBase { public MlgFile(@NotNull FileViewProvider viewProvider) { super(viewProvider, OclGrammarLanguage.INSTANCE); } @Override public @NotNull FileType getFileType() { return MlgFileType.INSTANCE; } @Override public @NotNull String toString() { return MlgFileType.INSTANCE.getDescription(); } } ================================================ FILE: src/main/java/com/reason/ide/files/MlgFileType.java ================================================ package com.reason.ide.files; import com.intellij.openapi.fileTypes.*; import com.reason.ide.highlight.*; import com.reason.ide.ORIcons; import javax.swing.*; import com.reason.lang.ocamlgrammar.*; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; public class MlgFileType extends LanguageFileType { public static final MlgFileType INSTANCE = new MlgFileType(); private MlgFileType() { super(OclGrammarLanguage.INSTANCE); FileTypeEditorHighlighterProviders.getInstance().addExplicitExtension(this, (project, fileType, virtualFile, colors) -> new OclGrammarEditorHighlighter(project, virtualFile, colors)); } @Override public @NotNull String getName() { return "MLG"; } @Override public @NotNull String getDescription() { return "OCaml grammar file"; } @Override public @NotNull String getDefaultExtension() { return "mlg"; } @Override public @Nullable Icon getIcon() { return ORIcons.OCL_GREEN_FILE; } } ================================================ FILE: src/main/java/com/reason/ide/files/MlgFileViewProvider.java ================================================ package com.reason.ide.files; import com.intellij.lang.*; import com.intellij.openapi.vfs.*; import com.intellij.psi.*; import com.intellij.psi.impl.source.*; import com.intellij.psi.templateLanguages.*; import com.intellij.psi.tree.*; import com.reason.lang.ocaml.*; import com.reason.lang.ocamlgrammar.*; import org.jetbrains.annotations.*; import java.util.*; public class MlgFileViewProvider extends MultiplePsiFilesPerDocumentFileViewProvider implements TemplateLanguageFileViewProvider { public static final IElementType OUTER_ELEMENT = new OuterLanguageElementType("OUTER_ELEMENT", OclGrammarLanguage.INSTANCE); private static final IElementType TEMPLATE_DATA = new TemplateDataElementType("TEMPLATE_DATA", OclGrammarLanguage.INSTANCE, OclGrammarTypes.INSTANCE.TEMPLATE_OCAML_TEXT, OUTER_ELEMENT); public MlgFileViewProvider(@NotNull PsiManager manager, @NotNull VirtualFile file, boolean eventSystemEnabled) { super(manager, file, eventSystemEnabled); } @Override public @NotNull Language getBaseLanguage() { return OclGrammarLanguage.INSTANCE; } @Override public @NotNull Language getTemplateDataLanguage() { return OclLanguage.INSTANCE; } @Override public @NotNull Set getLanguages() { return Set.of(OclGrammarLanguage.INSTANCE, getTemplateDataLanguage()); } @Override protected @NotNull MultiplePsiFilesPerDocumentFileViewProvider cloneInner(@NotNull VirtualFile fileCopy) { return new MlgFileViewProvider(getManager(), fileCopy, false); } @Nullable protected PsiFile createFile(@NotNull Language lang) { ParserDefinition parser = LanguageParserDefinitions.INSTANCE.forLanguage(lang); if (parser == null) { return null; } if (lang == OclLanguage.INSTANCE) { PsiFileImpl file = (PsiFileImpl) parser.createFile(this); file.setContentElementType(TEMPLATE_DATA); return file; } return lang == getBaseLanguage() ? parser.createFile(this) : null; } } ================================================ FILE: src/main/java/com/reason/ide/files/MlgFileViewProviderFactory.java ================================================ package com.reason.ide.files; import com.intellij.lang.*; import com.intellij.openapi.vfs.*; import com.intellij.psi.*; import org.jetbrains.annotations.*; public class MlgFileViewProviderFactory implements FileViewProviderFactory { @Override public @NotNull FileViewProvider createFileViewProvider(@NotNull VirtualFile file, Language language, @NotNull PsiManager manager, boolean eventSystemEnabled) { return new MlgFileViewProvider(manager, file, eventSystemEnabled); } } ================================================ FILE: src/main/java/com/reason/ide/files/MllFile.java ================================================ package com.reason.ide.files; import com.intellij.extapi.psi.*; import com.intellij.openapi.fileTypes.FileType; import com.intellij.psi.FileViewProvider; import com.reason.lang.ocamllex.OclLexLanguage; import org.jetbrains.annotations.NotNull; public class MllFile extends PsiFileBase { public MllFile(@NotNull FileViewProvider viewProvider) { super(viewProvider, OclLexLanguage.INSTANCE); } @Override public @NotNull FileType getFileType() { return MllFileType.INSTANCE; } @Override public @NotNull String toString() { return MllFileType.INSTANCE.getDescription(); } } ================================================ FILE: src/main/java/com/reason/ide/files/MllFileType.java ================================================ package com.reason.ide.files; import com.intellij.openapi.fileTypes.*; import com.reason.ide.*; import com.reason.ide.highlight.*; import com.reason.lang.ocamllex.*; import org.jetbrains.annotations.*; import javax.swing.*; public class MllFileType extends LanguageFileType { public static final MllFileType INSTANCE = new MllFileType(); private MllFileType() { super(OclLexLanguage.INSTANCE); FileTypeEditorHighlighterProviders.getInstance().addExplicitExtension(this, (project, fileType, virtualFile, colors) -> new OclLexEditorHighlighter(project, virtualFile, colors)); } @Override public @NotNull String getName() { return "MLL"; } @Override public @NotNull String getDescription() { return "OCaml lexer"; } @Override public @NotNull String getDefaultExtension() { return "mll"; } @Override public @Nullable Icon getIcon() { return ORIcons.OCL_GREEN_FILE; } } ================================================ FILE: src/main/java/com/reason/ide/files/MllFileViewProvider.java ================================================ package com.reason.ide.files; import com.intellij.lang.*; import com.intellij.openapi.vfs.*; import com.intellij.psi.*; import com.intellij.psi.impl.source.*; import com.intellij.psi.templateLanguages.*; import com.intellij.psi.tree.*; import com.reason.lang.ocaml.*; import com.reason.lang.ocamllex.*; import org.jetbrains.annotations.*; import java.util.*; public class MllFileViewProvider extends MultiplePsiFilesPerDocumentFileViewProvider implements TemplateLanguageFileViewProvider { public static final IElementType OUTER_ELEMENT = new OuterLanguageElementType("OUTER_ELEMENT", OclLexLanguage.INSTANCE); private static final IElementType TEMPLATE_DATA = new TemplateDataElementType("TEMPLATE_DATA", OclLexLanguage.INSTANCE, OclLexTypes.INSTANCE.TEMPLATE_OCAML_TEXT, OUTER_ELEMENT); public MllFileViewProvider(@NotNull PsiManager manager, @NotNull VirtualFile file, boolean eventSystemEnabled) { super(manager, file, eventSystemEnabled); } @Override public @NotNull Language getBaseLanguage() { return OclLexLanguage.INSTANCE; } @Override public @NotNull Language getTemplateDataLanguage() { return OclLanguage.INSTANCE; } @Override public @NotNull Set getLanguages() { return Set.of(OclLexLanguage.INSTANCE, getTemplateDataLanguage()); } @Override protected @NotNull MultiplePsiFilesPerDocumentFileViewProvider cloneInner(@NotNull VirtualFile fileCopy) { return new MllFileViewProvider(getManager(), fileCopy, false); } @Override protected @Nullable PsiFile createFile(@NotNull Language lang) { ParserDefinition parser = LanguageParserDefinitions.INSTANCE.forLanguage(lang); if (parser == null) { return null; } if (lang == OclLanguage.INSTANCE) { PsiFileImpl file = (PsiFileImpl) parser.createFile(this); file.setContentElementType(TEMPLATE_DATA); return file; } return lang == getBaseLanguage() ? parser.createFile(this) : null; } } ================================================ FILE: src/main/java/com/reason/ide/files/MllFileViewProviderFactory.java ================================================ package com.reason.ide.files; import com.intellij.lang.*; import com.intellij.openapi.vfs.*; import com.intellij.psi.*; import org.jetbrains.annotations.*; public class MllFileViewProviderFactory implements FileViewProviderFactory { @Override public @NotNull FileViewProvider createFileViewProvider(@NotNull VirtualFile file, Language language, @NotNull PsiManager manager, boolean eventSystemEnabled) { return new MllFileViewProvider(manager, file, eventSystemEnabled); } } ================================================ FILE: src/main/java/com/reason/ide/files/MlyFile.java ================================================ package com.reason.ide.files; import com.intellij.extapi.psi.*; import com.intellij.openapi.fileTypes.*; import com.intellij.psi.*; import com.reason.lang.ocamlyacc.*; import org.jetbrains.annotations.*; public class MlyFile extends PsiFileBase { public MlyFile(@NotNull FileViewProvider viewProvider) { super(viewProvider, OclYaccLanguage.INSTANCE); } @Override public @NotNull FileType getFileType() { return MlyFileType.INSTANCE; } @Override public @NotNull String toString() { return MlyFileType.INSTANCE.getDescription(); } } ================================================ FILE: src/main/java/com/reason/ide/files/MlyFileType.java ================================================ package com.reason.ide.files; import com.intellij.openapi.fileTypes.*; import com.reason.ide.*; import com.reason.ide.highlight.*; import com.reason.lang.ocamlyacc.*; import org.jetbrains.annotations.*; import javax.swing.*; public class MlyFileType extends LanguageFileType { public static final MlyFileType INSTANCE = new MlyFileType(); private MlyFileType() { super(OclYaccLanguage.INSTANCE); FileTypeEditorHighlighterProviders.getInstance().addExplicitExtension(this, (project, fileType, virtualFile, colors) -> new OclYaccEditorHighlighter(project, virtualFile, colors)); } @Override public @NotNull String getName() { return "MlY"; } @Override public @NotNull String getDescription() { return "OCaml yacc parser"; } @Override public @NotNull String getDefaultExtension() { return "mly"; } @Override public @Nullable Icon getIcon() { return ORIcons.OCL_GREEN_FILE; } } ================================================ FILE: src/main/java/com/reason/ide/files/MlyFileViewProvider.java ================================================ package com.reason.ide.files; import com.intellij.lang.*; import com.intellij.openapi.vfs.*; import com.intellij.psi.*; import com.intellij.psi.impl.source.*; import com.intellij.psi.templateLanguages.*; import com.intellij.psi.tree.*; import com.reason.lang.ocaml.*; import com.reason.lang.ocamlyacc.*; import org.jetbrains.annotations.*; import java.util.*; public class MlyFileViewProvider extends MultiplePsiFilesPerDocumentFileViewProvider implements TemplateLanguageFileViewProvider { public static final IElementType OUTER_ELEMENT = new OuterLanguageElementType("OUTER_ELEMENT", OclYaccLanguage.INSTANCE); private static final IElementType TEMPLATE_DATA = new TemplateDataElementType("TEMPLATE_DATA", OclYaccLanguage.INSTANCE, OclYaccTypes.INSTANCE.TEMPLATE_OCAML_TEXT, OUTER_ELEMENT); public MlyFileViewProvider(@NotNull PsiManager manager, @NotNull VirtualFile file, boolean eventSystemEnabled) { super(manager, file, eventSystemEnabled); } @Override public @NotNull Language getBaseLanguage() { return OclYaccLanguage.INSTANCE; } @Override public @NotNull Language getTemplateDataLanguage() { return OclLanguage.INSTANCE; } @Override public @NotNull Set getLanguages() { return Set.of(OclYaccLanguage.INSTANCE, getTemplateDataLanguage()); } @Override protected @NotNull MultiplePsiFilesPerDocumentFileViewProvider cloneInner(@NotNull VirtualFile fileCopy) { return new MlyFileViewProvider(getManager(), fileCopy, false); } @Override protected @Nullable PsiFile createFile(@NotNull Language lang) { ParserDefinition parser = LanguageParserDefinitions.INSTANCE.forLanguage(lang); if (parser == null) { return null; } if (lang == OclLanguage.INSTANCE) { PsiFileImpl file = (PsiFileImpl) parser.createFile(this); file.setContentElementType(TEMPLATE_DATA); return file; } return lang == getBaseLanguage() ? parser.createFile(this) : null; } } ================================================ FILE: src/main/java/com/reason/ide/files/MlyFileViewProviderFactory.java ================================================ package com.reason.ide.files; import com.intellij.lang.*; import com.intellij.openapi.vfs.*; import com.intellij.psi.*; import org.jetbrains.annotations.*; public class MlyFileViewProviderFactory implements FileViewProviderFactory { @Override public @NotNull FileViewProvider createFileViewProvider(@NotNull VirtualFile file, Language language, @NotNull PsiManager manager, boolean eventSystemEnabled) { return new MlyFileViewProvider(manager, file, eventSystemEnabled); } } ================================================ FILE: src/main/java/com/reason/ide/files/ORConfigJsonFileType.java ================================================ package com.reason.ide.files; import com.intellij.json.*; import com.reason.ide.*; import org.jetbrains.annotations.*; import javax.swing.*; public class ORConfigJsonFileType extends JsonFileType { public static final ORConfigJsonFileType INSTANCE = new ORConfigJsonFileType(); // used in plugin.xml @Override public @NotNull String getName() { return "Compiler configuration"; } @Override public @NotNull String getDescription() { return "Compiler configuration file"; } @Override public @Nullable Icon getIcon() { return ORIcons.BUCKLESCRIPT_TOOL; } } ================================================ FILE: src/main/java/com/reason/ide/files/ORTargetElementEvaluator.java ================================================ package com.reason.ide.files; import com.intellij.codeInsight.*; import com.intellij.psi.*; import com.reason.lang.core.psi.*; import org.jetbrains.annotations.*; /** * Allow to target a different element when user navigates to a PsiElement * (see github issue). */ public class ORTargetElementEvaluator extends TargetElementEvaluatorEx2 { @Override public boolean includeSelfInGotoImplementation(@NotNull PsiElement element) { return false; } @Override public @Nullable PsiElement getGotoDeclarationTarget(@NotNull PsiElement element, @Nullable PsiElement navElement) { if (navElement == element && element instanceof RPsiModule module) { if (module.isComponent()) { PsiElement make = module.getMakeFunction(); if (make != null) { return make; } } } return super.getGotoDeclarationTarget(element, navElement); } } ================================================ FILE: src/main/java/com/reason/ide/files/OclFile.java ================================================ package com.reason.ide.files; import com.intellij.openapi.fileTypes.*; import com.intellij.psi.*; import com.reason.lang.ocaml.*; import org.jetbrains.annotations.*; public class OclFile extends FileBase { public OclFile(@NotNull FileViewProvider viewProvider) { super(viewProvider, OclLanguage.INSTANCE); } @Override public @NotNull FileType getFileType() { return OclFileType.INSTANCE; } @Override public @NotNull String toString() { return getName(); } } ================================================ FILE: src/main/java/com/reason/ide/files/OclFileType.java ================================================ package com.reason.ide.files; import com.intellij.openapi.fileTypes.LanguageFileType; import com.reason.lang.ocaml.OclLanguage; import com.reason.ide.ORIcons; import javax.swing.*; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; public class OclFileType extends LanguageFileType { public static final OclFileType INSTANCE = new OclFileType(); private OclFileType() { super(OclLanguage.INSTANCE); } @Override public @NotNull String getName() { return "OCAML"; } @Override public @NotNull String getDescription() { return "OCaml language file"; } @Override public @NotNull String getDefaultExtension() { return "ml"; } @Override public @Nullable Icon getIcon() { return ORIcons.OCL_FILE; } @Override public @NotNull String toString() { return getName(); } } ================================================ FILE: src/main/java/com/reason/ide/files/OclInterfaceFile.java ================================================ package com.reason.ide.files; import com.intellij.openapi.fileTypes.*; import com.intellij.psi.*; import com.reason.lang.ocaml.*; import org.jetbrains.annotations.*; public class OclInterfaceFile extends FileBase { public OclInterfaceFile(@NotNull FileViewProvider viewProvider) { super(viewProvider, OclLanguage.INSTANCE); } @NotNull @Override public FileType getFileType() { return OclInterfaceFileType.INSTANCE; } @NotNull @Override public String toString() { return getName(); } } ================================================ FILE: src/main/java/com/reason/ide/files/OclInterfaceFileType.java ================================================ package com.reason.ide.files; import com.intellij.openapi.fileTypes.*; import com.reason.ide.*; import com.reason.lang.ocaml.*; import org.jetbrains.annotations.*; import javax.swing.*; public class OclInterfaceFileType extends LanguageFileType { public static final OclInterfaceFileType INSTANCE = new OclInterfaceFileType(); private OclInterfaceFileType() { super(OclLanguage.INSTANCE); } @Override public @NotNull String getName() { return "OCAML_INTF"; } @Override public @NotNull @Nls String getDisplayName() { return "OCaml interface"; } @Override public @NotNull String getDescription() { return "OCaml language interface file"; } @Override public @NotNull String getDefaultExtension() { return "mli"; } @Override public @Nullable Icon getIcon() { return ORIcons.OCL_INTERFACE_FILE; } @Override public @NotNull String toString() { return getName(); } } ================================================ FILE: src/main/java/com/reason/ide/files/ResFile.java ================================================ package com.reason.ide.files; import com.intellij.openapi.fileTypes.FileType; import com.intellij.psi.FileViewProvider; import com.reason.lang.rescript.ResLanguage; import org.jetbrains.annotations.NotNull; public class ResFile extends FileBase { public ResFile(@NotNull FileViewProvider viewProvider) { super(viewProvider, ResLanguage.INSTANCE); } @Override public @NotNull FileType getFileType() { return ResFileType.INSTANCE; } @Override public @NotNull String toString() { return getName(); } } ================================================ FILE: src/main/java/com/reason/ide/files/ResFileType.java ================================================ package com.reason.ide.files; import com.intellij.openapi.fileTypes.*; import com.intellij.openapi.fileTypes.ex.*; import com.intellij.openapi.vfs.*; import com.intellij.openapi.vfs.newvfs.*; import com.reason.ide.*; import com.reason.lang.rescript.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; import javax.swing.*; public class ResFileType extends LanguageFileType implements FileTypeIdentifiableByVirtualFile { public static final ResFileType INSTANCE = new ResFileType(); private static final Log LOG = Log.create("fileType"); private ResFileType() { super(ResLanguage.INSTANCE); } @Override public boolean isMyFileType(@NotNull VirtualFile file) { if (!file.isDirectory() && "res".equals(file.getExtension())) { // must protect from .res resources files found in jar files VirtualFileSystem entryFileSystem = file.getFileSystem(); boolean isArchive = entryFileSystem instanceof ArchiveFileSystem; if (LOG.isTraceEnabled()) { LOG.trace("Testing entry file: " + entryFileSystem + ", archive? " + isArchive + " (" + file.getPath() + "/" + file.getName() + ")"); } return !isArchive; } return false; } @Override public @NotNull String getName() { return "RESCRIPT"; } @Override public @NotNull String getDescription() { return "Rescript language file"; } @Override public @NotNull String getDefaultExtension() { return ""; // Can't define an extension, use isMyFileType instead } @Override public @NotNull Icon getIcon() { return ORIcons.RES_FILE; } @Override public @NotNull String toString() { return getName(); } } ================================================ FILE: src/main/java/com/reason/ide/files/ResInterfaceFile.java ================================================ package com.reason.ide.files; import com.intellij.openapi.fileTypes.FileType; import com.intellij.psi.FileViewProvider; import com.reason.lang.rescript.ResLanguage; import org.jetbrains.annotations.NotNull; public class ResInterfaceFile extends FileBase { public ResInterfaceFile(@NotNull FileViewProvider viewProvider) { super(viewProvider, ResLanguage.INSTANCE); } @Override public @NotNull FileType getFileType() { return ResInterfaceFileType.INSTANCE; } @Override public @NotNull String toString() { return getName(); } } ================================================ FILE: src/main/java/com/reason/ide/files/ResInterfaceFileType.java ================================================ package com.reason.ide.files; import com.intellij.openapi.fileTypes.LanguageFileType; import com.reason.lang.rescript.ResLanguage; import com.reason.ide.ORIcons; import javax.swing.*; import org.jetbrains.annotations.*; public class ResInterfaceFileType extends LanguageFileType { public static final ResInterfaceFileType INSTANCE = new ResInterfaceFileType(); private ResInterfaceFileType() { super(ResLanguage.INSTANCE); } @Override public @NotNull String getName() { return "RESCRIPT_INTF"; } @Override public @NotNull @Nls String getDisplayName() { return "Rescript interface"; } @Override public @NotNull String getDescription() { return "Rescript language interface file"; } @Override public @NotNull String getDefaultExtension() { return "resi"; } @Override public @NotNull Icon getIcon() { return ORIcons.RES_INTERFACE_FILE; } @Override public @NotNull String toString() { return getName(); } } ================================================ FILE: src/main/java/com/reason/ide/files/RmlFile.java ================================================ package com.reason.ide.files; import com.intellij.openapi.fileTypes.FileType; import com.intellij.psi.FileViewProvider; import com.reason.lang.reason.RmlLanguage; import org.jetbrains.annotations.NotNull; public class RmlFile extends FileBase { public RmlFile(@NotNull FileViewProvider viewProvider) { super(viewProvider, RmlLanguage.INSTANCE); } @NotNull @Override public FileType getFileType() { return RmlFileType.INSTANCE; } @NotNull @Override public String toString() { return getName(); } } ================================================ FILE: src/main/java/com/reason/ide/files/RmlFileType.java ================================================ package com.reason.ide.files; import com.intellij.openapi.fileTypes.LanguageFileType; import com.reason.lang.reason.RmlLanguage; import com.reason.ide.ORIcons; import javax.swing.*; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; public class RmlFileType extends LanguageFileType { public static final RmlFileType INSTANCE = new RmlFileType(); private RmlFileType() { super(RmlLanguage.INSTANCE); } @NotNull @Override public String getName() { return "REASON"; } @NotNull @Override public String getDescription() { return "Reason language file"; } @NotNull @Override public String getDefaultExtension() { return "re"; } @Nullable @Override public Icon getIcon() { return ORIcons.RML_FILE; } @NotNull @Override public String toString() { return getName(); } } ================================================ FILE: src/main/java/com/reason/ide/files/RmlInterfaceFile.java ================================================ package com.reason.ide.files; import com.intellij.openapi.fileTypes.FileType; import com.intellij.psi.FileViewProvider; import com.reason.lang.reason.RmlLanguage; import org.jetbrains.annotations.NotNull; public class RmlInterfaceFile extends FileBase { public RmlInterfaceFile(@NotNull FileViewProvider viewProvider) { super(viewProvider, RmlLanguage.INSTANCE); } @NotNull @Override public FileType getFileType() { return RmlInterfaceFileType.INSTANCE; } @NotNull @Override public String toString() { return getName(); } } ================================================ FILE: src/main/java/com/reason/ide/files/RmlInterfaceFileType.java ================================================ package com.reason.ide.files; import com.intellij.openapi.fileTypes.*; import com.reason.ide.*; import com.reason.lang.reason.*; import org.jetbrains.annotations.*; import javax.swing.*; public class RmlInterfaceFileType extends LanguageFileType { public static final RmlInterfaceFileType INSTANCE = new RmlInterfaceFileType(); private RmlInterfaceFileType() { super(RmlLanguage.INSTANCE); } @Override public @NotNull String getName() { return "REASON_INTF"; } @Override public @NotNull @Nls String getDisplayName() { return "Reason interface"; } @Override public @NotNull String getDescription() { return "Reason language interface file"; } @Override public @NotNull String getDefaultExtension() { return "rei"; } @Override public @Nullable Icon getIcon() { return ORIcons.RML_INTERFACE_FILE; } @Override public @NotNull String toString() { return getName(); } } ================================================ FILE: src/main/java/com/reason/ide/folding/DuneFoldingBuilder.java ================================================ package com.reason.ide.folding; import com.intellij.lang.*; import com.intellij.lang.folding.*; import com.intellij.openapi.editor.*; import com.intellij.openapi.util.*; import com.intellij.psi.*; import com.intellij.psi.util.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.impl.*; import com.reason.lang.dune.*; import org.jetbrains.annotations.*; import java.util.*; public class DuneFoldingBuilder extends FoldingBuilderEx { @Override public @NotNull FoldingDescriptor[] buildFoldRegions(@NotNull PsiElement root, @NotNull Document document, boolean quick) { List descriptors = new ArrayList<>(); PsiTreeUtil.processElements(root, element -> { if (isMultiline(element.getTextRange(), document)) { FoldingDescriptor fold = null; if (element instanceof RPsiDuneStanza) { fold = foldStanza((RPsiDuneStanza) element); } else if (DuneTypes.INSTANCE.C_SEXPR == element.getNode().getElementType()) { fold = fold(element); } if (fold != null) { descriptors.add(fold); } } return true; }); return descriptors.toArray(new FoldingDescriptor[0]); } private static boolean isMultiline(@NotNull TextRange range, @NotNull Document document) { return document.getLineNumber(range.getStartOffset()) < document.getLineNumber(range.getEndOffset()); } private @Nullable FoldingDescriptor foldStanza(@NotNull RPsiDuneStanza root) { RPsiDuneFields fields = ORUtil.findImmediateFirstChildOfClass(root, RPsiDuneFields.class); return fields == null ? null : new FoldingDescriptor(root.getNode(), fields.getTextRange()); } private @Nullable FoldingDescriptor fold(@Nullable PsiElement root) { if (root == null) { return null; } // find next element ASTNode element = root.getFirstChild().getNode(); ASTNode nextElement = element == null ? null : ORUtil.nextSiblingNode(element); ASTNode nextNextElement = nextElement == null ? null : ORUtil.nextSiblingNode(nextElement); if (nextNextElement != null) { TextRange rootRange = root.getTextRange(); TextRange nextRange = nextElement.getTextRange(); return new FoldingDescriptor( root, TextRange.create(nextRange.getEndOffset(), rootRange.getEndOffset() - 1)); } return null; } @Override public @Nullable String getPlaceholderText(@NotNull ASTNode node) { return "..."; } @Override public boolean isCollapsedByDefault(@NotNull ASTNode node) { return false; } } ================================================ FILE: src/main/java/com/reason/ide/folding/ORFoldingBuilder.java ================================================ package com.reason.ide.folding; import com.intellij.lang.*; import com.intellij.lang.folding.*; import com.intellij.openapi.editor.*; import com.intellij.openapi.util.*; import com.intellij.psi.*; import com.intellij.psi.tree.*; import com.intellij.psi.util.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import com.reason.lang.core.psi.ocamlgrammar.*; import com.reason.lang.core.psi.ocamllex.*; import com.reason.lang.core.psi.ocamlyacc.*; import com.reason.lang.core.type.*; import com.reason.lang.ocaml.*; import com.reason.lang.ocamlgrammar.*; import com.reason.lang.reason.*; import org.jetbrains.annotations.*; import java.util.*; public class ORFoldingBuilder extends FoldingBuilderEx { @Override public @NotNull FoldingDescriptor[] buildFoldRegions(@NotNull PsiElement root, @NotNull Document document, boolean quick) { List descriptors = new ArrayList<>(); ORLangTypes types = ORUtil.getTypes(root.getLanguage()); PsiTreeUtil.processElements(root, element -> { if (element instanceof RPsiLet) { foldLet(descriptors, (RPsiLet) element); } else if (element instanceof RPsiType) { foldType(descriptors, (RPsiType) element); } else if (element instanceof RPsiInnerModule) { foldModule(descriptors, (RPsiInnerModule) element); } else if (element instanceof RPsiFunction) { foldFunction(descriptors, (RPsiFunction) element); } else if (element instanceof RPsiFunctor) { foldFunctor(descriptors, (RPsiFunctor) element); } else if (element instanceof RPsiFunctorResult) { foldFunctorResult(descriptors, (RPsiFunctorResult) element); } else if (element instanceof RPsiTag) { foldTag(descriptors, (RPsiTag) element); } else if (element instanceof RPsiPatternMatch) { foldPatternMatch(descriptors, (RPsiPatternMatch) element); } else if (element instanceof RPsiSwitch) { foldSwitch(descriptors, (RPsiSwitch) element); } else if (element instanceof RPsiTry) { foldTry(descriptors, (RPsiTry) element); } else if (types.MULTI_COMMENT == element.getNode().getElementType()) { FoldingDescriptor fold = fold(element); if (fold != null) { descriptors.add(fold); } } // Lex else if (element instanceof RPsiLexRule || element instanceof RPsiOCamlInjection) { FoldingDescriptor fold = fold(element); if (fold != null) { descriptors.add(fold); } } // Yacc else if (element instanceof RPsiYaccHeader || element instanceof RPsiYaccRuleBody) { FoldingDescriptor fold = fold(element); if (fold != null) { descriptors.add(fold); } } // Grammar else if (element instanceof RPsiGrammarVernac || element instanceof RPsiGrammarTactic || element instanceof RPsiGrammarArgument || element instanceof RPsiGrammarGrammar) { FoldingDescriptor fold = fold(element); if (fold != null) { descriptors.add(fold); } } return true; }); return descriptors.toArray(new FoldingDescriptor[0]); } private void foldLet(@NotNull List descriptors, @NotNull RPsiLet letExpression) { FoldingDescriptor fold = fold(letExpression.getBinding()); if (fold != null) { descriptors.add(fold); } } private void foldType(@NotNull List descriptors, @NotNull RPsiType typeExpression) { PsiElement constrName = ORUtil.findImmediateFirstChildOfClass(typeExpression, RPsiLowerSymbol.class); if (constrName != null) { PsiElement binding = typeExpression.getBinding(); if (binding != null && binding.getTextLength() > 5) { descriptors.add(new FoldingDescriptor(typeExpression, binding.getTextRange())); } } } private void foldModule(@NotNull List descriptors, @NotNull RPsiInnerModule module) { FoldingDescriptor foldSignature = fold(module.getModuleSignature()); if (foldSignature != null) { descriptors.add(foldSignature); } FoldingDescriptor foldBody = fold(module.getBody()); if (foldBody != null) { descriptors.add(foldBody); } } private void foldFunction(@NotNull List descriptors, @NotNull RPsiFunction func) { FoldingDescriptor foldBinding = fold(func.getBody()); if (foldBinding != null) { descriptors.add(foldBinding); } } private void foldFunctor(@NotNull List descriptors, @NotNull RPsiFunctor element) { FoldingDescriptor foldBinding = fold(element.getBody()); if (foldBinding != null) { descriptors.add(foldBinding); } } private void foldFunctorResult(@NotNull List descriptors, @NotNull RPsiFunctorResult element) { FoldingDescriptor foldBinding = fold(element); if (foldBinding != null) { descriptors.add(foldBinding); } } private void foldTag(@NotNull List descriptors, @NotNull RPsiTag element) { RPsiTagStart start = ORUtil.findImmediateFirstChildOfClass(element, RPsiTagStart.class); RPsiTagClose close = start == null ? null : ORUtil.findImmediateFirstChildOfClass(element, RPsiTagClose.class); // Auto-closed tags are not foldable if (close != null) { PsiElement lastChild = start.getLastChild(); TextRange textRange = TextRange.create(lastChild.getTextOffset(), element.getTextRange().getEndOffset() - 1); descriptors.add(new FoldingDescriptor((PsiElement) element, textRange)); } } private void foldSwitch(@NotNull List descriptors, @NotNull RPsiSwitch element) { RPsiBinaryCondition condition = element.getCondition(); if (condition != null) { int startOffset = condition.getTextOffset() + condition.getTextLength() + 1; int endOffset = element.getTextRange().getEndOffset(); if (startOffset < endOffset) { TextRange textRange = TextRange.create(startOffset, endOffset); descriptors.add(new FoldingDescriptor((PsiElement) element, textRange)); } } } private void foldTry(@NotNull List descriptors, @NotNull RPsiTry element) { FoldingDescriptor fold = fold(element.getBody()); if (fold != null) { descriptors.add(fold); } } private void foldPatternMatch(@NotNull List descriptors, @NotNull RPsiPatternMatch element) { FoldingDescriptor fold = fold(element.getBody()); if (fold != null) { descriptors.add(fold); } } @Override public @Nullable String getPlaceholderText(@NotNull ASTNode node) { IElementType elementType = node.getElementType(); if (elementType == RmlTypes.INSTANCE.MULTI_COMMENT) { return "/*...*/"; } else if (elementType == OclTypes.INSTANCE.MULTI_COMMENT) { return "(*...*)"; } else if (elementType == OclTypes.INSTANCE.C_MODULE_SIGNATURE) { return "sig..."; } else if (elementType == OclGrammarTypes.INSTANCE.C_VERNAC) { return "vernac..."; } else if (elementType == OclGrammarTypes.INSTANCE.C_TACTIC) { return "tactic..."; } else if (elementType == OclGrammarTypes.INSTANCE.C_ARGUMENT) { return "argument..."; } else if (elementType == OclGrammarTypes.INSTANCE.C_GRAMMAR) { return "grammar..."; } else if (elementType == OclGrammarTypes.INSTANCE.C_INJECTION) { return "ocaml..."; } return "..."; } @Override public boolean isCollapsedByDefault(@NotNull ASTNode node) { return false; } @Nullable private FoldingDescriptor fold(@Nullable PsiElement element) { if (element == null) { return null; } TextRange textRange = element.getTextRange(); return textRange.getLength() > 5 ? new FoldingDescriptor(element, textRange) : null; } } ================================================ FILE: src/main/java/com/reason/ide/format/FormatterProcessor.java ================================================ package com.reason.ide.format; import org.jetbrains.annotations.*; public interface FormatterProcessor { @Nullable String apply(@NotNull String textToFormat); } ================================================ FILE: src/main/java/com/reason/ide/format/ORPostFormatProcessor.java ================================================ package com.reason.ide.format; import com.intellij.lang.*; import com.intellij.openapi.editor.*; import com.intellij.openapi.fileEditor.*; import com.intellij.openapi.fileTypes.*; import com.intellij.openapi.project.*; import com.intellij.openapi.util.*; import com.intellij.openapi.vfs.*; import com.intellij.psi.*; import com.intellij.psi.codeStyle.*; import com.intellij.psi.impl.source.codeStyle.*; import com.reason.*; import com.reason.comp.bs.*; import com.reason.comp.ocaml.*; import com.reason.comp.rescript.*; import com.reason.ide.*; import com.reason.ide.files.*; import com.reason.ide.settings.*; import com.reason.lang.core.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; /* java invocation: CodeStyleManager manager = project.getService(CodeStyleManager.class); PsiElement reformat = manager.reformat(file); */ /** * This class is called when user action 'reformat' (ctrl+alt+L) is fired, after standard format processor */ // It should be com.intellij.psi.codeStyle.ExternalFormatProcessor, but I can't make it work... public class ORPostFormatProcessor implements PostFormatProcessor { private static final Log LOG = Log.create("format.postprocessor"); @Override public @NotNull PsiElement processElement(@NotNull PsiElement source, @NotNull CodeStyleSettings settings) { FormatterProcessor formatter = source instanceof FileBase ? getFormatterProcessor((PsiFile) source) : null; if (formatter != null) { Language language = source.getLanguage(); LOG.trace("Process element for language", language); String formattedText = formatter.apply(source.getText()); return formattedText == null ? source : ORCodeFactory.createFileFromText(source.getProject(), language, formattedText); } return source; } @Override public @NotNull TextRange processText(@NotNull PsiFile source, @NotNull TextRange rangeToReformat, @NotNull CodeStyleSettings settings) { FormatterProcessor formatter = getFormatterProcessor(source); if (formatter != null) { Language language = source.getLanguage(); PostFormatProcessorHelper postProcessorHelper = new PostFormatProcessorHelper(settings.getCommonSettings(language)); LOG.trace("Process text for language", language); postProcessorHelper.setResultTextRange(rangeToReformat); int oldTextLength = source.getTextLength(); Editor textEditor = FileEditorManager.getInstance(source.getProject()).getSelectedTextEditor(); CaretModel caretModel = textEditor == null ? null : textEditor.getCaretModel(); int caretOffset = caretModel == null ? 0 : caretModel.getOffset(); String formattedText = formatter.apply(source.getText()); if (formattedText != null) { PsiElement formattedElement = ORCodeFactory.createFileFromText(source.getProject(), language, formattedText); CodeEditUtil.removeChildren(source.getNode(), source.getNode().getFirstChildNode(), source.getNode().getLastChildNode()); CodeEditUtil.addChildren(source.getNode(), formattedElement.getNode().getFirstChildNode(), formattedElement.getNode().getLastChildNode(), null); if (caretModel != null) { caretModel.moveToOffset(caretOffset); } int newTextLength = source.getTextLength(); if (newTextLength > oldTextLength) { postProcessorHelper.updateResultRange(oldTextLength, newTextLength); } return postProcessorHelper.getResultTextRange(); } } return rangeToReformat; } public static @Nullable FormatterProcessor getFormatterProcessor(@NotNull PsiFile file) { FileType fileType = file.getFileType(); if (FileHelper.isReason(fileType)) { return new RmlFormatProcessor(file); } if (FileHelper.isRescript(fileType)) { return new ResFormatProcessor(file); } if (FileHelper.isOCaml(fileType)) { return new OclFormatProcessor(file); } return null; } static class RmlFormatProcessor implements FormatterProcessor { private final Project myProject; private final @Nullable VirtualFile myFile; private final boolean myIsInterface; RmlFormatProcessor(@NotNull PsiFile file) { myProject = file.getProject(); myFile = ORFileUtils.getVirtualFile(file); myIsInterface = FileHelper.isInterface(file.getFileType()); } @Override public @Nullable String apply(@NotNull String textToFormat) { if (myProject.getService(ORSettings.class).isBsEnabled() && myFile != null && myFile.exists()) { LOG.trace("Apply ReasonML formatter, is interface", myIsInterface); BsFormatProcess process = myProject.getService(BsFormatProcess.class); return process.convert(myFile, myIsInterface, "re", "re", textToFormat); } return null; } } static class ResFormatProcessor implements FormatterProcessor { private final Project myProject; private final @Nullable VirtualFile myFile; private final boolean myIsInterface; ResFormatProcessor(@NotNull PsiFile file) { myProject = file.getProject(); myFile = ORFileUtils.getVirtualFile(file); myIsInterface = FileHelper.isInterface(file.getFileType()); } @Override public @Nullable String apply(@NotNull String textToFormat) { if (myFile != null && myFile.exists()) { LOG.trace("Apply Rescript formatter, is interface", myIsInterface); ResFormatProcess process = myProject.getService(ResFormatProcess.class); return process.format(myFile, myIsInterface, textToFormat); } return null; } } private static class OclFormatProcessor implements FormatterProcessor { private final Project myProject; private final @Nullable VirtualFile myFile; public OclFormatProcessor(@NotNull PsiFile file) { myProject = file.getProject(); myFile = ORFileUtils.getVirtualFile(file); } @Override public @Nullable String apply(@NotNull String textToFormat) { if (myFile != null && myFile.exists()) { LOG.trace("Apply OCaml formatter"); OcamlFormatProcess process = myProject.getService(OcamlFormatProcess.class); return process == null ? null : process.format(myFile, textToFormat); } return null; } } } ================================================ FILE: src/main/java/com/reason/ide/format/ReformatOnSave.java ================================================ package com.reason.ide.format; import com.intellij.openapi.application.*; import com.intellij.openapi.application.ex.*; import com.intellij.openapi.command.*; import com.intellij.openapi.editor.*; import com.intellij.openapi.fileEditor.*; import com.intellij.openapi.project.*; import com.intellij.openapi.roots.*; import com.intellij.openapi.util.*; import com.intellij.openapi.vfs.*; import com.intellij.psi.*; import com.reason.ide.*; import com.reason.ide.settings.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; /** * IntelliJ doesn't offer an easy way to change document text before it is saved. *

* When we detect that a document is about to be saved, we run an async write command * on the main thread that will change the edited document. * This writes action is run after the initial save (We also use a command to have undo). * Then we save the document again, ie we will always generate a minimum of 2 document saves. */ public class ReformatOnSave { private static final Key REFORMAT_COUNT = new Key<>("ReasonML.format.count"); private static final Log LOG = Log.create("format.auto"); private ReformatOnSave() { } public static void apply(@NotNull Project project, @NotNull Document document) { ORSettings settings = project.getService(ORSettings.class); if (settings.isFormatOnSaveEnabled()) { PsiFile psiFile = PsiDocumentManager.getInstance(project).getPsiFile(document); if (psiFile != null && psiFile.isWritable()) { VirtualFile virtualFile = ORFileUtils.getVirtualFile(psiFile); if (virtualFile != null && virtualFile.exists()) { ProjectFileIndex projectFileIndex = ProjectRootManager.getInstance(project).getFileIndex(); if (!projectFileIndex.isInContent(virtualFile)) { if (LOG.isTraceEnabled()) { LOG.trace("File " + virtualFile + " not in content root of project " + project + ", skip"); } return; } Integer psiCount = psiFile.getUserData(REFORMAT_COUNT); int count = psiCount == null ? 1 : psiCount; if (LOG.isDebugEnabled()) { LOG.debug("Before document saving (" + project.getName() + ", autoSave=true, count=" + count + "): " + virtualFile); } if (count > 2) { LOG.warn(" -> Too many saves (" + count + "), auto reformat is cancelled"); psiFile.putUserData(REFORMAT_COUNT, 1); return; } psiFile.putUserData(REFORMAT_COUNT, count + 1); ApplicationManagerEx.getApplicationEx() .invokeLater(() -> WriteAction.run(() -> { if (!project.isDisposed()) { PsiFile newFile = PsiDocumentManager.getInstance(project).getPsiFile(document); if (newFile != null) { String textToReformat = document.getText(); FormatterProcessor formatterProcessor = ORPostFormatProcessor.getFormatterProcessor(newFile); String newText = formatterProcessor == null ? textToReformat : formatterProcessor.apply(textToReformat); if (newText == null || textToReformat.equals(newText)) { LOG.debug(" -> Text null or unchanged, abort format"); newFile.putUserData(REFORMAT_COUNT, 1); } else { CommandProcessor.getInstance().runUndoTransparentAction(() -> { LOG.debug(" -> Applying text formatting"); document.setText(newText); }); if (count == 1) { // Only re-save first time, to avoid infinite loop FileDocumentManager.getInstance().saveDocument(document); } } } } })); } } } } } ================================================ FILE: src/main/java/com/reason/ide/go/FileModuleDataModuleRendererFactory.java ================================================ package com.reason.ide.go; import com.intellij.ide.util.*; import com.intellij.util.*; import org.jetbrains.annotations.*; public class FileModuleDataModuleRendererFactory extends ModuleRendererFactory { @Override protected boolean handles(Object element) { return element instanceof ORModuleContributor.FileModuleDataNavigationItem; } @Override public @Nullable TextWithIcon getModuleTextWithIcon(Object element) { ORModuleContributor.FileModuleDataNavigationItem dataNavigation = (ORModuleContributor.FileModuleDataNavigationItem) element; return new TextWithIcon(dataNavigation.getLocation(), dataNavigation.getLocationIcon()); } } ================================================ FILE: src/main/java/com/reason/ide/go/ORLineMarkerProvider.java ================================================ package com.reason.ide.go; import com.intellij.codeInsight.daemon.*; import com.intellij.codeInsight.navigation.*; import com.intellij.codeInsight.navigation.impl.*; import com.intellij.openapi.editor.markup.*; import com.intellij.openapi.project.*; import com.intellij.platform.backend.presentation.*; import com.intellij.psi.*; import com.intellij.psi.search.*; import com.intellij.psi.util.*; import com.intellij.util.*; import com.reason.ide.*; import com.reason.ide.files.*; import com.reason.ide.search.index.*; import com.reason.ide.search.reference.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import com.reason.lang.ocaml.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; import javax.swing.*; import java.util.*; import java.util.stream.*; import static java.util.Collections.*; public class ORLineMarkerProvider extends RelatedItemLineMarkerProvider { private static final Log LOG = Log.create("goto"); @Override protected void collectNavigationMarkers(@NotNull PsiElement element, @NotNull Collection> result) { Project project = element.getProject(); GlobalSearchScope scope = GlobalSearchScope.allScope(project); LOG.trace("collectNavigationMarkers", element, project, scope); if (element instanceof RPsiUpperSymbol uSymbolElement) { PsiElement parentElement = uSymbolElement.getParent(); if (parentElement instanceof RPsiInnerModule moduleElement) { collectInnerModuleNavigationMarkers(moduleElement, project, scope, result); } else if (parentElement instanceof RPsiException exceptionElement) { collectExceptionNavigationMarkers(exceptionElement, project, scope, result); } } // else if (element instanceof RPsiLowerSymbol lSymbolElement) { PsiElement parentElement = lSymbolElement.getParent(); if (parentElement instanceof RPsiVal valElement) { collectValNavigationMarkers(valElement, project, scope, result); } else if (parentElement instanceof RPsiLet letElement) { collectLetNavigationMarkers(letElement, project, scope, result); } else if (parentElement instanceof RPsiType typeElement) { collectTypeNavigationMarkers(typeElement, project, scope, result); } else if (parentElement instanceof RPsiExternal externalElement) { collectExternalNavigationMarkers(externalElement, project, scope, result); } else if (parentElement instanceof RPsiClass classElement) { collectClassNavigationMarkers(classElement, project, scope, result); } else if (parentElement instanceof RPsiClassMethodImpl methodElement) { collectClassMethodNavigationMarkers(methodElement, project, scope, result); } } } private void collectLetNavigationMarkers(@NotNull RPsiLet element, @NotNull Project project, @NotNull GlobalSearchScope scope, @NotNull Collection> result) { LOG.debug("collect_LET_NavigationMarkers", element); List implementedTargets = new ArrayList<>(); List implementingTargets = new ArrayList<>(); RPsiModule parentModule = PsiTreeUtil.getStubOrPsiParentOfType(element, RPsiModule.class); boolean isInterfaceFile = ORUtil.isInterfaceFile(element); boolean isOcaml = element.getLanguage() == OclLanguage.INSTANCE; if (parentModule instanceof RPsiInnerModule innerModule) { LOG.trace(" from inner module", innerModule); String letName = element.getName(); if (letName != null) { if (element.getParent() instanceof RPsiModuleSignature) { // The let is inside an inline signature of an inner module, implementedTargets.addAll(PsiTreeUtil.getChildrenOfTypeAsList(parentModule.getBody(), RPsiVar.class) .stream().filter(var -> letName.equals(var.getName())) .toList()); // Check if there is a module signature with same name than the module in an interface String qName = parentModule.getQualifiedName(); List modules = qName != null ? ModuleFqnIndex.getElements(qName, project, scope) .stream().map(m -> m instanceof RPsiInnerModule ? (RPsiInnerModule) m : null) .filter(Objects::nonNull) .filter(m -> m != parentModule && !m.isModuleType()).toList() : List.of(); List targets = isInterfaceFile ? implementedTargets : implementingTargets; for (RPsiInnerModule module : modules) { List vars = new ArrayList<>(); // Vars in signature vars.addAll(PsiTreeUtil.getChildrenOfTypeAsList(module.getModuleSignature(), RPsiVar.class) .stream().filter(var -> letName.equals(var.getName())).toList()); // Vars in body vars.addAll(PsiTreeUtil.getChildrenOfTypeAsList(module.getBody(), RPsiVar.class) .stream().filter(var -> letName.equals(var.getName())).toList()); // Need special marker targets.addAll(vars); } } else if (innerModule.isModuleType()) { // Check if there is a module type in interface/implementation file String parentQName = parentModule.getQualifiedName(); List modules = parentQName != null ? ModuleFqnIndex.getElements(parentQName, project, scope) .stream().map(m -> m instanceof RPsiInnerModule ? (RPsiInnerModule) m : null) .filter(Objects::nonNull) .filter(m -> m != parentModule && m.isModuleType()) .toList() : List.of(); // Vars in body List targets = isInterfaceFile ? implementedTargets : implementingTargets; for (RPsiInnerModule module : modules) { List vars = new ArrayList<>(PsiTreeUtil.getChildrenOfTypeAsList(module.getBody(), RPsiVar.class) .stream().filter(var -> letName.equals(var.getName())).toList()); targets.addAll(vars); } // Look for the corresponding implementation String qName = innerModule.getQualifiedName(); List moduleImplementations = findModulesUsingSignatureName(innerModule.getModuleName(), qName, project, scope); for (RPsiInnerModule moduleImplementation : moduleImplementations) { RPsiModuleSignature moduleSignature = moduleImplementation.getModuleSignature(); PsiElement moduleBlock = moduleSignature != null && moduleSignature.getNameIdentifier() == null ? moduleSignature : moduleImplementation.getBody(); List vars = PsiTreeUtil.getChildrenOfTypeAsList(moduleBlock, RPsiVar.class).stream().filter(var -> letName.equals(var.getName())).toList(); implementedTargets.addAll(vars); } } else { // `let` is inside an inner module body RPsiModuleSignature moduleSignature = ((RPsiInnerModule) parentModule).getModuleSignature(); if (moduleSignature != null && moduleSignature.getNameIdentifier() == null) { // Inline signature implementingTargets.addAll(PsiTreeUtil.getChildrenOfTypeAsList(moduleSignature, RPsiVar.class).stream().filter(var -> letName.equals(var.getName())).toList()); // Check if there is a module signature with same name than the module in an interface String qName = parentModule.getQualifiedName(); List modules = qName != null ? ModuleFqnIndex.getElements(qName, project, scope) .stream().map(m -> m instanceof RPsiInnerModule ? (RPsiInnerModule) m : null) .filter(Objects::nonNull) .filter(m -> m != parentModule && !m.isModuleType()) .toList() : List.of(); List targets = isInterfaceFile ? implementedTargets : implementingTargets; for (RPsiInnerModule module : modules) { // Vars in signature targets.addAll(PsiTreeUtil.getChildrenOfTypeAsList(module.getModuleSignature(), RPsiVar.class) .stream().filter(var -> letName.equals(var.getName())).toList()); // Vars in body targets.addAll(PsiTreeUtil.getChildrenOfTypeAsList(module.getBody(), RPsiVar.class) .stream().filter(var -> letName.equals(var.getName())).toList()); } } else { boolean inInterface = ORUtil.inInterface(element); List targets = inInterface ? implementedTargets : implementingTargets; targets.addAll(inInterface ? findTargetFromInterfaceModule(innerModule, letName, RPsiVar.class, scope) : findTargetFromImplementationModule(innerModule, letName, RPsiVar.class, project, scope)); } } } } else if (parentModule != null) { LOG.trace(" from file module", parentModule); // Top module navigation String qName = element.getQualifiedName(); Collection resolvedElements; boolean inInterface = ORUtil.inInterface(element); List targets = inInterface ? implementedTargets : implementingTargets; if (isOcaml && !inInterface) { resolvedElements = qName == null ? null : ValFqnIndex.getElements(qName, project, scope); } else { resolvedElements = qName == null ? null : LetFqnIndex.getElements(qName, project, scope); } targets.addAll(resolveTargetFromIndex(inInterface, resolvedElements)); } createMarkerInfo(result, element, "let/val", implementedTargets, implementingTargets); } private void collectValNavigationMarkers(@NotNull RPsiVal element, @NotNull Project project, @NotNull GlobalSearchScope scope, @NotNull Collection> result) { List implementedTargets = new ArrayList<>(); List implementingTargets = new ArrayList<>(); RPsiModule parentModule = PsiTreeUtil.getStubOrPsiParentOfType(element, RPsiModule.class); boolean isInterfaceFile = ORUtil.isInterfaceFile(element); if (parentModule instanceof RPsiInnerModule innerModule) { String valName = element.getName(); if (valName != null) { if (element.getParent() instanceof RPsiSignature) { // The val is inside an inline signature of an inner module, implementedTargets.addAll(PsiTreeUtil.getChildrenOfTypeAsList(parentModule.getBody(), RPsiVar.class) .stream().filter(var -> valName.equals(var.getName())) .toList()); // Check if there is a module signature with same name than the module in an interface String qName = parentModule.getQualifiedName(); List modules = qName != null ? ModuleFqnIndex.getElements(qName, project, scope) .stream().map(m -> m instanceof RPsiInnerModule ? (RPsiInnerModule) m : null) .filter(Objects::nonNull) .filter(m -> m != parentModule && !m.isModuleType()).toList() : List.of(); List targets = isInterfaceFile ? implementedTargets : implementingTargets; for (RPsiInnerModule module : modules) { List vars = new ArrayList<>(); // Vars in signature vars.addAll(PsiTreeUtil.getChildrenOfTypeAsList(module.getModuleSignature(), RPsiVar.class) .stream().filter(var -> valName.equals(var.getName())).toList()); // Vars in body vars.addAll(PsiTreeUtil.getChildrenOfTypeAsList(module.getBody(), RPsiVar.class) .stream().filter(var -> valName.equals(var.getName())).toList()); // Need special marker targets.addAll(vars); } } else if (innerModule.isModuleType()) { // Val is inside a module type // Check if there is a module type in interface/implementation file String parentQName = parentModule.getQualifiedName(); List modules = parentQName != null ? ModuleFqnIndex.getElements(parentQName, project, scope) .stream().map(m -> m instanceof RPsiInnerModule ? (RPsiInnerModule) m : null) .filter(Objects::nonNull) .filter(m -> m != parentModule && m.isModuleType()) .toList() : List.of(); // Vars in body List targets = isInterfaceFile ? implementedTargets : implementingTargets; for (RPsiInnerModule module : modules) { List vars = new ArrayList<>(PsiTreeUtil.getChildrenOfTypeAsList(module.getBody(), RPsiVar.class) .stream().filter(var -> valName.equals(var.getName())).toList()); targets.addAll(vars); } // Look for the corresponding implementation String qName = innerModule.getQualifiedName(); List moduleImplementations = findModulesUsingSignatureName(innerModule.getModuleName(), qName, project, scope); for (RPsiInnerModule moduleImplementation : moduleImplementations) { RPsiModuleSignature moduleSignature = moduleImplementation.getModuleSignature(); PsiElement moduleBlock = moduleSignature != null && moduleSignature.getNameIdentifier() == null ? moduleSignature : moduleImplementation.getBody(); List vars = PsiTreeUtil.getChildrenOfTypeAsList(moduleBlock, RPsiVar.class).stream().filter(var -> valName.equals(var.getName())).toList(); implementedTargets.addAll(vars); } } else { boolean isImplements = innerModule.isModuleType() || ORUtil.inInterface(element); List targets = isImplements ? implementedTargets : implementingTargets; targets.addAll(isImplements ? findTargetFromInterfaceModule(innerModule, valName, RPsiVar.class, scope) : findTargetFromImplementationModule(innerModule, valName, RPsiVar.class, project, scope)); } } } else if (parentModule != null) { // Top module navigation String qName = element.getQualifiedName(); Collection resolvedElements = qName == null ? null : LetFqnIndex.getElements(qName, project, scope); List targets = isInterfaceFile ? implementedTargets : implementingTargets; targets.addAll(resolveTargetFromIndex(isInterfaceFile, resolvedElements)); } createMarkerInfo(result, element, "let/val", implementedTargets, implementingTargets); } private void collectTypeNavigationMarkers(@NotNull RPsiType element, @NotNull Project project, @NotNull GlobalSearchScope scope, @NotNull Collection> result) { List implementedTargets = new ArrayList<>(); List implementingTargets = new ArrayList<>(); RPsiModule parentModule = PsiTreeUtil.getStubOrPsiParentOfType(element, RPsiModule.class); boolean inInterface = ORUtil.inInterface(element); if (parentModule instanceof RPsiInnerModule innerModule) { inInterface = innerModule.isModuleType() || inInterface; String typeName = element.getName(); if (typeName != null) { List targets = inInterface ? implementedTargets : implementingTargets; targets.addAll(inInterface ? findTargetFromInterfaceModule(innerModule, typeName, RPsiType.class, scope) : findTargetFromImplementationModule(innerModule, typeName, RPsiType.class, project, scope)); } } else if (parentModule != null) { // Top module navigation String qName = element.getQualifiedName(); Collection resolvedElements = qName == null ? null : TypeFqnIndex.getElements(qName, project, scope); List targets = inInterface ? implementedTargets : implementingTargets; targets.addAll(resolveTargetFromIndex(inInterface, resolvedElements)); } createMarkerInfo(result, element, "type", implementedTargets, implementingTargets); } private void collectExternalNavigationMarkers(@NotNull RPsiExternal element, @NotNull Project project, @NotNull GlobalSearchScope scope, @NotNull Collection> result) { List implementedTargets = new ArrayList<>(); List implementingTargets = new ArrayList<>(); RPsiModule parentModule = PsiTreeUtil.getStubOrPsiParentOfType(element, RPsiModule.class); boolean inInterface = ORUtil.inInterface(element); if (parentModule instanceof RPsiInnerModule innerModule) { inInterface = innerModule.isModuleType() || inInterface; String elementName = element.getName(); if (elementName != null) { List targets = inInterface ? implementedTargets : implementingTargets; targets.addAll(inInterface ? findTargetFromInterfaceModule(innerModule, elementName, RPsiExternal.class, scope) : findTargetFromImplementationModule(innerModule, elementName, RPsiExternal.class, project, scope)); } } else { // Top module navigation String qName = element.getQualifiedName(); Collection resolvedElements = qName == null ? null : ExternalFqnIndex.getElements(qName, project, scope); List targets = inInterface ? implementedTargets : implementingTargets; targets.addAll(resolveTargetFromIndex(inInterface, resolvedElements)); } createMarkerInfo(result, element, "external", implementedTargets, implementingTargets); } private void collectClassNavigationMarkers(@NotNull RPsiClass element, @NotNull Project project, @NotNull GlobalSearchScope scope, @NotNull Collection> result) { List implementedTargets = new ArrayList<>(); List implementingTargets = new ArrayList<>(); RPsiModule parentModule = PsiTreeUtil.getStubOrPsiParentOfType(element, RPsiModule.class); if (parentModule instanceof RPsiInnerModule innerModule) { String elementName = element.getName(); if (elementName != null) { List targets = innerModule.isModuleType() ? implementedTargets : implementingTargets; targets.addAll(innerModule.isModuleType() ? findTargetFromInterfaceModule(innerModule, elementName, RPsiClass.class, scope) : findTargetFromImplementationModule(innerModule, elementName, RPsiClass.class, project, scope)); } } else { // Top module navigation String qName = element.getQualifiedName(); Collection resolvedElements = qName == null ? null : ClassFqnIndex.getElements(qName, project, scope); boolean isInterface = ORUtil.isInterfaceFile(element); List targets = isInterface ? implementedTargets : implementingTargets; targets.addAll(resolveTargetFromIndex(isInterface, resolvedElements)); } createMarkerInfo(result, element, "class", implementedTargets, implementingTargets); } private void collectClassMethodNavigationMarkers(@NotNull RPsiClassMethod element, @NotNull Project project, @NotNull GlobalSearchScope scope, @NotNull Collection> result) { List implementedTargets = new ArrayList<>(); List implementingTargets = new ArrayList<>(); RPsiModule parentModule = PsiTreeUtil.getStubOrPsiParentOfType(element, RPsiModule.class); boolean inInterface = ORUtil.inInterface(element); List targets = inInterface ? implementedTargets : implementingTargets; if (parentModule instanceof RPsiInnerModule innerModule) { String elementName = element.getName(); if (elementName != null) { targets.addAll(inInterface ? findTargetFromInterfaceModule(innerModule, elementName, RPsiClassMethod.class, scope) : findTargetFromImplementationModule(innerModule, elementName, RPsiClassMethod.class, project, scope)); } } else { // Top module navigation String qName = element.getQualifiedName(); Collection resolvedElements = qName != null ? ClassMethodFqnIndex.getElements(qName, project, scope).stream().filter(m -> ORUtil.inInterface(m) != inInterface).toList() : null; if (resolvedElements != null) { targets.addAll(resolvedElements); } } createMarkerInfo(result, element, "method", implementedTargets, implementingTargets); } private void collectExceptionNavigationMarkers(@NotNull RPsiException element, @NotNull Project project, @NotNull GlobalSearchScope scope, @NotNull Collection> result) { List implementedTargets = new ArrayList<>(); List implementingTargets = new ArrayList<>(); RPsiModule parentModule = PsiTreeUtil.getStubOrPsiParentOfType(element, RPsiModule.class); boolean inInterface = ORUtil.inInterface(element); if (parentModule instanceof RPsiInnerModule innerModule) { inInterface = innerModule.isModuleType(); String elementName = element.getName(); if (elementName != null) { List targets = inInterface ? implementedTargets : implementingTargets; targets.addAll(inInterface ? findTargetFromInterfaceModule(innerModule, elementName, RPsiException.class, scope) : findTargetFromImplementationModule(innerModule, elementName, RPsiException.class, project, scope)); } } else { // Top module navigation String qName = element.getQualifiedName(); Collection resolvedElements = qName == null ? null : ExceptionFqnIndex.getElements(qName, project, scope); List targets = inInterface ? implementedTargets : implementingTargets; targets.addAll(resolveTargetFromIndex(inInterface, resolvedElements)); } createMarkerInfo(result, element, "exception", implementedTargets, implementingTargets); } private void collectInnerModuleNavigationMarkers(@NotNull RPsiInnerModule element, @NotNull Project project, @NotNull GlobalSearchScope scope, @NotNull Collection> result) { List implementedTargets = new ArrayList<>(); List implementingTargets = new ArrayList<>(); String qName = element.getQualifiedName(); boolean isModuleType = element.isModuleType(); // A module type define a signature if (isModuleType) { String signatureName = element.getModuleName(); if (signatureName != null) { // Find module(s) that use the interface as a result List signatureModules = findModulesUsingSignatureName(signatureName, qName, project, scope); implementedTargets.addAll(signatureModules); } } else { RPsiModuleSignature moduleSignature = element.getModuleSignature(); RPsiUpperSymbol signatureIdentifier = moduleSignature != null ? moduleSignature.getNameIdentifier() : null; if (signatureIdentifier != null) { // Module is implementing a named signature (module type), we need to find its definition PsiElement resolvedElement = signatureIdentifier.getReference().resolveInterface(); if (resolvedElement instanceof RPsiInnerModule resolvedModule) { implementingTargets.add(resolvedModule); } } } // Find module(s) in the related file Collection modules = qName != null ? ModuleFqnIndex.getElements(qName, project, scope) : null; if (modules != null) { boolean isInterfaceFile = ORUtil.isInterfaceFile(element); List relatedModules = modules.stream() .map(m -> m instanceof RPsiInnerModule ? (RPsiInnerModule) m : null) .filter(Objects::nonNull) .filter(m -> ORUtil.isInterfaceFile(m) != isInterfaceFile && m.isModuleType() == isModuleType) .toList(); if (isInterfaceFile) { implementedTargets.addAll(relatedModules); } else { implementingTargets.addAll(relatedModules); } } createMarkerInfo(result, element, "module", implementedTargets, implementingTargets); } private static @NotNull List findModulesUsingSignatureName(@Nullable String name, @Nullable String qName, @NotNull Project project, @NotNull GlobalSearchScope scope) { if (name == null || qName == null) { return List.of(); } return ModuleSignatureIndex.getElements(name, project, scope) .stream() .map(m -> { RPsiInnerModule result = null; if (m instanceof RPsiInnerModule innerModule) { RPsiModuleSignature moduleSignature = innerModule.getModuleSignature(); RPsiUpperSymbol moduleSignatureIdentifier = moduleSignature != null ? moduleSignature.getNameIdentifier() : null; ORPsiUpperSymbolReference reference = moduleSignatureIdentifier != null ? moduleSignatureIdentifier.getReference() : null; PsiElement resolvedSignature = reference != null ? reference.resolve() : null; if (resolvedSignature instanceof RPsiModule resolvedSignatureModule) { String sigQName = resolvedSignatureModule.getQualifiedName(); result = sigQName != null && sigQName.equals(qName) ? innerModule : null; } } return result; }) .filter(Objects::nonNull) .toList(); } private static @NotNull List resolveTargetFromIndex(boolean inInterfaceFile, @Nullable Collection resolvedElements) { if (resolvedElements != null) { for (T resolvedElement : resolvedElements) { RPsiModule targetModule = PsiTreeUtil.getStubOrPsiParentOfType(resolvedElement, RPsiModule.class); boolean targetInterface = ORUtil.isInterfaceFile(targetModule); if (inInterfaceFile && !targetInterface) { return singletonList(resolvedElement); } else if (!inInterfaceFile && targetInterface) { return singletonList(resolvedElement); } } } return emptyList(); } private @NotNull List findTargetFromImplementationModule(@NotNull RPsiInnerModule sourceModule, @NotNull String elementName, @NotNull Class expectedClass, @NotNull Project project, @NotNull GlobalSearchScope scope) { RPsiModuleSignature sourceModuleSignature = sourceModule.getModuleSignature(); RPsiUpperSymbol sourceSignatureIdentifier = sourceModuleSignature != null ? sourceModuleSignature.getNameIdentifier() : null; if (sourceSignatureIdentifier != null) { PsiElement resolvedElement = sourceSignatureIdentifier.getReference().resolveInterface(); if (resolvedElement instanceof RPsiInnerModule resolvedModule) { List result = new ArrayList<>(); // if the resolved module is inside an implementation file, we need to look for same definition in the interface file String resolvedQName = resolvedModule.getQualifiedName(); List modules = resolvedQName != null ? ModuleFqnIndex.getElements(resolvedQName, project, scope) .stream().map(m -> m instanceof RPsiInnerModule ? (RPsiInnerModule) m : null) .filter(m -> m != resolvedModule && m != null).toList() : List.of(); for (RPsiInnerModule module : modules) { result.addAll(PsiTreeUtil.getChildrenOfTypeAsList(module.getBody(), expectedClass).stream().filter(i -> elementName.equals(i.getName())).toList()); } result.addAll(PsiTreeUtil.getChildrenOfTypeAsList(resolvedModule.getBody(), expectedClass).stream().filter(i -> elementName.equals(i.getName())).toList()); return result; } } return emptyList(); } private @NotNull List findTargetFromInterfaceModule(@NotNull RPsiInnerModule sourceModule, @NotNull String elementName, @NotNull Class expectedClass, @Nullable GlobalSearchScope scope) { // Find all modules that return that type name String interfaceQName = sourceModule.getQualifiedName(); String interfaceName = sourceModule.getModuleName(); List refModules = interfaceName != null ? ModuleSignatureIndex.getElements(interfaceName, sourceModule.getProject(), scope).stream().toList() : emptyList(); List targetModules = refModules.stream() .filter(m -> m instanceof RPsiInnerModule) .map(module -> { RPsiModuleSignature moduleSignature = ((RPsiInnerModule) module).getModuleSignature(); RPsiUpperSymbol moduleSignatureIdentifier = moduleSignature != null ? moduleSignature.getNameIdentifier() : null; ORPsiUpperSymbolReference reference = moduleSignatureIdentifier != null ? moduleSignatureIdentifier.getReference() : null; PsiElement resolvedSignature = reference != null ? reference.resolve() : null; if (resolvedSignature instanceof RPsiModule resolvedSignatureModule) { String sigQName = resolvedSignatureModule.getQualifiedName(); return sigQName != null && sigQName.equals(interfaceQName) ? module : null; } return null; }) .filter(Objects::nonNull) .toList(); if (!targetModules.isEmpty()) { // Iterate over potential modules to find the correct ones return targetModules.stream().map(module -> { for (T targetElement : PsiTreeUtil.getChildrenOfTypeAsList(module.getBody(), expectedClass)) { if (elementName.equals(targetElement.getName())) { return targetElement; } } return null; }) .filter(Objects::nonNull) .collect(Collectors.toList()); } return emptyList(); } private void createMarkerInfo(@NotNull Collection> result, @NotNull PsiNameIdentifierOwner psiSource, @NotNull String method, @NotNull Collection implementedTargets, @NotNull Collection implementingTargets) { PsiElement nameIdentifier = psiSource.getNameIdentifier(); if (nameIdentifier != null && !(implementedTargets.isEmpty() && implementingTargets.isEmpty())) { PresentationRenderer renderer = new PresentationRenderer(psiSource); if (!implementedTargets.isEmpty()) { // Prefer source items unless there are none List sourceImplementedTargets = implementedTargets.stream().filter(Platform::isElementInSourceContent).toList(); Collection targets = sourceImplementedTargets.isEmpty() ? implementedTargets : sourceImplementedTargets; result.add(NavigationGutterIconBuilder.create(ORIcons.IMPLEMENTED) .setAlignment(GutterIconRenderer.Alignment.RIGHT) .setTargetRenderer(() -> renderer) .setTooltipText("Implements " + method) .setTargets(targets) .createLineMarkerInfo(nameIdentifier)); } if (!implementingTargets.isEmpty()) { // Prefer source items unless there are none List sourceImplementingTargets = implementingTargets.stream().filter(Platform::isElementInSourceContent).toList(); Collection targets = sourceImplementingTargets.isEmpty() ? implementingTargets : sourceImplementingTargets; result.add(NavigationGutterIconBuilder.create(ORIcons.IMPLEMENTING) .setAlignment(GutterIconRenderer.Alignment.RIGHT) .setTargetRenderer(() -> renderer) .setTooltipText("Declare " + method) .setTargets(targets) .createLineMarkerInfo(nameIdentifier)); } } } static class PresentationRenderer extends PsiTargetPresentationRenderer { private final PsiNameIdentifierOwner mySource; public PresentationRenderer(PsiNameIdentifierOwner psiSource) { mySource = psiSource; } @Override @SuppressWarnings("UnstableApiUsage") public @NotNull TargetPresentation getPresentation(@NotNull PsiElement element) { if (element instanceof PsiQualifiedNamedElement namedElement) { String name = namedElement.getName(); PsiFile elementFile = name != null ? namedElement.getContainingFile() : null; if (elementFile instanceof FileBase elementBaseFile) { boolean sameFile = mySource.getContainingFile() == elementFile; Icon locationIcon = elementBaseFile.isInterface() ? ORIcons.INNER_MODULE_INTF : ORIcons.INNER_MODULE; Icon icon = PsiIconUtil.getIconFromProviders(element, 0); boolean inSignature = PsiTreeUtil.getStubOrPsiParentOfType(element, RPsiSignature.class) != null; return new TargetPresentationBuilderImpl(null, icon, name, null, inSignature ? "sig" : null, null, sameFile ? null : elementBaseFile.getName(), sameFile ? null : locationIcon).presentation(); } } return super.getPresentation(element); } } } ================================================ FILE: src/main/java/com/reason/ide/go/ORModuleContributor.java ================================================ package com.reason.ide.go; import com.intellij.navigation.*; import com.intellij.openapi.project.*; import com.intellij.psi.*; import com.intellij.psi.search.*; import com.intellij.psi.stubs.*; import com.intellij.util.*; import com.intellij.util.indexing.*; import com.reason.*; import com.reason.ide.*; import com.reason.ide.files.*; import com.reason.ide.search.*; import com.reason.ide.search.index.*; import com.reason.lang.core.psi.*; import org.jetbrains.annotations.*; import javax.swing.*; import java.util.*; import static com.intellij.openapi.application.ApplicationManager.*; // Implements the goto class public class ORModuleContributor implements GotoClassContributor, ChooseByNameContributorEx { @Override public void processNames(@NotNull Processor processor, @NotNull GlobalSearchScope scope, @Nullable IdFilter filter) { Project project = scope.getProject(); if (project != null) { List keys = new ArrayList<>(); keys.addAll(FileModuleIndexService.getInstance().getAllKeys(project)); keys.addAll(StubIndex.getInstance().getAllKeys(IndexKeys.MODULES, project)); for (String key : keys) { processor.process(key); } } } @Override public void processElementsWithName(@NotNull String name, @NotNull Processor processor, @NotNull FindSymbolParameters parameters) { Project project = parameters.getProject(); GlobalSearchScope scope = GlobalSearchScope.allScope(project); // Top level modules FileModuleIndexService fileModuleIndexService = getApplication().getService(FileModuleIndexService.class); for (FileModuleData moduleDatum : fileModuleIndexService.getTopModuleData(name, scope)) { processor.process(new FileModuleDataNavigationItem(moduleDatum, project) ); } // Inner modules for (RPsiModule module : ModuleIndex.getElements(name, project, scope)) { if (module instanceof NavigationItem) { processor.process((NavigationItem) module); } } } @Override public @Nullable String getQualifiedName(@NotNull NavigationItem item) { if (item instanceof FileBase) { return ((FileBase) item).getModuleName(); } else if (item instanceof PsiQualifiedNamedElement) { return ((PsiQualifiedNamedElement) item).getQualifiedName(); } return null; } @Override public @Nullable String getQualifiedNameSeparator() { return null; } private static class FileModuleDataPresentation implements ItemPresentation { private final FileModuleData myItem; public FileModuleDataPresentation(@NotNull FileModuleData moduleDatum) { myItem = moduleDatum; } @Override public @Nullable String getPresentableText() { return myItem.getModuleName(); } @Override public @Nullable String getLocationString() { return null; } @Override public Icon getIcon(boolean unused) { return IconProvider.getDataModuleIcon(myItem); } } public static class FileModuleDataNavigationItem implements NavigationItem { private final FileModuleData myData; private final Project myProject; public FileModuleDataNavigationItem(FileModuleData moduleDatum, Project project) { myData = moduleDatum; myProject = project; } @Override public String getName() { return myData.getModuleName(); } @Override public ItemPresentation getPresentation() { return new FileModuleDataPresentation(myData); } @Override public void navigate(boolean requestFocus) { RPsiModule module = FileHelper.getPsiModule(myData, myProject); if (module instanceof FileBase) { ((FileBase) module).navigate(requestFocus); } } @Override public boolean canNavigate() { return true; } @Override public boolean canNavigateToSource() { return true; } public String getLocation() { return FileHelper.shortLocation(myData.getPath(), myProject); } public Icon getLocationIcon() { return IconProvider.getDataModuleFileIcon(myData); } } } ================================================ FILE: src/main/java/com/reason/ide/handlers/ORTypedHandler.java ================================================ package com.reason.ide.handlers; import com.intellij.codeInsight.editorActions.TypedHandlerDelegate; import com.intellij.openapi.editor.CaretModel; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.fileTypes.FileType; import com.intellij.openapi.project.Project; import com.intellij.psi.PsiComment; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.reason.ide.files.OclFileType; import com.reason.ide.files.OclInterfaceFileType; import org.jetbrains.annotations.NotNull; public class ORTypedHandler extends TypedHandlerDelegate { @NotNull @Override public Result beforeCharTyped( char c, @NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file, @NotNull FileType fileType) { if (!(fileType instanceof OclFileType || fileType instanceof OclInterfaceFileType)) { return Result.CONTINUE; } // #62 - don't insert a right parenthesis when at the end of a comment if (c == ')') { Document doc = editor.getDocument(); PsiDocumentManager.getInstance(project).commitDocument(doc); CaretModel caretModel = editor.getCaretModel(); // * ) int offsetBefore = caretModel.getOffset(); if (offsetBefore < doc.getTextLength()) { CharSequence charsSequence = doc.getCharsSequence(); char c1 = charsSequence.charAt(offsetBefore - 1); char c2 = charsSequence.charAt(offsetBefore); if (c1 == '*' && c2 == ')') { PsiElement leaf = file.findElementAt(offsetBefore); if (leaf instanceof PsiComment) { caretModel.moveToOffset(offsetBefore + 1); return Result.STOP; } } } } return super.beforeCharTyped(c, project, editor, file, fileType); } } ================================================ FILE: src/main/java/com/reason/ide/handlers/OclQuoteHandler.java ================================================ package com.reason.ide.handlers; import com.intellij.codeInsight.editorActions.SimpleTokenSetQuoteHandler; import com.intellij.psi.TokenType; import com.reason.lang.ocaml.OclTypes; public class OclQuoteHandler extends SimpleTokenSetQuoteHandler { public OclQuoteHandler() { super(OclTypes.INSTANCE.STRING_VALUE, OclTypes.INSTANCE.DOUBLE_QUOTE, TokenType.BAD_CHARACTER); } } ================================================ FILE: src/main/java/com/reason/ide/handlers/ResQuoteHandler.java ================================================ package com.reason.ide.handlers; import com.intellij.codeInsight.editorActions.SimpleTokenSetQuoteHandler; import com.intellij.psi.TokenType; import com.reason.lang.rescript.ResTypes; public class ResQuoteHandler extends SimpleTokenSetQuoteHandler { public ResQuoteHandler() { super(ResTypes.INSTANCE.STRING_VALUE, ResTypes.INSTANCE.DOUBLE_QUOTE, TokenType.BAD_CHARACTER); } } ================================================ FILE: src/main/java/com/reason/ide/handlers/RmlQuoteHandler.java ================================================ package com.reason.ide.handlers; import com.intellij.codeInsight.editorActions.SimpleTokenSetQuoteHandler; import com.intellij.psi.TokenType; import com.reason.lang.reason.RmlTypes; public class RmlQuoteHandler extends SimpleTokenSetQuoteHandler { public RmlQuoteHandler() { super(RmlTypes.INSTANCE.STRING_VALUE, RmlTypes.INSTANCE.DOUBLE_QUOTE, TokenType.BAD_CHARACTER); } } ================================================ FILE: src/main/java/com/reason/ide/highlight/DuneSyntaxAnnotator.java ================================================ package com.reason.ide.highlight; import static com.intellij.lang.annotation.HighlightSeverity.INFORMATION; import com.intellij.lang.annotation.AnnotationHolder; import com.intellij.lang.annotation.Annotator; import com.intellij.openapi.editor.colors.TextAttributesKey; import com.intellij.psi.PsiElement; import com.intellij.psi.tree.IElementType; import com.reason.lang.core.psi.impl.RPsiDuneField; import com.reason.lang.core.psi.impl.RPsiDuneStanza; import com.reason.lang.dune.DuneTypes; import org.jetbrains.annotations.NotNull; public class DuneSyntaxAnnotator implements Annotator { @Override public void annotate(@NotNull PsiElement element, @NotNull AnnotationHolder holder) { IElementType elementType = element.getNode().getElementType(); if (element instanceof RPsiDuneStanza) { PsiElement identifier = ((RPsiDuneStanza) element).getNameIdentifier(); if (identifier != null) { color(holder, identifier, DuneSyntaxHighlighter.STANZAS_); } } if (element instanceof RPsiDuneField) { PsiElement identifier = ((RPsiDuneField) element).getNameIdentifier(); if (identifier != null) { color(holder, identifier, DuneSyntaxHighlighter.FIELDS_); } } else if (elementType == DuneTypes.INSTANCE.C_VAR) { color(holder, element, DuneSyntaxHighlighter.VAR_); } } private void color( @NotNull AnnotationHolder holder, @NotNull PsiElement element, @NotNull TextAttributesKey key) { holder.newSilentAnnotation(INFORMATION).range(element).textAttributes(key).create(); } } ================================================ FILE: src/main/java/com/reason/ide/highlight/DuneSyntaxHighlighter.java ================================================ package com.reason.ide.highlight; import com.intellij.lexer.*; import com.intellij.openapi.editor.*; import com.intellij.openapi.editor.colors.*; import com.intellij.openapi.fileTypes.*; import com.intellij.psi.tree.*; import com.reason.lang.dune.*; import org.jetbrains.annotations.*; import static com.intellij.openapi.editor.colors.TextAttributesKey.*; import static com.intellij.psi.TokenType.*; public class DuneSyntaxHighlighter extends SyntaxHighlighterBase { public static final TextAttributesKey PARENS_ = createTextAttributesKey("DUNE_PAREN", DefaultLanguageHighlighterColors.PARENTHESES); public static final TextAttributesKey COMMENT_ = createTextAttributesKey("DUNE_COMMENT", DefaultLanguageHighlighterColors.BLOCK_COMMENT); public static final TextAttributesKey STANZAS_ = createTextAttributesKey("DUNE_STANZA", DefaultLanguageHighlighterColors.FUNCTION_DECLARATION); public static final TextAttributesKey FIELDS_ = createTextAttributesKey("DUNE_FIELD", DefaultLanguageHighlighterColors.KEYWORD); public static final TextAttributesKey OPTIONS_ = createTextAttributesKey("DUNE_OPTION", DefaultLanguageHighlighterColors.LABEL); public static final TextAttributesKey STRING_ = createTextAttributesKey("DUNE_STRING", DefaultLanguageHighlighterColors.STRING); public static final TextAttributesKey ATOM_ = createTextAttributesKey("DUNE_ATOM", DefaultLanguageHighlighterColors.CONSTANT); public static final TextAttributesKey VAR_ = createTextAttributesKey("DUNE_VAR", DefaultLanguageHighlighterColors.LOCAL_VARIABLE); private static final TextAttributesKey BAD_CHAR_ = createTextAttributesKey("DUNE_BAD_CHARACTER", HighlighterColors.BAD_CHARACTER); private static final TextAttributesKey[] COMMENT_KEYS = new TextAttributesKey[]{COMMENT_}; private static final TextAttributesKey[] PAREN_KEYS = new TextAttributesKey[]{PARENS_}; private static final TextAttributesKey[] STANZAS_KEYS = new TextAttributesKey[]{STANZAS_}; private static final TextAttributesKey[] FIELDS_KEYS = new TextAttributesKey[]{FIELDS_}; private static final TextAttributesKey[] OPTIONS_KEYS = new TextAttributesKey[]{OPTIONS_}; private static final TextAttributesKey[] STRING_KEYS = new TextAttributesKey[]{STRING_}; private static final TextAttributesKey[] ATOM_KEYS = new TextAttributesKey[]{ATOM_}; private static final TextAttributesKey[] VAR_KEYS = new TextAttributesKey[]{VAR_}; private static final TextAttributesKey[] BAD_CHAR_KEYS = new TextAttributesKey[]{BAD_CHAR_}; private static final TextAttributesKey[] EMPTY_KEYS = new TextAttributesKey[0]; @Override public @NotNull Lexer getHighlightingLexer() { return new FlexAdapter(new DuneLexer(DuneTypes.INSTANCE)); } @Override public @NotNull TextAttributesKey[] getTokenHighlights(@NotNull IElementType tokenType) { if (tokenType.equals(DuneTypes.INSTANCE.LPAREN) || tokenType.equals(DuneTypes.INSTANCE.RPAREN)) { return PAREN_KEYS; } else if (tokenType.equals(DuneTypes.INSTANCE.SINGLE_COMMENT) || tokenType.equals(DuneTypes.INSTANCE.MULTI_COMMENT)) { return COMMENT_KEYS; } else if (tokenType.equals(DuneTypes.INSTANCE.STRING)) { return STRING_KEYS; } else if (tokenType.equals(DuneTypes.INSTANCE.ATOM)) { return ATOM_KEYS; } else if (BAD_CHARACTER.equals(tokenType)) { return BAD_CHAR_KEYS; } return EMPTY_KEYS; } } ================================================ FILE: src/main/java/com/reason/ide/highlight/DuneSyntaxHighlighterFactory.java ================================================ package com.reason.ide.highlight; import com.intellij.openapi.fileTypes.SyntaxHighlighterFactory; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.annotations.NotNull; public class DuneSyntaxHighlighterFactory extends SyntaxHighlighterFactory { @NotNull @Override public com.intellij.openapi.fileTypes.SyntaxHighlighter getSyntaxHighlighter( Project project, VirtualFile virtualFile) { return new DuneSyntaxHighlighter(); } } ================================================ FILE: src/main/java/com/reason/ide/highlight/ORSyntaxAnnotator.java ================================================ package com.reason.ide.highlight; import com.intellij.lang.*; import com.intellij.lang.annotation.*; import com.intellij.openapi.editor.colors.*; import com.intellij.openapi.editor.markup.*; import com.intellij.openapi.util.*; import com.intellij.psi.*; import com.intellij.psi.tree.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.impl.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; import static com.intellij.lang.annotation.HighlightSeverity.*; import static com.reason.ide.highlight.ORSyntaxHighlighter.*; public abstract class ORSyntaxAnnotator implements Annotator { private final ORLangTypes myTypes; ORSyntaxAnnotator(@NotNull ORLangTypes types) { myTypes = types; } public void annotate(@NotNull PsiElement element, @NotNull AnnotationHolder holder) { ASTNode elementNode = element.getNode(); IElementType elementType = elementNode.getElementType(); if (elementType == myTypes.C_TAG_START) { PsiElement nameIdentifier = ((RPsiTagStart) element).getNameIdentifier(); if (nameIdentifier != null) { TextRange range = TextRange.create(element.getTextRange().getStartOffset(), nameIdentifier.getTextRange().getEndOffset()); enforceColor(holder, range, MARKUP_TAG_); PsiElement lastChild = element.getLastChild(); IElementType lastElementType = lastChild == null ? null : lastChild.getNode().getElementType(); if (lastElementType == myTypes.TAG_AUTO_CLOSE || lastElementType == myTypes.GT) { enforceColor(holder, lastChild, MARKUP_TAG_); } } else { enforceColor(holder, element, MARKUP_TAG_); } } else if (elementType == myTypes.C_TAG_CLOSE) { enforceColor(holder, element, MARKUP_TAG_); } else if (elementType == myTypes.PROPERTY_NAME) { enforceColor(holder, element, MARKUP_ATTRIBUTE_); } else if (elementType == myTypes.C_MACRO_NAME) { enforceColor(holder, element, ANNOTATION_); } else if (elementType == myTypes.C_INTERPOLATION_PART) { enforceColor(holder, element, STRING_); } else if (elementType == myTypes.C_INTERPOLATION_REF) { enforceColor(holder, element, INTERPOLATED_REF_); } else if (elementType == myTypes.C_RECORD_FIELD) { PsiElement name = element.getFirstChild(); IElementType nameType = name != null ? name.getNode().getElementType() : null; if (nameType == myTypes.LIDENT) { enforceColor(holder, name, FIELD_NAME_); } } else if (elementType == myTypes.C_LET_DECLARATION || elementType == myTypes.C_VAL_DECLARATION) { PsiElement name = ORUtil.findImmediateFirstChildOfType(element, myTypes.LIDENT); IElementType nameType = name != null ? name.getNode().getElementType() : null; if (nameType == myTypes.LIDENT) { enforceColor(holder, name, LET_NAME_); } } // remapped tokens are not seen by syntaxAnnotator else if (elementType == myTypes.A_VARIANT_NAME) { enforceColor(holder, element, VARIANT_NAME_); } else if (elementType == myTypes.A_MODULE_NAME) { enforceColor(holder, element, MODULE_NAME_); } else if (elementType == myTypes.LIDENT) { IElementType parentElementType = elementNode.getTreeParent().getElementType(); if (parentElementType == myTypes.C_TYPE_DECLARATION || parentElementType == myTypes.C_EXTERNAL_DECLARATION || parentElementType == myTypes.C_RECORD_FIELD) { eraseColor(element, holder); } } } private static void eraseColor(@NotNull PsiElement element, @NotNull AnnotationHolder holder) { holder.newSilentAnnotation(INFORMATION).range(element).enforcedTextAttributes(TextAttributes.ERASE_MARKER).create(); } @SuppressWarnings("SameParameterValue") private void enforceColor(@NotNull AnnotationHolder holder, @NotNull TextRange range, @NotNull TextAttributesKey key) { holder.newSilentAnnotation(INFORMATION).range(range).enforcedTextAttributes(TextAttributes.ERASE_MARKER).create(); holder.newSilentAnnotation(INFORMATION).range(range).textAttributes(key).create(); } private void enforceColor(@NotNull AnnotationHolder holder, @Nullable PsiElement element, @NotNull TextAttributesKey key) { if (element != null) { holder.newSilentAnnotation(INFORMATION).range(element).enforcedTextAttributes(TextAttributes.ERASE_MARKER).create(); holder.newSilentAnnotation(INFORMATION).range(element).textAttributes(key).create(); } } } ================================================ FILE: src/main/java/com/reason/ide/highlight/ORSyntaxHighlighter.java ================================================ package com.reason.ide.highlight; import com.intellij.lexer.*; import com.intellij.openapi.editor.*; import com.intellij.openapi.editor.colors.*; import com.intellij.openapi.fileTypes.*; import com.intellij.psi.tree.*; import com.reason.lang.core.type.*; import com.reason.lang.ocaml.*; import com.reason.lang.reason.*; import com.reason.lang.rescript.*; import org.jetbrains.annotations.*; import java.util.*; import static com.intellij.openapi.editor.colors.TextAttributesKey.*; import static com.intellij.psi.TokenType.*; public class ORSyntaxHighlighter extends SyntaxHighlighterBase { private static final TextAttributesKey TYPE_ARGUMENT_KEY = TextAttributesKey.createTextAttributesKey("TYPE_ARGUMENT"); public static final TextAttributesKey ANNOTATION_ = createTextAttributesKey("REASONML_ANNOTATION", DefaultLanguageHighlighterColors.METADATA); public static final TextAttributesKey BRACES_ = createTextAttributesKey("REASONML_BRACES", DefaultLanguageHighlighterColors.BRACES); public static final TextAttributesKey BRACKETS_ = createTextAttributesKey("REASONML_BRACKETS", DefaultLanguageHighlighterColors.BRACKETS); public static final TextAttributesKey CODE_LENS_ = createTextAttributesKey("REASONML_CODE_LENS", DefaultLanguageHighlighterColors.BLOCK_COMMENT); public static final TextAttributesKey FIELD_NAME_ = createTextAttributesKey("REASONML_FIELD_NAME", DefaultLanguageHighlighterColors.INSTANCE_FIELD); public static final TextAttributesKey LET_NAME_ = createTextAttributesKey("REASONML_LET_NAME", DefaultLanguageHighlighterColors.IDENTIFIER); public static final TextAttributesKey KEYWORD_ = createTextAttributesKey("REASONML_KEYWORD", DefaultLanguageHighlighterColors.KEYWORD); public static final TextAttributesKey MACRO_ = createTextAttributesKey("REASONML_MACRO", DefaultLanguageHighlighterColors.KEYWORD); public static final TextAttributesKey INTERPOLATED_REF_ = createTextAttributesKey("REASONML_INTERPOLATED_REF", DefaultLanguageHighlighterColors.IDENTIFIER); public static final TextAttributesKey MARKUP_TAG_ = createTextAttributesKey("REASONML_MARKUP_TAG", DefaultLanguageHighlighterColors.MARKUP_TAG); public static final TextAttributesKey MARKUP_ATTRIBUTE_ = createTextAttributesKey("REASONML_MARKUP_ATTRIBUTE", DefaultLanguageHighlighterColors.MARKUP_ATTRIBUTE); public static final TextAttributesKey MODULE_NAME_ = createTextAttributesKey("REASONML_MODULE_NAME", DefaultLanguageHighlighterColors.CLASS_NAME); public static final TextAttributesKey NUMBER_ = createTextAttributesKey("REASONML_NUMBER", DefaultLanguageHighlighterColors.NUMBER); public static final TextAttributesKey OPERATION_SIGN_ = createTextAttributesKey("REASONML_OPERATION_SIGN", DefaultLanguageHighlighterColors.OPERATION_SIGN); public static final TextAttributesKey OPTION_ = createTextAttributesKey("REASONML_OPTION", DefaultLanguageHighlighterColors.STATIC_FIELD); public static final TextAttributesKey PARENS_ = createTextAttributesKey("REASONML_PARENS", DefaultLanguageHighlighterColors.PARENTHESES); public static final TextAttributesKey POLY_VARIANT_ = createTextAttributesKey("REASONML_POLY_VARIANT", DefaultLanguageHighlighterColors.STATIC_FIELD); public static final TextAttributesKey COMMENT_ = createTextAttributesKey("REASONML_COMMENT", DefaultLanguageHighlighterColors.BLOCK_COMMENT); public static final TextAttributesKey SEMICOLON_ = createTextAttributesKey("REASONML_SEMICOLON", DefaultLanguageHighlighterColors.SEMICOLON); public static final TextAttributesKey STRING_ = createTextAttributesKey("REASONML_STRING", DefaultLanguageHighlighterColors.STRING); public static final TextAttributesKey TYPE_ARGUMENT_ = createTextAttributesKey("REASONML_TYPE_ARGUMENT", TYPE_ARGUMENT_KEY); public static final TextAttributesKey VARIANT_NAME_ = createTextAttributesKey("REASONML_VARIANT_NAME", DefaultLanguageHighlighterColors.STATIC_FIELD); private static final TextAttributesKey BAD_CHAR_ = createTextAttributesKey("REASONML_BAD_CHARACTER", HighlighterColors.BAD_CHARACTER); private static final TextAttributesKey[] NUMBER_KEYS = new TextAttributesKey[]{NUMBER_}; static final TextAttributesKey[] COMMENT_KEYS = new TextAttributesKey[]{COMMENT_}; static final TextAttributesKey[] STRING_KEYS = new TextAttributesKey[]{STRING_}; private static final TextAttributesKey[] TYPE_ARGUMENT_KEYS = new TextAttributesKey[]{TYPE_ARGUMENT_}; private static final TextAttributesKey[] POLY_VARIANT_KEYS = new TextAttributesKey[]{POLY_VARIANT_}; static final TextAttributesKey[] BRACKET_KEYS = new TextAttributesKey[]{BRACKETS_}; static final TextAttributesKey[] BRACE_KEYS = new TextAttributesKey[]{BRACES_}; static final TextAttributesKey[] PAREN_KEYS = new TextAttributesKey[]{PARENS_}; private static final TextAttributesKey[] OPTION_KEYS = new TextAttributesKey[]{OPTION_}; static final TextAttributesKey[] KEYWORD_KEYS = new TextAttributesKey[]{KEYWORD_}; static final TextAttributesKey[] MACRO_KEYS = new TextAttributesKey[]{MACRO_}; static final TextAttributesKey[] SEMICOLON_KEYS = new TextAttributesKey[]{SEMICOLON_}; private static final TextAttributesKey[] DOT_KEYS = new TextAttributesKey[]{OPERATION_SIGN_}; private static final TextAttributesKey[] COMMA_KEYS = new TextAttributesKey[]{OPERATION_SIGN_}; private static final TextAttributesKey[] MARKUP_TAG_KEYS = new TextAttributesKey[]{MARKUP_TAG_}; static final TextAttributesKey[] OPERATION_SIGN_KEYS = new TextAttributesKey[]{OPERATION_SIGN_}; static final TextAttributesKey[] BAD_CHAR_KEYS = new TextAttributesKey[]{BAD_CHAR_}; private static final TextAttributesKey[] EMPTY_KEYS = new TextAttributesKey[0]; private final ORLangTypes myTypes; private final Set myKeywordTypes; private final Set myOperationSignTypes; private final Set myOptionTypes; private final Set myMacroTypes; public ORSyntaxHighlighter(ORLangTypes types, Set keywordTypes, Set operationSignTypes, Set optionTypes, Set macroTypes) { myTypes = types; myKeywordTypes = keywordTypes; myOperationSignTypes = operationSignTypes; myOptionTypes = optionTypes; myMacroTypes = macroTypes; } @Override public @NotNull Lexer getHighlightingLexer() { return myTypes instanceof RmlTypes ? new RmlLexer() : myTypes instanceof ResTypes ? new ResLexer() : new OclLexer(); } @Override public @NotNull TextAttributesKey[] getTokenHighlights(@NotNull IElementType tokenType) { if (tokenType.equals(myTypes.MULTI_COMMENT) || tokenType.equals(myTypes.SINGLE_COMMENT)) { return COMMENT_KEYS; } else if (tokenType.equals(myTypes.LBRACE) || tokenType.equals(myTypes.RBRACE)) { return BRACE_KEYS; } else if (tokenType.equals(myTypes.LBRACKET) || tokenType.equals(myTypes.RBRACKET) || tokenType.equals(myTypes.LARRAY) || tokenType.equals(myTypes.RARRAY) || tokenType.equals(myTypes.ML_STRING_OPEN) || tokenType.equals(myTypes.ML_STRING_CLOSE) || tokenType.equals(myTypes.JS_STRING_OPEN) || tokenType.equals(myTypes.JS_STRING_CLOSE)) { return BRACKET_KEYS; } else if (tokenType.equals(myTypes.LPAREN) || tokenType.equals(myTypes.RPAREN)) { return PAREN_KEYS; } else if (tokenType.equals(myTypes.INT_VALUE) || tokenType.equals(myTypes.FLOAT_VALUE)) { return NUMBER_KEYS; } else if (myTypes.DOT.equals(tokenType)) { return DOT_KEYS; } else if (myTypes.TYPE_ARGUMENT.equals(tokenType)) { return TYPE_ARGUMENT_KEYS; } else if (myTypes.POLY_VARIANT.equals(tokenType)) { return POLY_VARIANT_KEYS; } else if (myTypes.COMMA.equals(tokenType)) { return COMMA_KEYS; } else if (myTypes.TAG_AUTO_CLOSE.equals(tokenType) || myTypes.TAG_LT_SLASH.equals(tokenType) || myTypes.A_LOWER_TAG_NAME.equals(tokenType) || myTypes.A_UPPER_TAG_NAME.equals(tokenType)) { return MARKUP_TAG_KEYS; } else if (myTypes.SEMI.equals(tokenType) || myTypes.SEMISEMI.equals(tokenType)) { return SEMICOLON_KEYS; } else if (myTypes.STRING_VALUE.equals(tokenType) || myTypes.CHAR_VALUE.equals(tokenType)) { return STRING_KEYS; } else if (myKeywordTypes.contains(tokenType)) { return KEYWORD_KEYS; } else if (myOperationSignTypes.contains(tokenType)) { return OPERATION_SIGN_KEYS; } else if (myOptionTypes.contains(tokenType)) { return OPTION_KEYS; } else if (myMacroTypes.contains(tokenType)) { return MACRO_KEYS; } else if (BAD_CHARACTER.equals(tokenType)) { return BAD_CHAR_KEYS; } return EMPTY_KEYS; } @NotNull static Set of(IElementType... types) { Set result = new HashSet<>(); Collections.addAll(result, types); return result; } } ================================================ FILE: src/main/java/com/reason/ide/highlight/OclGrammarEditorHighlighter.java ================================================ package com.reason.ide.highlight; import com.intellij.lexer.*; import com.intellij.openapi.editor.colors.*; import com.intellij.openapi.editor.ex.util.*; import com.intellij.openapi.fileTypes.*; import com.intellij.openapi.project.*; import com.intellij.openapi.vfs.*; import com.intellij.psi.tree.*; import com.reason.ide.files.*; import com.reason.lang.ocamlgrammar.*; import org.jetbrains.annotations.*; public final class OclGrammarEditorHighlighter extends LayeredLexerEditorHighlighter { public OclGrammarEditorHighlighter(@Nullable Project project, @Nullable VirtualFile file, @NotNull EditorColorsScheme colors) { super(new OclGrammarSyntaxHighlighterFactory().getSyntaxHighlighter(project, file), colors); SyntaxHighlighter oclSyntaxHighlighter = SyntaxHighlighterFactory.getSyntaxHighlighter(OclFileType.INSTANCE, project, file); if (oclSyntaxHighlighter != null) { registerLayer(OclGrammarTypes.INSTANCE.TEMPLATE_OCAML_TEXT, new LayerDescriptor(new SyntaxHighlighter() { @Override public @NotNull Lexer getHighlightingLexer() { return oclSyntaxHighlighter.getHighlightingLexer(); } @Override public @NotNull TextAttributesKey[] getTokenHighlights(IElementType tokenType) { return oclSyntaxHighlighter.getTokenHighlights(tokenType); } }, "")); } } } ================================================ FILE: src/main/java/com/reason/ide/highlight/OclGrammarSyntaxHighlighterFactory.java ================================================ package com.reason.ide.highlight; import com.intellij.lexer.*; import com.intellij.openapi.editor.colors.*; import com.intellij.openapi.fileTypes.*; import com.intellij.openapi.project.*; import com.intellij.openapi.vfs.*; import com.intellij.psi.tree.*; import com.reason.lang.ocamlgrammar.*; import org.jetbrains.annotations.*; import java.util.*; import static com.intellij.psi.TokenType.*; import static com.reason.ide.highlight.ORSyntaxHighlighter.*; public class OclGrammarSyntaxHighlighterFactory extends SyntaxHighlighterFactory { private static final Set KEYWORD_TYPES = of( OclGrammarTypes.INSTANCE.DECLARE, OclGrammarTypes.INSTANCE.PLUGIN, OclGrammarTypes.INSTANCE.GRAMMAR, OclGrammarTypes.INSTANCE.VERNAC, OclGrammarTypes.INSTANCE.EXTEND, OclGrammarTypes.INSTANCE.COMMAND, OclGrammarTypes.INSTANCE.TACTIC, OclGrammarTypes.INSTANCE.ARGUMENT, OclGrammarTypes.INSTANCE.END ); private static final Set OPERATION_SIGNS = of( OclGrammarTypes.INSTANCE.PIPE, OclGrammarTypes.INSTANCE.ARROW ); @Override public @NotNull SyntaxHighlighter getSyntaxHighlighter(@Nullable Project project, @Nullable VirtualFile virtualFile) { return new SyntaxHighlighterBase() { private final OclGrammarTypes myTypes = OclGrammarTypes.INSTANCE; @Override public @NotNull Lexer getHighlightingLexer() { return new FlexAdapter(new OclGrammarLexer()); } @Override public @NotNull TextAttributesKey[] getTokenHighlights(IElementType tokenType) { if (tokenType == myTypes.SINGLE_COMMENT || tokenType == myTypes.MULTI_COMMENT) { return COMMENT_KEYS; } if (tokenType == BAD_CHARACTER) { return BAD_CHAR_KEYS; } if (tokenType == myTypes.STRING_VALUE) { return STRING_KEYS; } if (tokenType == myTypes.LBRACE || tokenType == myTypes.RBRACE) { return BRACE_KEYS; } if (tokenType == myTypes.LBRACKET || tokenType == myTypes.RBRACKET) { return BRACKET_KEYS; } if (tokenType == myTypes.LPAREN || tokenType == myTypes.RPAREN) { return PAREN_KEYS; } if (KEYWORD_TYPES.contains(tokenType)) { return KEYWORD_KEYS; } if (OPERATION_SIGNS.contains(tokenType)) { return OPERATION_SIGN_KEYS; } return TextAttributesKey.EMPTY_ARRAY; } }; } } ================================================ FILE: src/main/java/com/reason/ide/highlight/OclLexEditorHighlighter.java ================================================ package com.reason.ide.highlight; import com.intellij.lexer.*; import com.intellij.openapi.editor.colors.*; import com.intellij.openapi.editor.ex.util.*; import com.intellij.openapi.fileTypes.*; import com.intellij.openapi.project.*; import com.intellij.openapi.vfs.*; import com.intellij.psi.tree.*; import com.reason.ide.files.*; import com.reason.lang.ocamllex.*; import org.jetbrains.annotations.*; public final class OclLexEditorHighlighter extends LayeredLexerEditorHighlighter { public OclLexEditorHighlighter(@Nullable Project project, @Nullable VirtualFile file, @NotNull EditorColorsScheme colors) { super(new OclLexSyntaxHighlighterFactory().getSyntaxHighlighter(project, file), colors); SyntaxHighlighter oclSyntaxHighlighter = SyntaxHighlighterFactory.getSyntaxHighlighter(OclFileType.INSTANCE, project, file); if (oclSyntaxHighlighter != null) { registerLayer(OclLexTypes.INSTANCE.TEMPLATE_OCAML_TEXT, new LayerDescriptor(new SyntaxHighlighter() { @Override public @NotNull Lexer getHighlightingLexer() { return oclSyntaxHighlighter.getHighlightingLexer(); } @Override public @NotNull TextAttributesKey[] getTokenHighlights(IElementType tokenType) { return oclSyntaxHighlighter.getTokenHighlights(tokenType); } }, "")); } } } ================================================ FILE: src/main/java/com/reason/ide/highlight/OclLexSyntaxHighlighterFactory.java ================================================ package com.reason.ide.highlight; import com.intellij.lexer.*; import com.intellij.openapi.editor.colors.*; import com.intellij.openapi.fileTypes.*; import com.intellij.openapi.project.*; import com.intellij.openapi.vfs.*; import com.intellij.psi.tree.*; import com.reason.lang.ocamllex.*; import org.jetbrains.annotations.*; import java.util.*; import static com.intellij.psi.TokenType.BAD_CHARACTER; import static com.reason.ide.highlight.ORSyntaxHighlighter.*; public class OclLexSyntaxHighlighterFactory extends SyntaxHighlighterFactory { private static final Set KEYWORD_TYPES = of(OclLexTypes.INSTANCE.LET, OclLexTypes.INSTANCE.RULE, OclLexTypes.INSTANCE.PARSE, OclLexTypes.INSTANCE.AND); private static final Set OPERATION_SIGNS = of(OclLexTypes.INSTANCE.PIPE, OclLexTypes.INSTANCE.DASH, OclLexTypes.INSTANCE.LBRACE, OclLexTypes.INSTANCE.RBRACE, OclLexTypes.INSTANCE.LBRACKET, OclLexTypes.INSTANCE.RBRACKET, OclLexTypes.INSTANCE.EQ); @Override public @NotNull SyntaxHighlighter getSyntaxHighlighter(@Nullable Project project, @Nullable VirtualFile virtualFile) { return new SyntaxHighlighterBase() { private final OclLexTypes myTypes = OclLexTypes.INSTANCE; @Override public @NotNull Lexer getHighlightingLexer() { return new FlexAdapter(new OclLexLexer()); } @Override public @NotNull TextAttributesKey[] getTokenHighlights(IElementType tokenType) { if (myTypes.SINGLE_COMMENT.equals(tokenType)) { return ORSyntaxHighlighter.COMMENT_KEYS; } if (myTypes.STRING_VALUE.equals(tokenType)) { return STRING_KEYS; } if (BAD_CHARACTER.equals(tokenType)) { return BAD_CHAR_KEYS; } if (myTypes.LBRACE.equals(tokenType) || myTypes.RBRACE.equals(tokenType)) { return BRACE_KEYS; } else if (myTypes.LBRACKET.equals(tokenType) || myTypes.RBRACKET.equals(tokenType)) { return BRACKET_KEYS; } if (KEYWORD_TYPES.contains(tokenType)) { return KEYWORD_KEYS; } if (OPERATION_SIGNS.contains(tokenType)) { return OPERATION_SIGN_KEYS; } return TextAttributesKey.EMPTY_ARRAY; } }; } } ================================================ FILE: src/main/java/com/reason/ide/highlight/OclSyntaxAnnotator.java ================================================ package com.reason.ide.highlight; import com.reason.lang.ocaml.OclTypes; public class OclSyntaxAnnotator extends ORSyntaxAnnotator { public OclSyntaxAnnotator() { super(OclTypes.INSTANCE); } } ================================================ FILE: src/main/java/com/reason/ide/highlight/OclSyntaxHighlighterFactory.java ================================================ package com.reason.ide.highlight; import com.intellij.openapi.fileTypes.*; import com.intellij.openapi.project.*; import com.intellij.openapi.vfs.*; import com.intellij.psi.tree.*; import com.reason.lang.ocaml.*; import org.jetbrains.annotations.*; import java.util.*; import static com.reason.ide.highlight.ORSyntaxHighlighter.*; public class OclSyntaxHighlighterFactory extends SyntaxHighlighterFactory { private static final Set KEYWORDS_TYPES = of( // reserved OclTypes.INSTANCE.ASSERT, OclTypes.INSTANCE.AND, OclTypes.INSTANCE.ASR, OclTypes.INSTANCE.AS, OclTypes.INSTANCE.BEGIN, OclTypes.INSTANCE.CLASS, OclTypes.INSTANCE.CONSTRAINT, OclTypes.INSTANCE.DOWNTO, OclTypes.INSTANCE.DONE, OclTypes.INSTANCE.DO, OclTypes.INSTANCE.EXCEPTION, OclTypes.INSTANCE.EXTERNAL, OclTypes.INSTANCE.ELSE, OclTypes.INSTANCE.END, OclTypes.INSTANCE.FUNCTION, OclTypes.INSTANCE.FUNCTOR, OclTypes.INSTANCE.BOOL_VALUE/*false*/, OclTypes.INSTANCE.FUN, OclTypes.INSTANCE.FOR, OclTypes.INSTANCE.INITIALIZER, OclTypes.INSTANCE.INCLUDE, OclTypes.INSTANCE.INHERIT, OclTypes.INSTANCE.IF, OclTypes.INSTANCE.IN, OclTypes.INSTANCE.LAND, OclTypes.INSTANCE.LAZY, OclTypes.INSTANCE.LXOR, OclTypes.INSTANCE.LET, OclTypes.INSTANCE.LOR, OclTypes.INSTANCE.LSL, OclTypes.INSTANCE.LSR, OclTypes.INSTANCE.MUTABLE, OclTypes.INSTANCE.METHOD, OclTypes.INSTANCE.MODULE, OclTypes.INSTANCE.MATCH, OclTypes.INSTANCE.MOD, OclTypes.INSTANCE.NONREC, OclTypes.INSTANCE.NEW, OclTypes.INSTANCE.OBJECT, OclTypes.INSTANCE.OPEN, OclTypes.INSTANCE.OF, OclTypes.INSTANCE.OR, OclTypes.INSTANCE.PRIVATE, OclTypes.INSTANCE.REC, OclTypes.INSTANCE.STRUCT, OclTypes.INSTANCE.SIG, OclTypes.INSTANCE.THEN, /*true, */ OclTypes.INSTANCE.TYPE, OclTypes.INSTANCE.TRY, OclTypes.INSTANCE.TO, OclTypes.INSTANCE.VIRTUAL, OclTypes.INSTANCE.VAL, OclTypes.INSTANCE.WHEN, // not reserved OclTypes.INSTANCE.RAISE, OclTypes.INSTANCE.WHILE, OclTypes.INSTANCE.PRI, OclTypes.INSTANCE.WITH, OclTypes.INSTANCE.RECORD, OclTypes.INSTANCE.REF); private static final Set OPERATION_SIGN_TYPES = of( OclTypes.INSTANCE.L_AND, OclTypes.INSTANCE.L_OR, OclTypes.INSTANCE.SHORTCUT, OclTypes.INSTANCE.ARROW, OclTypes.INSTANCE.PIPE_FORWARD, OclTypes.INSTANCE.EQEQEQ, OclTypes.INSTANCE.EQEQ, OclTypes.INSTANCE.EQ, OclTypes.INSTANCE.NOT_EQEQ, OclTypes.INSTANCE.NOT_EQ, OclTypes.INSTANCE.DIFF, OclTypes.INSTANCE.COLON, OclTypes.INSTANCE.SINGLE_QUOTE, OclTypes.INSTANCE.DOUBLE_QUOTE, OclTypes.INSTANCE.CARRET, OclTypes.INSTANCE.PLUSDOT, OclTypes.INSTANCE.MINUSDOT, OclTypes.INSTANCE.SLASHDOT, OclTypes.INSTANCE.STARDOT, OclTypes.INSTANCE.PLUS, OclTypes.INSTANCE.MINUS, OclTypes.INSTANCE.SLASH, OclTypes.INSTANCE.STAR, OclTypes.INSTANCE.PERCENT, OclTypes.INSTANCE.PIPE, OclTypes.INSTANCE.ARROBASE, OclTypes.INSTANCE.SHARP, OclTypes.INSTANCE.SHARPSHARP, OclTypes.INSTANCE.QUESTION_MARK, OclTypes.INSTANCE.EXCLAMATION_MARK, OclTypes.INSTANCE.LT_OR_EQUAL, OclTypes.INSTANCE.GT_OR_EQUAL, OclTypes.INSTANCE.AMPERSAND, OclTypes.INSTANCE.LEFT_ARROW, OclTypes.INSTANCE.RIGHT_ARROW, OclTypes.INSTANCE.COLON_EQ, OclTypes.INSTANCE.COLON_GT, OclTypes.INSTANCE.STRING_CONCAT, OclTypes.INSTANCE.GT, OclTypes.INSTANCE.GT_BRACE, OclTypes.INSTANCE.GT_BRACKET, OclTypes.INSTANCE.BRACKET_GT, OclTypes.INSTANCE.BRACKET_LT, OclTypes.INSTANCE.BRACE_LT, OclTypes.INSTANCE.DOTDOT, OclTypes.INSTANCE.PIPE_FIRST); private static final Set OPTIONS_TYPES = of(OclTypes.INSTANCE.NONE, OclTypes.INSTANCE.SOME); private static final Set MACRO_TYPES = of( OclTypes.INSTANCE.DIRECTIVE_IF, OclTypes.INSTANCE.DIRECTIVE_ELSE, OclTypes.INSTANCE.DIRECTIVE_ELIF, OclTypes.INSTANCE.DIRECTIVE_END, OclTypes.INSTANCE.DIRECTIVE_ENDIF); @Override public @NotNull SyntaxHighlighter getSyntaxHighlighter(@Nullable Project project, @Nullable VirtualFile virtualFile) { return new ORSyntaxHighlighter(OclTypes.INSTANCE, KEYWORDS_TYPES, OPERATION_SIGN_TYPES, OPTIONS_TYPES, MACRO_TYPES); } } ================================================ FILE: src/main/java/com/reason/ide/highlight/OclYaccEditorHighlighter.java ================================================ package com.reason.ide.highlight; import com.intellij.lexer.*; import com.intellij.openapi.editor.colors.*; import com.intellij.openapi.editor.ex.util.*; import com.intellij.openapi.fileTypes.*; import com.intellij.openapi.project.*; import com.intellij.openapi.vfs.*; import com.intellij.psi.tree.*; import com.reason.ide.files.*; import com.reason.lang.ocamlyacc.*; import org.jetbrains.annotations.*; public final class OclYaccEditorHighlighter extends LayeredLexerEditorHighlighter { public OclYaccEditorHighlighter(@Nullable Project project, @Nullable VirtualFile file, @NotNull EditorColorsScheme colors) { super(new OclYaccSyntaxHighlighterFactory().getSyntaxHighlighter(project, file), colors); SyntaxHighlighter oclSyntaxHighlighter = SyntaxHighlighterFactory.getSyntaxHighlighter(OclFileType.INSTANCE, project, file); if (oclSyntaxHighlighter != null) { registerLayer(OclYaccTypes.INSTANCE.TEMPLATE_OCAML_TEXT, new LayerDescriptor(new SyntaxHighlighter() { @Override public @NotNull Lexer getHighlightingLexer() { return oclSyntaxHighlighter.getHighlightingLexer(); } @Override public @NotNull TextAttributesKey[] getTokenHighlights(IElementType tokenType) { return oclSyntaxHighlighter.getTokenHighlights(tokenType); } }, "")); } } } ================================================ FILE: src/main/java/com/reason/ide/highlight/OclYaccSyntaxHighlighterFactory.java ================================================ package com.reason.ide.highlight; import com.intellij.lexer.*; import com.intellij.openapi.editor.colors.*; import com.intellij.openapi.fileTypes.*; import com.intellij.openapi.project.*; import com.intellij.openapi.vfs.*; import com.intellij.psi.tree.*; import com.reason.lang.ocamlyacc.*; import org.jetbrains.annotations.*; import java.util.*; import static com.intellij.psi.TokenType.*; import static com.reason.ide.highlight.ORSyntaxHighlighter.*; public class OclYaccSyntaxHighlighterFactory extends SyntaxHighlighterFactory { private static final Set KEYWORD_TYPES = of( OclYaccTypes.INSTANCE.TOKEN, OclYaccTypes.INSTANCE.TYPE, OclYaccTypes.INSTANCE.START, OclYaccTypes.INSTANCE.LEFT, OclYaccTypes.INSTANCE.RIGHT, OclYaccTypes.INSTANCE.NON_ASSOC, OclYaccTypes.INSTANCE.INLINE ); private static final Set OPERATION_SIGNS = of( OclYaccTypes.INSTANCE.HEADER_START, OclYaccTypes.INSTANCE.HEADER_STOP, OclYaccTypes.INSTANCE.SECTION_SEPARATOR, OclYaccTypes.INSTANCE.COLON, OclYaccTypes.INSTANCE.PIPE ); @Override public @NotNull SyntaxHighlighter getSyntaxHighlighter(@Nullable Project project, @Nullable VirtualFile virtualFile) { return new SyntaxHighlighterBase() { private final OclYaccTypes myTypes = OclYaccTypes.INSTANCE; @Override public @NotNull Lexer getHighlightingLexer() { return new FlexAdapter(new OclYaccLexer()); } @Override public @NotNull TextAttributesKey[] getTokenHighlights(IElementType tokenType) { if (tokenType == myTypes.SINGLE_COMMENT || tokenType == myTypes.MULTI_COMMENT) { return COMMENT_KEYS; } if (tokenType == BAD_CHARACTER) { return BAD_CHAR_KEYS; } if (tokenType == myTypes.SEMI) { return SEMICOLON_KEYS; } if (tokenType == myTypes.LBRACE || tokenType == myTypes.RBRACE) { return BRACE_KEYS; } if (KEYWORD_TYPES.contains(tokenType)) { return KEYWORD_KEYS; } if (OPERATION_SIGNS.contains(tokenType)) { return OPERATION_SIGN_KEYS; } return TextAttributesKey.EMPTY_ARRAY; } }; } } ================================================ FILE: src/main/java/com/reason/ide/highlight/ResSyntaxAnnotator.java ================================================ package com.reason.ide.highlight; import com.reason.lang.rescript.ResTypes; public class ResSyntaxAnnotator extends ORSyntaxAnnotator { public ResSyntaxAnnotator() { super(ResTypes.INSTANCE); } } ================================================ FILE: src/main/java/com/reason/ide/highlight/ResSyntaxHighlighterFactory.java ================================================ package com.reason.ide.highlight; import com.intellij.openapi.fileTypes.*; import com.intellij.openapi.project.*; import com.intellij.openapi.vfs.*; import com.intellij.psi.tree.*; import com.reason.lang.rescript.*; import org.jetbrains.annotations.*; import java.util.*; import static com.reason.ide.highlight.ORSyntaxHighlighter.*; public class ResSyntaxHighlighterFactory extends SyntaxHighlighterFactory { private static final Set KEYWORD_TYPES = of( ResTypes.INSTANCE.OPEN, ResTypes.INSTANCE.MODULE, ResTypes.INSTANCE.FUN, ResTypes.INSTANCE.LET, ResTypes.INSTANCE.TYPE, ResTypes.INSTANCE.INCLUDE, ResTypes.INSTANCE.EXTERNAL, ResTypes.INSTANCE.LIST, ResTypes.INSTANCE.IF, ResTypes.INSTANCE.ELSE, ResTypes.INSTANCE.SWITCH, ResTypes.INSTANCE.TRY, ResTypes.INSTANCE.CATCH, ResTypes.INSTANCE.RAISE, ResTypes.INSTANCE.FOR, ResTypes.INSTANCE.IN, ResTypes.INSTANCE.TO, ResTypes.INSTANCE.BOOL_VALUE, ResTypes.INSTANCE.REF, ResTypes.INSTANCE.EXCEPTION, ResTypes.INSTANCE.WHEN, ResTypes.INSTANCE.AND, ResTypes.INSTANCE.REC, ResTypes.INSTANCE.WHILE, ResTypes.INSTANCE.ASR, ResTypes.INSTANCE.CLASS, ResTypes.INSTANCE.CONSTRAINT, ResTypes.INSTANCE.DOWNTO, ResTypes.INSTANCE.FUNCTOR, ResTypes.INSTANCE.INHERIT, ResTypes.INSTANCE.INITIALIZER, ResTypes.INSTANCE.LAND, ResTypes.INSTANCE.LOR, ResTypes.INSTANCE.LSL, ResTypes.INSTANCE.LSR, ResTypes.INSTANCE.LXOR, ResTypes.INSTANCE.METHOD, ResTypes.INSTANCE.MOD, ResTypes.INSTANCE.NEW, ResTypes.INSTANCE.NONREC, ResTypes.INSTANCE.OR, ResTypes.INSTANCE.PRIVATE, ResTypes.INSTANCE.VIRTUAL, ResTypes.INSTANCE.VAL, ResTypes.INSTANCE.PUB, ResTypes.INSTANCE.PRI, ResTypes.INSTANCE.OBJECT, ResTypes.INSTANCE.MUTABLE, ResTypes.INSTANCE.UNIT, ResTypes.INSTANCE.WITH, ResTypes.INSTANCE.DIRECTIVE_IF, ResTypes.INSTANCE.DIRECTIVE_ELSE, ResTypes.INSTANCE.DIRECTIVE_ELIF, ResTypes.INSTANCE.DIRECTIVE_END, ResTypes.INSTANCE.DIRECTIVE_ENDIF, ResTypes.INSTANCE.UNPACK, // ResTypes.INSTANCE.ASYNC, ResTypes.INSTANCE.AWAIT); private static final Set OPERATION_SIGN_TYPES = of( ResTypes.INSTANCE.L_AND, ResTypes.INSTANCE.L_OR, ResTypes.INSTANCE.SHORTCUT, ResTypes.INSTANCE.ARROW, ResTypes.INSTANCE.PIPE_FORWARD, ResTypes.INSTANCE.EQEQEQ, ResTypes.INSTANCE.EQEQ, ResTypes.INSTANCE.EQ, ResTypes.INSTANCE.NOT_EQEQ, ResTypes.INSTANCE.NOT_EQ, ResTypes.INSTANCE.DIFF, ResTypes.INSTANCE.COLON, ResTypes.INSTANCE.SINGLE_QUOTE, ResTypes.INSTANCE.DOUBLE_QUOTE, ResTypes.INSTANCE.CARRET, ResTypes.INSTANCE.PLUSDOT, ResTypes.INSTANCE.MINUSDOT, ResTypes.INSTANCE.SLASHDOT, ResTypes.INSTANCE.STARDOT, ResTypes.INSTANCE.PLUS, ResTypes.INSTANCE.MINUS, ResTypes.INSTANCE.SLASH, ResTypes.INSTANCE.STAR, ResTypes.INSTANCE.PERCENT, ResTypes.INSTANCE.PIPE, ResTypes.INSTANCE.ARROBASE, ResTypes.INSTANCE.SHARP, ResTypes.INSTANCE.SHARPSHARP, ResTypes.INSTANCE.QUESTION_MARK, ResTypes.INSTANCE.EXCLAMATION_MARK, ResTypes.INSTANCE.LT_OR_EQUAL, ResTypes.INSTANCE.GT_OR_EQUAL, ResTypes.INSTANCE.AMPERSAND, ResTypes.INSTANCE.LEFT_ARROW, ResTypes.INSTANCE.RIGHT_ARROW, ResTypes.INSTANCE.COLON_EQ, ResTypes.INSTANCE.COLON_GT, ResTypes.INSTANCE.LT, ResTypes.INSTANCE.GT, ResTypes.INSTANCE.GT_BRACE, ResTypes.INSTANCE.GT_BRACKET, ResTypes.INSTANCE.BRACKET_GT, ResTypes.INSTANCE.BRACKET_LT, ResTypes.INSTANCE.BRACE_LT, ResTypes.INSTANCE.DOTDOT, ResTypes.INSTANCE.STRING_CONCAT); private static final Set OPTION_TYPES = of(ResTypes.INSTANCE.NONE, ResTypes.INSTANCE.SOME); private static final Set MACRO_TYPES = Collections.emptySet(); @Override public @NotNull SyntaxHighlighter getSyntaxHighlighter(@Nullable Project project, @Nullable VirtualFile virtualFile) { return new ORSyntaxHighlighter(ResTypes.INSTANCE, KEYWORD_TYPES, OPERATION_SIGN_TYPES, OPTION_TYPES, MACRO_TYPES); } } ================================================ FILE: src/main/java/com/reason/ide/highlight/RmlSyntaxAnnotator.java ================================================ package com.reason.ide.highlight; import com.reason.lang.reason.RmlTypes; public class RmlSyntaxAnnotator extends ORSyntaxAnnotator { public RmlSyntaxAnnotator() { super(RmlTypes.INSTANCE); } } ================================================ FILE: src/main/java/com/reason/ide/highlight/RmlSyntaxHighlighterFactory.java ================================================ package com.reason.ide.highlight; import com.intellij.openapi.fileTypes.*; import com.intellij.openapi.project.*; import com.intellij.openapi.vfs.*; import com.intellij.psi.tree.*; import com.reason.lang.reason.*; import org.jetbrains.annotations.*; import java.util.*; import static com.reason.ide.highlight.ORSyntaxHighlighter.*; public class RmlSyntaxHighlighterFactory extends SyntaxHighlighterFactory { public static final Set KEYWORD_TYPES = of(RmlTypes.INSTANCE.OPEN, RmlTypes.INSTANCE.MODULE, RmlTypes.INSTANCE.FUN, RmlTypes.INSTANCE.LET, RmlTypes.INSTANCE.TYPE, RmlTypes.INSTANCE.INCLUDE, RmlTypes.INSTANCE.EXTERNAL, RmlTypes.INSTANCE.IF, RmlTypes.INSTANCE.ELSE, RmlTypes.INSTANCE.SWITCH, RmlTypes.INSTANCE.TRY, RmlTypes.INSTANCE.RAISE, RmlTypes.INSTANCE.FOR, RmlTypes.INSTANCE.IN, RmlTypes.INSTANCE.TO, RmlTypes.INSTANCE.BOOL_VALUE, RmlTypes.INSTANCE.REF, RmlTypes.INSTANCE.EXCEPTION, RmlTypes.INSTANCE.WHEN, RmlTypes.INSTANCE.AND, RmlTypes.INSTANCE.REC, RmlTypes.INSTANCE.WHILE, RmlTypes.INSTANCE.ASR, RmlTypes.INSTANCE.CLASS, RmlTypes.INSTANCE.CONSTRAINT, RmlTypes.INSTANCE.DOWNTO, RmlTypes.INSTANCE.FUNCTOR, RmlTypes.INSTANCE.INHERIT, RmlTypes.INSTANCE.INITIALIZER, RmlTypes.INSTANCE.LAND, RmlTypes.INSTANCE.LOR, RmlTypes.INSTANCE.LSL, RmlTypes.INSTANCE.LSR, RmlTypes.INSTANCE.LXOR, RmlTypes.INSTANCE.METHOD, RmlTypes.INSTANCE.MOD, RmlTypes.INSTANCE.NEW, RmlTypes.INSTANCE.NONREC, RmlTypes.INSTANCE.OR, RmlTypes.INSTANCE.PRIVATE, RmlTypes.INSTANCE.VIRTUAL, RmlTypes.INSTANCE.VAL, RmlTypes.INSTANCE.PUB, RmlTypes.INSTANCE.PRI, RmlTypes.INSTANCE.OBJECT, RmlTypes.INSTANCE.MUTABLE, RmlTypes.INSTANCE.UNIT, RmlTypes.INSTANCE.WITH, RmlTypes.INSTANCE.DIRECTIVE_IF, RmlTypes.INSTANCE.DIRECTIVE_ELSE, RmlTypes.INSTANCE.DIRECTIVE_ELIF, RmlTypes.INSTANCE.DIRECTIVE_END, RmlTypes.INSTANCE.DIRECTIVE_ENDIF); public static final Set OPERATION_SIGN_TYPES = of( RmlTypes.INSTANCE.L_AND, RmlTypes.INSTANCE.L_OR, RmlTypes.INSTANCE.SHORTCUT, RmlTypes.INSTANCE.ARROW, RmlTypes.INSTANCE.PIPE_FORWARD, RmlTypes.INSTANCE.EQEQEQ, RmlTypes.INSTANCE.EQEQ, RmlTypes.INSTANCE.EQ, RmlTypes.INSTANCE.NOT_EQEQ, RmlTypes.INSTANCE.NOT_EQ, RmlTypes.INSTANCE.DIFF, RmlTypes.INSTANCE.COLON, RmlTypes.INSTANCE.SINGLE_QUOTE, RmlTypes.INSTANCE.DOUBLE_QUOTE, RmlTypes.INSTANCE.CARRET, RmlTypes.INSTANCE.PLUSDOT, RmlTypes.INSTANCE.MINUSDOT, RmlTypes.INSTANCE.SLASHDOT, RmlTypes.INSTANCE.STARDOT, RmlTypes.INSTANCE.PLUS, RmlTypes.INSTANCE.MINUS, RmlTypes.INSTANCE.SLASH, RmlTypes.INSTANCE.STAR, RmlTypes.INSTANCE.PERCENT, RmlTypes.INSTANCE.PIPE, RmlTypes.INSTANCE.ARROBASE, RmlTypes.INSTANCE.SHARP, RmlTypes.INSTANCE.SHARPSHARP, RmlTypes.INSTANCE.QUESTION_MARK, RmlTypes.INSTANCE.EXCLAMATION_MARK, RmlTypes.INSTANCE.LT_OR_EQUAL, RmlTypes.INSTANCE.GT_OR_EQUAL, RmlTypes.INSTANCE.AMPERSAND, RmlTypes.INSTANCE.LEFT_ARROW, RmlTypes.INSTANCE.RIGHT_ARROW, RmlTypes.INSTANCE.COLON_EQ, RmlTypes.INSTANCE.COLON_GT, RmlTypes.INSTANCE.GT, RmlTypes.INSTANCE.GT_BRACE, RmlTypes.INSTANCE.GT_BRACKET, RmlTypes.INSTANCE.BRACKET_GT, RmlTypes.INSTANCE.BRACKET_LT, RmlTypes.INSTANCE.BRACE_LT, RmlTypes.INSTANCE.DOTDOT, RmlTypes.INSTANCE.STRING_CONCAT); public static final Set OPTION_TYPES = of(RmlTypes.INSTANCE.NONE, RmlTypes.INSTANCE.SOME); public static final Set MACRO_TYPES = Collections.emptySet(); @Override public @NotNull SyntaxHighlighter getSyntaxHighlighter(@Nullable Project project, @Nullable VirtualFile virtualFile) { return new ORSyntaxHighlighter(RmlTypes.INSTANCE, KEYWORD_TYPES, OPERATION_SIGN_TYPES, OPTION_TYPES, MACRO_TYPES); } } ================================================ FILE: src/main/java/com/reason/ide/hints/CodeLens.java ================================================ package com.reason.ide.hints; import com.intellij.openapi.editor.*; import com.intellij.openapi.util.*; import org.jetbrains.annotations.*; import java.util.*; public class CodeLens { public static final Key CODE_LENS = Key.create("reasonml.codelens"); @NotNull final Map m_signatures = new HashMap<>(); public synchronized @Nullable String get(int line) { InferredTypes.LogicalPositionSignature signature = m_signatures.get(line); return signature == null ? null : signature.signature; } public synchronized void putAll(@NotNull Map signatures) { m_signatures.clear(); m_signatures.putAll(signatures); } public synchronized void move(@NotNull LogicalPosition cursorPosition, int direction) { Map newSignatures = new HashMap<>(); int startLine = cursorPosition.line; for (Map.Entry signatureEntry : m_signatures.entrySet()) { Integer line = signatureEntry.getKey(); InferredTypes.LogicalPositionSignature value = signatureEntry.getValue(); if (startLine == line) { if (cursorPosition.column < value.colEnd) { newSignatures.put(line + direction, value); } else { newSignatures.put(line, value); } } else if (startLine < line) { newSignatures.put(line + direction, value); } else { newSignatures.put(line, value); } } m_signatures.clear(); m_signatures.putAll(newSignatures); } public void remove(int line) { m_signatures.remove(line); } } ================================================ FILE: src/main/java/com/reason/ide/hints/InferredTypes.java ================================================ package com.reason.ide.hints; import com.intellij.openapi.editor.*; import com.reason.lang.*; import com.reason.lang.core.psi.*; import org.jetbrains.annotations.*; import java.util.*; public interface InferredTypes { class LogicalPositionSignature { String signature; int colStart; int colEnd; } @NotNull Map signaturesByLines(@Nullable ORLanguageProperties lang); @Nullable RPsiSignature getSignatureByPosition(@NotNull LogicalPosition elementPosition); } ================================================ FILE: src/main/java/com/reason/ide/hints/InferredTypesImplementation.java ================================================ package com.reason.ide.hints; import com.intellij.openapi.editor.*; import com.intellij.openapi.project.*; import com.intellij.psi.*; import com.intellij.psi.util.*; import com.intellij.util.containers.Stack; import com.reason.lang.*; import com.reason.lang.core.psi.*; import com.reason.lang.ocaml.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; import java.util.*; public class InferredTypesImplementation implements InferredTypes { private static final String OPEN = "Op"; private static final String MODULE_GHOST = "Mg"; private static final String VALUE = "Va"; private static final String IDENT = "Id"; private static final String PARAM = "Pa"; private static final String RECORD_FIELD = "Rf"; private final Map> myOpens = new HashMap<>(); private final Map myVals = new HashMap<>(); private final Map mySignatures = new HashMap<>(); public @NotNull Map signaturesByLines(@Nullable ORLanguageProperties lang) { Map result = new HashMap<>(); for (Stack openStack : myOpens.values()) { for (OpenModule openModule : openStack) { String exposing = openModule.getExposing(); if (exposing != null) { result.put(openModule.getLine(), makeLogicalPositionSignature(0, 0, exposing)); } } } for (Map.Entry entry : myVals.entrySet()) { result.put(entry.getKey(), makeLogicalPositionSignature(lang, entry.getValue())); } return result; } private @NotNull LogicalPositionSignature makeLogicalPositionSignature(@Nullable ORLanguageProperties lang, @NotNull LogicalORSignature value) { return makeLogicalPositionSignature(value.getLogicalStart().column, value.getLogicalEnd().column, value.getSignature().asText(lang)); } private @NotNull LogicalPositionSignature makeLogicalPositionSignature(int colStart, int colEnd, @NotNull String signature) { LogicalPositionSignature result = new LogicalPositionSignature(); result.colStart = colStart; result.colEnd = colEnd; result.signature = signature; return result; } @Override public @Nullable RPsiSignature getSignatureByPosition(@NotNull LogicalPosition elementPosition) { return mySignatures.get(elementPosition); } public void add(@NotNull Project project, @NotNull String entry, @NotNull LogicalPosition start, @NotNull LogicalPosition end, @NotNull String line) { switch (entry) { case OPEN: // Pattern :: Name Stack openStack = myOpens.computeIfAbsent(line, k -> new Stack<>()); openStack.push(new OpenModule(start)); break; case VALUE: { // Pattern :: Name|type String[] tokens = line.split("\\|", 2); PsiFile psiFile = PsiFileFactory.getInstance(project).createFileFromText("Dummy", OclLanguage.INSTANCE, "let x:" + tokens[1]); RPsiSignature psiSignature = PsiTreeUtil.findChildOfType(psiFile, RPsiSignature.class); if (psiSignature != null) { addVisibleSignature(start, end, psiSignature); mySignatures.put(start, psiSignature); } break; } case MODULE_GHOST: { // Pattern :: name|type String[] tokens = line.split("\\|", 2); String signature = tokens[1].startsWith("type t = ") ? tokens[1].substring(9) : tokens[1]; PsiFile psiFile = PsiFileFactory.getInstance(project).createFileFromText("Dummy", OclLanguage.INSTANCE, "let x:" + signature); RPsiSignature psiSignature = PsiTreeUtil.findChildOfType(psiFile, RPsiSignature.class); if (psiSignature != null) { addVisibleSignature(start, end, psiSignature); mySignatures.put(start, psiSignature); } break; } case IDENT: { // Pattern :: name|qName|type String[] tokens = line.split("\\|", 3); PsiFile psiFile = PsiFileFactory.getInstance(project).createFileFromText("Dummy", OclLanguage.INSTANCE, "let x:" + tokens[2]); RPsiSignature psiSignature = PsiTreeUtil.findChildOfType(psiFile, RPsiSignature.class); if (psiSignature != null) { mySignatures.put(start, psiSignature); } if (!tokens[0].equals(tokens[1])) { int lastDot = tokens[1].lastIndexOf("."); if (0 < lastDot) { String path = tokens[1].substring(0, lastDot); Stack open = myOpens.get(path); if (open != null) { open.peek().addId(tokens[0]); } } } break; } case PARAM: { // Pattern :: name|type String[] tokens = line.split("\\|", 2); PsiFile psiFile = PsiFileFactory.getInstance(project).createFileFromText("Dummy", OclLanguage.INSTANCE, "let x:" + tokens[1]); RPsiSignature psiSignature = PsiTreeUtil.findChildOfType(psiFile, RPsiSignature.class); if (psiSignature != null) { mySignatures.put(start, psiSignature); } } case RECORD_FIELD: { // Pattern :: name|type String[] tokens = line.split("\\|", 2); PsiFile psiFile = PsiFileFactory.getInstance(project).createFileFromText("Dummy", OclLanguage.INSTANCE, "let x:" + tokens[1]); RPsiSignature psiSignature = PsiTreeUtil.findChildOfType(psiFile, RPsiSignature.class); if (psiSignature != null) { mySignatures.put(start, psiSignature); } } } } private void addVisibleSignature(@NotNull LogicalPosition lStart, @NotNull LogicalPosition lEnd, @NotNull RPsiSignature signature) { LogicalORSignature savedSignature = myVals.get(lStart.line); if (savedSignature == null || lStart.column < savedSignature.getLogicalStart().column) { myVals.put(lStart.line, new LogicalORSignature(lStart, lEnd, signature)); } } static class OpenModule { @NotNull private final LogicalPosition m_position; private final Set m_values = new HashSet<>(); OpenModule(@NotNull LogicalPosition start) { m_position = start; } Integer getLine() { return m_position.line; } @Nullable String getExposing() { return m_values.isEmpty() ? null : "exposing: " + Joiner.join(", ", m_values); } void addId(String id) { m_values.add(id); } } static class LogicalORSignature { @NotNull private final LogicalPosition m_lStart; @NotNull private final LogicalPosition m_lEnd; @NotNull private final RPsiSignature m_signature; LogicalORSignature(@NotNull LogicalPosition lStart, @NotNull LogicalPosition lEnd, @NotNull RPsiSignature signature) { m_lStart = lStart; m_lEnd = lEnd; m_signature = signature; } @NotNull LogicalPosition getLogicalStart() { return m_lStart; } @NotNull LogicalPosition getLogicalEnd() { return m_lEnd; } @NotNull public RPsiSignature getSignature() { return m_signature; } } } ================================================ FILE: src/main/java/com/reason/ide/hints/InferredTypesService.java ================================================ package com.reason.ide.hints; import com.intellij.openapi.application.*; import com.intellij.openapi.editor.*; import com.intellij.openapi.fileEditor.*; import com.intellij.openapi.fileTypes.*; import com.intellij.openapi.project.*; import com.intellij.openapi.vfs.*; import com.intellij.psi.*; import com.intellij.util.concurrency.*; import com.reason.*; import com.reason.comp.*; import com.reason.hints.*; import com.reason.ide.*; import com.reason.ide.files.*; import com.reason.lang.*; import com.reason.lang.core.psi.impl.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; import java.nio.file.*; import java.util.concurrent.*; import static com.reason.comp.ORCompiler.CompilerType.*; import static com.reason.ide.hints.CodeLens.*; public class InferredTypesService { private static final Log LOG = Log.create("hints.inferredTypes"); private InferredTypesService() { } public static @Nullable PsiFile getPsiFile(@NotNull Project project) { Editor selectedTextEditor = FileEditorManager.getInstance(project).getSelectedTextEditor(); if (selectedTextEditor != null && !selectedTextEditor.isDisposed()) { Document document = selectedTextEditor.getDocument(); PsiFile psiFile = PsiDocumentManager.getInstance(project).getPsiFile(document); if (psiFile instanceof FileBase && !FileHelper.isInterface(psiFile.getFileType())) { VirtualFile sourceFile = ORFileUtils.getVirtualFile(psiFile); FileType fileType = sourceFile == null ? null : sourceFile.getFileType(); return FileHelper.isCompilable(fileType) ? psiFile : null; } } return null; } public static void queryTypes(@NotNull Project project, @NotNull PsiFile psiFile) { try { // Try to get the inferred types cached at the psi file user data VirtualFile sourceFile = ORFileUtils.getVirtualFile(psiFile); Application application = ApplicationManager.getApplication(); SignatureProvider.InferredTypesWithLines sigContext = psiFile.getUserData(SignatureProvider.SIGNATURES_CONTEXT); InferredTypes signatures = sigContext == null ? null : sigContext.getTypes(); ORLanguageProperties languageProperties = ORLanguageProperties.cast(psiFile.getLanguage()); if (signatures == null) { InsightManager insightManager = project.getService(InsightManager.class); // Find namespace if ocaml is compiled through dune final String[] namespace = {""}; ORResolvedCompiler compiler = project.getService(ORCompilerManager.class).getCompiler(sourceFile); if (compiler != null && compiler.getType() == DUNE) { VirtualFile duneSource = ORFileUtils.findAncestor(project, sourceFile, "dune"); PsiFile dune = duneSource == null ? null : PsiManager.getInstance(project).findFile(duneSource); if (dune instanceof DuneFile) { RPsiDuneStanza library = ((DuneFile) dune).getStanza("library"); RPsiDuneField name = library == null ? null : library.getField("name"); if (name == null && library != null) { name = library.getField("public_name"); } if (name != null) { namespace[0] = StringUtil.toFirstLower(name.getValue()) + "__"; } } } if (!DumbService.isDumb(project)) { ReadAction.nonBlocking((Callable) () -> { LOG.debug("Reading types from file", psiFile); String nameWithoutExtension = sourceFile == null ? "" : sourceFile.getNameWithoutExtension(); VirtualFile cmtFile = ORFileUtils.findCmtFileFromSource(project, nameWithoutExtension, namespace[0]); if (cmtFile != null) { Path cmtPath = FileSystems.getDefault().getPath(cmtFile.getPath()); insightManager.queryTypes(sourceFile, cmtPath, types -> application.runReadAction(() -> annotatePsiFile(project, languageProperties, sourceFile, types))); } return null; }) .coalesceBy(insightManager) .submit(AppExecutorUtil.getAppExecutorService()); } } else { LOG.debug("Signatures found in user data cache"); application.runReadAction(() -> annotatePsiFile(project, languageProperties, sourceFile, signatures)); } } catch (Error e) { // might produce an AssertionError when project is being disposed, but the invokeLater still // process that code } } public static void annotatePsiFile(@NotNull Project project, @Nullable ORLanguageProperties lang, @Nullable VirtualFile sourceFile, @Nullable InferredTypes types) { if (types == null || sourceFile == null) { return; } if (FileHelper.isInterface(sourceFile.getFileType())) { return; } LOG.debug("Updating signatures in user data cache for file", sourceFile); getSignatures(sourceFile).putAll(types.signaturesByLines(lang)); PsiFile psiFile = PsiManager.getInstance(project).findFile(sourceFile); if (psiFile != null && !FileHelper.isInterface(psiFile.getFileType())) { String[] lines = psiFile.getText().split("\n"); psiFile.putUserData(SignatureProvider.SIGNATURES_CONTEXT, new SignatureProvider.InferredTypesWithLines(types, lines)); } } public static @NotNull CodeLens getSignatures(@NotNull VirtualFile file) { CodeLens userData = file.getUserData(CODE_LENS); if (userData == null) { userData = new CodeLens(); file.putUserData(CODE_LENS, userData); } return userData; } } ================================================ FILE: src/main/java/com/reason/ide/hints/OREditorLinePainter.java ================================================ package com.reason.ide.hints; import com.intellij.openapi.editor.*; import com.intellij.openapi.editor.colors.*; import com.intellij.openapi.editor.markup.*; import com.intellij.openapi.project.*; import com.intellij.openapi.vfs.*; import com.reason.ide.highlight.*; import org.jetbrains.annotations.*; import java.util.*; import static com.reason.ide.hints.CodeLens.*; public class OREditorLinePainter extends EditorLinePainter { @Override public @Nullable Collection getLineExtensions(@NotNull Project project, @NotNull VirtualFile file, int lineNumber) { Collection result = null; CodeLens signatures = file.getUserData(CODE_LENS); if (signatures != null) { String signature = signatures.get(lineNumber); if (signature != null) { EditorColorsScheme globalScheme = EditorColorsManager.getInstance().getGlobalScheme(); TextAttributes codeLens = globalScheme.getAttributes(ORSyntaxHighlighter.CODE_LENS_); result = List.of(new LineExtensionInfo(" ", TextAttributes.ERASE_MARKER), new LineExtensionInfo(signature, codeLens)); } } return result; } } ================================================ FILE: src/main/java/com/reason/ide/hints/ORParameterInfoHandler.java ================================================ package com.reason.ide.hints; import com.intellij.lang.parameterInfo.*; import com.intellij.openapi.util.*; import com.intellij.psi.*; import com.reason.ide.search.reference.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; public abstract class ORParameterInfoHandler implements ParameterInfoHandler { protected static final Log LOG = Log.create("param"); @Nullable abstract RPsiParameters findFunctionParams(@NotNull PsiFile file, int offset); abstract int computeParameterIndex(@NotNull RPsiParameters paramsOwner, @NotNull UpdateParameterInfoContext context); @Override public @Nullable RPsiParameters findElementForParameterInfo(@NotNull CreateParameterInfoContext context) { RPsiParameters paramsOwner = findFunctionParams(context.getFile(), context.getOffset()); if (paramsOwner != null) { if (LOG.isDebugEnabled()) { LOG.debug("Found parameters for function", paramsOwner.getParent().getText()); } context.setItemsToShow(calculateParameterInfo(paramsOwner)); } return paramsOwner; } @Override public @Nullable RPsiParameters findElementForUpdatingParameterInfo(@NotNull UpdateParameterInfoContext context) { RPsiParameters paramsOwner = findFunctionParams(context.getFile(), context.getOffset()); if (paramsOwner != null) { PsiElement currentOwner = context.getParameterOwner(); if (currentOwner == null || currentOwner == paramsOwner) { return paramsOwner; } } return null; } @Override public void showParameterInfo(@NotNull RPsiParameters paramsOwner, @NotNull CreateParameterInfoContext context) { context.showHint(paramsOwner, paramsOwner.getTextOffset(), this); } @Override public void updateParameterInfo(@NotNull RPsiParameters paramsOwner, @NotNull UpdateParameterInfoContext context) { if (context.getParameterOwner() == null || paramsOwner.equals(context.getParameterOwner())) { int paramIndex = computeParameterIndex(paramsOwner, context); context.setParameterOwner(paramsOwner); context.setCurrentParameter(paramIndex); } else { context.removeHint(); } } @Override public void updateUI(@Nullable ORParameterInfoHandler.ArgumentsDescription arguments, @NotNull ParameterInfoUIContext context) { if (arguments == null) { context.setUIComponentEnabled(false); return; } int paramIndex = context.getCurrentParameterIndex(); TextRange paramRange = arguments.getRange(paramIndex); context.setupUIComponentPresentation( arguments.getText(), paramRange.getStartOffset(), paramRange.getEndOffset(), !context.isUIComponentEnabled(), false, true, context.getDefaultParameterColor()); } @Nullable ArgumentsDescription[] calculateParameterInfo(@NotNull RPsiParameters paramsOwner) { PsiElement resolvedElement = null; PsiElement parent = paramsOwner.getParent(); if (parent instanceof RPsiFunctionCall) { PsiElement functionName = parent.getFirstChild(); PsiReference reference = functionName == null ? null : functionName.getReference(); if (reference instanceof ORPsiLowerSymbolReference lowerSymbolReference) { PsiElement resolvedRef = lowerSymbolReference.resolveInterface(); resolvedElement = (resolvedRef instanceof RPsiLowerSymbol) ? resolvedRef.getParent() : resolvedRef; } } if (resolvedElement instanceof PsiQualifiedNamedElement) { LOG.trace("Resolved element", resolvedElement); if (resolvedElement instanceof RPsiSignatureElement) { RPsiSignature signature = ((RPsiSignatureElement) resolvedElement).getSignature(); if (signature != null) { return new ArgumentsDescription[]{new ArgumentsDescription((PsiQualifiedNamedElement) resolvedElement, signature)}; } } } return null; } public static class ArgumentsDescription { final PsiQualifiedNamedElement resolvedElement; final RPsiSignature signature; final String[] arguments; // ? public ArgumentsDescription(PsiQualifiedNamedElement resolvedElement, @NotNull RPsiSignature signature) { this.resolvedElement = resolvedElement; this.signature = signature; this.arguments = signature.getItems().stream().map(PsiElement::getText).toArray(String[]::new); } String getText() { return signature.getText(); } @NotNull TextRange getRange(int index) { if (index < 0 || index >= arguments.length) { return TextRange.EMPTY_RANGE; } return signature.getItems().get(index).getTextRangeInParent(); } } } ================================================ FILE: src/main/java/com/reason/ide/hints/OclParameterInfoHandler.java ================================================ package com.reason.ide.hints; import com.intellij.lang.parameterInfo.*; import com.intellij.psi.*; import com.intellij.psi.util.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.impl.*; import com.reason.lang.ocaml.*; import org.jetbrains.annotations.*; public class OclParameterInfoHandler extends ORParameterInfoHandler { @Override public boolean isWhitespaceSensitive() { return true; } @Override int computeParameterIndex(@NotNull RPsiParameters paramsOwner, @NotNull UpdateParameterInfoContext context) { return ParameterInfoUtils.getCurrentParameterIndex(paramsOwner.getNode(), context.getOffset(), TokenType.WHITE_SPACE); } @Override @Nullable RPsiParameters findFunctionParams(@NotNull PsiFile file, int offset) { PsiElement elementAt = file.findElementAt(offset); if (elementAt == null) { // Maybe at the end of file if (offset == file.getTextLength()) { elementAt = file.findElementAt(offset - 1); } } if (elementAt instanceof PsiWhiteSpace) { elementAt = PsiTreeUtil.prevVisibleLeaf(elementAt); if (elementAt != null && elementAt.getNode().getElementType() == OclTypes.INSTANCE.LIDENT) { elementAt = elementAt.getParent(); } } if (elementAt instanceof RPsiFunctionCall) { return ORUtil.findImmediateFirstChildOfClass(elementAt, RPsiParameters.class); } return elementAt == null ? null : PsiTreeUtil.getParentOfType(elementAt, RPsiParameters.class); } } ================================================ FILE: src/main/java/com/reason/ide/hints/ResParameterInfoHandler.java ================================================ package com.reason.ide.hints; import com.intellij.lang.parameterInfo.*; import com.intellij.psi.*; import com.intellij.psi.util.*; import com.reason.lang.core.psi.impl.*; import com.reason.lang.rescript.*; import org.jetbrains.annotations.*; public class ResParameterInfoHandler extends ORParameterInfoHandler { @Override int computeParameterIndex(@NotNull RPsiParameters paramsOwner, @NotNull UpdateParameterInfoContext context) { return ParameterInfoUtils.getCurrentParameterIndex(paramsOwner.getNode(), context.getOffset(), ResTypes.INSTANCE.COMMA); } @Override @Nullable RPsiParameters findFunctionParams(@NotNull PsiFile file, int offset) { PsiElement elementAt = file.findElementAt(offset); return elementAt == null ? null : PsiTreeUtil.getParentOfType(elementAt, RPsiParameters.class); } } ================================================ FILE: src/main/java/com/reason/ide/hints/RmlParameterInfoHandler.java ================================================ package com.reason.ide.hints; import com.intellij.lang.parameterInfo.*; import com.intellij.psi.*; import com.intellij.psi.util.*; import com.reason.lang.core.psi.impl.*; import com.reason.lang.reason.*; import org.jetbrains.annotations.*; public class RmlParameterInfoHandler extends ORParameterInfoHandler { @Override int computeParameterIndex(@NotNull RPsiParameters paramsOwner, @NotNull UpdateParameterInfoContext context) { return ParameterInfoUtils.getCurrentParameterIndex(paramsOwner.getNode(), context.getOffset(), RmlTypes.INSTANCE.COMMA); } @Override @Nullable RPsiParameters findFunctionParams(@NotNull PsiFile file, int offset) { PsiElement elementAt = file.findElementAt(offset); return elementAt == null ? null : PsiTreeUtil.getParentOfType(elementAt, RPsiParameters.class); } } ================================================ FILE: src/main/java/com/reason/ide/hints/RmlTypeProvider.java ================================================ package com.reason.ide.hints; import com.intellij.lang.*; import com.intellij.psi.*; import org.jetbrains.annotations.*; import java.util.*; public class RmlTypeProvider extends ExpressionTypeProvider { @Override public @NotNull String getInformationHint(@NotNull PsiElement element) { return "Hint"; } @Override public @NotNull String getErrorHint() { return "Can't understand target"; } @Override public @NotNull List getExpressionsAt(@NotNull PsiElement elementAt) { return Collections.emptyList(); } } ================================================ FILE: src/main/java/com/reason/ide/hints/SignatureProvider.java ================================================ package com.reason.ide.hints; import com.intellij.openapi.editor.*; import com.intellij.openapi.util.*; import com.reason.ide.*; import com.reason.lang.core.psi.*; import org.jetbrains.annotations.*; // just an experiment, cancelled for now public class SignatureProvider /*implements InlayParameterHintsProvider*/ { private SignatureProvider() { } public static class InferredTypesWithLines { private final InferredTypes myTypes; private final EditorPosition myEditorPosition; InferredTypesWithLines(@NotNull InferredTypes types, @NotNull String[] lines) { myTypes = types; myEditorPosition = new EditorPosition(lines); } public @Nullable RPsiSignature getSignatureByOffset(int textOffset) { LogicalPosition elementPosition = myEditorPosition.getPositionFromOffset(textOffset); return elementPosition == null ? null : myTypes.getSignatureByPosition(elementPosition); } public @NotNull InferredTypes getTypes() { return myTypes; } } public static final Key SIGNATURES_CONTEXT = Key.create("REASONML_SIGNATURES_CONTEXT"); } ================================================ FILE: src/main/java/com/reason/ide/importWizard/DuneExternalConstants.java ================================================ package com.reason.ide.importWizard; public class DuneExternalConstants { public static final String PROJECT_BUILD_FILE = "dune-project"; public static final String BUILD_FILE = "dune"; private DuneExternalConstants() { } } ================================================ FILE: src/main/java/com/reason/ide/importWizard/DuneProjectImportBuilder.java ================================================ package com.reason.ide.importWizard; import com.intellij.openapi.application.*; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.*; import com.intellij.openapi.progress.*; import com.intellij.openapi.project.*; import com.intellij.openapi.roots.*; import com.intellij.openapi.roots.ui.configuration.*; import com.intellij.openapi.vfs.*; import com.intellij.packaging.artifacts.*; import com.intellij.projectImport.*; import com.intellij.psi.*; import com.intellij.psi.impl.*; import com.intellij.psi.util.*; import com.reason.ide.*; import com.reason.ide.files.*; import com.reason.ide.settings.*; import com.reason.lang.core.psi.impl.*; import org.jetbrains.annotations.*; import javax.swing.*; import java.util.*; /** * Builder used when using menu File | New | Project from existing sources */ public class DuneProjectImportBuilder extends ProjectImportBuilder { private VirtualFile myProjectRoot = null; private List myFoundDuneBuilds = new ArrayList<>(); private OpamSettings myOpamSettings; @NonNls @Override public @NotNull String getName() { return "Dune Import Builder"; } @Override public Icon getIcon() { return ORIcons.DUNE; } @Override public void setList(@Nullable List selectedDuneApps) { if (selectedDuneApps != null) { myFoundDuneBuilds = selectedDuneApps; } } @Override public boolean isMarked(ImportedDuneBuild importedDuneBuild) { return false; } @Override public void setOpenProjectSettingsAfter(boolean on) { } @Override public void cleanup() { myProjectRoot = null; myFoundDuneBuilds = new ArrayList<>(); } public void setOpamSettings(OpamSettings settings) { myOpamSettings = settings; } public void setProjectRoot(@NotNull final VirtualFile projectRoot) { if (projectRoot.equals(myProjectRoot)) { return; } myProjectRoot = projectRoot; ProgressManager.getInstance().run(new Task.Modal(getCurrentProject(), "Scanning Dune Projects", true) { public void run(@NotNull final ProgressIndicator indicator) { myProjectRoot.refresh(false, true); VfsUtilCore.visitChildrenRecursively(myProjectRoot, new VirtualFileVisitor() { @Override public boolean visitFile(@NotNull VirtualFile file) { indicator.checkCanceled(); if (file.isDirectory()) { if (".git".equals(file.getName())) { return false; } indicator.setText2(file.getPath()); } else if (file.getName().equalsIgnoreCase(DuneExternalConstants.BUILD_FILE)) { myFoundDuneBuilds.add(new ImportedDuneBuild(file)); } return true; } }); } }); } @Override public @Nullable List commit(Project project, ModifiableModuleModel moduleModel, ModulesProvider modulesProvider, ModifiableArtifactModel artifactModel) { List createdModules = new ArrayList<>(); ModifiableModuleModel obtainedModuleModel = moduleModel == null ? ModuleManager.getInstance(project).getModifiableModel() : moduleModel; String ideaModuleDirPath = myProjectRoot.getCanonicalPath(); if (ideaModuleDirPath == null) { return createdModules; } Module module = obtainedModuleModel.newModule(ideaModuleDirPath, JavaModuleType.getModuleType().getId()); createdModules.add(module); ModifiableRootModel rootModel = ModuleRootManager.getInstance(module).getModifiableModel(); ContentEntry content = rootModel.addContentEntry(myProjectRoot); for (ImportedDuneBuild importedDuneBuild : myFoundDuneBuilds) { boolean isTest = false; PsiFile file = PsiManagerEx.getInstanceEx(project).getFileManager().findFile(importedDuneBuild.getBuild()); if (file instanceof DuneFile duneFile) { Collection stanzas = PsiTreeUtil.findChildrenOfType(duneFile, RPsiDuneStanza.class); Optional hasTest = stanzas.stream().map(RPsiDuneStanza::getName).filter(name -> name != null && name.startsWith("test")).findFirst(); isTest = hasTest.isPresent(); } content.addSourceFolder(importedDuneBuild.getRoot(), isTest); } // Commit project structure. ApplicationManager.getApplication().runWriteAction(() -> { rootModel.commit(); obtainedModuleModel.commit(); if (myOpamSettings != null) { ORSettings settings = project.getService(ORSettings.class); settings.setOpamLocation(myOpamSettings.getOpamLocation()); settings.setSwitchName(myOpamSettings.getOpamSwitch()); settings.setIsWsl(myOpamSettings.isWsl()); settings.setCygwinBash(myOpamSettings.getCygwinBash()); } }); return createdModules; } public interface OpamSettings { String getOpamLocation(); String getOpamSwitch(); boolean isWsl(); String getCygwinBash(); } } ================================================ FILE: src/main/java/com/reason/ide/importWizard/DuneProjectImportProvider.java ================================================ package com.reason.ide.importWizard; import com.intellij.ide.util.projectWizard.*; import com.intellij.openapi.vfs.*; import com.intellij.projectImport.*; import org.jetbrains.annotations.*; /** * An extension point for 'Import Module from Existing Sources' with ability to import Dune projects. */ public class DuneProjectImportProvider extends ProjectImportProvider { @Override protected ProjectImportBuilder doGetBuilder() { return ProjectImportBuilder.EXTENSIONS_POINT_NAME.findExtensionOrFail(DuneProjectImportBuilder.class); } @Override public ModuleWizardStep[] createSteps(WizardContext context) { return new ModuleWizardStep[]{ new DuneProjectRootStep(context) }; } public static boolean canImport(@NotNull VirtualFile entry) { if (entry.isDirectory()) { entry = entry.findChild(DuneExternalConstants.PROJECT_BUILD_FILE); } return entry != null && !entry.isDirectory() && DuneExternalConstants.PROJECT_BUILD_FILE.equals(entry.getName()); } @Override protected boolean canImportFromFile(@NotNull VirtualFile entry) { return canImport(entry); } @Override public @NotNull String getPathToBeImported(@NotNull VirtualFile file) { return file.isDirectory() ? file.getPath() : file.getParent().getPath(); } @Override public @NotNull String getFileSample() { return "Dune project file (dune-project)"; } } ================================================ FILE: src/main/java/com/reason/ide/importWizard/DuneProjectOpenProcessor.java ================================================ package com.reason.ide.importWizard; import com.intellij.ide.util.projectWizard.*; import com.intellij.openapi.vfs.*; import com.intellij.projectImport.*; import org.jetbrains.annotations.*; public class DuneProjectOpenProcessor extends ProjectOpenProcessorBase { @Override public @NotNull String[] getSupportedExtensions() { return new String[]{DuneExternalConstants.PROJECT_BUILD_FILE}; } @Override public boolean doQuickImport(@NotNull VirtualFile configFile, @NotNull WizardContext wizardContext) { VirtualFile projectRoot = configFile.getParent(); wizardContext.setProjectName(projectRoot.getName()); getBuilder().setProjectRoot(projectRoot); return true; } @Override protected @NotNull DuneProjectImportBuilder doGetBuilder() { return ProjectImportBuilder.EXTENSIONS_POINT_NAME.findExtensionOrFail(DuneProjectImportBuilder.class); } } ================================================ FILE: src/main/java/com/reason/ide/importWizard/DuneProjectRootStep.form ================================================

================================================ FILE: src/main/java/com/reason/ide/importWizard/DuneProjectRootStep.java ================================================ package com.reason.ide.importWizard; import com.intellij.ide.util.projectWizard.*; import com.intellij.openapi.fileChooser.*; import com.intellij.openapi.project.*; import com.intellij.openapi.ui.*; import com.intellij.openapi.util.text.*; import com.intellij.openapi.vfs.*; import com.intellij.projectImport.*; import com.reason.ide.settings.*; import javax.swing.*; public class DuneProjectRootStep extends ProjectImportWizardStep { private JPanel myPanel; private TextFieldWithBrowseButton myProjectRootComponent; private OpamConfigurationTab myOpamConfigurationTab; public DuneProjectRootStep(WizardContext context) { super(context); Project project = context.getProject(); context.setProjectBuilder(ProjectImportBuilder.EXTENSIONS_POINT_NAME.findExtensionOrFail(DuneProjectImportBuilder.class)); //noinspection DialogTitleCapitalization myProjectRootComponent.addBrowseFolderListener("Select `dune-project` of a Dune project to import", "", null, FileChooserDescriptorFactory.createSingleFolderDescriptor()); myProjectRootComponent.setText(context.getProjectFileDirectory()); // provide project path myOpamConfigurationTab.createComponent(project, ""); } @Override public JComponent getComponent() { return myPanel; } @Override public boolean validate() { String projectRootPath = myProjectRootComponent.getText(); if (StringUtil.isEmpty(projectRootPath)) { return false; } VirtualFile projectRoot = LocalFileSystem.getInstance().refreshAndFindFileByPath(projectRootPath); if (projectRoot == null) { return false; } DuneProjectImportBuilder builder = (DuneProjectImportBuilder) getWizardContext().getProjectBuilder(); if (builder == null) { return false; } builder.setProjectRoot(projectRoot); builder.setOpamSettings(new DuneProjectImportBuilder.OpamSettings() { @Override public String getOpamLocation() { return myOpamConfigurationTab.getOpamLocation().getText(); } @Override public String getOpamSwitch() { return myOpamConfigurationTab.getSelectedSwitch(); } @Override public boolean isWsl() { return myOpamConfigurationTab.isWsl(); } @Override public String getCygwinBash() { return myOpamConfigurationTab.getCygwinBash(); } }); return true; } @Override public void updateDataModel() { String projectRoot = myProjectRootComponent.getText(); if (!projectRoot.isEmpty()) { suggestProjectNameAndPath(null, projectRoot); } } } ================================================ FILE: src/main/java/com/reason/ide/importWizard/ImportedDuneBuild.java ================================================ package com.reason.ide.importWizard; import com.intellij.openapi.vfs.*; import org.jetbrains.annotations.*; public class ImportedDuneBuild { private final VirtualFile myBuild; public ImportedDuneBuild(@NotNull VirtualFile build) { myBuild = build; } @NotNull public VirtualFile getBuild() { return myBuild; } @NotNull public VirtualFile getRoot() { return myBuild.getParent(); } } ================================================ FILE: src/main/java/com/reason/ide/insight/CompletionUtils.java ================================================ package com.reason.ide.insight; import com.intellij.psi.PsiElement; import com.intellij.psi.tree.IElementType; import com.intellij.psi.util.PsiTreeUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; public class CompletionUtils { public static final int KEYWORD_PRIORITY = 10; private CompletionUtils() { } @Nullable static IElementType getPrevNodeType(@NotNull PsiElement element) { PsiElement prevLeaf = PsiTreeUtil.prevVisibleLeaf(element); return prevLeaf == null ? null : prevLeaf.getNode().getElementType(); } } ================================================ FILE: src/main/java/com/reason/ide/insight/KeywordCompletionContributor.java ================================================ package com.reason.ide.insight; import com.intellij.codeInsight.completion.*; import com.intellij.codeInsight.lookup.*; import com.intellij.openapi.project.*; import com.intellij.psi.*; import com.intellij.psi.tree.*; import com.intellij.util.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import com.reason.lang.core.type.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; import static com.intellij.patterns.PlatformPatterns.*; abstract class KeywordCompletionContributor extends com.intellij.codeInsight.completion.CompletionContributor implements DumbAware { private static final Log LOG = Log.create("insight.keyword"); protected static final InsertHandler INSERT_SPACE = new AddSpaceInsertHandler(false); KeywordCompletionContributor(@NotNull ORLangTypes types) { extend(CompletionType.BASIC, psiElement(), new CompletionProvider<>() { @Override protected void addCompletions(@NotNull CompletionParameters parameters, @NotNull ProcessingContext context, @NotNull CompletionResultSet result) { PsiElement position = parameters.getPosition(); PsiElement originalPosition = parameters.getOriginalPosition(); PsiElement element = originalPosition == null ? position : originalPosition; IElementType prevNodeType = CompletionUtils.getPrevNodeType(element); PsiElement parent = element.getParent(); if (LOG.isTraceEnabled()) { LOG.trace("»» Completion: position: " + position + ", " + position.getText()); LOG.trace(" original: " + originalPosition + ", " + (originalPosition == null ? null : originalPosition.getText())); LOG.trace(" element: " + element); LOG.trace(" parent: " + parent); LOG.trace(" file: " + parameters.getOriginalFile()); } if (originalPosition == null || originalPosition instanceof PsiWhiteSpace) { if (prevNodeType != types.DOT && prevNodeType != types.COMMA && prevNodeType != types.SHARPSHARP && prevNodeType != types.LIDENT && prevNodeType != types.EQ && prevNodeType != types.LT && prevNodeType != types.WITH && prevNodeType != types.LBRACKET) { if (!(parent instanceof RPsiTagStart || parent instanceof RPsiOpen || parent instanceof RPsiInclude)) { addFileKeywords(result); } } } } }); } protected abstract void addFileKeywords(@NotNull CompletionResultSet result); } ================================================ FILE: src/main/java/com/reason/ide/insight/ORCompletionContributor.java ================================================ package com.reason.ide.insight; import com.intellij.codeInsight.completion.*; import com.intellij.psi.*; import com.intellij.psi.search.*; import com.intellij.psi.tree.*; import com.intellij.psi.util.*; import com.intellij.util.*; import com.reason.ide.insight.provider.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import com.reason.lang.core.type.*; import com.reason.lang.reason.*; import com.reason.lang.rescript.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; abstract class ORCompletionContributor extends com.intellij.codeInsight.completion.CompletionContributor { static final Log LOG = Log.create("insight"); ORCompletionContributor(@NotNull ORLangTypes types) { extend(CompletionType.BASIC, com.intellij.patterns.PlatformPatterns.psiElement(), new CompletionProvider<>() { @Override protected void addCompletions(@NotNull CompletionParameters parameters, @NotNull ProcessingContext context, @NotNull CompletionResultSet result) { PsiElement position = parameters.getPosition(); PsiElement originalPosition = parameters.getOriginalPosition(); PsiElement element = originalPosition == null ? position : originalPosition; PsiElement prevLeaf = PsiTreeUtil.prevVisibleLeaf(element); IElementType prevNodeType = prevLeaf == null ? null : prevLeaf.getNode().getElementType(); PsiElement parent = element.getParent(); PsiElement grandParent = parent == null ? null : parent.getParent(); GlobalSearchScope searchScope = GlobalSearchScope.allScope(position.getProject()); if (LOG.isTraceEnabled()) { LOG.debug("»» Completion: position: " + position + ", " + position.getText()); LOG.debug(" original: " + originalPosition + ", " + (originalPosition == null ? null : originalPosition.getText())); LOG.debug(" element: " + element); LOG.debug(" parent: " + parent); LOG.debug(" grand-parent: " + grandParent); LOG.debug(" file: " + parameters.getOriginalFile()); LOG.debug(" search scope: " + searchScope); } // A comment, stop completion if (element instanceof PsiComment) { LOG.debug("comment, stop"); return; } // Just after an open/include keyword if (prevNodeType == types.OPEN || prevNodeType == types.INCLUDE) { LOG.debug("the previous keyword is OPEN/INCLUDE"); ModuleCompletionProvider.addCompletions(element, searchScope, result); return; } if (parent instanceof RPsiOpen || parent instanceof RPsiInclude || grandParent instanceof RPsiOpen || grandParent instanceof RPsiInclude) { LOG.debug("Inside OPEN/INCLUDE"); ModuleCompletionProvider.addCompletions(element, searchScope, result); return; } // Just after a DOT if (prevNodeType == types.DOT) { // But not in a guaranteed uncurried function assert prevLeaf != null; PsiElement prevPrevLeaf = prevLeaf.getPrevSibling(); if (prevPrevLeaf != null && prevPrevLeaf.getNode().getElementType() != types.LPAREN) { LOG.debug("the previous element is DOT"); if (parent instanceof RPsiTagStart) { LOG.debug("Inside a Tag start"); JsxNameCompletionProvider.addCompletions(element, types, searchScope, result); return; } DotExpressionCompletionProvider.addCompletions(element, searchScope, result); return; } } if (types == RmlTypes.INSTANCE && prevNodeType == types.SHARPSHARP) { LOG.debug("the previous element is SHARPSHARP"); ObjectCompletionProvider.addCompletions(element, parent, types, result); return; } if (types == ResTypes.INSTANCE && prevNodeType == types.LBRACKET) { PsiElement prevSibling = parent != null ? parent.getPrevSibling() : null; if (prevSibling instanceof RPsiLowerSymbol || prevSibling instanceof RPsiArray) { LOG.debug("the previous element is ["); if (ObjectCompletionProvider.addCompletions(element, parent, types, result)) { return; } } } // Jsx if (element instanceof RPsiUpperTagName) { LOG.debug("Previous element type is TAG_NAME"); JsxNameCompletionProvider.addCompletions(element, types, searchScope, result); return; } if (parent instanceof RPsiTagProperty /*inside the prop name*/ || parent instanceof RPsiTagStart || grandParent instanceof RPsiTagStart) { LOG.debug("Inside a Tag start"); JsxAttributeCompletionProvider.addCompletions(element, searchScope, result); return; } LOG.debug("Nothing found, free expression"); FreeExpressionCompletionProvider.addCompletions(element, parent, searchScope, result); } }); } } ================================================ FILE: src/main/java/com/reason/ide/insight/OclCompletionContributor.java ================================================ package com.reason.ide.insight; import com.reason.lang.ocaml.*; public class OclCompletionContributor extends ORCompletionContributor { OclCompletionContributor() { super(OclTypes.INSTANCE/*, OclQNameFinder.INSTANCE*/); } } ================================================ FILE: src/main/java/com/reason/ide/insight/OclKeywordCompletionContributor.java ================================================ package com.reason.ide.insight; import static com.reason.ide.insight.CompletionUtils.KEYWORD_PRIORITY; import com.intellij.codeInsight.completion.CompletionResultSet; import com.intellij.codeInsight.completion.PrioritizedLookupElement; import com.intellij.codeInsight.lookup.LookupElementBuilder; import com.reason.lang.ocaml.OclTypes; import org.jetbrains.annotations.NotNull; public class OclKeywordCompletionContributor extends KeywordCompletionContributor { public static final String[] KEYWORDS = new String[] {"open", "include", "module", "type", "let", "external", "exception"}; OclKeywordCompletionContributor() { super(OclTypes.INSTANCE); } @Override protected void addFileKeywords(@NotNull CompletionResultSet result) { for (String keyword : KEYWORDS) { LookupElementBuilder builder = LookupElementBuilder.create(keyword).withInsertHandler(INSERT_SPACE).bold(); result.addElement(PrioritizedLookupElement.withPriority(builder, KEYWORD_PRIORITY)); } } } ================================================ FILE: src/main/java/com/reason/ide/insight/ResCompletionContributor.java ================================================ package com.reason.ide.insight; import com.reason.lang.rescript.*; public class ResCompletionContributor extends ORCompletionContributor { ResCompletionContributor() { super(ResTypes.INSTANCE/*, ResQNameFinder.INSTANCE*/); } } ================================================ FILE: src/main/java/com/reason/ide/insight/ResKeywordCompletionContributor.java ================================================ package com.reason.ide.insight; import static com.reason.ide.insight.CompletionUtils.KEYWORD_PRIORITY; import com.intellij.codeInsight.completion.CompletionResultSet; import com.intellij.codeInsight.completion.PrioritizedLookupElement; import com.intellij.codeInsight.lookup.LookupElementBuilder; import com.reason.lang.rescript.ResTypes; import org.jetbrains.annotations.NotNull; public class ResKeywordCompletionContributor extends KeywordCompletionContributor { public static final String[] KEYWORDS = new String[]{"open", "include", "module", "type", "let", "external", "exception"}; ResKeywordCompletionContributor() { super(ResTypes.INSTANCE); } @Override protected void addFileKeywords(@NotNull CompletionResultSet result) { for (String keyword : KEYWORDS) { LookupElementBuilder builder = LookupElementBuilder.create(keyword).withInsertHandler(INSERT_SPACE).bold(); result.addElement(PrioritizedLookupElement.withPriority(builder, KEYWORD_PRIORITY)); } } } ================================================ FILE: src/main/java/com/reason/ide/insight/RmlCompletionContributor.java ================================================ package com.reason.ide.insight; import com.reason.lang.reason.*; public class RmlCompletionContributor extends ORCompletionContributor { RmlCompletionContributor() { super(RmlTypes.INSTANCE/*, RmlQNameFinder.INSTANCE*/); } } ================================================ FILE: src/main/java/com/reason/ide/insight/RmlKeywordCompletionContributor.java ================================================ package com.reason.ide.insight; import static com.reason.ide.insight.CompletionUtils.KEYWORD_PRIORITY; import com.intellij.codeInsight.completion.CompletionResultSet; import com.intellij.codeInsight.completion.PrioritizedLookupElement; import com.intellij.codeInsight.lookup.LookupElementBuilder; import com.reason.lang.reason.RmlTypes; import org.jetbrains.annotations.NotNull; public class RmlKeywordCompletionContributor extends KeywordCompletionContributor { public static final String[] KEYWORDS = new String[] {"open", "include", "module", "type", "let", "external", "exception"}; RmlKeywordCompletionContributor() { super(RmlTypes.INSTANCE); } @Override protected void addFileKeywords(@NotNull CompletionResultSet result) { for (String keyword : KEYWORDS) { LookupElementBuilder builder = LookupElementBuilder.create(keyword).withInsertHandler(INSERT_SPACE).bold(); result.addElement(PrioritizedLookupElement.withPriority(builder, KEYWORD_PRIORITY)); } } } ================================================ FILE: src/main/java/com/reason/ide/insight/pattern/ORElementPatternMatcher.java ================================================ package com.reason.ide.insight.pattern; import com.intellij.psi.PsiElement; import com.intellij.util.ProcessingContext; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @FunctionalInterface public interface ORElementPatternMatcher { boolean accepts(@NotNull PsiElement element, @Nullable ProcessingContext context); } ================================================ FILE: src/main/java/com/reason/ide/insight/provider/DotExpressionCompletionProvider.java ================================================ package com.reason.ide.insight.provider; import com.intellij.codeInsight.completion.*; import com.intellij.codeInsight.lookup.*; import com.intellij.openapi.project.*; import com.intellij.openapi.vfs.*; import com.intellij.psi.*; import com.intellij.psi.impl.*; import com.intellij.psi.search.*; import com.intellij.psi.util.*; import com.intellij.util.*; import com.reason.ide.files.*; import com.reason.ide.search.index.*; import com.reason.ide.search.reference.*; import com.reason.lang.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import com.reason.lang.reason.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; import java.util.*; public class DotExpressionCompletionProvider { private static final Log LOG = Log.create("insight.dot"); private DotExpressionCompletionProvider() { } public static void addCompletions(@NotNull PsiElement element, @NotNull GlobalSearchScope searchScope, @NotNull CompletionResultSet resultSet) { LOG.debug("DOT expression completion"); PsiElement dotLeaf = PsiTreeUtil.prevVisibleLeaf(element); PsiElement previousElement = dotLeaf == null ? null : dotLeaf.getPrevSibling(); ORLanguageProperties langProperties = ORLanguageProperties.cast(element.getLanguage()); if (previousElement instanceof RPsiUpperSymbol previousUpper) { // File. // File.Module. LOG.debug(" -> upper symbol", previousUpper); PsiElement resolvedElement = previousUpper.getReference().resolveInterface(); LOG.debug(" -> resolved to", resolvedElement); Collection expressions = new ArrayList<>(); if (resolvedElement instanceof RPsiInnerModule) { addInnerModuleExpressions((RPsiInnerModule) resolvedElement, expressions, searchScope); } else if (resolvedElement instanceof RPsiFunctor) { addFunctorExpressions((RPsiFunctor) resolvedElement, expressions, searchScope); } else if (resolvedElement instanceof FileBase) { addFileExpressions((FileBase) resolvedElement, expressions, searchScope); } if (expressions.isEmpty()) { LOG.trace(" -> no expressions found"); } else { LOG.trace(" -> expressions", expressions); addExpressions(resultSet, expressions, langProperties); } } else if (previousElement instanceof RPsiLowerSymbol previousLower) { // Records: let x = {a:1, b:2}; x. // let x: z = y; x. // let x = { y: { a: 1 } }; x.y. LOG.debug(" -> lower symbol", previousLower); PsiElement resolvedElement = previousLower.getReference().resolveInterface(); if (LOG.isDebugEnabled()) { LOG.debug(" -> resolved to", resolvedElement == null ? null : resolvedElement.getParent()); } if (resolvedElement instanceof RPsiVar resolvedVar) { addRecordFields(resolvedVar.getRecordFields(), langProperties, resultSet); } else if (resolvedElement instanceof RPsiRecordField resolvedField) { RPsiFieldValue fieldValue = resolvedField.getValue(); PsiElement firstChild = fieldValue != null ? fieldValue.getFirstChild() : null; if (firstChild instanceof RPsiRecord recordChild) { addRecordFields(recordChild.getFields(), langProperties, resultSet); } } else if (resolvedElement instanceof RPsiSignatureElement resolvedSignatureElement) { RPsiSignature signature = resolvedSignatureElement.getSignature(); List items = signature != null ? signature.getItems() : null; if (items != null && items.size() == 1) { RPsiLowerSymbol signatureSymbol = ORUtil.findImmediateLastChildOfClass(items.getFirst(), RPsiLowerSymbol.class); PsiReference reference = signatureSymbol != null ? signatureSymbol.getReference() : null; PsiElement resolve = reference != null ? reference.resolve() : null; if (resolve instanceof RPsiType signatureType) { addRecordFields(signatureType.getRecordFields(), langProperties, resultSet); } } } } } private static void addRecordFields(@Nullable Collection recordFields, @Nullable ORLanguageProperties langProperties, @NotNull CompletionResultSet resultSet) { if (recordFields != null) { for (RPsiRecordField recordField : recordFields) { resultSet.addElement( LookupElementBuilder.create(recordField) .withTypeText(RPsiSignatureUtil.getSignature(recordField, langProperties)) .withIcon(PsiIconUtil.getIconFromProviders(recordField, 0))); } } } private static void addModuleExpressions(@Nullable PsiElement resolvedElement, @NotNull Collection expressions, @NotNull GlobalSearchScope scope) { if (resolvedElement instanceof RPsiInnerModule) { addInnerModuleExpressions((RPsiInnerModule) resolvedElement, expressions, scope); } else if (resolvedElement instanceof FileBase) { addFileExpressions((FileBase) resolvedElement, expressions, scope); } else if (resolvedElement instanceof RPsiFunctor) { RPsiFunctorResult returnType = ((RPsiFunctor) resolvedElement).getReturnType(); if (returnType == null) { addChildren(((RPsiFunctor) resolvedElement).getBody(), expressions); } else { RPsiUpperSymbol referenceIdentifier = returnType.getModuleType(); PsiElement resolvedResult = referenceIdentifier == null ? null : referenceIdentifier.getReference().resolveInterface(); if (resolvedResult != null) { addModuleExpressions(resolvedResult, expressions, scope); } } } } private static void addInnerModuleExpressions(@NotNull RPsiInnerModule module, @NotNull Collection expressions, @NotNull GlobalSearchScope scope) { PsiElement signature = module.getModuleSignature(); PsiElement body = signature != null ? signature : module.getBody(); addChildren(body, expressions); Project project = module.getProject(); Collection alternateQNames = ORModuleResolutionPsiGist.getData(module.getContainingFile()).getValues(module); for (String alternateQName : alternateQNames) { if (alternateQName.contains(".")) { for (RPsiModule alternateModule : ModuleFqnIndex.getElements(alternateQName, project, scope)) { if (alternateModule != module) { addModuleExpressions(alternateModule, expressions, scope); } } } else { for (VirtualFile topModule : FileModuleIndex.getContainingFiles(alternateQName, scope)) { PsiFile file = PsiManagerEx.getInstanceEx(project).findFile(topModule); addModuleExpressions(file, expressions, scope); } } } } private static void addFileExpressions(@NotNull FileBase file, @NotNull Collection expressions, @NotNull GlobalSearchScope scope) { Collection alternativeQNames = ORModuleResolutionPsiGist.getData(file).getValues(file); for (String alternativeQName : alternativeQNames) { for (RPsiModule module : ModuleFqnIndex.getElements(alternativeQName, file.getProject(), scope)) { addModuleExpressions(module, expressions, scope); } } expressions.addAll(PsiTreeUtil.getStubChildrenOfTypeAsList(file, RPsiType.class)); List lets = PsiTreeUtil.getStubChildrenOfTypeAsList(file, RPsiLet.class); if (file.getLanguage() == RmlLanguage.INSTANCE) { for (RPsiLet let : lets) { if (!let.isPrivate()) { expressions.add(let); } } } else { expressions.addAll(lets); } expressions.addAll(PsiTreeUtil.getStubChildrenOfTypeAsList(file, RPsiVal.class)); expressions.addAll(PsiTreeUtil.getStubChildrenOfTypeAsList(file, RPsiInnerModule.class)); expressions.addAll(PsiTreeUtil.getStubChildrenOfTypeAsList(file, RPsiFunctor.class)); expressions.addAll(PsiTreeUtil.getStubChildrenOfTypeAsList(file, RPsiClass.class)); expressions.addAll(PsiTreeUtil.getStubChildrenOfTypeAsList(file, RPsiExternal.class)); expressions.addAll(PsiTreeUtil.getStubChildrenOfTypeAsList(file, RPsiException.class)); } private static void addFunctorExpressions(@NotNull RPsiFunctor functor, @NotNull Collection expressions, @NotNull GlobalSearchScope scope) { RPsiFunctorResult returnType = functor.getReturnType(); if (returnType == null) { PsiElement functorBody = functor.getBody(); if (functorBody != null) { addChildren(functorBody, expressions); } } else { RPsiUpperSymbol moduleType = returnType.getModuleType(); PsiElement resolvedReturnType = moduleType != null ? moduleType.getReference().resolveInterface() : null; if (resolvedReturnType instanceof RPsiInnerModule resolvedReturnModuleType) { addInnerModuleExpressions(resolvedReturnModuleType, expressions, scope); } } } private static void addExpressions(@NotNull CompletionResultSet resultSet, @NotNull Collection expressions, @Nullable ORLanguageProperties language) { for (PsiNamedElement expression : expressions) { if (!(expression instanceof RPsiOpen) && !(expression instanceof RPsiInclude) && !(expression instanceof RPsiAnnotation)) { String name = expression.getName(); if (name != null) { String signature = RPsiSignatureUtil.getSignature(expression, language); resultSet.addElement( LookupElementBuilder.create(name) .withTypeText(signature) .withIcon(PsiIconUtil.getIconFromProviders(expression, 0))); } if (expression instanceof RPsiType eType) { Collection variants = eType.getVariants(); if (!variants.isEmpty()) { for (RPsiVariantDeclaration variant : variants) { String variantName = variant.getName(); if (variantName != null) { resultSet.addElement( LookupElementBuilder.create(variantName) .withTypeText(eType.getName()) .withIcon(PsiIconUtil.getIconFromProviders(variant, 0))); } } } } } } } private static void addChildren(@Nullable PsiElement body, @NotNull Collection expressions) { List includes = PsiTreeUtil.getStubChildrenOfTypeAsList(body, RPsiInclude.class); for (RPsiInclude include : includes) { RPsiUpperSymbol moduleSymbol = ORUtil.findImmediateLastChildOfClass(include, RPsiUpperSymbol.class); ORPsiUpperSymbolReference reference = moduleSymbol != null ? moduleSymbol.getReference() : null; PsiElement resolvedResult = reference != null ? reference.resolveInterface() : null; if (resolvedResult instanceof RPsiModule resolvedModule) { PsiElement resolvedBody = resolvedModule instanceof RPsiInnerModule ? ((RPsiInnerModule) resolvedModule).getModuleSignature() : null; if (resolvedBody == null) { resolvedBody = resolvedModule.getBody(); } addChildren(resolvedBody, expressions); } } expressions.addAll(PsiTreeUtil.getStubChildrenOfTypeAsList(body, RPsiType.class)); expressions.addAll(PsiTreeUtil.getStubChildrenOfTypeAsList(body, RPsiLet.class)); expressions.addAll(PsiTreeUtil.getStubChildrenOfTypeAsList(body, RPsiVal.class)); expressions.addAll(PsiTreeUtil.getStubChildrenOfTypeAsList(body, RPsiInnerModule.class)); expressions.addAll(PsiTreeUtil.getStubChildrenOfTypeAsList(body, RPsiFunctor.class)); expressions.addAll(PsiTreeUtil.getStubChildrenOfTypeAsList(body, RPsiClass.class)); expressions.addAll(PsiTreeUtil.getStubChildrenOfTypeAsList(body, RPsiExternal.class)); expressions.addAll(PsiTreeUtil.getStubChildrenOfTypeAsList(body, RPsiException.class)); } } ================================================ FILE: src/main/java/com/reason/ide/insight/provider/FreeExpressionCompletionProvider.java ================================================ package com.reason.ide.insight.provider; import com.intellij.codeInsight.completion.*; import com.intellij.codeInsight.lookup.*; import com.intellij.lang.*; import com.intellij.openapi.editor.*; import com.intellij.openapi.project.*; import com.intellij.openapi.vfs.*; import com.intellij.psi.*; import com.intellij.psi.search.*; import com.intellij.util.*; import com.reason.comp.*; import com.reason.comp.bs.*; import com.reason.ide.*; import com.reason.ide.files.*; import com.reason.ide.search.*; import com.reason.ide.search.index.*; import com.reason.ide.search.reference.*; import com.reason.lang.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import com.reason.lang.rescript.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; import java.util.*; import static com.intellij.openapi.application.ApplicationManager.*; import static com.reason.ide.ORFileUtils.*; public class FreeExpressionCompletionProvider { private static final Log LOG = Log.create("insight.free"); private FreeExpressionCompletionProvider() { } public static void addCompletions(@NotNull PsiElement element, @Nullable PsiElement parent, @NotNull GlobalSearchScope searchScope, @NotNull CompletionResultSet resultSet) { LOG.debug("FREE expression completion"); Project project = element.getProject(); PsiFile containingFile = element.getContainingFile(); String topModuleName = containingFile instanceof FileBase ? ((FileBase) containingFile).getModuleName() : null; Language language = element.getLanguage(); ORLanguageProperties languageProperties = ORLanguageProperties.cast(language); FileModuleIndexService fileModuleIndexService = getApplication().getService(FileModuleIndexService.class); // Add virtual namespaces for (String namespace : fileModuleIndexService.getNamespaces(project, searchScope)) { resultSet.addElement( LookupElementBuilder.create(namespace) .withTypeText("Generated namespace") .withIcon(ORIcons.VIRTUAL_NAMESPACE)); } // Add file modules (that are not a component & not namespaced) for (FileModuleData topModuleData : fileModuleIndexService.getTopModules(project, searchScope)) { if (!topModuleData.getModuleName().equals(topModuleName) && !topModuleData.isComponent() && !topModuleData.hasNamespace()) { resultSet.addElement( LookupElementBuilder.create(topModuleData.getModuleName()) .withTypeText(topModuleData.getFullName()) .withIcon(IconProvider.getDataModuleIcon(topModuleData))); } } // Add expressions from opened dependencies in config VirtualFile virtualFile = getVirtualFile(containingFile); BsConfig config = project.getService(ORCompilerConfigManager.class).getNearestConfig(virtualFile); if (config != null) { for (String dependency : config.getOpenedDeps()) { RPsiModule topModule = FileModuleIndexService.getInstance().getTopModule(dependency, project, searchScope); if (topModule != null) { addModuleExpressions(topModule, languageProperties, searchScope, resultSet); } } } // Pervasives is always included RPsiModule pervasives = FileModuleIndexService.getInstance().getTopModule("Pervasives", project, searchScope); if (pervasives != null) { addModuleExpressions(pervasives, languageProperties, searchScope, resultSet); } if (parent instanceof RPsiRecordField recordField) { if (recordField.getParent() instanceof RPsiRecord record) { completeRecord(element, record, resultSet); } } else if (parent instanceof RPsiRecord record) { completeRecord(element, record, resultSet); } else { // Add all local expressions by going backward until the start of the file is found PsiElement item = element.getPrevSibling(); if (item == null) { item = element.getParent(); } boolean skipLet = false; while (item != null) { if (item instanceof RPsiLetBinding) { skipLet = true; } else if (item instanceof RPsiParameters parameters) { for (PsiElement parameter : parameters.getParametersList()) { if (parameter instanceof RPsiParameterDeclaration parameterDeclaration) { String name = parameterDeclaration.getName(); resultSet.addElement( LookupElementBuilder.create(name != null ? name : parameterDeclaration.getText()) .withTypeText(RPsiSignatureUtil.getSignature(parameterDeclaration, languageProperties)) ); } } } else if (item instanceof RPsiInnerModule || item instanceof RPsiLet || item instanceof RPsiType || item instanceof RPsiExternal || item instanceof RPsiException || item instanceof RPsiVal) { RPsiLet letItem = item instanceof RPsiLet ? (RPsiLet) item : null; if (letItem != null && skipLet) { skipLet = false; } else if (letItem != null && letItem.isDeconstruction()) { for (PsiElement deconstructedElement : letItem.getDeconstructedElements()) { resultSet.addElement( LookupElementBuilder.create(deconstructedElement.getText()) .withTypeText(RPsiSignatureUtil.getSignature(item, languageProperties)) .withIcon(ORIcons.LET)); } } else if (letItem == null || !letItem.isAnonymous()) { if (item instanceof RPsiType) { expandType((RPsiType) item, resultSet, languageProperties); } else { PsiNamedElement expression = (PsiNamedElement) item; resultSet.addElement( LookupElementBuilder.create(expression) .withTypeText(RPsiSignatureUtil.getSignature(expression, languageProperties)) .withIcon(PsiIconUtil.getIconFromProviders(expression, 0))); } } } else if (item instanceof RPsiOpen openItem) { RPsiUpperSymbol moduleSymbol = ORUtil.findImmediateLastChildOfClass(openItem, RPsiUpperSymbol.class); ORPsiUpperSymbolReference reference = moduleSymbol != null ? moduleSymbol.getReference() : null; PsiElement resolved = reference != null ? reference.resolveInterface() : null; if (resolved instanceof RPsiModule resolvedModule) { addModuleExpressions(resolvedModule, languageProperties, searchScope, resultSet); } } else if (item instanceof RPsiInclude includeItem) { RPsiUpperSymbol moduleSymbol = ORUtil.findImmediateLastChildOfClass(includeItem, RPsiUpperSymbol.class); ORPsiUpperSymbolReference reference = moduleSymbol != null ? moduleSymbol.getReference() : null; PsiElement resolved = reference != null ? reference.resolveInterface() : null; if (resolved instanceof RPsiModule resolvedModule) { addModuleExpressions(resolvedModule, languageProperties, searchScope, resultSet); } } PsiElement prevItem = item.getPrevSibling(); if (prevItem == null) { parent = item.getParent(); item = parent instanceof RPsiInnerModule ? parent.getPrevSibling() : parent; } else { item = prevItem; } } } } private static void completeRecord(@NotNull PsiElement element, RPsiRecord record, @NotNull CompletionResultSet resultSet) { RPsiMixinField mixin = ORUtil.findImmediateLastChildOfClass(record, RPsiMixinField.class); if (mixin != null) { RPsiLowerSymbol mixinIdentifier = ORUtil.findImmediateLastChildOfClass(mixin, RPsiLowerSymbol.class); ORPsiLowerSymbolReference reference = mixinIdentifier != null ? mixinIdentifier.getReference() : null; PsiElement resolvedElement = reference != null ? reference.resolve() : null; if (resolvedElement instanceof RPsiLet resolvedLet) { Collection fields = resolvedLet.getRecordFields(); for (RPsiRecordField field : fields) { String name = field.getName(); if (name != null) { resultSet.addElement(LookupElementBuilder.create(name) .withTypeText(RPsiSignatureUtil.getSignature(element, ORLanguageProperties.cast(element.getLanguage()))) .withIcon(ORIcons.VAL) ) ; } } } } } private static void expandType(@NotNull RPsiType type, @NotNull CompletionResultSet resultSet, @Nullable ORLanguageProperties lang) { Collection variants = type.getVariants(); if (!variants.isEmpty()) { for (RPsiVariantDeclaration variant : variants) { String name = variant.getName(); if (name != null) { String lookupName = lang == ResLanguage.INSTANCE ? name : "`" + name.substring(1); resultSet.addElement( LookupElementBuilder.create(lookupName) .withTypeText(type.getName()) .withIcon(PsiIconUtil.getIconFromProviders(variant, 0))); } } } } private static void addModuleExpressions(@NotNull RPsiModule rootModule, @Nullable ORLanguageProperties language, @NotNull GlobalSearchScope searchScope, @NotNull CompletionResultSet resultSet) { Project project = rootModule.getProject(); // alternate names (include inside module) ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(rootModule.getContainingFile()); for (String alternateName : data.getValues(rootModule)) { // Try to resolve as an inner module or a top module Collection alternateModules = ModuleFqnIndex.getElements(alternateName, project, searchScope); RPsiModule topModule = FileModuleIndexService.getInstance().getTopModule(alternateName, project, searchScope); if (topModule != null) { alternateModules.add(topModule); } for (RPsiModule alternateModule : alternateModules) { addModuleExpressions(alternateModule, language, searchScope, resultSet); } } for (PsiNamedElement item : ORUtil.findImmediateChildrenOfClass(rootModule.getBody(), PsiNamedElement.class)) { if (!(item instanceof RPsiLet) || !(((RPsiLet) item).isPrivate() || ((RPsiLet) item).isAnonymous())) { if (item instanceof RPsiLet && ((RPsiLet) item).isDeconstruction()) { for (PsiElement deconstructedElement : ((RPsiLet) item).getDeconstructedElements()) { resultSet.addElement( LookupElementBuilder.create(deconstructedElement.getText()) .withTypeText(RPsiSignatureUtil.getSignature(item, language)) .withIcon(ORIcons.LET)); } } else if (item instanceof RPsiType) { expandType((RPsiType) item, resultSet, language); } else if (!(item instanceof RPsiAnnotation)) { String itemName = item.getName(); if (itemName != null && !itemName.isEmpty() && !itemName.equals("unknown")) { resultSet.addElement( LookupElementBuilder.create(item) .withTypeText(RPsiSignatureUtil.getSignature(item, language)) .withIcon(PsiIconUtil.getIconFromProviders(item, 0)) .withInsertHandler(FreeExpressionCompletionProvider::insertExpression)); } } } } } private static void insertExpression(@NotNull InsertionContext insertionContext, @NotNull LookupElement element) { PsiElement psiElement = element.getPsiElement(); if (psiElement instanceof RPsiLet let) { if (let.isFunction()) { insertionContext.setAddCompletionChar(false); Editor editor = insertionContext.getEditor(); EditorModificationUtil.insertStringAtCaret(editor, "()"); editor.getCaretModel().moveToOffset(editor.getCaretModel().getOffset() - 1); } } } } ================================================ FILE: src/main/java/com/reason/ide/insight/provider/JsxAttributeCompletionProvider.java ================================================ package com.reason.ide.insight.provider; import com.intellij.codeInsight.completion.*; import com.intellij.codeInsight.lookup.*; import com.intellij.openapi.editor.*; import com.intellij.openapi.project.*; import com.intellij.psi.*; import com.intellij.psi.impl.source.tree.*; import com.intellij.psi.search.*; import com.intellij.psi.util.*; import com.intellij.ui.*; import com.reason.comp.*; import com.reason.comp.bs.*; import com.reason.ide.*; import com.reason.ide.search.index.*; import com.reason.ide.search.reference.*; import com.reason.lang.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import com.reason.lang.core.type.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; import javax.swing.*; import java.util.*; import java.util.function.*; import static java.util.stream.Collectors.*; public class JsxAttributeCompletionProvider { private static final Log LOG = Log.create("insight.jsx.attribute"); private JsxAttributeCompletionProvider() { } public static void addCompletions(@NotNull PsiElement element, @NotNull GlobalSearchScope scope, @NotNull CompletionResultSet resultSet) { LOG.debug("JSX attribute completion"); RPsiTagStart tag = PsiTreeUtil.getStubOrPsiParentOfType(element, RPsiTagStart.class); ORLanguageProperties langProperties = ORLanguageProperties.cast(tag == null ? null : tag.getLanguage()); Project project = element.getProject(); // Attributes already used List usedAttributes = tag == null ? Collections.emptyList() : tag.getProperties(); List usedNames = usedAttributes.stream().map(RPsiTagProperty::getName).collect(toList()); LOG.debug("used names", usedNames); PsiElement tagName = tag == null ? null : tag.getNameIdentifier(); if (tagName instanceof RPsiUpperSymbol) { // Custom component ORPsiUpperSymbolReference tagReference = (ORPsiUpperSymbolReference) tagName.getReference(); PsiElement resolvedModule = tagReference == null ? null : tagReference.resolve(); PsiElement resolvedElement = resolvedModule instanceof RPsiModule ? ((RPsiModule) resolvedModule).getMakeFunction() : null; // Additional attributes for UpperSymbol => only key and ref if (resolvedElement != null) { LOG.debug("Tag found", resolvedElement); if (!usedNames.contains("key")) { addProperty(null, "key", "string=?", false, resultSet); } if (!usedNames.contains("ref")) { addProperty(null, "ref", "domRef=?", false, resultSet); } } Predicate propertyFilter = i -> { String name = i.getName(); return !"children".equals(name) && !"_children".equals(name) && !usedNames.contains(name); }; if (resolvedElement instanceof RPsiLet) { RPsiFunction makeFunction = ((RPsiLet) resolvedElement).getFunction(); if (makeFunction != null) { makeFunction.getParameters().stream() .filter(propertyFilter) .forEach(i -> addProperty(i, i.getName(), getParameterSignature(i, langProperties), i.getDefaultValue() == null, resultSet)); } } else if (resolvedElement instanceof RPsiExternal) { RPsiSignature signature = ((RPsiExternal) resolvedElement).getSignature(); if (signature != null) { signature.getItems().stream() .filter(propertyFilter) .forEach(i -> addProperty(i, i.getName(), i.asText(langProperties), !i.isOptional(), resultSet)); } } } else if (tagName instanceof LeafPsiElement) { LOG.debug("Completion of a standard tag"); Collection propsType; // Try to locate the definition of the react props // ----------------------------------------------- BsConfig config = project.getService(ORCompilerConfigManager.class).getNearestConfig(element.getContainingFile()); String jsxVersion = config != null && config.getJsxVersion() != null ? config.getJsxVersion() : "3"; if (jsxVersion.equals("4")) { String domPropsQName = "JsxDOM" + (config.isUncurried() ? "U" : "C") + ".domProps"; LOG.debug("New Jsx props, using", domPropsQName); propsType = TypeFqnIndex.getElements(domPropsQName, project, scope); } else { propsType = TypeFqnIndex.getElements("ReactDOM.Props.domProps", project, scope); if (propsType.isEmpty()) { // Old bindings propsType = TypeFqnIndex.getElements("ReactDom.props", project, scope); if (propsType.isEmpty()) { propsType = TypeFqnIndex.getElements("ReactDomRe.props", project, scope); } } } // Add props to the list of completions // ------------------------------------ RPsiType props = propsType.isEmpty() ? null : propsType.iterator().next(); if (props != null) { if (LOG.isDebugEnabled()) { LOG.debug("Properties found", props.getRecordFields().size()); } ORLangTypes types = ORUtil.getTypes(tag.getLanguage()); for (RPsiRecordField field : props.getRecordFields()) { String fieldName = field.getName(); if (usedNames.contains(fieldName) || "children".equals(fieldName)) { continue; } RPsiSignature signature = field.getSignature(); List items = signature != null ? signature.getItems() : null; RPsiSignatureItem firstSigItem = items == null || items.isEmpty() ? null : items.get(0); PsiElement nextSibling = ORUtil.nextSibling(field.getNameIdentifier()); boolean isOptionalKey = nextSibling != null && nextSibling.getNode().getElementType() == types.QUESTION_MARK; boolean isMandatory = !isOptionalKey && firstSigItem != null && !firstSigItem.isOptional(); for (RPsiAnnotation annotation : ORUtil.prevAnnotations(field)) { if ("@bs.optional".equals(annotation.getName())) { isMandatory = false; break; } } addProperty(field, field.getName(), signature != null ? signature.asText(langProperties) : "", isMandatory, resultSet); } } } } private static @NotNull String getParameterSignature(RPsiParameterDeclaration param, @Nullable ORLanguageProperties languageProperties) { RPsiSignature signature = param.getSignature(); if (signature == null) { return (param.getDefaultValue() != null ? "=" + param.getDefaultValue().getText() : ""); } return signature.asText(languageProperties); } private static void addProperty(@Nullable PsiElement element, @Nullable String name, @Nullable String type, boolean isMandatory, CompletionResultSet resultSet) { if (name != null) { Icon icon = isMandatory ? LayeredIcon.create(ORIcons.ATTRIBUTE, ORIcons.OVERLAY_MANDATORY) : ORIcons.ATTRIBUTE; LookupElementBuilder lookupElementBuilder = element == null ? LookupElementBuilder.create(name) : LookupElementBuilder.createWithSmartPointer(name, element); resultSet.addElement(PrioritizedLookupElement.withPriority( lookupElementBuilder .withBoldness(isMandatory) .withTypeText(type, true) .withIcon(icon) .withInsertHandler((context, item) -> insertTagAttributeHandler(context)), isMandatory ? 1 : 0)); } } private static void insertTagAttributeHandler(@NotNull InsertionContext context) { context.setAddCompletionChar(false); Editor editor = context.getEditor(); EditorModificationUtil.insertStringAtCaret(editor, "={}"); editor.getCaretModel().moveToOffset(editor.getCaretModel().getOffset() - 1); } } ================================================ FILE: src/main/java/com/reason/ide/insight/provider/JsxNameCompletionProvider.java ================================================ package com.reason.ide.insight.provider; import com.intellij.codeInsight.*; import com.intellij.codeInsight.completion.*; import com.intellij.codeInsight.lookup.*; import com.intellij.openapi.editor.*; import com.intellij.openapi.project.*; import com.intellij.psi.*; import com.intellij.psi.search.*; import com.intellij.psi.util.*; import com.reason.ide.*; import com.reason.ide.files.*; import com.reason.ide.search.index.*; import com.reason.ide.search.reference.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import com.reason.lang.core.type.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; import java.util.*; import static com.intellij.openapi.application.ApplicationManager.*; import static com.intellij.util.PsiIconUtil.*; public class JsxNameCompletionProvider { private static final Log LOG = Log.create("insight.jsx.name"); private JsxNameCompletionProvider() { } public static void addCompletions(@NotNull PsiElement element, @NotNull ORLangTypes types, @NotNull GlobalSearchScope scope, @NotNull CompletionResultSet resultSet) { LOG.debug("JSX name expression completion"); Collection expressions = new ArrayList<>(); Project project = element.getProject(); PsiElement prevLeaf = PsiTreeUtil.prevVisibleLeaf(element); if (prevLeaf != null && prevLeaf.getNode().getElementType() == types.DOT) { // Inner component completion PsiElement previousElement = prevLeaf.getPrevSibling(); if (previousElement instanceof RPsiUpperSymbol) { ORPsiUpperSymbolReference reference = (ORPsiUpperSymbolReference) previousElement.getReference(); PsiElement resolvedElement = reference == null ? null : reference.resolveInterface(); LOG.debug(" -> resolved to", resolvedElement); // A component is resolved to the make function if (resolvedElement != null) { PsiElement resolvedModule = PsiTreeUtil.getStubOrPsiParentOfType(resolvedElement, RPsiModule.class); if (resolvedModule == null) { resolvedModule = resolvedElement.getContainingFile(); } for (RPsiModule module : PsiTreeUtil.getStubChildrenOfTypeAsList(resolvedModule, RPsiModule.class)) { if (module.isComponent()) { expressions.add(module); } } } } } else { // List inner components above List localModules = ORUtil.findPreviousSiblingsOrParentOfClass(element, RPsiInnerModule.class); for (RPsiInnerModule localModule : localModules) { if (localModule.isComponent() && !localModule.isModuleType()) { expressions.add(localModule); } } // List all top level components final RPsiModule currentModule = PsiTreeUtil.getStubOrPsiParentOfType(element, RPsiModule.class); String currentModuleName = currentModule == null ? "" : currentModule.getModuleName(); getApplication().getService(FileModuleIndexService.class).getTopModules(project, scope) .forEach(data -> { if ((data.isComponent() || data.hasComponents()) && !data.getModuleName().equals(currentModuleName)) { resultSet.addElement(LookupElementBuilder.create(data.getModuleName()) .withIcon(IconProvider.getDataModuleIcon(data)) .withInsertHandler((context, item) -> insertTagNameHandler(project, context, data.getModuleName()))); } }); } for (RPsiModule expression : expressions) { String componentName = expression instanceof FileBase ? ((FileBase) expression).getModuleName() : expression.getName(); if (componentName != null) { resultSet.addElement(LookupElementBuilder.create(componentName) .withIcon(getIconFromProviders(expression, 0)) .withInsertHandler((context, item) -> insertTagNameHandler(project, context, componentName))); } } } private static void insertTagNameHandler(@NotNull Project project, @NotNull InsertionContext context, @NotNull String tagName) { char completionChar = context.getCompletionChar(); if (completionChar == ' ') { context.setAddCompletionChar(false); } boolean closeTag = false; CharSequence chars = context.getDocument().getCharsSequence(); int tagPrefixOffset = context.getStartOffset() - 2; if (tagPrefixOffset >= 0) { CharSequence tagPrefix = chars.subSequence(tagPrefixOffset, context.getStartOffset()); closeTag = "') { EditorModificationUtil.insertStringAtCaret(editor, ">"); } } else { EditorModificationUtil.insertStringAtCaret(editor, " >"); editor.getCaretModel().moveToOffset(editor.getCaretModel().getOffset() - 4 - tagName.length()); AutoPopupController.getInstance(project).autoPopupMemberLookup(editor, null); } } } ================================================ FILE: src/main/java/com/reason/ide/insight/provider/ModuleCompletionProvider.java ================================================ package com.reason.ide.insight.provider; import com.intellij.codeInsight.completion.*; import com.intellij.codeInsight.lookup.*; import com.intellij.openapi.project.*; import com.intellij.psi.*; import com.intellij.psi.search.*; import com.intellij.psi.util.*; import com.intellij.util.*; import com.reason.ide.*; import com.reason.ide.search.*; import com.reason.ide.search.index.*; import com.reason.ide.search.reference.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; import static com.intellij.openapi.application.ApplicationManager.*; public class ModuleCompletionProvider { private static final Log LOG = Log.create("insight.module"); private ModuleCompletionProvider() { } public static void addCompletions(@NotNull PsiElement element, @NotNull GlobalSearchScope scope, @NotNull CompletionResultSet resultSet) { LOG.debug("MODULE expression completion"); Project project = element.getProject(); PsiElement dotLeaf = PsiTreeUtil.prevVisibleLeaf(element); PsiElement previousElement = dotLeaf == null ? null : dotLeaf.getPrevSibling(); if (previousElement instanceof RPsiUpperSymbol previousUpperSymbol) { LOG.debug(" -> upper symbol", previousUpperSymbol); PsiElement resolvedElement = previousUpperSymbol.getReference().resolve(); LOG.debug(" -> resolved to", resolvedElement); if (resolvedElement instanceof RPsiModule resolvedModule) { for (RPsiInnerModule module : PsiTreeUtil.getStubChildrenOfTypeAsList(resolvedModule.getBody(), RPsiInnerModule.class)) { addModule(module, resultSet); } // Find alternatives ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(resolvedModule.getContainingFile()); for (String alternateName : data.getValues(resolvedModule)) { for (PsiElement alternateElement : ORReferenceAnalyzer.resolvePath(alternateName, project, scope, true, 0)) { if (alternateElement instanceof RPsiModule alternateModule) { for (RPsiInnerModule module : PsiTreeUtil.getStubChildrenOfTypeAsList(alternateModule.getBody(), RPsiInnerModule.class)) { addModule(module, resultSet); } } } } } } else { // Empty path FileModuleIndexService fileModuleIndexService = getApplication().getService(FileModuleIndexService.class); // First module to complete, use the list of files PsiFile containingFile = element.getContainingFile(); if (containingFile instanceof RPsiModule) { String topModuleName = ((RPsiModule) containingFile).getModuleName(); for (FileModuleData moduleData : fileModuleIndexService.getTopModules(project, scope)) { if (!moduleData.getModuleName().equals(topModuleName) && !moduleData.hasNamespace()) { resultSet.addElement(LookupElementBuilder. create(moduleData.getModuleName()) .withTypeText(moduleData.getFullName()) .withIcon(IconProvider.getDataModuleIcon(moduleData))); } } } // Add virtual namespaces for (String namespace : fileModuleIndexService.getNamespaces(project, scope)) { resultSet.addElement( LookupElementBuilder.create(namespace) .withTypeText("Generated namespace") .withIcon(ORIcons.VIRTUAL_NAMESPACE)); } } } private static void addModule(@NotNull RPsiModule module, @NotNull CompletionResultSet resultSet) { String name = module.getModuleName(); resultSet.addElement(LookupElementBuilder. create(name == null ? "unknown" : name) .withIcon(PsiIconUtil.getIconFromProviders(module, 0))); } } ================================================ FILE: src/main/java/com/reason/ide/insight/provider/ObjectCompletionProvider.java ================================================ package com.reason.ide.insight.provider; import com.intellij.codeInsight.completion.*; import com.intellij.codeInsight.lookup.*; import com.intellij.psi.*; import com.intellij.psi.util.*; import com.intellij.util.*; import com.reason.ide.search.reference.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import com.reason.lang.core.type.*; import com.reason.lang.rescript.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; import java.util.*; public class ObjectCompletionProvider { private static final Log LOG = Log.create("insight.object"); private ObjectCompletionProvider() { } public static boolean addCompletions(@NotNull PsiElement element, @Nullable PsiElement parent, @NotNull ORLangTypes types, @NotNull CompletionResultSet resultSet) { LOG.debug("OBJECT expression completion"); PsiElement separator = PsiTreeUtil.prevVisibleLeaf(element); PsiElement objectElement = separator != null ? separator.getPrevSibling() : null; // Special case for js object in rescript if (types == ResTypes.INSTANCE) { if (parent instanceof RPsiArray) { objectElement = parent.getPrevSibling(); } } PsiElement resolvedElement = null; if (objectElement instanceof RPsiLowerSymbol previousLowerSymbol) { LOG.debug(" -> lower symbol", previousLowerSymbol); resolvedElement = previousLowerSymbol.getReference().resolveInterface(); if (LOG.isDebugEnabled()) { LOG.debug(" -> resolved to", resolvedElement == null ? null : resolvedElement.getParent()); } } else if (objectElement instanceof RPsiArray previousArray) { LOG.debug(" -> array", previousArray); RPsiLiteralString stringElement = ORUtil.findImmediateFirstChildOfClass(previousArray, RPsiLiteralString.class); PsiReference stringReference = stringElement != null ? stringElement.getReference() : null; resolvedElement = stringReference != null ? stringReference.resolve() : null; } if (resolvedElement != null) { Collection fields = getFields(resolvedElement); if (fields == null) { LOG.debug(" -> Not a js object/record"); } else { for (RPsiObjectField field : fields) { String fieldName = types == ResTypes.INSTANCE ? "\"" + field.getName() + "\"" : field.getName(); resultSet.addElement(LookupElementBuilder.create(fieldName) .withIcon(PsiIconUtil.getIconFromProviders(field, 0))); } return true; } } LOG.debug(" -> Nothing found"); return false; } static @Nullable Collection getFields(@Nullable PsiElement resolvedElement) { if (resolvedElement instanceof RPsiLet let) { if (let.isJsObject()) { RPsiJsObject jsObject = ORUtil.findImmediateFirstChildOfClass(let.getBinding(), RPsiJsObject.class); return jsObject == null ? null : jsObject.getFields(); } else { RPsiSignature letSignature = let.getSignature(); if (letSignature != null && !letSignature.isFunction()) { LOG.debug("Testing let signature", letSignature.getText()); RPsiLowerSymbol sigTerm = ORUtil.findImmediateLastChildOfClass(letSignature.getItems().get(0), RPsiLowerSymbol.class); ORPsiLowerSymbolReference sigReference = sigTerm == null ? null : sigTerm.getReference(); PsiElement resolvedSignature = sigReference == null ? null : sigReference.resolve(); if (resolvedSignature instanceof RPsiType && ((RPsiType) resolvedSignature).isJsObject()) { return ((RPsiType) resolvedSignature).getJsObjectFields(); } } } } else if (resolvedElement instanceof RPsiObjectField resolvedObjectField) { RPsiFieldValue value = resolvedObjectField.getValue(); PsiElement valueElement = value != null ? value.getFirstChild() : null; if (valueElement instanceof RPsiJsObject valueObject) { return valueObject.getFields(); } else if (valueElement instanceof RPsiLowerSymbol valueLowerSymbol) { // Must be an object defined outside PsiElement valueResolvedElement = valueLowerSymbol.getReference().resolveInterface(); return valueResolvedElement != null ? getFields(valueResolvedElement) : null; } else if (valueElement instanceof RPsiUpperSymbol) { // Must be a path of an object defined outside PsiElement lSymbol = ORUtil.nextSiblingWithTokenType(valueElement, ORUtil.getTypes(resolvedElement.getLanguage()).LIDENT); ORPsiLowerSymbolReference valueReference = lSymbol == null ? null : (ORPsiLowerSymbolReference) lSymbol.getReference(); PsiElement valueResolvedElement = valueReference == null ? null : valueReference.resolveInterface(); return valueResolvedElement == null ? null : getFields(valueResolvedElement); } } return null; } } ================================================ FILE: src/main/java/com/reason/ide/intentions/ExpandLocalOpenIntention.java ================================================ package com.reason.ide.intentions; import com.intellij.codeInsight.intention.*; import com.intellij.codeInspection.util.*; import com.intellij.lang.*; import com.intellij.openapi.editor.*; import com.intellij.openapi.project.*; import com.intellij.psi.*; import com.intellij.psi.util.*; import com.intellij.util.*; import com.reason.ide.files.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.impl.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; public class ExpandLocalOpenIntention implements IntentionAction { @Override public @IntentionName @NotNull String getText() { return "Expand local open"; } @Override public @NotNull @IntentionFamilyName String getFamilyName() { return "Expand local open"; } @Override public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) { if (file instanceof RmlFile) { PsiElement element = file.findElementAt(editor.getCaretModel().getOffset()); RPsiLocalOpen open = element == null ? null : PsiTreeUtil.getParentOfType(element, RPsiLocalOpen.class); return open != null; } return false; } @Override public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException { PsiElement element = file.findElementAt(editor.getCaretModel().getOffset()); RPsiLocalOpen localOpen = element == null ? null : PsiTreeUtil.getParentOfType(element, RPsiLocalOpen.class); if (localOpen != null) { // localOpen is the scope: Module.Module «( ... )» ORLangTypes types = ORUtil.getTypes(localOpen.getLanguage()); PsiElement grandParentElement = localOpen.getParent(); // Extract the module path (and remove path nodes) StringBuilder modulePath = new StringBuilder(); PsiElement sibling = PsiTreeUtil.prevVisibleLeaf(localOpen); while (sibling != null && (sibling.getNode().getElementType() == types.A_MODULE_NAME || sibling.getNode().getElementType() == types.DOT)) { ASTNode currentNode = sibling.getNode(); if (!modulePath.isEmpty() || currentNode.getElementType() != types.DOT) { modulePath.insert(0, sibling.getText()); } sibling = PsiTreeUtil.prevVisibleLeaf(sibling); grandParentElement.getNode().removeChild(currentNode); } String text = localOpen.getText(); PsiElement newOpen = ORCodeFactory.createExpression(project, localOpen.getLanguage(), "{ open " + modulePath + "; " + text.substring(1, text.length() - 1) + "; }"); if (newOpen != null) { grandParentElement.getNode().replaceChild(localOpen.getNode(), newOpen.getNode()); } } } @Override public boolean startInWriteAction() { return true; } } ================================================ FILE: src/main/java/com/reason/ide/intentions/FunctionBracesIntention.java ================================================ package com.reason.ide.intentions; import com.intellij.codeInsight.intention.*; import com.intellij.codeInspection.util.*; import com.intellij.lang.*; import com.intellij.openapi.command.*; import com.intellij.openapi.editor.*; import com.intellij.openapi.project.*; import com.intellij.psi.*; import com.intellij.psi.util.*; import com.intellij.util.*; import com.reason.ide.files.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import com.reason.lang.core.type.*; import com.reason.lang.reason.*; import org.jetbrains.annotations.*; public class FunctionBracesIntention implements IntentionAction { @Override public @IntentionName @NotNull String getText() { return "Add braces to blockless function"; } @Override public @NotNull @IntentionFamilyName String getFamilyName() { return "Add braces to blockless function"; } @Override public boolean isAvailable(@NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file) { if (file instanceof RmlFile || file instanceof ResFile) { RPsiFunction function = getTarget(editor, file); RPsiFunctionBody body = function != null ? function.getBody() : null; PsiElement bodyChild = body != null ? body.getFirstChild() : null; if (bodyChild instanceof RPsiScopedExpr scopedExpr) { PsiElement scopeChild = scopedExpr.getFirstChild(); ORLangTypes types = ORUtil.getTypes(function.getLanguage()); return scopeChild != null && scopeChild.getNode().getElementType() != types.LBRACE; } else { return true; } } return false; } @Override public void invoke(@NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file) throws IncorrectOperationException { RPsiFunction function = getTarget(editor, file); RPsiFunctionBody body = function != null ? function.getBody() : null; if (body != null) { String text = function.getText(); int bodyOffset = body.getStartOffsetInParent(); String def = text.substring(0, bodyOffset); String bodyText = text.substring(bodyOffset); Language language = function.getLanguage(); String newExpression = language == RmlLanguage.INSTANCE ? "let x = " + def + "{ " + bodyText + "; };" : "let x = " + def + "{ " + bodyText + " }"; PsiElement newSyntax = ORCodeFactory.createExpression(project, language, newExpression); if (newSyntax instanceof RPsiLet newLetSyntax) { RPsiFunction newFunction = newLetSyntax.getFunction(); RPsiFunctionBody newBody = newFunction != null ? newFunction.getBody() : null; if (newBody != null) { WriteCommandAction.runWriteCommandAction(project, null, null, () -> function.getNode().replaceChild(body.getNode(), newBody.getNode()), file); } } } } @Override public boolean startInWriteAction() { return true; } @Nullable private RPsiFunction getTarget(@NotNull Editor editor, @NotNull PsiFile file) { PsiElement element = file.findElementAt(editor.getCaretModel().getOffset()); return element != null ? PsiTreeUtil.getParentOfType(element, RPsiFunction.class) : null; } } ================================================ FILE: src/main/java/com/reason/ide/js/JsIconProvider.java ================================================ package com.reason.ide.js; import com.intellij.lang.javascript.psi.*; import com.intellij.psi.*; import com.reason.comp.*; import com.reason.ide.*; import org.jetbrains.annotations.*; import javax.swing.*; public class JsIconProvider extends com.intellij.ide.IconProvider { @Override public @Nullable Icon getIcon(@NotNull PsiElement psiElement, int flags) { if (psiElement instanceof PsiFile && isBsJsFile((PsiFile) psiElement)) { return ORIcons.BS_FILE; } return null; } /* needed as plugin.xml's filetype extension does NOT support extensions with multiple "." */ private static boolean isBsJsFile(PsiFile psiFile) { if (psiFile instanceof JSFile jsFile) { return jsFile.getName().endsWith("." + ORConstants.BS_JS_FILE_EXTENSION); } return false; } } ================================================ FILE: src/main/java/com/reason/ide/js/JsInjector.java ================================================ package com.reason.ide.js; import com.intellij.lang.*; import com.intellij.openapi.fileTypes.*; import com.intellij.openapi.util.*; import com.intellij.psi.*; import com.reason.lang.core.psi.impl.*; import org.jetbrains.annotations.*; public class JsInjector implements LanguageInjector { public void getLanguagesToInject(@NotNull PsiLanguageInjectionHost host, @NotNull InjectedLanguagePlaces injectionPlacesRegistrar) { if (host instanceof RPsiMacroBody body) { PsiElement parent = body.getParent(); if (parent instanceof RPsiMacro macro) { String name = macro.getName(); if ("%raw".equals(name) || "%%raw".equals(name)) { FileType jsFileType = FileTypeManager.getInstance().getFileTypeByExtension("js"); if (jsFileType instanceof LanguageFileType jsLanguageFileType) { Language jsLanguage = jsLanguageFileType.getLanguage(); RPsiMacroBody macroHost = (RPsiMacroBody) host; TextRange macroTextRange = macroHost.getMacroTextRange(); if (macroTextRange != null) { injectionPlacesRegistrar.addPlace(jsLanguage, macroTextRange, null, null); } } } } } } } ================================================ FILE: src/main/java/com/reason/ide/js/ORIndexableFileNamesProvider.java ================================================ package com.reason.ide.js; import com.intellij.lang.javascript.modules.*; import com.reason.comp.*; import org.jetbrains.annotations.*; import java.util.*; public class ORIndexableFileNamesProvider extends NodeModulesIndexableFileNamesProvider { private static final List EXTENSIONS = new ArrayList<>(6); private static final List FILES = new ArrayList<>(2); static { EXTENSIONS.add("ml"); EXTENSIONS.add("mli"); EXTENSIONS.add("re"); EXTENSIONS.add("rei"); EXTENSIONS.add("res"); EXTENSIONS.add("resi"); FILES.add(ORConstants.BS_CONFIG_FILENAME); FILES.add(ORConstants.RESCRIPT_CONFIG_FILENAME); } @Override protected @NotNull List getIndexableFileNames(@NotNull DependencyKind kind) { return FILES; } @Override protected @NotNull List getIndexableExtensions(@NotNull DependencyKind kind) { return EXTENSIONS; } } ================================================ FILE: src/main/java/com/reason/ide/js/ORJsLibraryManager.java ================================================ package com.reason.ide.js; import com.intellij.lang.javascript.library.*; import com.intellij.openapi.*; import com.intellij.openapi.application.*; import com.intellij.openapi.command.*; import com.intellij.openapi.project.*; import com.intellij.openapi.startup.*; import com.intellij.openapi.util.*; import com.intellij.openapi.vfs.*; import com.intellij.openapi.vfs.pointers.*; import com.intellij.util.concurrency.*; import com.intellij.webcore.libraries.*; import com.reason.comp.bs.*; import jpsplugin.com.reason.*; import kotlin.*; import kotlin.coroutines.*; import org.jetbrains.annotations.*; import java.util.*; import static com.intellij.openapi.vfs.VirtualFile.*; import static com.intellij.util.ArrayUtilRt.*; import static com.intellij.webcore.libraries.ScriptingLibraryModel.LibraryLevel.*; public class ORJsLibraryManager implements ProjectActivity, DumbAware { private static final Log LOG = Log.create("activity.js.lib"); private static final String LIB_NAME = "Bucklescript"; @Override public @Nullable Object execute(@NotNull Project project, @NotNull Continuation continuation) { DumbService.getInstance(project).smartInvokeLater(() -> runActivityLater(project)); return null; } private void runActivityLater(@NotNull Project project) { LOG.info("run Js library manager"); ReadAction.nonBlocking(() -> BsPlatform.findConfigFiles(project).stream().findFirst()) .finishOnUiThread(ModalityState.defaultModalityState(), bsConfigFileOptional -> { if (bsConfigFileOptional.isPresent()) { VirtualFile bsConfigFile = bsConfigFileOptional.get(); LOG.debug("bucklescript config file", bsConfigFile); String baseDir = "file://" + bsConfigFile.getParent().getPath() + "/node_modules/"; List sources = new ArrayList<>(readBsConfigDependencies(project, baseDir, bsConfigFile)); JSLibraryManager jsLibraryManager = JSLibraryManager.getInstance(project); ScriptingLibraryModel bucklescriptModel = jsLibraryManager.getLibraryByName(LIB_NAME); if (bucklescriptModel == null) { LOG.debug("Creating js library", LIB_NAME); jsLibraryManager.createLibrary( LIB_NAME, sources.toArray(new VirtualFile[0]), EMPTY_ARRAY, EMPTY_STRING_ARRAY, PROJECT, true); } else { LOG.debug("Updating js library", LIB_NAME); jsLibraryManager.updateLibrary( LIB_NAME, LIB_NAME, sources.toArray(new VirtualFile[0]), EMPTY_ARRAY, EMPTY_STRING_ARRAY); } WriteCommandAction.runWriteCommandAction(project, () -> jsLibraryManager.commitChanges(RootsChangeRescanningInfo.TOTAL_RESCAN)); } }) .submit(AppExecutorUtil.getAppExecutorService()); } private @NotNull List readBsConfigDependencies(@NotNull Project project, @NotNull String nodeModulesDir, @NotNull VirtualFile bsConfigFile) { List result = new ArrayList<>(); LOG.debug("Read deps from", bsConfigFile); VirtualFilePointerManager vFilePointerManager = VirtualFilePointerManager.getInstance(); BsConfig bsConfig = BsConfigReader.read(bsConfigFile); JSLibraryManager jsLibraryManager = JSLibraryManager.getInstance(project); Disposable disposable = Disposer.newDisposable(jsLibraryManager, "ORJsLibraryManager"); try { for (String dependency : bsConfig.getDependencies()) { String depDirUrl = nodeModulesDir + dependency; VirtualFilePointer dirPointer = vFilePointerManager.create(depDirUrl, disposable, new VirtualFilePointerListener() { @Override public void validityChanged(@NotNull VirtualFilePointer[] pointers) { if (LOG.isDebugEnabled()) { LOG.debug("validity changed for " + pointers[0]); } ScriptingLibraryModel bucklescriptModel = jsLibraryManager.getLibraryByName(LIB_NAME); if (bucklescriptModel != null) { List changedSources = new ArrayList<>(bucklescriptModel.getSourceFiles()); if (pointers[0].isValid()) { VirtualFile dirFile = pointers[0].getFile(); changedSources.add(dirFile); if (dirFile != null) { VirtualFile bsConfigDepFile = dirFile.findChild("bsconfig.json"); if (bsConfigDepFile != null) { changedSources.addAll(readBsConfigDependencies(project, nodeModulesDir, bsConfigDepFile)); } } jsLibraryManager.updateLibrary( LIB_NAME, LIB_NAME, changedSources.toArray(new VirtualFile[0]), EMPTY_ARRAY, EMPTY_STRING_ARRAY); WriteCommandAction.runWriteCommandAction(project, () -> jsLibraryManager.commitChanges(RootsChangeRescanningInfo.TOTAL_RESCAN)); } } } }); VirtualFile dirFile = dirPointer.getFile(); if (dirFile != null) { result.add(dirFile); if (dirFile.isValid()) { VirtualFile bsConfigDepFile = dirFile.findChild("bsconfig.json"); if (bsConfigDepFile != null) { result.addAll(readBsConfigDependencies(project, nodeModulesDir, bsConfigDepFile)); } } } } } finally { disposable.dispose(); } return result; } } ================================================ FILE: src/main/java/com/reason/ide/library/OclLibraryKind.java ================================================ package com.reason.ide.library; import com.intellij.openapi.roots.libraries.*; import org.jetbrains.annotations.*; public class OclLibraryKind extends PersistentLibraryKind { public static final OclLibraryKind INSTANCE = new OclLibraryKind(); public OclLibraryKind() { super("OCaml"); } @Override public @NotNull DummyLibraryProperties createDefaultProperties() { return DummyLibraryProperties.INSTANCE; } } ================================================ FILE: src/main/java/com/reason/ide/library/OclLibraryRootProvider.java ================================================ package com.reason.ide.library; import com.intellij.navigation.*; import com.intellij.openapi.project.*; import com.intellij.openapi.roots.*; import com.intellij.openapi.vfs.*; import com.reason.ide.*; import com.reason.ide.settings.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; import javax.swing.*; import java.nio.file.*; import java.util.*; /** * Manage external library based on opam settings. * It is updated automatically when module entries are changed. */ public class OclLibraryRootProvider extends AdditionalLibraryRootsProvider { private static final Log LOG = Log.create("library.rootProvider"); @Override public @NotNull Collection getAdditionalProjectLibraries(@NotNull Project project) { // checkReadAccessAllowed LOG.debug("Get additional project libraries"); ORSettings settings = project.getService(ORSettings.class); String opamLocation = settings.getOpamLocation(); String opamSwitch = settings.getSwitchName(); if (opamLocation.isEmpty() && opamSwitch.isEmpty()) { return Collections.emptyList(); } return List.of(new OpamLibrary(opamLocation, opamSwitch)); } @Override public @NotNull Collection getRootsToWatch(@NotNull Project project) { Collection libraries = getAdditionalProjectLibraries(project); LOG.debug("Roots to watch", libraries); return libraries.stream() .map(lib -> Collections.singleton(((OpamLibrary) lib).getOpamSwitchLocation())) .collect(ArrayList::new, Collection::addAll, Collection::addAll); } static final class OpamLibrary extends SyntheticLibrary implements ItemPresentation { private final Collection mySourceRoots = new ArrayList<>(); private final String myOpamRoot; private final String myOpamSwitch; private @Nullable VirtualFile getOpamSwitchLocation() { try { return VirtualFileManager.getInstance().findFileByNioPath(Path.of(myOpamRoot, myOpamSwitch)); } catch (InvalidPathException e) { return null; } } public OpamLibrary(@NotNull String opamRoot, @NotNull String opamSwitch) { myOpamRoot = opamRoot; myOpamSwitch = opamSwitch; VirtualFile opamFile = getOpamSwitchLocation(); if (opamFile != null) { VfsUtilCore.visitChildrenRecursively(opamFile, new VirtualFileVisitor() { @Override public @NotNull Result visitFileEx(@NotNull VirtualFile file) { if (file.isDirectory()) { String fileName = file.getName(); if (fileName.startsWith(".") || fileName.equals("doc")) { return SKIP_CHILDREN; } List children = VfsUtil.getChildren(file, child -> { String childName = child.getName(); return childName.endsWith(".ml") || childName.endsWith(".mli"); }); if (!children.isEmpty()) { mySourceRoots.add(file); return SKIP_CHILDREN; } } return CONTINUE; } }); } } @Override public @NotNull Collection getSourceRoots() { return mySourceRoots; } @Override public @NotNull String getPresentableText() { return "Opam switch <" + myOpamSwitch + ">"; } @Override public @NotNull String getLocationString() { try { return Path.of(myOpamRoot, myOpamSwitch).toString(); } catch (InvalidPathException e) { return ""; } } @Override public @NotNull Icon getIcon(boolean unused) { return ORIcons.OCL_SDK; } @Override public boolean equals(Object other) { return (other instanceof OpamLibrary); // only one opam lib authorized } @Override public int hashCode() { return mySourceRoots.hashCode(); } } } ================================================ FILE: src/main/java/com/reason/ide/library/OclLibraryRootsComponentDescriptor.java ================================================ package com.reason.ide.library; import com.intellij.openapi.fileTypes.*; import com.intellij.openapi.progress.*; import com.intellij.openapi.roots.*; import com.intellij.openapi.roots.libraries.ui.*; import com.intellij.openapi.roots.ui.configuration.libraryEditor.*; import com.intellij.openapi.vfs.*; import com.intellij.util.containers.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; import java.util.*; public class OclLibraryRootsComponentDescriptor extends LibraryRootsComponentDescriptor { @Override public @Nullable OrderRootTypePresentation getRootTypePresentation(@NotNull OrderRootType type) { return DefaultLibraryRootsComponentDescriptor.getDefaultPresentation(type); } @Override public @NotNull List getRootDetectors() { return List.of(new OclSourceRootDetector()); } @Override public @NotNull List createAttachButtons() { return Collections.emptyList(); } static class OclSourceRootDetector extends RootDetector { protected OclSourceRootDetector() { super(OclSourcesOrderRootType.getInstance(), false, "OCaml sources"); } @Override public @NotNull Collection detectRoots(@NotNull VirtualFile rootCandidate, @NotNull ProgressIndicator progressIndicator) { if (!rootCandidate.isDirectory()) { return ContainerUtil.emptyList(); } FileTypeManager typeManager = FileTypeManager.getInstance(); List foundDirectories = new ArrayList<>(); try { VfsUtilCore.visitChildrenRecursively(rootCandidate, new VirtualFileVisitor() { @Override public @NotNull Result visitFileEx(@NotNull VirtualFile file) { progressIndicator.checkCanceled(); String fileName = file.getName(); if (file.isDirectory()) { if (fileName.startsWith(".") || "testsuite".equals(fileName) || "test".equals(fileName) || "example".equals(fileName)) { return SKIP_CHILDREN; } } else { FileType type = typeManager.getFileTypeByFileName(fileName); if (type.getDefaultExtension().equals("ml")) { VirtualFile root = file.getParent(); if (root != null) { foundDirectories.add(root); return skipTo(root); } } } return CONTINUE; } }); } catch (ProcessCanceledException ignore) { } return foundDirectories; } } } ================================================ FILE: src/main/java/com/reason/ide/library/OclLibraryType.java ================================================ package com.reason.ide.library; import com.intellij.openapi.project.*; import com.intellij.openapi.roots.libraries.*; import com.intellij.openapi.roots.libraries.ui.*; import com.intellij.openapi.vfs.*; import com.reason.ide.*; import org.jetbrains.annotations.*; import javax.swing.*; // DownloadableLibraryType ?? and use code from SDK download public class OclLibraryType extends LibraryType { public OclLibraryType() { super(OclLibraryKind.INSTANCE); } @Override public @NotNull String getCreateActionName() { return "OCaml"; } @Override public @NotNull Icon getIcon(@Nullable DummyLibraryProperties properties) { return ORIcons.OCL_SDK; } @Override public @Nullable NewLibraryConfiguration createNewLibrary(@NotNull JComponent parentComponent, @Nullable VirtualFile contextDirectory, @NotNull Project project) { LibraryTypeService libTypeService = LibraryTypeService.getInstance(); return libTypeService.createLibraryFromFiles(createLibraryRootsComponentDescriptor(), parentComponent, contextDirectory, this, project); } @Override public @NotNull LibraryRootsComponentDescriptor createLibraryRootsComponentDescriptor() { return new OclLibraryRootsComponentDescriptor(); } @Override public @Nullable LibraryPropertiesEditor createPropertiesEditor(@NotNull LibraryEditorComponent editorComponent) { return null; } } ================================================ FILE: src/main/java/com/reason/ide/library/OclSdkSetupValidator.java ================================================ package com.reason.ide.library; import com.intellij.codeInsight.daemon.*; import com.intellij.openapi.editor.*; import com.intellij.openapi.module.Module; import com.intellij.openapi.options.*; import com.intellij.openapi.project.*; import com.intellij.openapi.vfs.*; import com.intellij.psi.*; import com.intellij.ui.*; import com.reason.*; import com.reason.comp.dune.*; import com.reason.ide.settings.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; import javax.swing.event.*; import java.util.*; /** * If the user open a file (.ml/.mli => FileHelper.isOCaml) and the SDK isn't set, * then he/she will see a message to set up the SDK, along with a button to fix this error. */ public class OclSdkSetupValidator implements ProjectSdkSetupValidator { public static final String CONFIGURE_OCAML_SDK = "Please configure the Opam switch"; @Override public boolean isApplicableFor(@NotNull Project project, @NotNull VirtualFile file) { return FileHelper.isOCaml(file.getFileType()); } @Override public @Nullable String getErrorMessage(@NotNull Project project, @NotNull VirtualFile file) { // Checking for Opam switch ORSettings orSettings = project.getService(ORSettings.class); if (orSettings.getSwitchName().isEmpty()) { Map contentRootsFor = Platform.findModulesFor(project, DunePlatform.DUNE_PROJECT_FILENAME); if (!contentRootsFor.isEmpty()) { // Error: this is a dune project, and no opam switch is selected return CONFIGURE_OCAML_SDK; } } return null; } @Override public @NotNull EditorNotificationPanel.ActionHandler getFixHandler(@NotNull Project project, @NotNull VirtualFile file) { return new EditorNotificationPanel.ActionHandler() { @Override public void handlePanelActionClick(@NotNull EditorNotificationPanel panel, @NotNull HyperlinkEvent event) { ShowSettingsUtil.getInstance().showSettingsDialog(project, ORSettingsConfigurable.class); } @Override public void handleQuickFixClick(@NotNull Editor editor, @NotNull PsiFile psiFile) { } }; } } ================================================ FILE: src/main/java/com/reason/ide/match/DunePairedBraceMatcher.java ================================================ package com.reason.ide.match; import com.intellij.lang.*; import com.intellij.psi.*; import com.intellij.psi.tree.*; import com.reason.lang.dune.*; import org.jetbrains.annotations.*; public class DunePairedBraceMatcher implements PairedBraceMatcher { private static final BracePair[] PAIRS = new BracePair[]{ new BracePair(DuneTypes.INSTANCE.LPAREN, DuneTypes.INSTANCE.RPAREN, true), }; @Override public @NotNull BracePair[] getPairs() { return PAIRS; } @Override public boolean isPairedBracesAllowedBeforeType(@NotNull IElementType lbraceType, @Nullable IElementType contextType) { return true; } @Override public int getCodeConstructStart(PsiFile file, int openingBraceOffset) { return openingBraceOffset; } } ================================================ FILE: src/main/java/com/reason/ide/match/OclPairedBraceMatcher.java ================================================ package com.reason.ide.match; import com.intellij.lang.BracePair; import com.intellij.lang.PairedBraceMatcher; import com.intellij.psi.PsiFile; import com.intellij.psi.tree.IElementType; import com.reason.lang.ocaml.OclTypes; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; public class OclPairedBraceMatcher implements PairedBraceMatcher { private static final BracePair[] PAIRS = new BracePair[]{ // new BracePair(OclTypes.INSTANCE.LBRACE, OclTypes.INSTANCE.RBRACE, false), // new BracePair(OclTypes.INSTANCE.BEGIN, OclTypes.INSTANCE.END, false), // new BracePair(OclTypes.INSTANCE.STRUCT, OclTypes.INSTANCE.END, false), // new BracePair(OclTypes.INSTANCE.SIG, OclTypes.INSTANCE.END, false), // new BracePair(OclTypes.INSTANCE.OBJECT, OclTypes.INSTANCE.END, false), // new BracePair(OclTypes.INSTANCE.LPAREN, OclTypes.INSTANCE.RPAREN, false), new BracePair(OclTypes.INSTANCE.LBRACKET, OclTypes.INSTANCE.RBRACKET, false), new BracePair(OclTypes.INSTANCE.LARRAY, OclTypes.INSTANCE.RARRAY, false), // new BracePair(OclTypes.INSTANCE.LT, OclTypes.INSTANCE.GT, false), // new BracePair(OclTypes.INSTANCE.DO, OclTypes.INSTANCE.DONE, false), // }; @Override public @NotNull BracePair[] getPairs() { return PAIRS; } @Override public boolean isPairedBracesAllowedBeforeType(@NotNull IElementType lbraceType, @Nullable IElementType contextType) { return true; } @Override public int getCodeConstructStart(PsiFile file, int openingBraceOffset) { return openingBraceOffset; } } ================================================ FILE: src/main/java/com/reason/ide/match/ResPairedBraceMatcher.java ================================================ package com.reason.ide.match; import com.intellij.lang.*; import com.intellij.psi.*; import com.intellij.psi.tree.*; import com.reason.lang.rescript.*; import org.jetbrains.annotations.*; public class ResPairedBraceMatcher implements PairedBraceMatcher { private static final @NotNull BracePair[] PAIRS = new BracePair[]{ // new BracePair(ResTypes.INSTANCE.LBRACE, ResTypes.INSTANCE.RBRACE, true), // new BracePair(ResTypes.INSTANCE.LPAREN, ResTypes.INSTANCE.RPAREN, true), new BracePair(ResTypes.INSTANCE.ML_STRING_OPEN, ResTypes.INSTANCE.ML_STRING_CLOSE, true), new BracePair(ResTypes.INSTANCE.LBRACKET, ResTypes.INSTANCE.RBRACKET, true), new BracePair(ResTypes.INSTANCE.LARRAY, ResTypes.INSTANCE.RARRAY, true), // }; @Override public @NotNull BracePair[] getPairs() { return PAIRS; } @Override public boolean isPairedBracesAllowedBeforeType(@NotNull IElementType lbraceType, @Nullable IElementType contextType) { return true; } @Override public int getCodeConstructStart(PsiFile file, int openingBraceOffset) { return openingBraceOffset; } } ================================================ FILE: src/main/java/com/reason/ide/match/RmlPairedBraceMatcher.java ================================================ package com.reason.ide.match; import com.intellij.lang.BracePair; import com.intellij.lang.PairedBraceMatcher; import com.intellij.psi.PsiFile; import com.intellij.psi.tree.IElementType; import com.reason.lang.reason.RmlTypes; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; public class RmlPairedBraceMatcher implements PairedBraceMatcher { @NotNull private static final BracePair[] PAIRS = new BracePair[]{ // new BracePair(RmlTypes.INSTANCE.LBRACE, RmlTypes.INSTANCE.RBRACE, true), // new BracePair(RmlTypes.INSTANCE.LPAREN, RmlTypes.INSTANCE.RPAREN, true), new BracePair(RmlTypes.INSTANCE.ML_STRING_OPEN, RmlTypes.INSTANCE.ML_STRING_CLOSE, true), new BracePair(RmlTypes.INSTANCE.LBRACKET, RmlTypes.INSTANCE.RBRACKET, true), new BracePair(RmlTypes.INSTANCE.LARRAY, RmlTypes.INSTANCE.RARRAY, true), // }; @Override public @NotNull BracePair[] getPairs() { return PAIRS; } @Override public boolean isPairedBracesAllowedBeforeType(@NotNull IElementType lbraceType, @Nullable IElementType contextType) { return true; } @Override public int getCodeConstructStart(PsiFile file, int openingBraceOffset) { return openingBraceOffset; } } ================================================ FILE: src/main/java/com/reason/ide/module/DuneModuleEditor.form ================================================
================================================ FILE: src/main/java/com/reason/ide/module/DuneModuleEditor.java ================================================ package com.reason.ide.module; import com.intellij.openapi.roots.ui.configuration.*; import org.jetbrains.annotations.*; import javax.swing.*; public class DuneModuleEditor extends ModuleElementsEditor { private JPanel myPanel; protected DuneModuleEditor(@NotNull ModuleConfigurationState state) { super(state); } @Override protected JComponent createComponentImpl() { return myPanel; } @NonNls @Override public String getDisplayName() { return "Dune"; } } ================================================ FILE: src/main/java/com/reason/ide/module/DuneModuleEditorProvider.java ================================================ package com.reason.ide.module; import com.intellij.openapi.module.*; import com.intellij.openapi.roots.ui.configuration.*; public class DuneModuleEditorProvider implements ModuleConfigurationEditorProviderEx { public DuneModuleEditorProvider() { } public boolean isCompleteEditorSet() { return false; } public ModuleConfigurationEditor[] createEditors(ModuleConfigurationState state) { //Module module = state.getCurrentRootModel().getModule(); return new ModuleConfigurationEditor[]{new DuneModuleEditor(state)}; } } ================================================ FILE: src/main/java/com/reason/ide/refactor/ORRefactoringSupportProvider.java ================================================ package com.reason.ide.refactor; import com.intellij.lang.refactoring.*; import com.intellij.psi.*; import com.reason.lang.core.psi.*; import org.jetbrains.annotations.*; public class ORRefactoringSupportProvider extends RefactoringSupportProvider { @Override public boolean isInplaceRenameAvailable(@NotNull PsiElement element, PsiElement context) { // Not working: getUseScope must return LocalSearchScope return element instanceof RPsiLet || element instanceof RPsiInnerModule; } } ================================================ FILE: src/main/java/com/reason/ide/repl/PromptConsole.java ================================================ package com.reason.ide.repl; import static com.intellij.execution.ui.ConsoleViewContentType.USER_INPUT; import static com.intellij.openapi.editor.EditorKind.CONSOLE; import com.intellij.execution.impl.ConsoleViewImpl; import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.IdeActions; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.command.undo.UndoUtil; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.EditorFactory; import com.intellij.openapi.editor.EditorSettings; import com.intellij.openapi.editor.ex.EditorEx; import com.intellij.openapi.editor.impl.ContextMenuPopupHandler; import com.intellij.openapi.editor.impl.EditorFactoryImpl; import com.intellij.openapi.editor.impl.EditorImpl; import com.intellij.openapi.project.Project; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiFileFactory; import com.intellij.ui.JBColor; import com.intellij.ui.SideBorder; import com.reason.lang.ocaml.OclLanguage; import java.awt.*; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import javax.swing.*; import org.jetbrains.annotations.NotNull; final class PromptConsole implements Disposable { private static final String PROMPT_INACTIVE = " "; private final JPanel m_mainPanel = new JPanel(new BorderLayout()); private final ConsoleViewImpl m_consoleView; @NotNull private final EditorImpl m_outputEditor; @NotNull private final EditorImpl m_promptEditor; private final PromptHistory m_history = new PromptHistory(); private boolean m_promptEnabled = true; PromptConsole(@NotNull Project project, ConsoleViewImpl consoleView) { m_consoleView = consoleView; EditorFactory editorFactory = EditorFactory.getInstance(); PsiFileFactory fileFactory = PsiFileFactory.getInstance(project); Document outputDocument = ((EditorFactoryImpl) editorFactory).createDocument(true); UndoUtil.disableUndoFor(outputDocument); m_outputEditor = (EditorImpl) editorFactory.createViewer(outputDocument, project, CONSOLE); PsiFile file = fileFactory.createFileFromText("PromptConsoleDocument.ml", OclLanguage.INSTANCE, ""); Document promptDocument = file.getViewProvider().getDocument(); assert promptDocument != null; m_promptEditor = (EditorImpl) editorFactory.createEditor(promptDocument, project, CONSOLE); setupOutputEditor(); setupPromptEditor(); m_consoleView.print( "(* ctrl+enter to send a command, ctrl+up/down to cycle through history *)\r\n", USER_INPUT); m_mainPanel.add(m_outputEditor.getComponent(), BorderLayout.CENTER); m_mainPanel.add(m_promptEditor.getComponent(), BorderLayout.SOUTH); } @Override public void dispose() { EditorFactory editorFactory = EditorFactory.getInstance(); editorFactory.releaseEditor(m_outputEditor); editorFactory.releaseEditor(m_promptEditor); } @NotNull JComponent getCenterComponent() { return m_mainPanel; } @NotNull EditorImpl getOutputEditor() { return m_outputEditor; } @NotNull JComponent getInputComponent() { return m_promptEditor.getContentComponent(); } void disablePrompt() { m_promptEnabled = false; m_promptEditor.setPrefixTextAndAttributes(PROMPT_INACTIVE, USER_INPUT.getAttributes()); m_promptEditor.getDocument().setText(""); m_promptEditor.setRendererMode(true); } private void setupOutputEditor() { ((EditorEx) m_outputEditor).getContentComponent().setFocusCycleRoot(false); ((EditorEx) m_outputEditor).setHorizontalScrollbarVisible(true); ((EditorEx) m_outputEditor).setVerticalScrollbarVisible(true); ((EditorEx) m_outputEditor).getScrollPane().setBorder(null); EditorSettings editorSettings = ((EditorEx) m_outputEditor).getSettings(); editorSettings.setLineNumbersShown(false); editorSettings.setIndentGuidesShown(false); editorSettings.setLineMarkerAreaShown(false); editorSettings.setFoldingOutlineShown(true); editorSettings.setRightMarginShown(false); editorSettings.setVirtualSpace(false); editorSettings.setAdditionalPageAtBottom(false); editorSettings.setAdditionalLinesCount(0); editorSettings.setAdditionalColumnsCount(0); editorSettings.setLineCursorWidth(1); editorSettings.setCaretRowShown(false); // output only editor m_outputEditor.setRendererMode(true); // tiny separation between output and prompt m_outputEditor.getComponent().setBorder(new SideBorder(JBColor.LIGHT_GRAY, SideBorder.BOTTOM)); } private void setupPromptEditor() { EditorSettings editorSettings = ((EditorEx) m_promptEditor).getSettings(); editorSettings.setAdditionalLinesCount(0); m_promptEditor.getComponent().setPreferredSize(new Dimension(0, 100)); // add copy/paste actions m_promptEditor.installPopupHandler( new ContextMenuPopupHandler.Simple(IdeActions.GROUP_CUT_COPY_PASTE)); // hook some key event on prompt editor m_promptEditor .getContentComponent() .addKeyListener( new KeyAdapter() { @Override public void keyReleased(@NotNull KeyEvent e) { if (m_promptEnabled && e.isControlDown()) { int keyCode = e.getKeyCode(); if (keyCode == KeyEvent.VK_ENTER) { String command = normalizeCommand(m_promptEditor.getDocument().getText()); m_history.addInHistory(command); m_consoleView.print(command + "\r\n", USER_INPUT); m_consoleView.scrollToEnd(); ApplicationManager.getApplication().runWriteAction(() -> setPromptCommand("")); } else if (keyCode == KeyEvent.VK_UP || keyCode == KeyEvent.VK_DOWN) { String history = m_history.getFromHistory(keyCode == KeyEvent.VK_DOWN); if (history != null) { ApplicationManager.getApplication() .runWriteAction(() -> setPromptCommand(history)); } } } } }); } @NotNull private String normalizeCommand(@NotNull String command) { String sanitizedCommand = command.trim(); if (!sanitizedCommand.endsWith(";;")) { if (!sanitizedCommand.endsWith(";")) { sanitizedCommand += ";;"; } else { sanitizedCommand += ";"; } } return sanitizedCommand; } private void setPromptCommand(@NotNull String text) { m_promptEditor.getDocument().setText(text); m_promptEditor.getScrollingModel().scrollHorizontally(0); m_promptEditor.getCaretModel().moveToOffset(text.length()); } } ================================================ FILE: src/main/java/com/reason/ide/repl/PromptConsoleView.java ================================================ package com.reason.ide.repl; import com.intellij.execution.impl.ConsoleState; import com.intellij.execution.impl.ConsoleViewImpl; import com.intellij.execution.impl.ConsoleViewRunningState; import com.intellij.execution.process.ProcessAdapter; import com.intellij.execution.process.ProcessEvent; import com.intellij.execution.process.ProcessHandler; import com.intellij.execution.ui.ConsoleViewContentType; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.editor.ex.EditorEx; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Disposer; import com.intellij.psi.search.GlobalSearchScope; import javax.swing.*; import org.jetbrains.annotations.NotNull; public class PromptConsoleView extends ConsoleViewImpl { @NotNull private final PromptConsole m_promptConsole; PromptConsoleView(@NotNull Project project, boolean viewer, final boolean attachToStdOut) { super( project, GlobalSearchScope.allScope(project), viewer, new ConsoleState.NotStartedStated() { @NotNull @Override public ConsoleState attachTo( @NotNull ConsoleViewImpl console, @NotNull ProcessHandler processHandler) { return new ConsoleViewRunningState(console, processHandler, this, attachToStdOut, true); } }, true); m_promptConsole = new PromptConsole(project, this); Disposer.register(this, m_promptConsole); } @NotNull @Override protected JComponent createCenterComponent() { return m_promptConsole.getCenterComponent(); } @NotNull @Override protected EditorEx doCreateConsoleEditor() { return m_promptConsole.getOutputEditor(); } @NotNull @Override public JComponent getPreferredFocusableComponent() { return m_promptConsole.getInputComponent(); } @Override public void print(@NotNull String text, @NotNull ConsoleViewContentType contentType) { super.print(text, contentType); } @Override public void attachToProcess(@NotNull ProcessHandler processHandler) { super.attachToProcess(processHandler); processHandler.addProcessListener( new ProcessAdapter() { @Override public void processTerminated(@NotNull ProcessEvent event) { ApplicationManager.getApplication() .invokeLater( () -> ApplicationManager.getApplication() .runWriteAction(m_promptConsole::disablePrompt)); } }); } } ================================================ FILE: src/main/java/com/reason/ide/repl/PromptHistory.java ================================================ package com.reason.ide.repl; import java.util.LinkedList; import java.util.List; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; /** * Handle the prompt history of commands. */ final class PromptHistory { private final List m_history = new LinkedList<>(); private int m_historyIndex = 0; void addInHistory(@NotNull String command) { if (!command.trim().isEmpty()) { m_history.remove(command); while (m_history.size() >= 100) { m_history.remove(0); } m_history.add(command); } m_historyIndex = m_history.size(); } @Nullable String getFromHistory(boolean next) { if (next) { if ((m_historyIndex + 1) < m_history.size()) { m_historyIndex++; return m_history.get(m_historyIndex); } else { m_historyIndex = m_history.size(); return ""; } } else { if (m_historyIndex > 0) { m_historyIndex--; return m_history.get(m_historyIndex); } else { return null; } } } } ================================================ FILE: src/main/java/com/reason/ide/repl/ReplConfigurationFactory.java ================================================ package com.reason.ide.repl; import com.intellij.execution.configurations.*; import com.intellij.openapi.project.*; import com.reason.ide.*; import org.jetbrains.annotations.*; import javax.swing.*; public class ReplConfigurationFactory extends ConfigurationFactory { ReplConfigurationFactory(@NotNull ConfigurationType type) { super(type); } @Override public @NotNull RunConfiguration createTemplateConfiguration(@NotNull Project project) { return new ReplRunConfiguration(project, this); } @Override public @NotNull String getId() { return getName(); } @Override public @NotNull Icon getIcon() { return ORIcons.REPL; } @Override public @NotNull String getName() { return "OCaml REPL configuration factory"; } } ================================================ FILE: src/main/java/com/reason/ide/repl/ReplGenericState.java ================================================ package com.reason.ide.repl; import com.intellij.execution.*; import com.intellij.execution.configurations.*; import com.intellij.execution.process.*; import com.intellij.execution.runners.*; import com.intellij.openapi.project.*; import com.intellij.openapi.vfs.*; import com.reason.comp.dune.*; import com.reason.comp.ocaml.*; import com.reason.ide.settings.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; import java.util.*; import static com.intellij.openapi.application.ApplicationManager.getApplication; public class ReplGenericState implements RunProfileState { private final ExecutionEnvironment myEnvironment; ReplGenericState(ExecutionEnvironment environment) { myEnvironment = environment; } @Override public @Nullable ExecutionResult execute(Executor executor, @NotNull ProgramRunner runner) throws ExecutionException { ProcessHandler processHandler = startProcess(); if (processHandler != null) { PromptConsoleView consoleView = new PromptConsoleView(myEnvironment.getProject(), true, true); consoleView.attachToProcess(processHandler); return new DefaultExecutionResult(consoleView, processHandler); } return null; } private @Nullable ProcessHandler startProcess() throws ExecutionException { //ReplRunConfiguration profile = (ReplRunConfiguration) myEnvironment.getRunProfile(); Project project = myEnvironment.getProject(); ORSettings settings = project.getService(ORSettings.class); String opamLocation = settings == null ? null : settings.getOpamLocation(); String switchName = settings == null ? null : settings.getSwitchName(); if (opamLocation == null || switchName == null) { return null; } VirtualFile baseRoot = DunePlatform.findConfigFiles(project).stream().findFirst().map(VirtualFile::getParent).orElse(null); GeneralCommandLine cli = new GeneralCommandLine("ocaml"); cli.setRedirectErrorStream(true); if (baseRoot != null) { cli.setWorkDirectory(baseRoot.getPath()); } Map env = getApplication().getService(OpamEnv.class).getEnv(switchName); if (env != null) { for (Map.Entry entry : env.entrySet()) { cli.withEnvironment(entry.getKey(), entry.getValue()); } } OCamlExecutable executable = OCamlExecutable.getExecutable(opamLocation, null); executable.patchCommandLine(cli, opamLocation + "/" + switchName + "/bin", false); OSProcessHandler handler = new OSProcessHandler(cli); ProcessTerminatedListener.attach(handler, project); return handler; } } ================================================ FILE: src/main/java/com/reason/ide/repl/ReplRunConfiguration.java ================================================ package com.reason.ide.repl; import com.intellij.execution.*; import com.intellij.execution.configurations.*; import com.intellij.execution.runners.*; import com.intellij.openapi.options.*; import com.intellij.openapi.project.*; import org.jetbrains.annotations.*; public class ReplRunConfiguration extends RunConfigurationBase { ReplRunConfiguration(@NotNull Project project, @NotNull ConfigurationFactory factory) { super(project, factory, "OCaml REPL"); } @Override public @NotNull SettingsEditor getConfigurationEditor() { return new ReplRunSettingsEditor(getProject()); } @Override public @Nullable RunProfileState getState(@NotNull Executor executor, @NotNull ExecutionEnvironment executionEnvironment) { return new ReplGenericState(executionEnvironment); } } ================================================ FILE: src/main/java/com/reason/ide/repl/ReplRunConfigurationType.java ================================================ package com.reason.ide.repl; import com.intellij.execution.configurations.*; import com.reason.ide.*; import org.jetbrains.annotations.*; import javax.swing.*; public class ReplRunConfigurationType implements ConfigurationType { @Override public @NotNull String getDisplayName() { return "OCaml REPL"; } @Override public @NotNull String getConfigurationTypeDescription() { return "OCaml REPL configuration Type"; } @Override public @NotNull Icon getIcon() { return ORIcons.OCAML; } @Override public @NotNull String getId() { return "OCAML_RUN_CONFIGURATION"; } @Override public @NotNull ConfigurationFactory[] getConfigurationFactories() { return new ConfigurationFactory[]{new ReplConfigurationFactory(this)}; } } ================================================ FILE: src/main/java/com/reason/ide/repl/ReplRunSettingsEditor.form ================================================
================================================ FILE: src/main/java/com/reason/ide/repl/ReplRunSettingsEditor.java ================================================ package com.reason.ide.repl; import com.intellij.openapi.options.*; import com.intellij.openapi.project.*; import com.reason.ide.settings.*; import org.jetbrains.annotations.*; import javax.swing.*; public class ReplRunSettingsEditor extends SettingsEditor { private final Project myProject; private JPanel myRootPanel; private JLabel mySwitchName; ReplRunSettingsEditor(@NotNull Project project) { myProject = project; } @Override protected @NotNull JComponent createEditor() { return myRootPanel; } private void createUIComponents() { init(); } @Override protected void resetEditorFrom(@NotNull ReplRunConfiguration configuration) { init(); } @Override protected void applyEditorTo(@NotNull ReplRunConfiguration runConfiguration) { } private void init() { ORSettings settings = myProject.getService(ORSettings.class); mySwitchName.setText(settings.getSwitchName()); } } ================================================ FILE: src/main/java/com/reason/ide/search/FileModuleData.java ================================================ package com.reason.ide.search; import com.intellij.openapi.util.*; import com.intellij.openapi.vfs.*; import org.jetbrains.annotations.*; import java.util.*; public class FileModuleData implements Comparable, IndexedFileModule { private final String myNamespace; private final String myModuleName; private final String myFullName; private final String myPath; private final boolean myIsOCaml; private final boolean myIsRescript; private final boolean myIsInterface; private final boolean myIsComponent; private final boolean myHasComponents; public FileModuleData(@NotNull VirtualFile file, @NotNull String namespace, String moduleName, boolean isOCaml, boolean isRescript, boolean isInterface, boolean isComponent, boolean hasComponents) { myNamespace = namespace; myModuleName = moduleName; myIsOCaml = isOCaml; myIsRescript = isRescript; myIsInterface = isInterface; myIsComponent = isComponent; myHasComponents = hasComponents; myPath = file.getPath(); String filename = file.getNameWithoutExtension(); myFullName = namespace.isEmpty() ? filename : filename + "-" + namespace; } public FileModuleData(String path, String fullName, String namespace, String moduleName, boolean isOCaml, boolean isRescript, boolean isInterface, boolean isComponent, boolean hasComponents) { myPath = path; myFullName = fullName; myNamespace = namespace; myModuleName = moduleName; myIsOCaml = isOCaml; myIsRescript = isRescript; myIsInterface = isInterface; myIsComponent = isComponent; myHasComponents = hasComponents; } @Override public @NotNull String getNamespace() { return myNamespace; } public boolean hasNamespace() { return !myNamespace.isEmpty(); } @Override public @NotNull String getModuleName() { return myModuleName; } @NotNull @Override public String getPath() { return myPath; } @NotNull @Override public String getFullName() { return myFullName; } @Override public boolean isOCaml() { return myIsOCaml; } @Override public boolean isRescript() { return myIsRescript; } @Override public boolean isInterface() { return myIsInterface; } @Override public boolean isComponent() { return myIsComponent; } public boolean hasComponents() { return myHasComponents; } @Override public int compareTo(@NotNull FileModuleData o) { int comp = Comparing.compare(myNamespace, o.myNamespace); if (comp == 0) { comp = Comparing.compare(myModuleName, o.myModuleName); if (comp == 0) { comp = Boolean.compare(myIsInterface, o.myIsInterface); } } return comp; } @Override public boolean equals(@Nullable Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } FileModuleData that = (FileModuleData) o; return myIsInterface == that.myIsInterface && Objects.equals(myNamespace, that.myNamespace) && Objects.equals(myModuleName, that.myModuleName) && Objects.equals(myPath, that.myPath); } @Override public int hashCode() { return Objects.hash(myNamespace, myModuleName, myIsInterface, myPath); } @NotNull @Override public String toString() { return "FileModuleData{" + "namespace='" + myNamespace + '\'' + ", moduleName='" + myModuleName + '\'' + ", isInterface=" + myIsInterface + ", isComponent=" + myIsComponent + ", hasComponents=" + myHasComponents + ", path=" + myPath + '}'; } } ================================================ FILE: src/main/java/com/reason/ide/search/IndexedFileModule.java ================================================ package com.reason.ide.search; import org.jetbrains.annotations.NotNull; public interface IndexedFileModule { @NotNull String getNamespace(); @NotNull String getModuleName(); @NotNull String getPath(); @NotNull String getFullName(); boolean isOCaml(); boolean isRescript(); boolean isInterface(); boolean isComponent(); } ================================================ FILE: src/main/java/com/reason/ide/search/ModuleIndexService.java ================================================ package com.reason.ide.search; import com.intellij.openapi.components.*; import com.intellij.openapi.project.*; import com.intellij.openapi.vfs.*; import com.intellij.psi.*; import com.intellij.psi.search.*; import com.reason.ide.files.*; import com.reason.ide.search.index.*; import com.reason.lang.core.psi.*; import org.jetbrains.annotations.*; import java.util.*; @Service(Service.Level.APP) public final class ModuleIndexService { public Collection getModules(@Nullable String name, @NotNull Project project, @NotNull GlobalSearchScope scope) { List result = new ArrayList<>(); if (name != null) { if (name.contains(".")) { // inner component return ModuleFqnIndex.getElements(name, project, scope); } else { // top level (file) component for (VirtualFile file : FileModuleIndex.getContainingFiles(name, scope)) { PsiFile psiFile = PsiManager.getInstance(project).findFile(file); if (psiFile instanceof FileBase) { result.add((FileBase) psiFile); } } } } return result; } } ================================================ FILE: src/main/java/com/reason/ide/search/ORFindUsagesProvider.java ================================================ package com.reason.ide.search; import com.intellij.lang.*; import com.intellij.lang.findUsages.*; import com.intellij.psi.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import org.jetbrains.annotations.*; public abstract class ORFindUsagesProvider implements FindUsagesProvider { @Override public boolean canFindUsagesFor(@NotNull PsiElement element) { return element instanceof RPsiModule/*Functor*/ || element instanceof RPsiException || element instanceof RPsiLet || element instanceof RPsiVal || element instanceof RPsiLowerName || element instanceof RPsiType || element instanceof RPsiExternal || element instanceof RPsiRecordField || element instanceof RPsiObjectField || element instanceof RPsiVariantDeclaration || element instanceof RPsiParameterDeclaration || (element instanceof RPsiLowerSymbol && element.getParent() instanceof RPsiDeconstruction); } @Override public @Nullable String getHelpId(@NotNull PsiElement psiElement) { return HelpID.FIND_OTHER_USAGES; } @Override public @NotNull String getType(@NotNull PsiElement element) { String type = PsiTypeElementProvider.getType(element); return type == null ? "unknown type" : type; } @Override public @NotNull String getDescriptiveName(@NotNull PsiElement element) { if (element instanceof RPsiModule) { return "Module " + ((RPsiModule) element).getName(); } else if (element instanceof PsiNamedElement) { String name = ((PsiNamedElement) element).getName(); return name == null ? "" : name; } return ""; } @Override public @NotNull String getNodeText(@NotNull PsiElement element, boolean useFullName) { if (element instanceof PsiQualifiedNamedElement) { String qName = ((PsiQualifiedNamedElement) element).getQualifiedName(); return qName == null ? "" : qName; } if (element instanceof PsiNamedElement) { String name = ((PsiNamedElement) element).getName(); if (name != null) { return name; } } return element.getText(); } } ================================================ FILE: src/main/java/com/reason/ide/search/ORImplementationSearch.java ================================================ package com.reason.ide.search; import com.intellij.openapi.application.*; import com.intellij.openapi.project.*; import com.intellij.psi.*; import com.intellij.psi.search.*; import com.intellij.psi.search.searches.*; import com.intellij.util.*; import com.reason.ide.*; import com.reason.ide.search.index.*; import com.reason.lang.core.psi.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; import java.util.*; public class ORImplementationSearch extends QueryExecutorBase { private static final Log LOG = Log.create("search"); @Override public void processQuery(@NotNull DefinitionsScopedSearch.SearchParameters queryParameters, @NotNull Processor consumer) { PsiElement source = queryParameters.getElement(); if (!source.isValid()) { return; } SearchScope scope = queryParameters.getScope(); if (scope instanceof GlobalSearchScope searchScope) { ReadAction.run(() -> { Project project = queryParameters.getProject(); if (source instanceof RPsiVal valSource) { String qName = valSource.getQualifiedName(); if (qName != null) { LOG.debug("Process implementation search for VAL", qName, scope, project); Collection elements = LetFqnIndex.getElements(qName, project, searchScope); for (RPsiLet element : elements) { System.out.println("Processing " + element.getQualifiedName() + " " + ORFileUtils.getVirtualFile(element.getContainingFile())); consumer.process(element); } } } }); } } } ================================================ FILE: src/main/java/com/reason/ide/search/OclFindUsagesProvider.java ================================================ package com.reason.ide.search; import com.intellij.lang.cacheBuilder.DefaultWordsScanner; import com.intellij.lang.cacheBuilder.WordsScanner; import com.intellij.psi.tree.TokenSet; import com.reason.lang.core.type.ORLangTypes; import com.reason.lang.ocaml.OclLexer; import com.reason.lang.ocaml.OclTypes; import org.jetbrains.annotations.Nullable; public class OclFindUsagesProvider extends ORFindUsagesProvider { @Override public @Nullable WordsScanner getWordsScanner() { ORLangTypes types = OclTypes.INSTANCE; return new DefaultWordsScanner( new OclLexer(), // TokenSet.create(types.UIDENT, types.LIDENT, types.A_MODULE_NAME, types.A_VARIANT_NAME), // TokenSet.create(types.MULTI_COMMENT), // TokenSet.create(types.FLOAT_VALUE, types.INT_VALUE, types.STRING_VALUE)); } } ================================================ FILE: src/main/java/com/reason/ide/search/PsiTypeElementProvider.java ================================================ package com.reason.ide.search; import com.intellij.psi.PsiElement; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; public class PsiTypeElementProvider { private PsiTypeElementProvider() { } @Nullable public static String getType(@NotNull PsiElement element) { if (element instanceof RPsiModule) { return "module"; } if (element instanceof RPsiException) { return "exception"; } if (element instanceof RPsiLet) { return "let"; } if (element instanceof RPsiVal) { return "val"; } if (element instanceof RPsiType) { return "type"; } if (element instanceof RPsiExternal) { return "external"; } if (element instanceof RPsiVariantDeclaration) { return "variant"; } if (element instanceof RPsiParameterDeclaration) { return "parameter"; } if (element instanceof RPsiRecordField) { return "record field"; } if (element instanceof RPsiObjectField) { return "object field"; } return null; } } ================================================ FILE: src/main/java/com/reason/ide/search/ResFindUsagesProvider.java ================================================ package com.reason.ide.search; import com.intellij.lang.cacheBuilder.DefaultWordsScanner; import com.intellij.lang.cacheBuilder.WordsScanner; import com.intellij.psi.tree.TokenSet; import com.reason.lang.core.type.ORLangTypes; import com.reason.lang.rescript.ResLexer; import com.reason.lang.rescript.ResTypes; import org.jetbrains.annotations.Nullable; public class ResFindUsagesProvider extends ORFindUsagesProvider { @Override public @Nullable WordsScanner getWordsScanner() { ORLangTypes types = ResTypes.INSTANCE; return new DefaultWordsScanner( new ResLexer(), // TokenSet.create(types.UIDENT, types.LIDENT, types.A_MODULE_NAME, types.A_VARIANT_NAME), // TokenSet.create(types.MULTI_COMMENT, types.SINGLE_COMMENT), // TokenSet.create(types.FLOAT_VALUE, types.INT_VALUE, types.STRING_VALUE)); } } ================================================ FILE: src/main/java/com/reason/ide/search/RmlFindUsagesProvider.java ================================================ package com.reason.ide.search; import com.intellij.lang.cacheBuilder.DefaultWordsScanner; import com.intellij.lang.cacheBuilder.WordsScanner; import com.intellij.psi.tree.TokenSet; import com.reason.lang.core.type.ORLangTypes; import com.reason.lang.reason.RmlLexer; import com.reason.lang.reason.RmlTypes; import org.jetbrains.annotations.Nullable; public class RmlFindUsagesProvider extends ORFindUsagesProvider { @Override public @Nullable WordsScanner getWordsScanner() { ORLangTypes types = RmlTypes.INSTANCE; return new DefaultWordsScanner( new RmlLexer(), // TokenSet.create(types.UIDENT, types.LIDENT, types.A_MODULE_NAME, types.A_VARIANT_NAME), // TokenSet.create(types.MULTI_COMMENT, types.SINGLE_COMMENT), // TokenSet.create(types.FLOAT_VALUE, types.INT_VALUE, types.STRING_VALUE)); } } ================================================ FILE: src/main/java/com/reason/ide/search/index/ClassFqnIndex.java ================================================ package com.reason.ide.search.index; import com.intellij.openapi.project.*; import com.intellij.psi.search.*; import com.intellij.psi.stubs.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.stub.type.*; import org.jetbrains.annotations.*; import java.util.*; public class ClassFqnIndex extends IntStubIndexExtension { @Override public int getVersion() { return super.getVersion() + ORStubVersions.CLASS; } @Override public @NotNull StubIndexKey getKey() { return IndexKeys.CLASSES_FQN; } public static @NotNull Collection getElements(@NotNull String qName, @NotNull Project project, @Nullable GlobalSearchScope scope) { return StubIndex.getElements(IndexKeys.CLASSES_FQN, qName.hashCode(), project, scope, RPsiClass.class); } } ================================================ FILE: src/main/java/com/reason/ide/search/index/ClassMethodFqnIndex.java ================================================ package com.reason.ide.search.index; import com.intellij.openapi.project.*; import com.intellij.psi.search.*; import com.intellij.psi.stubs.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.stub.type.*; import org.jetbrains.annotations.*; import java.util.*; public class ClassMethodFqnIndex extends IntStubIndexExtension { @Override public int getVersion() { return super.getVersion() + ORStubVersions.CLASS_METHOD; } @Override public @NotNull StubIndexKey getKey() { return IndexKeys.CLASS_METHODS_FQN; } public static @NotNull Collection getElements(String qName, @NotNull Project project, @Nullable GlobalSearchScope scope) { return StubIndex.getElements(IndexKeys.CLASS_METHODS_FQN, qName.hashCode(), project, scope, RPsiClassMethod.class); } } ================================================ FILE: src/main/java/com/reason/ide/search/index/ExceptionFqnIndex.java ================================================ package com.reason.ide.search.index; import com.intellij.openapi.project.*; import com.intellij.psi.search.*; import com.intellij.psi.stubs.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.stub.type.*; import org.jetbrains.annotations.*; import java.util.*; public class ExceptionFqnIndex extends IntStubIndexExtension { @Override public int getVersion() { return super.getVersion() + ORStubVersions.EXCEPTION; } @Override public @NotNull StubIndexKey getKey() { return IndexKeys.EXCEPTIONS_FQN; } public static @NotNull Collection getElements(@NotNull String qName, @NotNull Project project, @Nullable GlobalSearchScope scope) { return StubIndex.getElements(IndexKeys.EXCEPTIONS_FQN, qName.hashCode(), project, scope, RPsiException.class); } } ================================================ FILE: src/main/java/com/reason/ide/search/index/ExceptionIndex.java ================================================ package com.reason.ide.search.index; import com.intellij.openapi.project.*; import com.intellij.psi.search.*; import com.intellij.psi.stubs.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.stub.type.*; import org.jetbrains.annotations.*; import java.util.*; public class ExceptionIndex extends StringStubIndexExtension { @Override public int getVersion() { return super.getVersion() + ORStubVersions.EXCEPTION; } @Override public @NotNull StubIndexKey getKey() { return IndexKeys.EXCEPTIONS; } public static @NotNull Collection getElements(@NotNull String key, @NotNull Project project, @Nullable GlobalSearchScope scope) { return StubIndex.getElements(IndexKeys.EXCEPTIONS, key, project, scope, RPsiException.class); } } ================================================ FILE: src/main/java/com/reason/ide/search/index/ExternalFqnIndex.java ================================================ package com.reason.ide.search.index; import com.intellij.openapi.project.*; import com.intellij.psi.search.*; import com.intellij.psi.stubs.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.stub.type.*; import org.jetbrains.annotations.*; import java.util.*; public class ExternalFqnIndex extends IntStubIndexExtension { @Override public int getVersion() { return super.getVersion() + ORStubVersions.EXTERNAL; } @Override public @NotNull StubIndexKey getKey() { return IndexKeys.EXTERNALS_FQN; } public static @NotNull Collection getElements(String qName, @NotNull Project project, @Nullable GlobalSearchScope scope) { return StubIndex.getElements(IndexKeys.EXTERNALS_FQN, qName.hashCode(), project, scope, RPsiExternal.class); } } ================================================ FILE: src/main/java/com/reason/ide/search/index/ExternalIndex.java ================================================ package com.reason.ide.search.index; import com.intellij.openapi.project.*; import com.intellij.psi.search.*; import com.intellij.psi.stubs.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.stub.type.*; import org.jetbrains.annotations.*; import java.util.*; public class ExternalIndex extends StringStubIndexExtension { @Override public int getVersion() { return super.getVersion() + ORStubVersions.EXTERNAL; } @Override public @NotNull StubIndexKey getKey() { return IndexKeys.EXTERNALS; } public static @NotNull Collection getElements(@NotNull String key, @NotNull Project project, @Nullable GlobalSearchScope scope) { return StubIndex.getElements(IndexKeys.EXTERNALS, key, project, scope, RPsiExternal.class); } } ================================================ FILE: src/main/java/com/reason/ide/search/index/FileModuleIndex.java ================================================ package com.reason.ide.search.index; import com.intellij.openapi.project.*; import com.intellij.openapi.vfs.*; import com.intellij.psi.search.*; import com.intellij.psi.util.*; import com.intellij.util.indexing.*; import com.intellij.util.io.*; import com.reason.*; import com.reason.comp.*; import com.reason.comp.bs.*; import com.reason.ide.files.*; import com.reason.ide.search.*; import com.reason.lang.core.psi.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; import java.io.*; import java.util.*; public class FileModuleIndex extends FileBasedIndexExtension { private static final int VERSION = 2; private static final ID INDEX_ID = ID.create("reason.module.fileIndex"); private static final DataExternalizer EXTERNALIZER = new FileModuleDataExternalizer(); private static final Log LOG = Log.create("index.file"); public static @NotNull Collection getAllKeys(@NotNull Project project) { return FileBasedIndex.getInstance().getAllKeys(INDEX_ID, project); } public static @NotNull List getValues(@NotNull String name, @NotNull GlobalSearchScope scope) { return FileBasedIndex.getInstance().getValues(INDEX_ID, name, scope); } public static @NotNull Collection getContainingFiles(@NotNull String name, @NotNull GlobalSearchScope scope) { return FileBasedIndex.getInstance().getContainingFiles(INDEX_ID, name, scope); } @Override public @NotNull ID getName() { return INDEX_ID; } @Override public @NotNull KeyDescriptor getKeyDescriptor() { return EnumeratorStringDescriptor.INSTANCE; } @Override public @NotNull DataIndexer getIndexer() { return inputData -> { Map map = new HashMap<>(); FileBase psiFile = (FileBase) inputData.getPsiFile(); if (psiFile.canNavigate()) { String moduleName = psiFile.getModuleName(); String namespace = ""; VirtualFile bsConfigFile = inputData.getProject().getService(ORCompilerConfigManager.class).findNearestConfigFile(inputData.getFile()); if (bsConfigFile != null) { VirtualFile parent = bsConfigFile.getParent(); boolean useExternalAsSource = "bs-platform".equals(parent.getName()); BsConfig bsConfig = BsConfigReader.read(bsConfigFile, useExternalAsSource); namespace = bsConfig.getNamespace(); } boolean hasComponents = PsiTreeUtil.findChildrenOfType(psiFile, RPsiInnerModule.class).stream().anyMatch(RPsiInnerModule::isComponent); boolean isOCaml = FileHelper.isOCaml(inputData.getFileType()); boolean isRescript = FileHelper.isRescript(inputData.getFileType()); FileModuleData value = new FileModuleData(inputData.getFile(), namespace, moduleName, isOCaml, isRescript, psiFile.isInterface(), psiFile.isComponent(), hasComponents); if (LOG.isDebugEnabled()) { LOG.debug("indexing " + Platform.getRelativePathToModule(inputData.getPsiFile()) + ": " + value); } map.put(moduleName, value); if (!namespace.isEmpty()) { map.put(namespace + "." + moduleName, value); } } else { if (LOG.isDebugEnabled()) { LOG.debug("Skip file [not navigable]: " + Platform.getRelativePathToModule(inputData.getPsiFile())); } } return map; }; } @Override public @NotNull DataExternalizer getValueExternalizer() { return EXTERNALIZER; } @Override public int getVersion() { return VERSION; } @Override public @NotNull FileBasedIndex.InputFilter getInputFilter() { return new DefaultFileTypeSpecificInputFilter( RmlFileType.INSTANCE, RmlInterfaceFileType.INSTANCE, OclFileType.INSTANCE, OclInterfaceFileType.INSTANCE, ResFileType.INSTANCE, ResInterfaceFileType.INSTANCE); } @Override public boolean dependsOnFileContent() { return true; } static final class FileModuleDataExternalizer implements DataExternalizer { @Override public void save(@NotNull DataOutput out, @NotNull FileModuleData value) throws IOException { out.writeBoolean(value.isOCaml()); out.writeBoolean(value.isRescript()); out.writeBoolean(value.isInterface()); out.writeBoolean(value.isComponent()); out.writeBoolean(value.hasComponents()); out.writeUTF(value.getPath()); out.writeUTF(value.getNamespace()); out.writeUTF(value.getModuleName()); out.writeUTF(value.getFullName()); } @Override public @NotNull FileModuleData read(@NotNull DataInput in) throws IOException { boolean isOCaml = in.readBoolean(); boolean isRescript = in.readBoolean(); boolean isInterface = in.readBoolean(); boolean isComponent = in.readBoolean(); boolean hasComponents = in.readBoolean(); String path = in.readUTF(); String namespace = in.readUTF(); String moduleName = in.readUTF(); String fullName = in.readUTF(); return new FileModuleData(path, fullName, namespace, moduleName, isOCaml, isRescript, isInterface, isComponent, hasComponents); } } } ================================================ FILE: src/main/java/com/reason/ide/search/index/FileModuleIndexService.java ================================================ package com.reason.ide.search.index; import com.intellij.openapi.application.*; import com.intellij.openapi.components.*; import com.intellij.openapi.project.*; import com.intellij.openapi.vfs.*; import com.intellij.psi.*; import com.intellij.psi.search.*; import com.reason.*; import com.reason.ide.search.*; import com.reason.lang.core.psi.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; import java.util.*; @Service(Service.Level.APP) public final class FileModuleIndexService { private static final Log LOG = Log.create("index.fileservice"); private static final Comparator SORT_INTERFACE_FIRST = (o1, o2) -> FileHelper.isInterface(o1.getFileType()) ? -1 : FileHelper.isInterface(o2.getFileType()) ? 1 : 0; private static FileModuleIndexService myInstance = null; public static @NotNull FileModuleIndexService getInstance() { if (myInstance == null) { myInstance = ApplicationManager.getApplication().getService(FileModuleIndexService.class); } return myInstance; } @NotNull public List getTopModules(@NotNull Project project, @NotNull GlobalSearchScope scope) { List result = new ArrayList<>(); for (String key : getAllKeys(project)) { result.addAll(getTopModuleData(key, scope)); } return result; } public @Nullable RPsiModule getTopModule(@NotNull String name, @NotNull Project project, @NotNull GlobalSearchScope scope) { Collection containingFiles = FileModuleIndex.getContainingFiles(name, scope); return containingFiles.stream().min(SORT_INTERFACE_FIRST). map(virtualFile -> { PsiFile psiFile = PsiManager.getInstance(project).findFile(virtualFile); return psiFile instanceof RPsiModule ? (RPsiModule) psiFile : null; }).orElse(null); } public @NotNull List getTopModuleData(@NotNull String name, @NotNull GlobalSearchScope scope) { return FileModuleIndex.getValues(name, scope); } @NotNull public Collection getNamespaces(@NotNull Project project, @NotNull GlobalSearchScope scope) { Set result = new HashSet<>(); for (String key : getAllKeys(project)) { getTopModuleData(key, scope).stream() .filter(FileModuleData::hasNamespace) .findFirst().map(FileModuleData::getNamespace) .ifPresent(result::add); } LOG.debug("namespaces", result); return result; } public @NotNull Collection getAllKeys(@NotNull Project project) { return FileModuleIndex.getAllKeys(project); } } ================================================ FILE: src/main/java/com/reason/ide/search/index/IncludeIndex.java ================================================ package com.reason.ide.search.index; import com.intellij.openapi.project.*; import com.intellij.psi.search.*; import com.intellij.psi.stubs.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.stub.type.*; import org.jetbrains.annotations.*; import java.util.*; public class IncludeIndex extends StringStubIndexExtension { @Override public int getVersion() { return super.getVersion() + ORStubVersions.INCLUDE; } @Override public @NotNull StubIndexKey getKey() { return IndexKeys.INCLUDES; } public static @NotNull Collection getElements(@NotNull String key, @NotNull Project project, @Nullable GlobalSearchScope scope) { return StubIndex.getElements(IndexKeys.INCLUDES, key, project, scope, RPsiInclude.class); } } ================================================ FILE: src/main/java/com/reason/ide/search/index/IndexKeys.java ================================================ package com.reason.ide.search.index; import com.intellij.psi.stubs.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import org.jetbrains.annotations.*; public class IndexKeys { @FunctionalInterface public interface ProcessElement { void process(@NotNull T element); } public static final StubIndexKey MODULES_SIGNATURE = StubIndexKey.createIndexKey("reason.module_signature"); public static final StubIndexKey MODULES = StubIndexKey.createIndexKey("reason.module"); public static final StubIndexKey MODULES_FQN = StubIndexKey.createIndexKey("reason.module.fqn"); public static final StubIndexKey CLASSES_FQN = StubIndexKey.createIndexKey("reason.class.fqn"); public static final StubIndexKey CLASS_METHODS_FQN = StubIndexKey.createIndexKey("reason.method.fqn"); public static final StubIndexKey VARIANTS_FQN = StubIndexKey.createIndexKey("reason.variant.fqn"); public static final StubIndexKey LETS_COMP_FQN = StubIndexKey.createIndexKey("reason.let.component.fqn"); public static final StubIndexKey LETS_FQN = StubIndexKey.createIndexKey("reason.let.fqn"); public static final StubIndexKey VALS_FQN = StubIndexKey.createIndexKey("reason.val.fqn"); public static final StubIndexKey EXTERNALS = StubIndexKey.createIndexKey("reason.external"); public static final StubIndexKey EXTERNALS_FQN = StubIndexKey.createIndexKey("reason.external.fqn"); public static final StubIndexKey TYPES = StubIndexKey.createIndexKey("reason.type"); public static final StubIndexKey TYPES_FQN = StubIndexKey.createIndexKey("reason.type.fqn"); public static final StubIndexKey OBJECT_FIELDS = StubIndexKey.createIndexKey("reason.object_field"); public static final StubIndexKey RECORD_FIELDS = StubIndexKey.createIndexKey("reason.record_field"); public static final StubIndexKey EXCEPTIONS = StubIndexKey.createIndexKey("reason.exception"); public static final StubIndexKey EXCEPTIONS_FQN = StubIndexKey.createIndexKey("reason.exception.fqn"); public static final StubIndexKey PARAMETERS = StubIndexKey.createIndexKey("reason.parameter"); public static final StubIndexKey PARAMETERS_FQN = StubIndexKey.createIndexKey("reason.parameter.fqn"); public static final StubIndexKey INCLUDES = StubIndexKey.createIndexKey("reason.include"); public static final StubIndexKey OPENS = StubIndexKey.createIndexKey("reason.open"); private IndexKeys() { } } ================================================ FILE: src/main/java/com/reason/ide/search/index/LetComponentFqnIndex.java ================================================ package com.reason.ide.search.index; import com.intellij.openapi.project.*; import com.intellij.psi.search.*; import com.intellij.psi.stubs.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.stub.type.*; import org.jetbrains.annotations.*; import java.util.*; public class LetComponentFqnIndex extends IntStubIndexExtension { @Override public int getVersion() { return super.getVersion() + ORStubVersions.LET; } @Override public @NotNull StubIndexKey getKey() { return IndexKeys.LETS_COMP_FQN; } public static @NotNull Collection getElements(String qName, @NotNull Project project, @Nullable GlobalSearchScope scope) { return StubIndex.getElements(IndexKeys.LETS_COMP_FQN, qName.hashCode(), project, scope, RPsiLet.class); } } ================================================ FILE: src/main/java/com/reason/ide/search/index/LetFqnIndex.java ================================================ package com.reason.ide.search.index; import com.intellij.openapi.project.*; import com.intellij.psi.search.*; import com.intellij.psi.stubs.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.stub.type.*; import org.jetbrains.annotations.*; import java.util.*; public class LetFqnIndex extends IntStubIndexExtension { @Override public int getVersion() { return super.getVersion() + ORStubVersions.LET; } @Override public @NotNull StubIndexKey getKey() { return IndexKeys.LETS_FQN; } public static @NotNull Collection getElements(String qName, @NotNull Project project, @Nullable GlobalSearchScope scope) { return StubIndex.getElements(IndexKeys.LETS_FQN, qName.hashCode(), project, scope, RPsiLet.class); } } ================================================ FILE: src/main/java/com/reason/ide/search/index/ModuleFqnIndex.java ================================================ package com.reason.ide.search.index; import com.intellij.openapi.project.*; import com.intellij.psi.search.*; import com.intellij.psi.stubs.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.stub.type.*; import org.jetbrains.annotations.*; import java.util.*; public class ModuleFqnIndex extends IntStubIndexExtension { @Override public int getVersion() { return super.getVersion() + ORStubVersions.MODULE; } @Override public @NotNull StubIndexKey getKey() { return IndexKeys.MODULES_FQN; } public static @NotNull Collection getElements(@NotNull String qname, @NotNull Project project, @Nullable GlobalSearchScope scope) { return StubIndex.getElements(IndexKeys.MODULES_FQN, qname.hashCode(), project, scope, RPsiModule.class); } } ================================================ FILE: src/main/java/com/reason/ide/search/index/ModuleIndex.java ================================================ package com.reason.ide.search.index; import com.intellij.openapi.project.*; import com.intellij.psi.search.*; import com.intellij.psi.stubs.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.stub.type.*; import org.jetbrains.annotations.*; import java.util.*; public class ModuleIndex extends StringStubIndexExtension { @Override public int getVersion() { return super.getVersion() + ORStubVersions.MODULE; } @Override public @NotNull StubIndexKey getKey() { return IndexKeys.MODULES; } public static @NotNull Collection getElements(@NotNull String key, @NotNull Project project, @Nullable GlobalSearchScope scope) { return StubIndex.getElements(IndexKeys.MODULES, key, project, scope, RPsiModule.class); } } ================================================ FILE: src/main/java/com/reason/ide/search/index/ModuleSignatureIndex.java ================================================ package com.reason.ide.search.index; import com.intellij.openapi.project.*; import com.intellij.psi.search.*; import com.intellij.psi.stubs.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.stub.type.*; import org.jetbrains.annotations.*; import java.util.*; /** * Module indexed by the *name* of the signature (module type) */ public class ModuleSignatureIndex extends StringStubIndexExtension { @Override public int getVersion() { return super.getVersion() + ORStubVersions.MODULE; } @Override public @NotNull StubIndexKey getKey() { return IndexKeys.MODULES_SIGNATURE; } public static @NotNull Collection getElements(@NotNull String key, @NotNull Project project, @Nullable GlobalSearchScope scope) { return StubIndex.getElements(IndexKeys.MODULES_SIGNATURE, key, project, scope, RPsiModule.class); } } ================================================ FILE: src/main/java/com/reason/ide/search/index/NamespaceIndex.java ================================================ package com.reason.ide.search.index; import com.intellij.openapi.vfs.*; import com.intellij.util.indexing.*; import com.intellij.util.io.*; import com.reason.*; import com.reason.comp.bs.*; import com.reason.ide.files.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.impl.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; import java.util.*; public class NamespaceIndex extends ScalarIndexExtension { private static final int VERSION = 6; private static final ID INDEX_ID = ID.create("reason.index.bsconfig"); private static final Log LOG = Log.create("index.namespace"); @Override public @NotNull ID getName() { return INDEX_ID; } @Override public boolean dependsOnFileContent() { return true; } @Override public int getVersion() { return VERSION; } @Override public @NotNull DataIndexer getIndexer() { return inputData -> { VirtualFile dataFile = inputData.getFile(); if (inputData.getFileType() instanceof DuneFileType) { DuneFile duneFile = (DuneFile) inputData.getPsiFile(); List stanzas = ORUtil.findImmediateChildrenOfClass(duneFile, RPsiDuneStanza.class); for (RPsiDuneStanza stanza : stanzas) { if ("library".equals(stanza.getName())) { VirtualFile parent = dataFile.getParent(); String namespace = StringUtil.toFirstUpper(parent.getName()); if (LOG.isDebugEnabled()) { LOG.debug("Indexing " + dataFile + " with namespace " + namespace); } return Collections.singletonMap(namespace, null); } } } else { BsConfig config = BsConfigReader.read(dataFile); if (config.hasNamespace()) { VirtualFile contentRoot = BsPlatform.findConfigFiles(inputData.getProject()).stream().findFirst().orElse(null); if (contentRoot != null) { String namespace = config.getNamespace(); if (LOG.isDebugEnabled()) { LOG.debug("Indexing " + dataFile + " with namespace " + namespace); } return Collections.singletonMap(namespace, null); } } } return Collections.emptyMap(); }; } @Override public @NotNull KeyDescriptor getKeyDescriptor() { return EnumeratorStringDescriptor.INSTANCE; } @Override public @NotNull FileBasedIndex.InputFilter getInputFilter() { return file -> file.getFileType() instanceof DuneFileType || FileHelper.isRescriptConfigJson(file); } } ================================================ FILE: src/main/java/com/reason/ide/search/index/ObjectFieldIndex.java ================================================ package com.reason.ide.search.index; import com.intellij.openapi.project.*; import com.intellij.psi.search.*; import com.intellij.psi.stubs.*; import com.reason.lang.core.psi.impl.*; import com.reason.lang.core.stub.type.*; import org.jetbrains.annotations.*; import java.util.*; public class ObjectFieldIndex extends StringStubIndexExtension { @Override public int getVersion() { return super.getVersion() + ORStubVersions.OBJECT_FIELD; } @Override public @NotNull StubIndexKey getKey() { return IndexKeys.OBJECT_FIELDS; } public static @NotNull Collection getElements(@NotNull String key, @NotNull Project project, @Nullable GlobalSearchScope scope) { return StubIndex.getElements(IndexKeys.OBJECT_FIELDS, key, project, scope, RPsiObjectField.class); } } ================================================ FILE: src/main/java/com/reason/ide/search/index/OpenIndex.java ================================================ package com.reason.ide.search.index; import com.intellij.openapi.project.*; import com.intellij.psi.search.*; import com.intellij.psi.stubs.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.stub.type.*; import org.jetbrains.annotations.*; import java.util.*; public class OpenIndex extends StringStubIndexExtension { @Override public int getVersion() { return super.getVersion() + ORStubVersions.OPEN; } @Override public @NotNull StubIndexKey getKey() { return IndexKeys.OPENS; } public static @NotNull Collection getElements(@NotNull String key, @NotNull Project project, @Nullable GlobalSearchScope scope) { return StubIndex.getElements(IndexKeys.OPENS, key, project, scope, RPsiOpen.class); } } ================================================ FILE: src/main/java/com/reason/ide/search/index/ParameterFqnIndex.java ================================================ package com.reason.ide.search.index; import com.intellij.openapi.project.*; import com.intellij.psi.search.*; import com.intellij.psi.stubs.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.stub.type.*; import org.jetbrains.annotations.*; import java.util.*; public class ParameterFqnIndex extends IntStubIndexExtension { @Override public int getVersion() { return super.getVersion() + ORStubVersions.PARAMETER; } @Override public @NotNull StubIndexKey getKey() { return IndexKeys.PARAMETERS_FQN; } public static @NotNull Collection getElements(@NotNull String key, @NotNull Project project, @Nullable GlobalSearchScope scope) { return StubIndex.getElements(IndexKeys.PARAMETERS_FQN, key.hashCode(), project, scope, RPsiParameterDeclaration.class); } } ================================================ FILE: src/main/java/com/reason/ide/search/index/ParameterIndex.java ================================================ package com.reason.ide.search.index; import com.intellij.openapi.project.*; import com.intellij.psi.search.*; import com.intellij.psi.stubs.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.stub.type.*; import org.jetbrains.annotations.*; import java.util.*; public class ParameterIndex extends StringStubIndexExtension { @Override public int getVersion() { return super.getVersion() + ORStubVersions.PARAMETER; } @Override public @NotNull StubIndexKey getKey() { return IndexKeys.PARAMETERS; } public static @NotNull Collection getElements(@NotNull String key, @NotNull Project project, @Nullable GlobalSearchScope scope) { return StubIndex.getElements(IndexKeys.PARAMETERS, key, project, scope, RPsiParameterDeclaration.class); } } ================================================ FILE: src/main/java/com/reason/ide/search/index/RecordFieldIndex.java ================================================ package com.reason.ide.search.index; import com.intellij.openapi.project.*; import com.intellij.psi.search.*; import com.intellij.psi.stubs.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.stub.type.*; import org.jetbrains.annotations.*; import java.util.*; public class RecordFieldIndex extends StringStubIndexExtension { @Override public int getVersion() { return super.getVersion() + ORStubVersions.RECORD_FIELD; } @Override public @NotNull StubIndexKey getKey() { return IndexKeys.RECORD_FIELDS; } public static @NotNull Collection getElements(@NotNull String key, @NotNull Project project, @Nullable GlobalSearchScope scope) { return StubIndex.getElements(IndexKeys.RECORD_FIELDS, key, project, scope, RPsiRecordField.class); } } ================================================ FILE: src/main/java/com/reason/ide/search/index/TypeFqnIndex.java ================================================ package com.reason.ide.search.index; import com.intellij.openapi.project.*; import com.intellij.psi.search.*; import com.intellij.psi.stubs.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.stub.type.*; import org.jetbrains.annotations.*; import java.util.*; public class TypeFqnIndex extends IntStubIndexExtension { @Override public int getVersion() { return super.getVersion() + ORStubVersions.TYPE; } @Override public @NotNull StubIndexKey getKey() { return IndexKeys.TYPES_FQN; } public static @NotNull Collection getElements(String qName, @NotNull Project project, @Nullable GlobalSearchScope scope) { return StubIndex.getElements(IndexKeys.TYPES_FQN, qName.hashCode(), project, scope, RPsiType.class); } } ================================================ FILE: src/main/java/com/reason/ide/search/index/TypeIndex.java ================================================ package com.reason.ide.search.index; import com.intellij.openapi.project.*; import com.intellij.psi.search.*; import com.intellij.psi.stubs.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.stub.type.*; import org.jetbrains.annotations.*; import java.util.*; public class TypeIndex extends StringStubIndexExtension { @Override public int getVersion() { return super.getVersion() + ORStubVersions.TYPE; } @Override public @NotNull StubIndexKey getKey() { return IndexKeys.TYPES; } public static @NotNull Collection getElements(@NotNull String key, @NotNull Project project, @Nullable GlobalSearchScope scope) { return StubIndex.getElements(IndexKeys.TYPES, key, project, scope, RPsiType.class); } } ================================================ FILE: src/main/java/com/reason/ide/search/index/ValFqnIndex.java ================================================ package com.reason.ide.search.index; import com.intellij.openapi.project.*; import com.intellij.psi.search.*; import com.intellij.psi.stubs.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.stub.type.*; import org.jetbrains.annotations.*; import java.util.*; public class ValFqnIndex extends IntStubIndexExtension { @Override public int getVersion() { return super.getVersion() + ORStubVersions.VAL; } @Override public @NotNull StubIndexKey getKey() { return IndexKeys.VALS_FQN; } public static @NotNull Collection getElements(String qName, @NotNull Project project, @Nullable GlobalSearchScope scope) { return StubIndex.getElements(IndexKeys.VALS_FQN, qName.hashCode(), project, scope, RPsiVal.class); } } ================================================ FILE: src/main/java/com/reason/ide/search/index/VariantFqnIndex.java ================================================ package com.reason.ide.search.index; import com.intellij.openapi.project.*; import com.intellij.psi.search.*; import com.intellij.psi.stubs.*; import com.reason.lang.core.psi.impl.*; import com.reason.lang.core.stub.type.*; import org.jetbrains.annotations.*; import java.util.*; import static java.util.Collections.*; public class VariantFqnIndex extends IntStubIndexExtension { @Override public int getVersion() { return super.getVersion() + ORStubVersions.VARIANT; } @Override public @NotNull StubIndexKey getKey() { return IndexKeys.VARIANTS_FQN; } public static @NotNull Collection getElements(@NotNull String key, @NotNull Project project, @Nullable GlobalSearchScope scope) { return StubIndex.getElements(IndexKeys.VARIANTS_FQN, key.hashCode(), project, scope, RPsiVariantDeclaration.class); } public static @Nullable RPsiVariantDeclaration getElement(@Nullable String qname, @NotNull Project project, @Nullable GlobalSearchScope scope) { Collection variants = qname == null ? emptyList() : VariantFqnIndex.getElements(qname, project, scope); return variants.isEmpty() ? null : variants.iterator().next(); } } ================================================ FILE: src/main/java/com/reason/ide/search/reference/ORFakeResolvedElement.java ================================================ package com.reason.ide.search.reference; import com.intellij.openapi.util.*; import com.intellij.psi.*; import com.intellij.psi.impl.*; import org.jetbrains.annotations.*; public class ORFakeResolvedElement extends FakePsiElement { private final @NotNull PsiElement mySourceElement; public ORFakeResolvedElement(@NotNull PsiElement element) { mySourceElement = element; } @Override public @NotNull PsiElement getOriginalElement() { return mySourceElement; } @Override public @Nullable PsiElement getParent() { return mySourceElement.getParent(); } @Override public @Nullable String getText() { return mySourceElement.getText(); } @Override public @NotNull TextRange getTextRangeInParent() { return TextRange.EMPTY_RANGE; } } ================================================ FILE: src/main/java/com/reason/ide/search/reference/ORModuleResolutionPsiGist.java ================================================ package com.reason.ide.search.reference; import com.intellij.openapi.project.*; import com.intellij.openapi.util.*; import com.intellij.openapi.vfs.*; import com.intellij.psi.*; import com.intellij.psi.impl.*; import com.intellij.psi.search.*; import com.intellij.psi.tree.*; import com.intellij.psi.util.*; import com.intellij.util.gist.*; import com.intellij.util.io.*; import com.reason.ide.*; import com.reason.ide.files.*; import com.reason.ide.search.*; import com.reason.ide.search.index.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import com.reason.lang.core.type.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; import java.io.*; import java.util.*; import static com.intellij.openapi.application.ApplicationManager.*; /** * Resolved aliases and includes in a module. *
 * File A : module A1 = {}
 * File B : include A; include A1
 * 
* QNames fullResolution partial include/open names to their real target *
 * gist(B) : «A» -> "A" / «A1» -> "A.A1"
 * 
*/ public class ORModuleResolutionPsiGist { private static final Log LOG = Log.create("gist"); private static final int VERSION = 3; private static final String ID = "reasonML.gist.openIncludeQNames"; private static final Key RESOLUTION = Key.create(ID); private static final Key ELEMENT_INDEX = Key.create("reasonML.gist.elementIndex"); private static final PsiFileGist myGist = GistManager.getInstance() .newPsiFileGist(ID, VERSION, new ORModuleResolutionPsiGist.Externalizer(), ORModuleResolutionPsiGist::getFileData); private ORModuleResolutionPsiGist() { } public static @NotNull Data getData(@Nullable PsiFile psiFile) { // can be from serialisation return psiFile == null ? new Data() : myGist.getFileData(psiFile); } // Resolve all Open and Include paths to their real module definition (ie, remove aliases or intermediate constructions) private static Data getFileData(@NotNull PsiFile file) { Data data = new Data(); if (file instanceof FileBase psiFileBase) { Map visitedFiles = new HashMap<>(); Context context = new Context(getApplication().getService(ModuleIndexService.class), file.getProject(), GlobalSearchScope.allScope(file.getProject())); WalkInfo walkInfo = new WalkInfo(psiFileBase); long start = System.currentTimeMillis(); VirtualFile vFile = ORFileUtils.getVirtualFile(psiFileBase); LOG.info("Walk file to create gist: " + psiFileBase.getModuleName() + ", " + (vFile == null ? "" : vFile.getPath())); data = walkFile(context, walkInfo, visitedFiles); long time = System.currentTimeMillis() - start; LOG.info("Gist created for " + psiFileBase.getModuleName() + " (" + time + "ms), visited: [" + Joiner.join(", ", visitedFiles.keySet()) + "]"); } return data; } private static @NotNull Data walkFile(@NotNull Context context, @NotNull WalkInfo walkInfo, Map visitedFiles) { if (visitedFiles.containsKey(walkInfo.moduleName)) { LOG.trace(" Already visited file", (PsiFile) walkInfo.file); return visitedFiles.get(walkInfo.moduleName); } Data data = new Data(); visitedFiles.put(walkInfo.moduleName, data); PsiElement element = walkInfo.file.getFirstChild(); PsiElement firstChild = null; while (element != null) { // MODULE/FUNCTOR if (element instanceof RPsiInnerModule visitedModule) { visitInnerModule(context, walkInfo, visitedFiles, visitedModule, data); firstChild = element.getFirstChild(); } // FUNCTOR else if (element instanceof RPsiFunctor visitedFunctor) { visitFunctor(context, walkInfo, visitedFunctor, data); firstChild = element.getFirstChild(); } // MODULE BINDING else if (element instanceof RPsiModuleBinding) { walkInfo.moduleBindings.put(element, walkInfo.modulesInContext.size()); firstChild = element.getFirstChild(); } // INCLUDE/OPEN else if (element instanceof RPsiInclude || element instanceof RPsiOpen) { visitIncludeOpen(context, walkInfo, element, data); } // FIRST CLASS MODULE (LET/PARAM) else if (element instanceof RPsiVar || element instanceof RPsiParameterDeclaration) { visitFirstClassModule(context, walkInfo, (RPsiSignatureElement) element); firstChild = element.getFirstChild(); } // TAG else if (element instanceof RPsiTagStart visitedTag) { visitedTag(context, walkInfo, visitedTag, data); firstChild = element.getFirstChild(); } else { firstChild = element.getFirstChild(); } if (firstChild != null) { element = firstChild; firstChild = null; } else { PsiElement nextElement = ORUtil.nextSibling(element); if (nextElement != null) { element = nextElement; } else { // Close elements (go back to parent) PsiElement parent = element.getParent(); if (parent == walkInfo.file) { element = null; } else { closeVisitedElement(walkInfo, data, parent); while (parent != null) { element = ORUtil.nextSibling(parent); if (element != null) { parent = null; } else { parent = parent.getParent(); if (parent == walkInfo.file) { parent = null; } else { closeVisitedElement(walkInfo, data, parent); } } } } } } } return data; } private static void visitedTag(@NotNull Context context, @NotNull WalkInfo walkInfo, @NotNull RPsiTagStart visitedTag, @NotNull Data data) { boolean found = false; String tagName = visitedTag.getName(); if (tagName != null) { // Iterate backward to find a matching local resolution for (int i = walkInfo.modulesInContext.size() - 1; i >= 0; i--) { PsiElement elementInContext = walkInfo.modulesInContext.get(i); if (elementInContext instanceof RPsiInnerModule componentInContext && componentInContext.isComponent()) { String componentInContextName = componentInContext.getModuleName(); // local component declaration if (tagName.equals(componentInContextName)) { found = true; visitedTag.putUserData(RESOLUTION, componentInContext); walkInfo.modulesInContext.add(visitedTag); data.addValue(getIndex(visitedTag), componentInContext.getQualifiedName()); break; } } else if (elementInContext instanceof RPsiOpen openInContext) { RPsiQualifiedPathElement resolvedOpen = openInContext.getUserData(RESOLUTION); String pathToTest = (resolvedOpen == null ? "" : resolvedOpen.getQualifiedName() + ".") + tagName + ".make"; Collection componentFunctions = LetComponentFqnIndex.getElements(pathToTest, context.project, context.scope); if (!componentFunctions.isEmpty()) { RPsiLet resolvedLet = componentFunctions.iterator().next(); RPsiModule resolvedModule = PsiTreeUtil.getStubOrPsiParentOfType(resolvedLet, RPsiModule.class); String moduleQName = resolvedModule == null ? null : resolvedModule.getQualifiedName(); if (moduleQName != null) { found = true; visitedTag.putUserData(RESOLUTION, resolvedModule); walkInfo.modulesInContext.add(visitedTag); data.addValue(getIndex(visitedTag), moduleQName); break; } } } } // If nothing found, try direct access if (!found) { for (RPsiLet componentFunction : LetComponentFqnIndex.getElements(tagName + ".make", context.project, context.scope)) { RPsiModule module = PsiTreeUtil.getStubOrPsiParentOfType(componentFunction, RPsiModule.class); String moduleQName = module == null ? null : module.getQualifiedName(); if (moduleQName != null) { visitedTag.putUserData(RESOLUTION, module); walkInfo.modulesInContext.add(visitedTag); data.addValue(getIndex(visitedTag), moduleQName); break; } } } } } private static void visitFunctor(@NotNull Context context, @NotNull WalkInfo walkInfo, RPsiFunctor visitedFunctor, Data data) { RPsiFunctorResult returnType = visitedFunctor.getReturnType(); RPsiUpperSymbol moduleType = returnType == null ? null : returnType.getModuleType(); if (moduleType == null) { walkInfo.modulesInContext.add(visitedFunctor); data.addValue(getIndex(visitedFunctor), visitedFunctor.getQualifiedName()); } else { // Try to fullResolution type boolean found = false; String returnTypeQName = returnType.getText(); for (int i = walkInfo.modulesInContext.size() - 1; i >= 0; i--) { PsiElement elementInContext = walkInfo.modulesInContext.get(i); if (elementInContext instanceof RPsiInnerModule moduleInContext) { String moduleInContextName = moduleInContext.getModuleName() == null ? "" : moduleInContext.getModuleName(); // local module type (path == module.name) // module type S = {}; module F = () : S => {} if (moduleType.getText().equals(moduleInContextName)) { String resolvedQName = fullResolution(moduleInContext).getQualifiedName(); found = true; visitedFunctor.putUserData(RESOLUTION, fullResolution(moduleInContext)); walkInfo.modulesInContext.add(visitedFunctor); data.addValue(getIndex(visitedFunctor), resolvedQName); break; } else { String moduleQName = fullResolution(moduleInContext).getQualifiedName(); String pathToTest = moduleQName + returnTypeQName.replace(moduleInContextName, ""); Collection psiModules = ModuleFqnIndex.getElements(pathToTest, context.project, context.scope); if (!psiModules.isEmpty()) { RPsiQualifiedPathElement resolvedModule = fullResolution(psiModules.iterator().next()); String resolvedModuleQName = resolvedModule.getQualifiedName(); if (resolvedModuleQName != null) { found = true; visitedFunctor.putUserData(RESOLUTION, resolvedModule); walkInfo.modulesInContext.add(visitedFunctor); data.addValue(getIndex(visitedFunctor), resolvedModuleQName); break; } } } } } // If nothing found, try direct access if (!found) { for (RPsiModule module : ModuleFqnIndex.getElements(moduleType.getText(), context.project, context.scope)) { String moduleQName = module.getQualifiedName(); if (moduleQName != null) { visitedFunctor.putUserData(RESOLUTION, module); walkInfo.modulesInContext.add(visitedFunctor); data.addValue(getIndex(visitedFunctor), moduleQName); break; } } } } } private static void closeVisitedElement(@NotNull WalkInfo walkInfo, @NotNull Data data, PsiElement element) { if (element instanceof RPsiFunctor functor) { if (functor != walkInfo.modulesInContext.getLast()) { walkInfo.modulesInContext.add(functor); } } else if (element instanceof RPsiInnerModule module) { if (module != walkInfo.modulesInContext.getLast()) { walkInfo.modulesInContext.add(module); } // If there are any (top-binding) includes in the module, they are equivalent List includes = ORUtil.findImmediateChildrenOfClass(module.getBody(), RPsiInclude.class); if (!includes.isEmpty()) { for (RPsiInclude moduleInclude : includes) { Collection includeResolutions = data.getValues(moduleInclude); if (includeResolutions.isEmpty()) { data.addValue(getIndex(module), moduleInclude.getIncludePath()); } else { data.addValues(getIndex(module), includeResolutions); } } } } else if (element instanceof RPsiModuleBinding moduleBinding) { Integer bindingPos = walkInfo.moduleBindings.remove(moduleBinding); if (walkInfo.modulesInContext.size() > bindingPos) { walkInfo.modulesInContext.subList(bindingPos, walkInfo.modulesInContext.size()).clear(); } } } private static void visitFirstClassModule(@NotNull Context context, @NotNull WalkInfo walkInfo, @NotNull RPsiSignatureElement visitedElement) { if (visitedElement.getSignature() instanceof RPsiModuleSignature signature) { String visitedModuleQName = signature.getQName(); String[] visitedModulePath = visitedModuleQName.split("\\."); boolean found = false; // Iterate backward to find a matching local resolution for (int i = walkInfo.modulesInContext.size() - 1; i >= 0; i--) { PsiElement elementInContext = walkInfo.modulesInContext.get(i); if (elementInContext instanceof RPsiInnerModule moduleInContext) { String moduleInContextName = moduleInContext.getModuleName() == null ? "" : moduleInContext.getModuleName(); if (visitedModuleQName.equals(moduleInContextName)) { // first class of a local module found = true; visitedElement.putUserData(RESOLUTION, moduleInContext); break; } else if (visitedModulePath[0].equals(moduleInContextName)) { RPsiQualifiedPathElement resolvedModule = moduleInContext.getUserData(RESOLUTION); String resolvedModuleQName = resolvedModule != null ? resolvedModule.getQualifiedName() : moduleInContext.getQualifiedName(); String newModuleName = visitedModuleQName.replace(moduleInContextName, resolvedModuleQName == null ? "" : resolvedModuleQName); Collection psiModules = ModuleFqnIndex.getElements(newModuleName, context.project, context.scope); if (!psiModules.isEmpty()) { RPsiModule finalModule = psiModules.iterator().next(); String moduleQName = finalModule == null ? null : finalModule.getQualifiedName(); if (moduleQName != null) { found = true; visitedElement.putUserData(RESOLUTION, finalModule); break; } } } } else if (elementInContext instanceof RPsiInclude || elementInContext instanceof RPsiOpen) { RPsiQualifiedPathElement moduleInContext = follow(elementInContext, 0); if (moduleInContext != null) { String pathToTest = moduleInContext.getQualifiedName() + "." + visitedModuleQName; // duplication Collection psiModules = ModuleFqnIndex.getElements(pathToTest, context.project, context.scope); if (!psiModules.isEmpty()) { RPsiModule resolvedModule = psiModules.iterator().next(); String moduleQName = resolvedModule == null ? null : resolvedModule.getQualifiedName(); if (moduleQName != null) { found = true; visitedElement.putUserData(RESOLUTION, resolvedModule); break; } } } } } // If nothing found, try direct access if (!found) { String path = ORFileUtils.getParentPath(visitedElement.getContainingFile()); RPsiQualifiedPathElement resolvedModule = findGlobalModule(path, visitedModuleQName, context); if (resolvedModule != null) { String moduleQName = resolvedModule.getQualifiedName(); if (moduleQName != null) { visitedElement.putUserData(RESOLUTION, resolvedModule); found = true; } } } if (found && visitedElement instanceof RPsiQualifiedPathElement visitedQualified) { walkInfo.modulesInContext.add(new RPsiFirstClassModule(visitedQualified)); } } } private static void visitInnerModule(@NotNull Context context, @NotNull WalkInfo walkInfo, @NotNull Map visitedFiles, @NotNull RPsiInnerModule visitedModule, @NotNull Data data) { boolean found = false; String alias = visitedModule.getAlias(); if (alias == null) { RPsiUnpack unpack = visitedModule.getUnpack(); if (unpack != null) { // First class module // module M = unpack(P : I) RPsiLowerSymbol firstClassSymbol = unpack.getFirstClassSymbol(); String firstClassName = firstClassSymbol != null ? firstClassSymbol.getText() : null; // Iterate backward to find a matching local resolution for (int i = walkInfo.modulesInContext.size() - 1; i >= 0; i--) { PsiElement elementInContext = walkInfo.modulesInContext.get(i); if (elementInContext instanceof RPsiFirstClassModule moduleInContext) { String firstClassInContextName = moduleInContext.getElement().getName(); // alias to a local module (alias == module.name) // module A = {}; module B = A; if (firstClassInContextName != null && firstClassInContextName.equals(firstClassName)) { RPsiQualifiedPathElement userData = moduleInContext.getElement().getUserData(RESOLUTION); if (userData != null) { visitedModule.putUserData(RESOLUTION, userData); walkInfo.modulesInContext.add(visitedModule); data.addValue(getIndex(visitedModule), userData.getQualifiedName()); break; } } } } } else { RPsiFunctorCall functorCall = ORUtil.findImmediateFirstChildOfClass(visitedModule.getBody(), RPsiFunctorCall.class); if (functorCall != null) { // Iterate backward to find a matching local resolution for (int i = walkInfo.modulesInContext.size() - 1; i >= 0; i--) { PsiElement elementInContext = walkInfo.modulesInContext.get(i); if (elementInContext instanceof RPsiFunctor functorInContext) { // ? common String functorInContextName = functorInContext.getModuleName() == null ? "" : functorInContext.getModuleName(); // local functor declaration ? module F=():S => {}; module M = F({}); if (functorCall.getName().equals(functorInContextName)) { found = true; visitedModule.putUserData(RESOLUTION, functorInContext); walkInfo.modulesInContext.add(visitedModule); data.addValue(getIndex(visitedModule), functorInContext.getQualifiedName()); break; } } // Try to combine a previous include/open else if (elementInContext instanceof RPsiInclude || elementInContext instanceof RPsiOpen) { RPsiQualifiedPathElement moduleInContext = follow(elementInContext, 0); if (moduleInContext != null) { String path = ORUtil.getLongIdent(functorCall.getFirstChild()); String pathToTest = moduleInContext.getQualifiedName() + "." + path; // duplication Collection psiModules = ModuleFqnIndex.getElements(pathToTest, context.project, context.scope); if (!psiModules.isEmpty()) { RPsiModule resolvedModule = psiModules.iterator().next(); String moduleQName = resolvedModule == null ? null : resolvedModule.getQualifiedName(); if (moduleQName != null) { found = true; visitedModule.putUserData(RESOLUTION, resolvedModule); walkInfo.modulesInContext.add(visitedModule); data.addValue(getIndex(visitedModule), moduleQName); break; } } } } } // If nothing found, try direct access if (!found) { // Get functor call path String qName = visitedModule.getBody() == null ? functorCall.getName() : ORUtil.getLongIdent(visitedModule.getBody().getFirstChild()); ResolvedQName resolvedQName = resolveQName(qName, context, walkInfo, visitedFiles); if (resolvedQName.found) { RPsiQualifiedPathElement resolvedFunctor = resolvedQName.resolvedElement; String functorQName = resolvedFunctor.getQualifiedName(); if (functorQName != null) { visitedModule.putUserData(RESOLUTION, resolvedFunctor); walkInfo.modulesInContext.add(visitedModule); data.addValue(getIndex(visitedModule), functorQName); } } } } else { RPsiModuleSignature moduleType = visitedModule.getModuleSignature(); if (moduleType != null) { visitModuleResultType(context, walkInfo, moduleType, data); } } } } else { // Alias // module X = A.B.C ResolvedQName resolvedQName = resolveQName(alias, context, walkInfo, visitedFiles); RPsiQualifiedPathElement resolvedPart = resolvedQName.resolvedElement; if (resolvedPart != null && resolvedQName.found) { String index = getIndex(visitedModule); visitedModule.putUserData(RESOLUTION, resolvedPart); walkInfo.modulesInContext.add(visitedModule); data.addValue(index, resolvedPart.getQualifiedName()); PsiFile resolvedContainingFile = resolvedPart.getContainingFile(); if (resolvedContainingFile instanceof FileBase resolvedContainingFileBase) { String resolvedFileModuleName = resolvedContainingFileBase.getModuleName(); if (!walkInfo.moduleName.equals(resolvedFileModuleName)) { // Maybe resolved module include other modules, and we need to add them as alternate names // We can’t reuse the gist here because of mutual calls and possibility of stack overflow. if (LOG.isTraceEnabled()) { LOG.trace(" Use another GIST [from " + walkInfo.moduleName + "] : " + ORFileUtils.getVirtualFile(resolvedContainingFile) + ", visited: [" + Joiner.join(", ", visitedFiles.keySet()) + "]"); } Data anotherData = walkFile(context, new WalkInfo(resolvedContainingFileBase), visitedFiles); for (String value : anotherData.getValues(resolvedPart)) { data.addValue(index, value); } } } } } } private static void visitModuleResultType(@NotNull Context context, @NotNull WalkInfo walkInfo, @NotNull RPsiModuleSignature element, @NotNull Data data) { PsiElement firstModuleName = ORUtil.findImmediateFirstChildOfType(element, walkInfo.types.A_MODULE_NAME); String elementName = firstModuleName != null ? firstModuleName.getText() : ""; String elementLongIdent = ORUtil.getLongIdent(element); // Iterate backward to find a matching local resolution for (int i = walkInfo.modulesInContext.size() - 1; i >= 0; i--) { PsiElement elementInContext = walkInfo.modulesInContext.get(i); // local module type declaration -> module type S = {}; module M: S = {}; if (elementInContext instanceof RPsiModule moduleInContext) { String moduleInContextName = moduleInContext.getModuleName() != null ? moduleInContext.getModuleName() : ""; if (moduleInContextName.equals(elementName)) { RPsiQualifiedPathElement fullModuleResolution = follow(moduleInContext, 0); RPsiQualifiedPathElement resolvedModule = fullModuleResolution != null ? fullModuleResolution : moduleInContext; // If it is a path, must resolve it if (!elementName.equals(elementLongIdent)) { String pathToResolve = resolvedModule.getQualifiedName() + elementLongIdent.replaceFirst(moduleInContextName, ""); Collection modules = ModuleFqnIndex.getElements(pathToResolve, context.project, context.scope); if (modules.size() == 1) { resolvedModule = modules.iterator().next(); } else { resolvedModule = null; } } if (resolvedModule != null) { element.putUserData(RESOLUTION, resolvedModule); data.addValue(getIndex(element), resolvedModule.getQualifiedName()); return; } } } // Try to combine a previous include/open else if (elementInContext instanceof RPsiInclude || elementInContext instanceof RPsiOpen) { RPsiQualifiedPathElement moduleInContext = follow(elementInContext, 0); if (moduleInContext != null) { String pathToTest = moduleInContext.getQualifiedName() + "." + elementLongIdent; // duplication Collection psiModules = ModuleFqnIndex.getElements(pathToTest, context.project, context.scope); if (!psiModules.isEmpty()) { RPsiModule resolvedModule = psiModules.iterator().next(); String moduleQName = resolvedModule == null ? null : resolvedModule.getQualifiedName(); if (moduleQName != null) { element.putUserData(RESOLUTION, resolvedModule); data.addValue(getIndex(element), moduleQName); return; } } } } } // If nothing found, try direct access for (RPsiModule module : ModuleFqnIndex.getElements(elementLongIdent, context.project, context.scope)) { String moduleQName = module.getQualifiedName(); if (moduleQName != null) { element.putUserData(RESOLUTION, module); data.addValue(getIndex(element), moduleQName); } } } private static void visitIncludeOpen(@NotNull Context context, @NotNull WalkInfo walkInfo, @NotNull PsiElement visitedElement, @NotNull Data data) { IElementType type = visitedElement.getNode().getElementType(); boolean isInclude = type == walkInfo.types.C_INCLUDE; String visitedPath = isInclude ? ((RPsiInclude) visitedElement).getIncludePath() : ((RPsiOpen) visitedElement).getPath(); boolean found = false; boolean topLevel = walkInfo.moduleBindings.isEmpty(); // Reverse iterate to find resolution for (int i = walkInfo.modulesInContext.size() - 1; i >= 0; i--) { PsiElement elementInContext = walkInfo.modulesInContext.get(i); if (elementInContext instanceof RPsiInnerModule moduleInContext) { // include/open local module (path == module.name) // module A = ? // include/open A; String moduleInContextName = moduleInContext.getModuleName() == null ? "" : moduleInContext.getModuleName(); if (visitedPath.equals(moduleInContextName)) { RPsiQualifiedPathElement finalResolution = fullResolution(moduleInContext); found = true; visitedElement.putUserData(RESOLUTION, finalResolution); walkInfo.modulesInContext.add(visitedElement); String resolvedQName = finalResolution.getQualifiedName(); data.addValue(getIndex(visitedElement), resolvedQName); // File A: include X --> A === X if (topLevel && isInclude) { data.addValue("", resolvedQName); } break; } else { // try fullResolution from local module. // module A = {...} «OR» module A = B // include/open A.A1 String moduleQName = fullResolution(moduleInContext).getQualifiedName(); String pathToTest = moduleQName + visitedPath.replace(moduleInContextName, ""); Collection psiModules = ModuleFqnIndex.getElements(pathToTest, context.project, context.scope); if (!psiModules.isEmpty()) { RPsiQualifiedPathElement resolvedModule = fullResolution(psiModules.iterator().next()); String resolvedModuleQName = resolvedModule.getQualifiedName(); if (resolvedModuleQName != null) { found = true; visitedElement.putUserData(RESOLUTION, resolvedModule); walkInfo.modulesInContext.add(visitedElement); data.addValue(getIndex(visitedElement), resolvedModuleQName); // File A: include X --> A === X if (topLevel && isInclude) { data.addValue("", resolvedModuleQName); } break; } } } } // else if (elementInContext instanceof RPsiFunctor functorInContext) { String functorInContextName = functorInContext.getModuleName() == null ? "" : functorInContext.getModuleName(); // include/open a local functor (path == module.name) // module A = ? // include/open A if (visitedPath.equals(functorInContextName)) { RPsiQualifiedPathElement finalResolution = fullResolution(functorInContext); found = true; visitedElement.putUserData(RESOLUTION, finalResolution); walkInfo.modulesInContext.add(visitedElement); String resolvedQName = finalResolution.getQualifiedName(); data.addValue(getIndex(visitedElement), resolvedQName); // File A: include X --> A === X if (topLevel && isInclude) { data.addValue("", resolvedQName); } break; } } // Try to combine a previous include/open // module A = { module A1 = {} } // include A // include A1 else if (elementInContext instanceof RPsiInclude || elementInContext instanceof RPsiOpen) { RPsiQualifiedPathElement moduleInContext = follow(elementInContext, 0); if (moduleInContext != null) { // include »X«; include Y; String pathToTest = moduleInContext.getQualifiedName() + "." + visitedPath; // duplication Collection psiModules = ModuleFqnIndex.getElements(pathToTest, context.project, context.scope); if (!psiModules.isEmpty()) { RPsiModule resolvedModule = psiModules.iterator().next(); String moduleQName = resolvedModule == null ? null : resolvedModule.getQualifiedName(); if (moduleQName != null) { found = true; visitedElement.putUserData(RESOLUTION, resolvedModule); walkInfo.modulesInContext.add(visitedElement); // File A: include A1; include A2 --> A2 === A1.A2 data.addValue(getIndex(visitedElement), moduleQName); // File A: include X --> A === X if (topLevel && isInclude) { data.addValue("", moduleQName); } break; } } } } } // If nothing found, try direct access if (!found) { RPsiQualifiedPathElement globalResolvedElement = findGlobalModule(walkInfo.path, visitedPath, context); if (globalResolvedElement instanceof RPsiModule globalModule) { String moduleQName = globalModule.getQualifiedName(); if (moduleQName != null) { visitedElement.putUserData(RESOLUTION, globalModule); walkInfo.modulesInContext.add(visitedElement); // File A: include X --> A === X if (topLevel && isInclude) { data.addValue("", moduleQName); } // File A: open B; include B2 --> B2 === B.B2 if (!moduleQName.equals(visitedPath)) { data.addValue(getIndex(visitedElement), moduleQName); } } } } } private static @Nullable RPsiQualifiedPathElement follow(@Nullable PsiElement elementInContext, int guard) { if (elementInContext == null) { return null; } if (guard > 10) { LOG.warn("Follow reached limit for " + elementInContext + ", " + ORFileUtils.getVirtualFile(elementInContext.getContainingFile())); return null; } RPsiQualifiedPathElement resolvedElement = elementInContext.getUserData(RESOLUTION); if (resolvedElement != null && resolvedElement != elementInContext) { boolean isRecursive = true; if (elementInContext instanceof RPsiQualifiedPathElement qualifiedElementInContext) { String qualifiedName = qualifiedElementInContext.getQualifiedName(); if (qualifiedName != null && qualifiedName.equals(resolvedElement.getQualifiedName())) { LOG.warn("Same resolution than element: " + qualifiedName); isRecursive = false; //} else { // if (LOG.isTraceEnabled()) { // LOG.trace(" follow: " + elementInContext + (elementInContext instanceof PsiQualifiedNamedElement ? " [" + ((PsiQualifiedNamedElement) elementInContext).getQualifiedName() + ", " + ORFileUtils.getVirtualFile(elementInContext.getContainingFile()) + "]" : "") // + ", " + resolvedElement + (resolvedElement instanceof PsiQualifiedNamedElement ? " [" + ((PsiQualifiedNamedElement) resolvedElement).getQualifiedName() + ", " + ORFileUtils.getVirtualFile(resolvedElement.getContainingFile()) + "]" : "")); } } if (isRecursive) { RPsiQualifiedPathElement alternateResolution = follow(resolvedElement, guard + 1); if (alternateResolution != null) { resolvedElement = alternateResolution; } } } return resolvedElement; } private static @NotNull RPsiQualifiedPathElement fullResolution(@NotNull RPsiModule module) { RPsiQualifiedPathElement resolved = follow(module, 0); return resolved != null ? resolved : module; } // Follow a path one element by one element, using alternate names for each resolution private static @NotNull ResolvedQName resolveQName(@NotNull String qName, @NotNull Context context, @NotNull WalkInfo walkInfo, @NotNull Map visitedFiles) { String[] aliasPath = qName.split("\\."); int aliasLength = aliasPath.length; RPsiQualifiedPathElement resolvedPart = null; boolean found = false; // First element of path, iterate backward to find a matching local resolution for (int i1 = walkInfo.modulesInContext.size() - 1; i1 >= 0; i1--) { PsiElement elementInContext = walkInfo.modulesInContext.get(i1); if (elementInContext instanceof RPsiInnerModule moduleInContext) { String moduleInContextName = moduleInContext.getModuleName() == null ? "" : moduleInContext.getModuleName(); if (aliasPath[0].equals(moduleInContextName)) { RPsiQualifiedPathElement resolvedModule = moduleInContext.getUserData(RESOLUTION); resolvedPart = resolvedModule == null ? moduleInContext : resolvedModule; break; } } // Try to combine a previous include/open // module A = { module A1 = {} }; module B = A; include B; module C = A1 else if (elementInContext instanceof RPsiInclude || elementInContext instanceof RPsiOpen) { RPsiQualifiedPathElement moduleInContext = follow(elementInContext, 0); if (moduleInContext != null) { String pathToTest = moduleInContext.getQualifiedName() + "." + aliasPath[0]; Collection psiModules = ModuleFqnIndex.getElements(pathToTest, context.project, context.scope); if (!psiModules.isEmpty()) { resolvedPart = fullResolution(psiModules.iterator().next()); } } } } // First element of path not defined in local file, try global access if (resolvedPart == null) { resolvedPart = findGlobalModule(walkInfo.path, aliasPath[0], context); } if (resolvedPart != null) { found = aliasLength == 1; List resolvedAlternateNames = new ArrayList<>(); resolvedAlternateNames.add(resolvedPart.getQualifiedName()); // Maybe resolved module include other modules and we need to add them as alternate names PsiFile resolvedContainingFile = resolvedPart.getContainingFile(); if (resolvedContainingFile instanceof FileBase resolvedContainingFileBase) { String resolvedFileModuleName = resolvedContainingFileBase.getModuleName(); if (!walkInfo.moduleName.equals(resolvedFileModuleName)) { // Maybe resolved module include other modules, and we need to add them as alternate names. // We can’t reuse the gist here because of mutual calls and possibility of stack overflow. if (LOG.isTraceEnabled()) { LOG.trace(" Use another GIST (parts) from " + walkInfo.moduleName + " : " + ORFileUtils.getVirtualFile(resolvedContainingFile)); } Data anotherData = walkFile(context, new WalkInfo(resolvedContainingFileBase), visitedFiles); Collection values = anotherData.getValues(resolvedPart); resolvedAlternateNames.addAll(values); } } // Resolve each part of alias, for each alternate names for (String resolvedAlternateName : resolvedAlternateNames) { String rQualifiedName = resolvedAlternateName; for (int i = 1; i < aliasLength; i++) { if (i > 1 && resolvedPart != null) { rQualifiedName = resolvedPart.getQualifiedName(); } Collection psiModules = ModuleFqnIndex.getElements(rQualifiedName + "." + aliasPath[i], context.project, context.scope); resolvedPart = !psiModules.isEmpty() ? psiModules.iterator().next() : null; // zzz mli/ml if (resolvedPart == null) { break; } else { if (resolvedPart instanceof RPsiInnerModule resolvedPartModule) { String resolvedAlias = resolvedPartModule.getAlias(); if (resolvedAlias != null) { PsiFile resolvedPartModuleContainingFile = resolvedPartModule.getContainingFile(); if (walkInfo.modulesInContext.getFirst() instanceof FileBase currentFile && resolvedPartModuleContainingFile instanceof FileBase resolvedPartFileBase) { if (currentFile != resolvedPartModuleContainingFile) { Data otherData = walkFile(context, new WalkInfo(resolvedPartFileBase), visitedFiles); Collection alternateNames = otherData.getValues(resolvedPartModule); if (!alternateNames.isEmpty()) { resolvedPart = findGlobalModule(walkInfo.path, alternateNames.iterator().next(), context); } } } } } found = i == aliasLength - 1; } } if (found) { break; } } } return new ResolvedQName(found, resolvedPart); } private static @Nullable RPsiQualifiedPathElement findGlobalModule(@NotNull String path, @NotNull String name, @NotNull Context context) { Collection modules = context.moduleIndexService.getModules(name, context.project, context.scope); if (!modules.isEmpty()) { if (modules.size() == 1) { return modules.iterator().next(); } else { List items = modules.stream().map(m -> { PsiFile file = m.getContainingFile(); if (file instanceof FileBase fileBase) { return new ModuleEx(m, ORFileUtils.getParentPath(file), fileBase.isInterface()); } else { return null; } }).filter(Objects::nonNull).sorted(new ModuleExComparator(path)).toList(); if (LOG.isTraceEnabled()) { LOG.trace(" Many global files found for " + name + " from " + path + ", take first", items); } return items.getFirst().module; } } return null; } public static @NotNull String getIndex(@NotNull PsiElement element) { if (element instanceof PsiFile) { return ""; } String index = element.getUserData(ELEMENT_INDEX); if (index != null) { return index; } ORLangTypes types = ORUtil.getTypes(element.getLanguage()); int count = 0; PsiElement prevSibling = element.getPrevSibling(); IElementType prevElementType = prevSibling != null ? prevSibling.getNode().getElementType() : null; while (prevElementType != null) { if (prevElementType == types.C_MODULE_DECLARATION || prevElementType == types.C_FUNCTOR_DECLARATION || prevElementType == types.C_INCLUDE || prevElementType == types.C_OPEN || prevElementType == types.C_TAG_START) { count++; } prevSibling = prevSibling.getPrevSibling(); prevElementType = prevSibling != null ? prevSibling.getNode().getElementType() : null; } PsiElement parent = element.getParent(); String parentIndex = parent != null ? getIndex(parent) : ""; String result = (parentIndex.isEmpty() ? "" : parentIndex + ".") + count; element.putUserData(ELEMENT_INDEX, result); return result; } public static class Data { private final Map> items = new HashMap<>(); public @NotNull Collection getValues(@Nullable PsiElement element) { Collection values = null; if (element != null) { values = items.get(getIndex(element)); } return values != null ? values : Collections.emptyList(); } public void addValue(@NotNull String key, @Nullable String value) { if (value != null) { Collection keyValues = items.computeIfAbsent(key, k -> new ArrayList<>()); keyValues.add(value); } } public void addValues(@NotNull String key, @NotNull Collection values) { Collection keyValues = items.computeIfAbsent(key, k -> new TreeSet<>(String::compareTo)); keyValues.addAll(values); } public int size() { return items.size(); } } public static class Externalizer implements DataExternalizer { @Override public void save(@NotNull DataOutput out, Data values) throws IOException { DataInputOutputUtil.writeINT(out, values.size()); for (Map.Entry> entry : values.items.entrySet()) { out.writeUTF(entry.getKey()); DataInputOutputUtil.writeSeq(out, entry.getValue(), out::writeUTF); } } @Override public Data read(@NotNull DataInput in) throws IOException { int size = DataInputOutputUtil.readINT(in); Data result = new Data(); for (int i = 0; i < size; i++) { String key = in.readUTF(); result.addValues(key, DataInputOutputUtil.readSeq(in, in::readUTF)); } return result; } } static class RPsiFirstClassModule extends FakePsiElement { private final RPsiQualifiedPathElement myElement; public RPsiFirstClassModule(RPsiQualifiedPathElement element) { myElement = element; } public RPsiQualifiedPathElement getElement() { return myElement; } @Override public @Nullable PsiElement getParent() { return myElement.getParent(); } @Override public @Nullable String getText() { return myElement.getText(); } @Override public @NotNull TextRange getTextRangeInParent() { return TextRange.EMPTY_RANGE; } } record Context(@NotNull ModuleIndexService moduleIndexService, @NotNull Project project, @NotNull GlobalSearchScope scope) { } record WalkInfo(@NotNull List modulesInContext, @NotNull Map moduleBindings, @NotNull FileBase file, @NotNull String path, @NotNull String moduleName, @NotNull ORLangTypes types) { public WalkInfo(FileBase file) { this(new ArrayList<>(), new HashMap<>(), file, ORFileUtils.getParentPath(file), file.getModuleName(), ORTypesUtil.getInstance(file.getLanguage())); this.modulesInContext.add(file); } } record ResolvedQName(boolean found, RPsiQualifiedPathElement resolvedElement) { } record ModuleEx(RPsiModule module, String path, boolean isInterface) { } static class ModuleExComparator implements Comparator { private final String myPath; ModuleExComparator(String path) { myPath = path; } @Override public int compare(ModuleEx m1, ModuleEx m2) { // Same folder, then interfaces, then implementations if (!m1.path.equals(m2.path)) { if (m1.path.equals(myPath)) { return -1; } if (m2.path.equals(myPath)) { return 1; } if (m1.path.contains("_build")) { return 1; } if (m2.path.contains("_build")) { return -1; } } if (m1.isInterface != m2.isInterface) { if (m1.isInterface) { return -1; } return 1; } return 0; } @Override public boolean equals(Object obj) { return false; } } } ================================================ FILE: src/main/java/com/reason/ide/search/reference/ORMultiSymbolReference.java ================================================ package com.reason.ide.search.reference; import com.intellij.openapi.util.*; import com.intellij.psi.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; import java.util.*; public abstract class ORMultiSymbolReference extends PsiPolyVariantReferenceBase { protected final @Nullable String myReferenceName; protected final @NotNull ORLangTypes myTypes; protected ORMultiSymbolReference(@NotNull T element, @NotNull ORLangTypes types) { super(element, TextRange.create(0, element.getTextLength())); myReferenceName = element.getText(); myTypes = types; } @Override public @Nullable PsiElement resolve() { ResolveResult[] resolveResults = multiResolve(false); return 0 < resolveResults.length ? resolveResults[0].getElement() : null; } public @Nullable PsiElement resolveInterface() { ResolveResult[] resolveResults = multiResolve(false); if (resolveResults.length < 1) { return null; } if (resolveResults.length == 1) { return resolveResults[0].getElement(); } // Move inner modules first (if we found inner module and file it means that there is alternative names) Arrays.sort(resolveResults, (m1, m2) -> m1.getElement() instanceof RPsiInnerModule ? -1 : (m2.getElement() instanceof RPsiInnerModule ? 1 : 0)); // Look into other resolved elements to find an equivalent interface if one exist for (ResolveResult resolved : resolveResults) { PsiElement element = resolved.getElement(); if (ORUtil.inInterface(element)) { return element; } } return resolveResults[0].getElement(); } } ================================================ FILE: src/main/java/com/reason/ide/search/reference/ORPsiLiteralStringReference.java ================================================ package com.reason.ide.search.reference; import com.intellij.openapi.project.*; import com.intellij.psi.*; import com.intellij.psi.search.*; import com.intellij.psi.tree.*; import com.reason.comp.*; import com.reason.comp.bs.*; import com.reason.ide.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import com.reason.lang.core.type.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; import java.util.*; public class ORPsiLiteralStringReference extends ORMultiSymbolReference { private static final Log LOG = Log.create("ref.string"); private static final Log LOG_PERF = Log.create("ref.perf.string"); public ORPsiLiteralStringReference(@NotNull RPsiLiteralString element, @NotNull ORLangTypes types) { super(element, types); } @Override public @NotNull ResolveResult[] multiResolve(boolean incompleteCode) { // Ref of a js object in rescript : "[" "string" "]" PsiElement prevSibling = myElement.getPrevSibling(); if (prevSibling == null || prevSibling.getNode().getElementType() != myTypes.LBRACKET) { return ResolveResult.EMPTY_ARRAY; } PsiElement parent = prevSibling.getParent(); if (parent == null || parent.getNode().getElementType() != myTypes.C_ARRAY) { return ResolveResult.EMPTY_ARRAY; } PsiElement parentPrevSibling = parent.getPrevSibling(); IElementType parentPrevSiblingType = parentPrevSibling != null ? parentPrevSibling.getNode().getElementType() : null; if (parentPrevSibling == null || (parentPrevSiblingType != myTypes.LIDENT && parentPrevSiblingType != myTypes.C_ARRAY)) { return ResolveResult.EMPTY_ARRAY; } // This is a reference to a JS object, can be resolved long startAll = System.currentTimeMillis(); Project project = myElement.getProject(); GlobalSearchScope searchScope = GlobalSearchScope.allScope(project); // ? LOG.debug("Find reference for string", myElement); if (LOG.isTraceEnabled()) { LOG.trace(" -> search scope: " + searchScope); } // Gather instructions from element up to the file root Deque instructions = ORReferenceAnalyzer.createInstructionsBackward(myElement, true, myTypes); // Source element is part of an object chain instructions.addLast(new ORReferenceAnalyzer.SymbolField(myElement, false)); if (LOG.isTraceEnabled()) { LOG.trace(" Instructions: [" + Joiner.join(" -> ", instructions) + "]"); } long endInstructions = System.currentTimeMillis(); BsConfig config = project.getService(ORCompilerConfigManager.class).getNearestConfig(myElement.getContainingFile()); Set openedModules = config == null ? null : config.getOpenedDeps(); if (LOG.isTraceEnabled()) { LOG.trace(" virtual file", ORFileUtils.getVirtualFile(myElement.getContainingFile())); } // Resolve aliases in the stack of instructions, this time from file down to element List resolvedInstructions = ORReferenceAnalyzer.resolveInstructions(instructions, openedModules, project, searchScope); if (LOG.isTraceEnabled()) { LOG.trace(" Resolved instructions: [" + Joiner.join(" -> ", resolvedInstructions) + "]"); } long endResolvedInstructions = System.currentTimeMillis(); if (LOG.isDebugEnabled()) { LOG.debug(" => found", Joiner.join(", ", resolvedInstructions, element -> element.getQualifiedName() + " [" + Platform.getRelativePathToModule(element.getContainingFile()) + "]")); } ResolveResult[] resolveResults = new ResolveResult[((Collection) resolvedInstructions).size()]; int i = 0; for (PsiElement element : resolvedInstructions) { resolveResults[i] = new ORPsiLowerSymbolReference.LowerResolveResult(element, myReferenceName); i++; } if (LOG_PERF.isDebugEnabled()) { long endAll = System.currentTimeMillis(); LOG_PERF.debug("Resolution of " + myReferenceName + " in " + (endAll - startAll) + "ms => " + " i:" + (endInstructions - startAll) + "," + " r:" + (endResolvedInstructions - endInstructions) ); } return resolveResults; // ... } } ================================================ FILE: src/main/java/com/reason/ide/search/reference/ORPsiLowerSymbolReference.java ================================================ package com.reason.ide.search.reference; import com.intellij.openapi.project.*; import com.intellij.psi.*; import com.intellij.psi.search.*; import com.intellij.util.*; import com.reason.comp.*; import com.reason.comp.bs.*; import com.reason.ide.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import com.reason.lang.core.type.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; import java.util.*; public class ORPsiLowerSymbolReference extends ORMultiSymbolReference { private static final Log LOG = Log.create("ref.lower"); private static final Log LOG_PERF = Log.create("ref.perf.lower"); public ORPsiLowerSymbolReference(@NotNull RPsiLowerSymbol element, @NotNull ORLangTypes types) { super(element, types); } @Override public @NotNull ResolveResult[] multiResolve(boolean incompleteCode) { if (myReferenceName == null) { return ResolveResult.EMPTY_ARRAY; } // If name is used in a definition, it's a declaration not a usage: ie, it's not a reference // http://www.jetbrains.org/intellij/sdk/docs/basics/architectural_overview/psi_references.html PsiElement parent = myElement.getParent(); if (parent instanceof RPsiVar || parent instanceof RPsiType || parent instanceof RPsiExternal || parent instanceof RPsiLowerName) { return ResolveResult.EMPTY_ARRAY; } long startAll = System.currentTimeMillis(); Project project = myElement.getProject(); GlobalSearchScope searchScope = GlobalSearchScope.allScope(project); // ? LOG.debug("Find reference for lower symbol", myReferenceName); if (LOG.isTraceEnabled()) { LOG.trace(" -> search scope: " + searchScope); } // Gather instructions from element up to the file root Deque instructions = ORReferenceAnalyzer.createInstructionsBackward(myElement, true, myTypes); // Test if source element is part of a record/object chain if (ORUtil.isPrevType(myElement, myTypes.SHARPSHARP)) { // ReasonML: JsObject field instructions.addLast(new ORReferenceAnalyzer.SymbolField(myElement, false)); } else if (ORUtil.isPrevType(myElement, myTypes.DOT) && ORUtil.prevPrevSibling(myElement) instanceof RPsiLowerSymbol) { // Record field: a.b instructions.addLast(new ORReferenceAnalyzer.SymbolField(myElement, true)); } else if (myElement.getParent() instanceof RPsiRecordField) { instructions.addLast(new ORReferenceAnalyzer.SymbolField(myElement, true)); } else { instructions.addLast(myElement); } if (LOG.isTraceEnabled()) { LOG.trace(" Instructions: [" + Joiner.join(" -> ", instructions) + "]"); } long endInstructions = System.currentTimeMillis(); BsConfig config = project.getService(ORCompilerConfigManager.class).getNearestConfig(myElement.getContainingFile()); Set openedModules = config == null ? null : config.getOpenedDeps(); if (LOG.isTraceEnabled()) { LOG.trace(" virtual file", ORFileUtils.getVirtualFile(myElement.getContainingFile())); } // Resolve aliases in the stack of instructions, this time from file down to element List resolvedInstructions = ORReferenceAnalyzer.resolveInstructions(instructions, openedModules, project, searchScope); if (LOG.isTraceEnabled()) { LOG.trace(" Resolved instructions: [" + Joiner.join(" -> ", resolvedInstructions) + "]"); } long endResolvedInstructions = System.currentTimeMillis(); if (LOG.isDebugEnabled()) { LOG.debug(" => found", Joiner.join(", ", resolvedInstructions, element -> element.getQualifiedName() + " [" + Platform.getRelativePathToModule(element.getContainingFile()) + "]")); } ResolveResult[] resolveResults = new ResolveResult[resolvedInstructions.size()]; int i = 0; for (PsiElement element : resolvedInstructions) { resolveResults[i] = new LowerResolveResult(element, myReferenceName); i++; } if (LOG_PERF.isDebugEnabled()) { long endAll = System.currentTimeMillis(); LOG_PERF.debug("Resolution of " + myReferenceName + " in " + (endAll - startAll) + "ms => " + " i:" + (endInstructions - startAll) + "," + " r:" + (endResolvedInstructions - endInstructions) ); } return resolveResults; } @Override public PsiElement handleElementRename(@NotNull String newName) throws IncorrectOperationException { PsiElement newId = ORCodeFactory.createLetName(myElement.getProject(), newName); return newId == null ? myElement : myElement.replace(newId); } public static class LowerResolveResult implements ResolveResult { private final @NotNull PsiElement myReferencedIdentifier; public LowerResolveResult(@NotNull PsiElement referencedElement, String sourceName) { if (referencedElement instanceof RPsiLet && ((RPsiLet) referencedElement).isDeconstruction()) { PsiElement identifierElement = referencedElement; for (PsiElement deconstructedElement : ((RPsiLet) referencedElement).getDeconstructedElements()) { if (deconstructedElement.getText().equals(sourceName)) { identifierElement = deconstructedElement; break; } } myReferencedIdentifier = identifierElement; } else { myReferencedIdentifier = referencedElement; } } @Override public @Nullable PsiElement getElement() { return myReferencedIdentifier; } @Override public boolean isValidResult() { return true; } public boolean inInterface() { return ORUtil.inInterface(myReferencedIdentifier); } } } ================================================ FILE: src/main/java/com/reason/ide/search/reference/ORPsiPropertyNameReference.java ================================================ package com.reason.ide.search.reference; import com.intellij.openapi.project.*; import com.intellij.openapi.util.*; import com.intellij.psi.*; import com.intellij.psi.search.*; import com.reason.comp.*; import com.reason.comp.bs.*; import com.reason.ide.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import com.reason.lang.core.type.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; import java.util.*; public class ORPsiPropertyNameReference extends PsiPolyVariantReferenceBase { private static final Log LOG = Log.create("ref.params"); private static final Log LOG_PERF = Log.create("ref.perf.params"); private final @Nullable String myReferenceName; private final @NotNull ORLangTypes myTypes; public ORPsiPropertyNameReference(@NotNull RPsiLeafPropertyName element, @NotNull ORLangTypes types) { super(element, TextRange.from(0, element.getTextLength())); myReferenceName = element.getText(); myTypes = types; } @Override public @NotNull ResolveResult[] multiResolve(boolean incompleteCode) { if (myReferenceName == null) { return ResolveResult.EMPTY_ARRAY; } // If name is used in a definition, it's a declaration not a usage: ie, it's not a reference // http://www.jetbrains.org/intellij/sdk/docs/basics/architectural_overview/psi_references.html PsiElement parent = myElement.getParent(); if (parent instanceof RPsiLet || parent instanceof RPsiVal || parent instanceof RPsiExternal) { return ResolveResult.EMPTY_ARRAY; } long startAll = System.currentTimeMillis(); Project project = myElement.getProject(); GlobalSearchScope searchScope = GlobalSearchScope.allScope(project); // ? LOG.debug("Find reference for propertyLeaf", myReferenceName); if (LOG.isTraceEnabled()) { LOG.trace(" -> search scope: " + searchScope); } // Gather instructions from element up to the file root Deque instructions = ORReferenceAnalyzer.createInstructionsBackward(myElement, true, myTypes); instructions.addLast(parent); if (LOG.isTraceEnabled()) { LOG.trace(" Instructions: ", Joiner.join(" -> ", instructions)); } long endInstructions = System.currentTimeMillis(); BsConfig config = project.getService(ORCompilerConfigManager.class).getNearestConfig(myElement.getContainingFile()); Set openedModules = config == null ? null : config.getOpenedDeps(); if (LOG.isTraceEnabled()) { LOG.trace(" virtual file", ORFileUtils.getVirtualFile(myElement.getContainingFile())); } // Resolve aliases in the stack of instructions, this time from file down to element List resolvedInstructions = ORReferenceAnalyzer.resolveInstructions(instructions, openedModules, project, searchScope); if (LOG.isTraceEnabled()) { LOG.trace(" Resolved instructions: " + Joiner.join(" -> ", resolvedInstructions)); } long endResolvedInstructions = System.currentTimeMillis(); if (LOG.isDebugEnabled()) { LOG.debug(" => found", Joiner.join(", ", resolvedInstructions, element -> element.getQualifiedName() + " [" + Platform.getRelativePathToModule(element.getContainingFile()) + "]")); } ResolveResult[] resolveResults = new ResolveResult[((Collection) resolvedInstructions).size()]; int i = 0; for (PsiElement element : resolvedInstructions) { resolveResults[i] = new JsxTagResolveResult(element, myReferenceName); i++; } if (LOG_PERF.isDebugEnabled()) { long endAll = System.currentTimeMillis(); LOG_PERF.debug("Resolution of " + myReferenceName + " in " + (endAll - startAll) + "ms => " + " i:" + (endInstructions - startAll) + "," + " r:" + (endResolvedInstructions - endInstructions) ); } return resolveResults; } @Override public @Nullable PsiElement resolve() { ResolveResult[] resolveResults = multiResolve(false); return 0 < resolveResults.length ? resolveResults[0].getElement() : null; } public static class JsxTagResolveResult implements ResolveResult { private @Nullable PsiElement myReferencedIdentifier = null; public JsxTagResolveResult(@NotNull PsiElement referencedElement, @NotNull String propertyName) { if (referencedElement instanceof RPsiLet) { RPsiFunction function = ((RPsiLet) referencedElement).getFunction(); if (function != null) { List parameters = function.getParameters(); for (RPsiParameterDeclaration parameter : parameters) { if (propertyName.equals(parameter.getName())) { myReferencedIdentifier = parameter; break; } } } } else if (referencedElement instanceof RPsiParameterDeclaration) { myReferencedIdentifier = referencedElement; } } @Override public @Nullable PsiElement getElement() { return myReferencedIdentifier; } @Override public boolean isValidResult() { return myReferencedIdentifier != null; } } } ================================================ FILE: src/main/java/com/reason/ide/search/reference/ORPsiUpperSymbolReference.java ================================================ package com.reason.ide.search.reference; import com.intellij.openapi.project.*; import com.intellij.openapi.util.text.*; import com.intellij.psi.*; import com.intellij.psi.search.*; import com.intellij.util.*; import com.reason.comp.*; import com.reason.comp.bs.*; import com.reason.ide.*; import com.reason.ide.files.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import com.reason.lang.core.type.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; import java.util.*; public class ORPsiUpperSymbolReference extends ORMultiSymbolReference { private static final Log LOG = Log.create("ref.upper"); private static final Log LOG_PERF = Log.create("ref.perf.upper"); public ORPsiUpperSymbolReference(@NotNull RPsiUpperSymbol element, @NotNull ORLangTypes types) { super(element, types); } @Override public @NotNull ResolveResult[] multiResolve(boolean incompleteCode) { if (myReferenceName == null) { return ResolveResult.EMPTY_ARRAY; } // If name is used in a definition, it's a declaration not a usage: ie, it's not a reference // http://www.jetbrains.org/intellij/sdk/docs/basics/architectural_overview/psi_references.html PsiElement parent = myElement.getParent(); if (parent instanceof RPsiInnerModule) { if (!(parent.getParent() instanceof RPsiModuleSignature)) { LOG.debug("Declaration found (inner module), skip reference resolution", myElement); return ResolveResult.EMPTY_ARRAY; } } else if (parent instanceof RPsiFunctor || parent instanceof RPsiException || parent instanceof RPsiVariantDeclaration) { //LOG.debug("Declaration found, skip reference resolution", myReferenceName); return ResolveResult.EMPTY_ARRAY; } long startAll = System.currentTimeMillis(); Project project = myElement.getProject(); GlobalSearchScope searchScope = GlobalSearchScope.allScope(project); // ? LOG.debug("Find reference for upper symbol", myReferenceName); if (LOG.isTraceEnabled()) { LOG.trace(" -> search scope: " + searchScope); } // Gather instructions from element up to the file root Deque instructions = ORReferenceAnalyzer.createInstructionsBackward(myElement, false, myTypes); boolean inPath = ORReferenceAnalyzer.isInPath(myElement, myTypes); instructions.addLast(inPath ? myElement : new ORReferenceAnalyzer.FirstInPath(myElement)); if (LOG.isTraceEnabled()) { LOG.trace(" Instructions", Joiner.join(" -> ", instructions)); } long endInstructions = System.currentTimeMillis(); BsConfig config = project.getService(ORCompilerConfigManager.class).getNearestConfig(myElement.getContainingFile()); Set openedModules = config == null ? null : config.getOpenedDeps(); if (LOG.isTraceEnabled()) { LOG.trace(" virtual file", ORFileUtils.getVirtualFile(myElement.getContainingFile())); } // Resolve aliases in the stack of instructions, this time from file down to element List resolvedInstructions = ORReferenceAnalyzer.resolveInstructions(instructions, openedModules, project, searchScope); if (LOG.isTraceEnabled()) { LOG.trace(" Resolutions", Joiner.join(", ", resolvedInstructions)); } long endResolvedInstructions = System.currentTimeMillis(); resolvedInstructions.sort((e1, e2) -> { if (e1 instanceof FileBase && ((FileBase) e1).isInterface()) { return -1; } if (e2 instanceof FileBase && ((FileBase) e2).isInterface()) { return 1; } return NaturalComparator.INSTANCE.compare(e1.getQualifiedName(), e2.getQualifiedName()); }); if (LOG.isDebugEnabled()) { LOG.debug(" => found", Joiner.join(", ", resolvedInstructions, element -> element.getQualifiedName() + " [" + Platform.getRelativePathToModule(element.getContainingFile()) + "]")); } ResolveResult[] resolveResults = new ResolveResult[resolvedInstructions.size()]; int i = 0; for (PsiElement element : resolvedInstructions) { resolveResults[i] = new UpperResolveResult(element); i++; } long endAll = System.currentTimeMillis(); if (LOG_PERF.isDebugEnabled()) { LOG_PERF.debug("Resolution of " + myReferenceName + " in " + (endAll - startAll) + "ms => " + " instructions: " + (endInstructions - startAll) + "ms," + " resolutions: " + (endResolvedInstructions - endInstructions) + "ms," ); } return resolveResults; } @Override public PsiElement handleElementRename(@NotNull String newName) throws IncorrectOperationException { PsiElement newId = ORCodeFactory.createModuleName(myElement.getProject(), newName); return newId != null ? myElement.replace(newId) : myElement; } private static class UpperResolveResult implements ResolveResult { private final PsiElement m_referencedIdentifier; public UpperResolveResult(@NotNull PsiElement referencedElement) { m_referencedIdentifier = referencedElement; } @Override public @Nullable PsiElement getElement() { return m_referencedIdentifier; } @Override public boolean isValidResult() { return true; } } } ================================================ FILE: src/main/java/com/reason/ide/search/reference/ORReferenceAnalyzer.java ================================================ package com.reason.ide.search.reference; import com.intellij.lang.*; import com.intellij.openapi.project.*; import com.intellij.psi.*; import com.intellij.psi.search.*; import com.intellij.psi.tree.*; import com.intellij.psi.util.*; import com.reason.ide.files.*; import com.reason.ide.search.*; import com.reason.ide.search.index.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import com.reason.lang.core.type.*; import com.reason.lang.rescript.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; import java.util.*; import java.util.stream.*; import static com.intellij.openapi.application.ApplicationManager.*; import static java.util.Collections.*; public class ORReferenceAnalyzer { private static final Log LOG = Log.create("ref.analyzer"); private static final int MAX_PATH_RESOLUTION_LEVEL = 10; private static final Comparator SORT_INTERFACE_FIRST = (e1, e2) -> { if (isInterface(e1)) { return -1; } if (isInterface(e2)) { return 1; } return 0; }; private ORReferenceAnalyzer() { } // Walk through the file - from element up to the root - and extract instructions static @NotNull Deque createInstructionsBackward(@NotNull PsiElement sourceElement, boolean isLower, @NotNull ORLangTypes types) { Deque instructions = new LinkedList<>(); boolean skipDeclaration = false; boolean inPath = isInPath(sourceElement, types); // use composite instead of leaf if not in a path PsiElement item = inPath ? PsiTreeUtil.prevCodeLeaf(sourceElement) : ORUtil.prevSibling(sourceElement); item = item == null ? sourceElement.getParent() : item; ASTNode itemNode = item != null ? item.getNode() : null; IElementType itemType = itemNode != null ? itemNode.getElementType() : null; if (itemType == types.LBRACKET && types instanceof ResTypes) { // Js access for Rescript item = ORUtil.prevSibling(sourceElement.getParent()); } else if (itemType == types.INCLUDE || itemType == types.OPEN) { // if this is the first element of an include/open, we skip the expression PsiElement sourceParent = sourceElement.getParent(); item = ORUtil.prevSibling(sourceParent); item = item == null ? sourceParent.getParent() : item; } else if (itemType == types.C_MODULE_BINDING) { PsiElement sourceParent = item.getParent(); if (sourceParent instanceof RPsiInnerModule gpModule && gpModule.getAlias() != null) { // source element is from a module alias, skip the module item = ORUtil.prevSibling(sourceParent); item = item == null ? sourceParent.getParent() : item; } } while (item != null) { itemNode = item.getNode(); itemType = itemNode != null ? itemNode.getElementType() : null; // Try to detect end of the path of the item if (inPath) { if (itemType == types.A_MODULE_NAME || itemType == types.A_UPPER_TAG_NAME) { PsiElement prevLeaf = PsiTreeUtil.prevLeaf(item); ASTNode prevLeafNode = prevLeaf != null ? prevLeaf.getNode() : null; IElementType prevLeafType = prevLeafNode != null ? prevLeafNode.getElementType() : null; if (prevLeafType != types.DOT && prevLeafType != types.SHARPSHARP) { inPath = false; instructions.push(new FirstInPath(item)); } else { instructions.push(item); } } else if (itemType == types.LIDENT) { PsiElement prevPrevItem = PsiTreeUtil.prevCodeLeaf(item); if (prevPrevItem != null) { IElementType prevPrevType = prevPrevItem.getNode().getElementType(); if (prevPrevType == types.DOT) { instructions.push(new SymbolField(item, true)); } else if (prevPrevType == types.SHARPSHARP) { instructions.push(new SymbolField(item, false)); } else { instructions.push(item); } } } else if (itemType == types.LPAREN) { // can be a local open A.(b) (.re) if (item.getParent() instanceof RPsiLocalOpen) { item = PsiTreeUtil.prevCodeLeaf(item); continue; } else { item = item.getParent(); inPath = false; } } else if (itemType == types.C_ARRAY && types instanceof ResTypes) { // a|>["b"]<|["c"] PsiElement arrayItem = ORUtil.nextSibling(item.getFirstChild()); if (arrayItem != null) { instructions.push(new SymbolField(arrayItem, false)); } } else if (itemType != types.DOT && itemType != types.SHARPSHARP) { inPath = false; } } // Standard expressions (not a start path) if (!inPath) { if (item instanceof RPsiLocalOpen) { // Restart the path inPath = true; } //else if (itemType == types.A_MODULE_NAME) { // Directly a module like: include X //instructions.push(new FirstInPath(item)); //} else if (item instanceof RPsiInnerModule) { instructions.push(item); // Process forward declarations (recursive declarations with 'and') PsiElement nextItem = PsiTreeUtil.nextCodeLeaf(item); IElementType nextItemType = nextItem != null ? nextItem.getNode().getElementType() : null; while (nextItemType == types.AND) { PsiElement nextElement = ORUtil.nextSibling(nextItem); if (nextElement instanceof RPsiInnerModule) { instructions.push(nextElement); nextItem = PsiTreeUtil.nextCodeLeaf(nextElement); nextItemType = nextItem != null ? nextItem.getNode().getElementType() : null; } else { break; } } } else if (item instanceof RPsiFunctor) { instructions.push(item); } else if (item instanceof RPsiOpen || item instanceof RPsiInclude) { instructions.push(item); } else if (item instanceof RPsiLetBinding) { // We are starting from inside a let, we don’t want to add the let in the instructions skipDeclaration = true; } else if (item instanceof RPsiLet) { if (!skipDeclaration) { instructions.push(item); } skipDeclaration = false; // Process forward declarations (recursive declarations with 'and') PsiElement nextItem = PsiTreeUtil.nextCodeLeaf(item); IElementType nextItemType = nextItem != null ? nextItem.getNode().getElementType() : null; while (nextItemType == types.AND) { PsiElement nextElement = ORUtil.nextSibling(nextItem); if (nextElement instanceof RPsiLet) { instructions.push(nextElement); nextItem = PsiTreeUtil.nextCodeLeaf(nextElement); nextItemType = nextItem != null ? nextItem.getNode().getElementType() : null; } else { break; } } } else if (item instanceof RPsiTypeBinding) { skipDeclaration = true; } else if (item instanceof RPsiType) { if (!skipDeclaration) { instructions.push(item); } skipDeclaration = false; // Process forward declarations (recursive declarations with 'and') PsiElement nextItem = PsiTreeUtil.nextCodeLeaf(item); IElementType nextItemType = nextItem != null ? nextItem.getNode().getElementType() : null; while (nextItemType == types.AND) { PsiElement nextElement = ORUtil.nextSibling(nextItem); if (nextElement instanceof RPsiType) { instructions.push(nextElement); nextItem = PsiTreeUtil.nextCodeLeaf(nextElement); nextItemType = nextItem != null ? nextItem.getNode().getElementType() : null; } else { break; } } } else if (item instanceof RPsiExternal) { instructions.push(item); } else if (item instanceof RPsiException) { instructions.push(item); } else if (item instanceof FileBase) { instructions.push(item); break; } else if (isLower && item instanceof RPsiParameters) { if (item.getParent() instanceof RPsiFunction) { // Inside a function declaration, need to add all parameters for (PsiElement parameterItem : ((RPsiParameters) item).getParametersList()) { if (parameterItem instanceof RPsiParameterDeclaration) { instructions.push(parameterItem); } } } } else if (isLower && item instanceof RPsiTagStart) { instructions.push(item); } } // one step backward PsiElement prevItem = ORUtil.prevSibling(item); item = prevItem == null ? item.getParent() : prevItem; } return instructions; } static @NotNull Deque createInstructionsForward(@NotNull PsiElement sourceElement, @NotNull ORLangTypes types) { Deque instructions = new LinkedList<>(); PsiElement item = sourceElement; ASTNode itemNode; IElementType itemType; while (item != null) { itemNode = item.getNode(); itemType = itemNode != null ? itemNode.getElementType() : null; if (itemType == types.A_MODULE_NAME) { instructions.addLast(instructions.isEmpty() ? new FirstInPath(item) : item); } else if (itemType == types.LIDENT) { instructions.addLast(item); } // one step forward PsiElement nextItem = ORUtil.nextSibling(item); item = nextItem == null ? item.getFirstChild() : nextItem; } return instructions; } public static boolean isInPath(@NotNull PsiElement sourceElement, @NotNull ORLangTypes types) { PsiElement prevLeaf = PsiTreeUtil.prevLeaf(sourceElement); IElementType prevLeafType = ORUtil.getNodeType(prevLeaf); return prevLeafType == types.DOT || prevLeafType == types.SHARPSHARP || (prevLeafType == types.LBRACKET && types instanceof ResTypes); } static @NotNull List resolveInstructions(@NotNull Deque instructions, @Nullable Set openedModules, @NotNull Project project, @NotNull GlobalSearchScope scope) { List resolutions = new ArrayList<>(); // temporary resolutions // First instruction is always current file, if not there is a problem during parsing PsiElement file = instructions.removeFirst(); if (!(file instanceof FileBase containingFile)) { return new ArrayList<>(); } PsiManager psiManager = PsiManager.getInstance(project); // Add pervasives (always included) RPsiModule pervasives = FileModuleIndexService.getInstance().getTopModule("Pervasives", project, scope); if (pervasives != null) { ResolutionElement resolutionElement = new ResolutionElement(pervasives, true); if (LOG.isTraceEnabled()) { LOG.trace(" > global module, add [" + resolutionElement + "]"); } resolutions.add(resolutionElement); } // Add all globally opened elements (implicit open) if (openedModules != null) { LOG.trace("Processing globally opened modules"); for (String openedModuleName : openedModules) { List modules = getTopModules(openedModuleName, psiManager, scope); RPsiModule module = modules.isEmpty() ? null : modules.getFirst(); if (module != null) { ResolutionElement resolutionElement = new ResolutionElement(module, true); if (LOG.isTraceEnabled()) { LOG.trace(" > global module, add [" + resolutionElement + "]"); } resolutions.add(resolutionElement); } } } return processInstructions(instructions, resolutions, containingFile, psiManager, project, scope); } static @NotNull List processInstructions(@NotNull Deque instructions, @NotNull List resolutions, @NotNull FileBase sourceFile, @NotNull PsiManager psiManager, @NotNull Project project, @NotNull GlobalSearchScope scope) { List result = new ArrayList<>(); LOG.trace("Instructions to process", instructions); label: while (!instructions.isEmpty()) { PsiElement instruction = instructions.removeFirst(); boolean firstInPath = false; if (instruction instanceof FirstInPath firstInstruction) { instruction = firstInstruction.getOriginalElement(); firstInPath = true; } boolean isLastInstruction = instructions.isEmpty(); // Record or object field // ---------------------- switch (instruction) { case SymbolField foundSymbol -> { if (LOG.isTraceEnabled()) { LOG.trace("Processing field", instruction); } boolean isRecord = foundSymbol.isRecord; for (int i = resolutions.size() - 1; i >= 0; i--) { ResolutionElement resolution = resolutions.get(i); PsiElement resolvedElement = resolution.getOriginalElement(); String fieldName = foundSymbol.getValue(); if (resolvedElement instanceof RPsiLet resolvedLet) { Collection fields = isRecord ? resolvedLet.getRecordFields() : resolvedLet.getJsObjectFields(); RPsiField field = fields.stream().filter(f -> fieldName.equals(f.getName())).findFirst().orElse(null); if (field != null) { if (instructions.isEmpty()) { resolutions.clear(); result.add(field); } else { resolutions.add(new ResolutionElement(field, true)); } } break; } else if (resolvedElement instanceof RPsiType resolvedType) { Collection fields = isRecord ? resolvedType.getRecordFields() : resolvedType.getJsObjectFields(); RPsiField field = fields.stream().filter(f -> fieldName.equals(f.getName())).findFirst().orElse(null); if (field != null) { if (instructions.isEmpty()) { resolutions.clear(); result.add(field); } else { resolutions.add(new ResolutionElement(field, true)); } } break; } else if (resolvedElement instanceof RPsiField resolvedField) { RPsiFieldValue resolvedFieldValue = resolvedField.getValue(); PsiElement resolvedValue = resolvedFieldValue == null ? null : resolvedFieldValue.getFirstChild(); if (resolvedValue == null && resolvedElement instanceof RPsiSignatureElement resolvedSignatureElement) { RPsiSignature signature = resolvedSignatureElement.getSignature(); List signatureItems = signature == null ? null : signature.getItems(); if (signatureItems != null && signatureItems.size() == 1 && signatureItems.getFirst().getFirstChild() instanceof RPsiJsObject signatureObject) { resolvedValue = signatureObject; } } // field of field Collection fields = resolvedValue instanceof RPsiJsObject ? ((RPsiJsObject) resolvedValue).getFields() : resolvedValue instanceof RPsiRecord ? ((RPsiRecord) resolvedValue).getFields() : emptyList(); RPsiField field = fields.stream().filter(f -> fieldName.equals(f.getName())).findFirst().orElse(null); if (field != null) { if (instructions.isEmpty()) { resolutions.clear(); result.add(field); } else { resolutions.add(new ResolutionElement(field, true)); } } break; } } } // identifier // ---------- case RPsiLowerSymbol foundLower -> { String foundLowerText = foundLower.getText(); String foundLowerName = foundLowerText != null && !foundLowerText.isEmpty() ? (foundLowerText.charAt(0) == '`' || foundLowerText.charAt(0) == '#' ? "#" + foundLowerText.substring(1) : foundLowerText) : foundLowerText; if (LOG.isTraceEnabled()) { LOG.trace("Processing lower symbol", foundLowerText); } // build potential paths by iterating backward the resolutions boolean found = false; int i = resolutions.size() - 1; while (i >= 0) { if (found || resolutions.isEmpty()) { // maybe break; } ResolutionElement resolution = resolutions.get(i); PsiElement resolvedElement = resolution.getOriginalElement(); i = i - 1; if (resolvedElement instanceof RPsiLet resolvedLet && resolvedLet.isDeconstruction() && !resolution.isInContext) { // Special case for let deconstruction List deconstructedElements = resolvedLet.getDeconstructedElements(); for (PsiElement deconstructedElement : deconstructedElements) { if (foundLowerText.equals(deconstructedElement.getText())) { if (instructions.isEmpty()) { resolutions.clear(); result.add(resolvedLet); found = true; } } } } else if (resolvedElement instanceof RPsiQualifiedPathElement resolvedQPathElement && !resolution.isInContext) { List resolvedSignatures = null; // Special case for a signature item that has same name than its item // ex: let _ = (store: store) => ... if (resolvedElement instanceof RPsiParameterDeclaration resolvedDeclaration) { RPsiSignature resolvedSignature = resolvedDeclaration.getSignature(); if (resolvedSignature != null) { RPsiSignature foundSignatureParent = PsiTreeUtil.getParentOfType(foundLower, RPsiSignature.class); if (resolvedSignature == foundSignatureParent) { // Do not process continue; } else if (resolvedSignature.getItems().size() == 1) { if (LOG.isTraceEnabled()) { LOG.trace("resolvedElement: " + resolvedElement.getText() + ", file: " + resolvedElement.getContainingFile()); } PsiElement sourceChild = resolvedSignature.getItems().getFirst(); if (sourceChild instanceof RPsiSignatureItem sourceSignatureItem) { if (LOG.isTraceEnabled()) { LOG.trace("sourceSignature: " + sourceSignatureItem.getText()); } Deque instructionsForward = createInstructionsForward(sourceSignatureItem, ORTypesUtil.getInstance(sourceSignatureItem.getLanguage())); if (LOG.isTraceEnabled()) { LOG.trace("instructionsForward: " + Joiner.join(", ", instructionsForward)); } ArrayList resolutionElements = new ArrayList<>(resolutions); // The resolved element is a parameter, hence we need to remove all elements that are found // after it, and the parameter itself if (resolutionElements.size() > i + 1) { resolutionElements.subList(i + 1, resolutionElements.size()).clear(); } if (LOG.isTraceEnabled()) { LOG.trace("resolutionElements: " + Joiner.join(", ", resolutionElements)); } resolvedSignatures = processInstructions(instructionsForward, resolutionElements, sourceFile, psiManager, project, scope); } } } } else if (resolvedElement instanceof RPsiType resolvedType) { RPsiVariantDeclaration resolvedVariant = resolvedType.getVariants().stream().filter(variant -> variant.getName().equals(foundLowerName)).findFirst().orElse(null); if (resolvedVariant != null && instructions.isEmpty()) { resolutions.clear(); result.add(resolvedVariant); found = true; continue; } } if (foundLowerText.equals(resolvedQPathElement.getName())) { found = true; if (instructions.isEmpty()) { // If latest instruction, it is final result resolutions.clear(); result.add(resolvedQPathElement); } else { if (resolvedSignatures != null) { // a declaration with a signature resolved to its type for (RPsiQualifiedPathElement resolvedSignature : resolvedSignatures) { resolutions.add(new ResolutionElement(resolvedSignature, true)); } } // can be a record or an object resolutions.add(new ResolutionElement(resolvedQPathElement, true)); } } } else if (resolvedElement instanceof RPsiModule resolvedModule && resolution.isInContext) { // Try to resolve instruction from a module String qName = resolvedModule.getQualifiedName(); String resolvedQName = qName != null ? qName : ""; for (PsiElement alternateResolvedElement : resolvePath(resolvedQName, project, scope, true, 0)) { if (alternateResolvedElement instanceof RPsiModule alternateResolvedModule) { String pathToResolve = alternateResolvedModule.getQualifiedName() + "." + foundLowerName; // Test val Collection vals = ValFqnIndex.getElements(pathToResolve, project, scope); if (!vals.isEmpty()) { if (instructions.isEmpty()) { resolutions.clear(); } result.addAll(vals); break; } else { // Test let Collection lets = LetFqnIndex.getElements(pathToResolve, project, scope); if (!lets.isEmpty()) { if (instructions.isEmpty()) { resolutions.clear(); result.addAll(lets); } else { // can be a record or an object resolutions.addAll(lets.stream().map(l -> new ResolutionElement(l, true)).toList()); } break; } else { // Test externals Collection externals = ExternalFqnIndex.getElements(pathToResolve, project, scope); if (!externals.isEmpty()) { if (instructions.isEmpty()) { resolutions.clear(); } result.addAll(externals); break; } else { // Test types Collection types = TypeFqnIndex.getElements(pathToResolve, project, scope); if (!types.isEmpty()) { if (instructions.isEmpty()) { resolutions.clear(); } result.addAll(types); break; } else { Collection variants = VariantFqnIndex.getElements(pathToResolve, project, scope); if (!variants.isEmpty()) { if (instructions.isEmpty()) { resolutions.clear(); } result.addAll(variants); break; } } } } } } } } } } // X.Y (module path) // ------------------ case RPsiUpperSymbol foundUpper -> { String foundUpperText = foundUpper.getText(); String foundUpperName = foundUpperText != null && !foundUpperText.isEmpty() ? (foundUpperText.charAt(0) == '`' || foundUpperText.charAt(0) == '#' ? "#" + foundUpperText.substring(1) : foundUpperText) : foundUpperText; if (LOG.isTraceEnabled()) { LOG.trace("Processing Upper symbol", foundUpperText); } boolean found = false; for (int i = resolutions.size() - 1; i >= 0; i--) { if (found || resolutions.isEmpty()) { break; } ResolutionElement resolution = resolutions.get(i); PsiElement resolvedElement = resolution.getOriginalElement(); if (resolvedElement instanceof RPsiModule resolvedModule && !resolution.isInContext) { // Local module can be used as the first element of the path, if the name matches // module M = ... !inContext // M.xxx if (foundUpperText.equals(resolvedModule.getName())) { found = true; if (instructions.isEmpty()) { // If latest instruction, it is final result result.add(resolvedModule); } else { // Starting point of resolutions, we clear the list resolutions.clear(); resolutions.add(new ResolutionElement(resolvedModule, true)); Collection alternateNames = ORModuleResolutionPsiGist.getData(sourceFile).getValues(resolvedElement); for (String alternateQName : alternateNames) { List resolutionElements = resolvePath(alternateQName, project, scope, !isLastInstruction, 0).stream().map(element -> new ResolutionElement(element, true)).toList(); if (LOG.isTraceEnabled()) { LOG.trace(" > local module found (!incontext), add alternate names [" + Joiner.join(", ", resolutionElements) + "]"); } resolutions.addAll(resolutionElements); // If latest instruction, it is final result if (instructions.isEmpty()) { List foundElements = resolutionElements.stream().map(r -> r.getOriginalElement() instanceof RPsiQualifiedPathElement ? ((RPsiQualifiedPathElement) r.getOriginalElement()) : null).filter(Objects::nonNull).toList(); result.addAll(foundElements); } } } } } // else if (resolvedElement instanceof RPsiModule resolvedModule) { // upper element is part of a path that has already been resolved, // we try to resolve the current upper value as a path String resolvedQName = resolvedModule.getQualifiedName(); String pathToResolve = resolvedQName + "." + foundUpperName; List resolutionElements = resolvePath(pathToResolve, project, scope, !isLastInstruction, 0).stream().map(element -> new ResolutionElement(element, true)).toList(); if (LOG.isTraceEnabled()) { LOG.trace(" > local module found, add alternate names [" + Joiner.join(", ", resolutionElements) + "]"); } if (!resolutionElements.isEmpty()) { found = true; resolutions.clear(); // If latest instruction, it is final result if (isLastInstruction) { List foundElements = resolutionElements.stream().map(r -> r.getOriginalElement() instanceof RPsiQualifiedPathElement ? ((RPsiQualifiedPathElement) r.getOriginalElement()) : null).filter(Objects::nonNull).toList(); result.addAll(foundElements); } else { resolutions.addAll(resolutionElements); } } // If it’s the last element, we can be more specific else if (instructions.isEmpty()) { // Test if it’s an exception Collection exceptions = ExceptionFqnIndex.getElements(pathToResolve, project, scope); if (!exceptions.isEmpty()) { resolutions.clear(); result.addAll(exceptions); break; } // Else test if it’s a variant Collection variants = VariantFqnIndex.getElements(pathToResolve, project, scope); if (!variants.isEmpty()) { resolutions.clear(); result.addAll(variants); break; } } } // else if (resolvedElement instanceof RPsiException resolvedException && instructions.isEmpty()) { if (foundUpperText.equals(resolvedException.getName())) { resolutions.clear(); result.add(resolvedException); break; } } // Maybe we are resolving a locally defined variant // type t = | Variant; ... Variant else if (resolvedElement instanceof RPsiType resolvedType && instructions.isEmpty()) { for (RPsiVariantDeclaration variant : resolvedType.getVariants()) { if (variant.getName().equals(foundUpperName)) { found = true; resolutions.clear(); result.add(variant); break; } } } } // For the first element of a path, we can try global modules if (!found && firstInPath) { if (LOG.isTraceEnabled()) { LOG.trace(" > No modules found, try top modules"); } // Try to resolve top module directly List modules = getTopModules(instruction.getText(), psiManager, scope); List list = modules.stream().map(m -> new ResolutionElement(m, true)).toList(); resolutions.addAll(list); found = !list.isEmpty(); if (found && instructions.isEmpty()) { resolutions.clear(); result.addAll(modules); } } if (!found) { // A path must be resolved/found to continue, else we stop everything instructions.clear(); resolutions.clear(); break label; } } // OPEN X // ------ case RPsiOpen foundOpen -> { if (LOG.isTraceEnabled()) { LOG.trace("Processing open", foundOpen.getPath()); } // Search for alternate paths using gist // This is a recursive function (if not end of instruction) ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(sourceFile); Collection alternateNames = data.getValues(foundOpen); // add path to data ? if (alternateNames.isEmpty()) { // No alternate names, it is a direct element // We need to analyze the path and resolve each part List resolvedPaths = resolvePath(foundOpen.getPath(), project, scope, !isLastInstruction, 0).stream().map(element -> new ResolutionElement(element, true)).toList(); if (LOG.isTraceEnabled()) { LOG.trace(" > no alternate names, add resolutions: [" + Joiner.join(", ", resolvedPaths) + "]"); } resolutions.addAll(resolvedPaths); } else { // resolve alternate top level modules for (String alternateName : alternateNames) { List resolvedPaths = resolvePath(alternateName, project, scope, !isLastInstruction, 0).stream().map(element -> new ResolutionElement(element, true)).toList(); if (LOG.isTraceEnabled()) { LOG.trace(" > alternate name [" + alternateName + "], add resolutions: [" + Joiner.join(", ", resolvedPaths) + "]"); } resolutions.addAll(resolvedPaths); } } } // INCLUDE X // --------- case RPsiInclude foundInclude -> { LOG.trace("Processing include"); // Search for alternate paths using gist Collection alternateNames = ORModuleResolutionPsiGist.getData(sourceFile).getValues(foundInclude); if (alternateNames.isEmpty()) { // No alternate names, it is a direct element, we need to analyze the path and resolve each part resolutions.addAll(resolvePath(foundInclude.getIncludePath(), project, scope, !isLastInstruction, 0).stream().map(element -> new ResolutionElement(element, true)).toList()); } else { // Resolve alternate top level modules for (String alternateName : alternateNames) { resolutions.addAll(resolvePath(alternateName, project, scope, !isLastInstruction, 0).stream().map(element -> new ResolutionElement(element, true)).toList()); } } } // START TAG // --------- case RPsiTagStart rPsiTagStart -> { LOG.trace("Processing start tag", instruction); if (instructions.getLast() instanceof RPsiTagProperty) { RPsiTagStart foundTag = (RPsiTagStart) instruction; // Search for alternate paths using gist Collection alternateNames = ORModuleResolutionPsiGist.getData(sourceFile).getValues(foundTag); if (alternateNames.isEmpty()) { // No alternate names, it is a direct element, we need to analyze the path and resolve each part String name = foundTag.getName(); if (name != null) { resolutions.addAll(resolvePath(name, project, scope, !isLastInstruction, 0).stream().map(element -> new ResolutionElement(element, true)).toList()); } } else { // Resolve alternate top level modules for (String alternateName : alternateNames) { resolutions.addAll(resolvePath(alternateName, project, scope, !isLastInstruction, 0).stream().map(element -> new ResolutionElement(element, true)).toList()); } } } } case RPsiTagProperty foundProperty -> { if (LOG.isTraceEnabled()) { LOG.trace("Processing property", instruction); } // Previous element should be the start tag ResolutionElement tag = resolutions.getLast(); if (tag.isInContext && tag.isComponent()) { String propertyQName = tag.getQualifiedName() + ".make[" + foundProperty.getName() + "]"; Collection parameters = ParameterFqnIndex.getElements(propertyQName, project, scope); if (!parameters.isEmpty() && instructions.isEmpty()) { resolutions.clear(); result.add(parameters.iterator().next()); } } } case null, default -> { // find alternate names if upper element ResolutionElement res = new ResolutionElement(instruction); if (LOG.isTraceEnabled()) { LOG.trace("Add instruction [" + res + "]"); } resolutions.add(res); } } } return result; } private static boolean isInterface(PsiElement element) { if (element instanceof FileBase && ((FileBase) element).isInterface()) { return true; } return element instanceof RPsiInnerModule && ((RPsiInnerModule) element).isModuleType(); } public static List resolvePath(@NotNull String path, @NotNull Project project, @NotNull GlobalSearchScope scope, boolean useAlternateNames, int level) { List pathResolutions = new ArrayList<>(); if (level < MAX_PATH_RESOLUTION_LEVEL) { String[] pathTokens = path.split("\\."); List topModules = getTopModules(pathTokens[0], PsiManager.getInstance(project), scope); if (!topModules.isEmpty()) { RPsiModule topLevel = topModules.getFirst(); pathResolutions.add(topLevel); ModuleIndexService moduleIndexService = getApplication().getService(ModuleIndexService.class); if (useAlternateNames) { // Get all alternate resolutions for top level file ORModuleResolutionPsiGist.Data topLevelData = ORModuleResolutionPsiGist.getData((FileBase) topLevel); for (String topLevelAlternateName : topLevelData.getValues(topLevel)) { Collection topLevelAlternates = moduleIndexService.getModules(topLevelAlternateName, project, scope); pathResolutions.addAll(topLevelAlternates); } } // Append path token to every resolved element to try to resolve new ones for (int i = 1; i < pathTokens.length; i++) { String pathToken = pathTokens[i]; List newPathResolutions = new ArrayList<>(); for (PsiElement pathResolution : pathResolutions) { String name = (pathResolution instanceof FileBase) ? ((FileBase) pathResolution).getModuleName() : ((RPsiQualifiedPathElement) pathResolution).getQualifiedName(); String pathToResolve = name + "." + pathToken; for (RPsiModule module : ModuleFqnIndex.getElements(pathToResolve, project, scope)) { newPathResolutions.add(module); if (useAlternateNames) { PsiFile containingFile = module.getContainingFile(); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(containingFile); for (String alternateModuleQName : data.getValues(module)) { if (!pathToResolve.equals(alternateModuleQName)) { newPathResolutions.addAll(resolvePath(alternateModuleQName, project, scope, true, level + 1)); } } } } } pathResolutions = newPathResolutions; } } pathResolutions.sort(SORT_INTERFACE_FIRST); } return pathResolutions; } private static @NotNull List getTopModules(@NotNull String name, @NotNull PsiManager psiManager, @NotNull GlobalSearchScope scope) { FileModuleIndexService.getInstance(); return FileModuleIndex.getContainingFiles(name, scope).stream().map(v -> { PsiFile psiFile = psiManager.findFile(v); return psiFile instanceof RPsiModule ? (RPsiModule) psiFile : null; }).filter(Objects::nonNull).collect(Collectors.toList()); } static class FirstInPath extends ORFakeResolvedElement { public FirstInPath(@NotNull PsiElement element) { super(element); } @Override public @NotNull String toString() { return "FirstInPath: " + getOriginalElement().getText(); } } static class SymbolField extends ORFakeResolvedElement { final boolean isRecord; public SymbolField(@NotNull PsiElement element, boolean isRecord) { super(element); this.isRecord = isRecord; } public @NotNull String getValue() { boolean isString = getOriginalElement() instanceof RPsiLiteralString; String text = getText(); if (text == null) { return ""; } return isString ? text.substring(1, text.length() - 1) : text; } @Override public String toString() { return getOriginalElement() + " (" + getOriginalElement().getText() + ") " + (isRecord ? "record" : "jsField"); } } static class ResolutionElement extends ORFakeResolvedElement { boolean isInContext = false; public ResolutionElement(@NotNull PsiElement element) { super(element); } public ResolutionElement(PsiElement element, boolean inContext) { super(element); this.isInContext = inContext; } public boolean isComponent() { PsiElement originalElement = getOriginalElement(); if (originalElement instanceof FileBase) { return ((FileBase) originalElement).isComponent(); } return originalElement instanceof RPsiModule && ((RPsiModule) originalElement).isComponent(); } public String getQualifiedName() { PsiElement originalElement = getOriginalElement(); return originalElement instanceof RPsiQualifiedPathElement ? ((RPsiQualifiedPathElement) originalElement).getQualifiedName() : ""; } @Override public String toString() { PsiElement originalElement = getOriginalElement(); return originalElement + (isInContext ? " -> in context" : ""); } } } ================================================ FILE: src/main/java/com/reason/ide/settings/DuneColorSettingsPage.java ================================================ package com.reason.ide.settings; import com.intellij.openapi.editor.colors.TextAttributesKey; import com.intellij.openapi.options.colors.AttributesDescriptor; import com.intellij.openapi.options.colors.ColorDescriptor; import com.intellij.openapi.options.colors.ColorSettingsPage; import com.reason.ide.highlight.DuneSyntaxHighlighter; import com.reason.ide.ORIcons; import java.util.HashMap; import java.util.Map; import javax.swing.*; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; public class DuneColorSettingsPage implements ColorSettingsPage { private static final AttributesDescriptor[] DESCRIPTORS = new AttributesDescriptor[]{ new AttributesDescriptor("Comment", DuneSyntaxHighlighter.COMMENT_), new AttributesDescriptor("Stanza", DuneSyntaxHighlighter.STANZAS_), new AttributesDescriptor("Fields", DuneSyntaxHighlighter.FIELDS_), new AttributesDescriptor("Options", DuneSyntaxHighlighter.OPTIONS_), new AttributesDescriptor("Atoms", DuneSyntaxHighlighter.ATOM_), new AttributesDescriptor("Variables", DuneSyntaxHighlighter.VAR_), new AttributesDescriptor("Parenthesis", DuneSyntaxHighlighter.PARENS_), }; @Override public @Nullable Icon getIcon() { return ORIcons.DUNE_FILE; } @Override public @NotNull com.intellij.openapi.fileTypes.SyntaxHighlighter getHighlighter() { return new DuneSyntaxHighlighter(); } @Override public @NotNull String getDemoText() { return """ ; A single line comment #| Block comments #| can be "nested" |# |# (executable (names (main)) #; (this S-expression (has been commented out) ) (libraries (hello_world))) (install (section bin) (files ((main.exe as hello_world)))) (rule (targets (config.full) (deps (config_common.ml config)) (action (run %{OCAML} %{path:real_configure.ml})))"""; } private static final Map additionalTags = new HashMap<>(); static { additionalTags.put("csField", DuneSyntaxHighlighter.FIELDS_); additionalTags.put("csStanza", DuneSyntaxHighlighter.STANZAS_); additionalTags.put("csVar", DuneSyntaxHighlighter.VAR_); } @Override public @Nullable Map getAdditionalHighlightingTagToDescriptorMap() { return additionalTags; } @Override public @NotNull AttributesDescriptor[] getAttributeDescriptors() { return DESCRIPTORS; } @Override public @NotNull ColorDescriptor[] getColorDescriptors() { return ColorDescriptor.EMPTY_ARRAY; } @Override public @NotNull String getDisplayName() { return "Dune"; } } ================================================ FILE: src/main/java/com/reason/ide/settings/ORColorSettingsPage.java ================================================ package com.reason.ide.settings; import com.intellij.openapi.editor.colors.*; import com.intellij.openapi.fileTypes.*; import com.intellij.openapi.options.colors.*; import com.reason.ide.*; import com.reason.ide.highlight.*; import com.reason.lang.reason.*; import org.jetbrains.annotations.*; import javax.swing.*; import java.util.*; public class ORColorSettingsPage implements ColorSettingsPage { private static final AttributesDescriptor[] DESCRIPTORS = new AttributesDescriptor[]{ new AttributesDescriptor("Annotation", ORSyntaxHighlighter.ANNOTATION_), new AttributesDescriptor("Braces", ORSyntaxHighlighter.BRACES_), new AttributesDescriptor("Brackets", ORSyntaxHighlighter.BRACKETS_), new AttributesDescriptor("Code lens", ORSyntaxHighlighter.CODE_LENS_), new AttributesDescriptor("Comment", ORSyntaxHighlighter.COMMENT_), new AttributesDescriptor("Field name", ORSyntaxHighlighter.FIELD_NAME_), new AttributesDescriptor("Let/val name", ORSyntaxHighlighter.LET_NAME_), new AttributesDescriptor("Keyword", ORSyntaxHighlighter.KEYWORD_), new AttributesDescriptor("Macro", ORSyntaxHighlighter.MACRO_), new AttributesDescriptor("Markup attribute", ORSyntaxHighlighter.MARKUP_ATTRIBUTE_), new AttributesDescriptor("Markup tag", ORSyntaxHighlighter.MARKUP_TAG_), new AttributesDescriptor("Module name", ORSyntaxHighlighter.MODULE_NAME_), new AttributesDescriptor("Number", ORSyntaxHighlighter.NUMBER_), new AttributesDescriptor("Option", ORSyntaxHighlighter.OPTION_), new AttributesDescriptor("Operation", ORSyntaxHighlighter.OPERATION_SIGN_), new AttributesDescriptor("Parenthesis", ORSyntaxHighlighter.PARENS_), new AttributesDescriptor("Poly variant", ORSyntaxHighlighter.POLY_VARIANT_), new AttributesDescriptor("Semicolon", ORSyntaxHighlighter.SEMICOLON_), new AttributesDescriptor("String", ORSyntaxHighlighter.STRING_), new AttributesDescriptor("Type argument", ORSyntaxHighlighter.TYPE_ARGUMENT_), new AttributesDescriptor("Variant name", ORSyntaxHighlighter.VARIANT_NAME_), new AttributesDescriptor("Interpolated ref", ORSyntaxHighlighter.INTERPOLATED_REF_), }; @Override public @Nullable Icon getIcon() { return ORIcons.RML_FILE; } @Override public @NotNull SyntaxHighlighter getHighlighter() { return new ORSyntaxHighlighter(RmlTypes.INSTANCE, RmlSyntaxHighlighterFactory.KEYWORD_TYPES, RmlSyntaxHighlighterFactory.OPERATION_SIGN_TYPES, RmlSyntaxHighlighterFactory.OPTION_TYPES, RmlSyntaxHighlighterFactory.MACRO_TYPES); } @Override public @NotNull String getDemoText() { return """ /* This is a comment */ module ModuleName = { type t = { key: int }; type tree 'a = | Node (tree 'a) (tree 'a) | Leaf; [@bs.deriving {accessors: accessors}] type t = [`Up | `Down | `Left | `Right]; let add = (x y) => x + y; int -> int let myList = [ 1.0, 2.0, 3. ]; let array = [| 1, 2, 3 |]; let choice x = switch (myOption) | None => "nok" | Some(value) => "ok"; let constant = "My constant"; string let numericConstant = 123; int let interpolation = {j|$var|j}; }; [@react.component] let make = () => prop=value> ;"""; } private static final Map additionalTags = new HashMap<>(); static { additionalTags.put("csAnnotation", ORSyntaxHighlighter.ANNOTATION_); additionalTags.put("csCodeLens", ORSyntaxHighlighter.CODE_LENS_); additionalTags.put("csField", ORSyntaxHighlighter.FIELD_NAME_); additionalTags.put("csLetName", ORSyntaxHighlighter.LET_NAME_); additionalTags.put("csMarkupAttribute", ORSyntaxHighlighter.MARKUP_ATTRIBUTE_); additionalTags.put("csMarkupTag", ORSyntaxHighlighter.MARKUP_TAG_); additionalTags.put("csModuleName", ORSyntaxHighlighter.MODULE_NAME_); additionalTags.put("csVariantName", ORSyntaxHighlighter.VARIANT_NAME_); additionalTags.put("csInterpolatedRef", ORSyntaxHighlighter.INTERPOLATED_REF_); } @Override public @Nullable Map getAdditionalHighlightingTagToDescriptorMap() { return additionalTags; } @Override public @NotNull AttributesDescriptor[] getAttributeDescriptors() { return DESCRIPTORS; } @Override public @NotNull ColorDescriptor[] getColorDescriptors() { return ColorDescriptor.EMPTY_ARRAY; } @Override public @NotNull String getDisplayName() { return "Rescript/Reason (OCaml)"; } } ================================================ FILE: src/main/java/com/reason/ide/settings/ORSettings.java ================================================ package com.reason.ide.settings; import com.intellij.openapi.components.*; import com.intellij.openapi.project.*; import org.jetbrains.annotations.*; @State(name = "ReasonSettings", storages = {@Storage("reason.xml")}) public class ORSettings implements PersistentStateComponent { private static final boolean IS_FORMAT_ON_SAVE_DEFAULT = true; private static final String FORMAT_WIDTH_COLUMNS_DEFAULT = "80"; private static final boolean IS_BS_ENABLED_DEFAULT = true; private final @NotNull Project m_project; // General private boolean m_isFormatOnSaveEnabled = IS_FORMAT_ON_SAVE_DEFAULT; private @Nullable String m_formatColumnWidth; private boolean myUseSuperErrors = false; // BuckleScript private boolean m_isBsEnabled = IS_BS_ENABLED_DEFAULT; private String m_bsPlatformLocation = ""; // Dune private String myOpamLocation = ""; private String mySwitchName = ""; private String myCygwinBash = null; private boolean myIsWsl = false; // Esy private String m_esyExecutable = ""; public ORSettings(@NotNull Project project) { m_project = project; } @Override public @NotNull ReasonSettingsState getState() { ReasonSettingsState state = new ReasonSettingsState(); state.isFormatOnSaveEnabled = m_isFormatOnSaveEnabled; state.formatColumnWidth = m_formatColumnWidth; state.isUseSuperErrors = myUseSuperErrors; state.isBsEnabled = m_isBsEnabled; state.bsPlatformLocation = m_bsPlatformLocation; state.opamLocation = myOpamLocation; state.cygwinBash = myCygwinBash; state.isWsl = myIsWsl; state.switchName = mySwitchName; state.esyExecutable = m_esyExecutable; return state; } @Override public void loadState(@NotNull ReasonSettingsState state) { m_isFormatOnSaveEnabled = state.isFormatOnSaveEnabled; m_formatColumnWidth = state.formatColumnWidth; myUseSuperErrors = state.isUseSuperErrors; m_isBsEnabled = state.isBsEnabled; m_bsPlatformLocation = state.bsPlatformLocation; myOpamLocation = state.opamLocation; myCygwinBash = state.cygwinBash; myIsWsl = state.isWsl; mySwitchName = state.switchName; m_esyExecutable = state.esyExecutable; } public @NotNull Project getProject() { return m_project; } public boolean isFormatOnSaveEnabled() { return m_isBsEnabled && m_isFormatOnSaveEnabled; } public void setFormatOnSaveEnabled(boolean isFormatOnSaveEnabled) { m_isFormatOnSaveEnabled = isFormatOnSaveEnabled; } @NotNull public String getFormatColumnWidth() { if (m_formatColumnWidth == null) { String systemRefmtWidth = System.getProperties().getProperty("refmtWidth"); return systemRefmtWidth == null ? FORMAT_WIDTH_COLUMNS_DEFAULT : systemRefmtWidth; } return m_formatColumnWidth; } public void setFormatColumnWidth(@Nullable String formatColumnWidth) { m_formatColumnWidth = formatColumnWidth != null && formatColumnWidth.isEmpty() ? null : formatColumnWidth; } public boolean isUseSuperErrors() { return myUseSuperErrors; } public void setUseSuperErrors(boolean useSuperErrors) { myUseSuperErrors = useSuperErrors; } public boolean isBsEnabled() { return m_isBsEnabled; } public void setBsEnabled(boolean isBsEnabled) { m_isBsEnabled = isBsEnabled; } public @NotNull String getBsPlatformLocation() { return m_bsPlatformLocation == null ? "" : m_bsPlatformLocation; } public void setBsPlatformLocation(String bsPlatformLocation) { m_bsPlatformLocation = bsPlatformLocation; } public @NotNull String getOpamLocation() { return myOpamLocation == null ? "" : myOpamLocation; } public void setOpamLocation(@Nullable String location) { myOpamLocation = location; } public String getCygwinBash() { return myCygwinBash; } public void setCygwinBash(@Nullable String cygwinBash) { myCygwinBash = cygwinBash; } public boolean isWsl() { return myIsWsl; } public void setIsWsl(boolean isWsl) { myIsWsl = isWsl; } public String getSwitchName() { return mySwitchName; } public void setSwitchName(@Nullable String name) { mySwitchName = name == null ? "" : name; } public @NotNull String getEsyExecutable() { return m_esyExecutable == null ? "" : m_esyExecutable; } public void setEsyExecutable(String esyExecutable) { m_esyExecutable = esyExecutable; } @SuppressWarnings("WeakerAccess") public static class ReasonSettingsState { // General public boolean isFormatOnSaveEnabled = IS_FORMAT_ON_SAVE_DEFAULT; public @Nullable String formatColumnWidth; public boolean isUseSuperErrors = false; // BuckleScript public boolean isBsEnabled = IS_BS_ENABLED_DEFAULT; public String bsPlatformLocation = ""; // Dune public String opamLocation = ""; public String switchName = ""; public String cygwinBash = null; public boolean isWsl = false; // Esy public String esyExecutable = ""; } } ================================================ FILE: src/main/java/com/reason/ide/settings/ORSettingsConfigurable.form ================================================
================================================ FILE: src/main/java/com/reason/ide/settings/ORSettingsConfigurable.java ================================================ package com.reason.ide.settings; import com.intellij.openapi.application.*; import com.intellij.openapi.fileChooser.*; import com.intellij.openapi.module.Module; import com.intellij.openapi.options.*; import com.intellij.openapi.project.*; import com.intellij.openapi.roots.*; import com.intellij.openapi.roots.libraries.*; import com.intellij.openapi.roots.libraries.ui.*; import com.intellij.openapi.roots.libraries.ui.impl.*; import com.intellij.openapi.ui.*; import com.intellij.openapi.vfs.*; import com.intellij.workspaceModel.ide.impl.legacyBridge.library.*; import com.reason.comp.dune.*; import com.reason.comp.ocaml.*; import com.reason.ide.console.*; import com.reason.ide.library.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; import javax.swing.*; import java.nio.file.*; import java.util.*; import static com.intellij.openapi.application.ApplicationManager.getApplication; public class ORSettingsConfigurable implements SearchableConfigurable, Configurable.NoScroll { @Nls private static final String BS_PLATFORM_LOCATION_LABEL = "Choose bs-platform Directory: "; @Nls private static final String ESY_EXECUTABLE_LABEL = "Choose esy Executable: "; private final @NotNull Project myProject; private ORSettings mySettings; private JPanel myRootPanel; private JTabbedPane myTabs; private boolean myIsWsl = false; private String myCygwinBash; // General private JTextField f_generalFormatWidthColumns; private JCheckBox f_generalIsFormatOnSave; private JCheckBox myUseSuperErrorsCheckBox; // BuckleScript private JCheckBox f_bsIsEnabled; private TextFieldWithBrowseButton f_bsPlatformLocation; // Esy private TextFieldWithBrowseButton f_esyExecutable; private OpamConfigurationTab myOpamConfigurationTab; public ORSettingsConfigurable(@NotNull Project project) { myProject = project; } @NotNull @Override public String getId() { return getHelpTopic(); } @Nls @Override public @NotNull String getDisplayName() { return "OCaml(Reason) / Rescript"; } @NotNull @Override public String getHelpTopic() { return "settings.reason"; } @Nullable @Override public JComponent createComponent() { mySettings = myProject.getService(ORSettings.class); createGeneralTab(); createBsTab(); myOpamConfigurationTab.createComponent(mySettings.getProject(), mySettings.getSwitchName()); createEsyTab(); return myRootPanel; } @Override public void apply() { // General mySettings.setFormatOnSaveEnabled(f_generalIsFormatOnSave.isSelected()); mySettings.setFormatColumnWidth(sanitizeInput(f_generalFormatWidthColumns.getText())); mySettings.setUseSuperErrors(myUseSuperErrorsCheckBox.isSelected()); // BuckleScript mySettings.setBsEnabled(f_bsIsEnabled.isSelected()); mySettings.setBsPlatformLocation(sanitizeInput(f_bsPlatformLocation)); // Opam mySettings.setOpamLocation(sanitizeInput(myOpamConfigurationTab.getOpamLocation())); mySettings.setCygwinBash(myCygwinBash); mySettings.setIsWsl(myIsWsl); mySettings.setSwitchName(myOpamConfigurationTab.getSelectedSwitch()); // Esy mySettings.setEsyExecutable(sanitizeInput(f_esyExecutable)); // Create external library based on the selected opam switch createExternalLibraryDependency(mySettings.getProject(), mySettings.getSwitchName(), mySettings.getOpamLocation()); // Compute env OpamEnv opamEnv = getApplication().getService(OpamEnv.class); opamEnv.computeEnv(mySettings.getOpamLocation(), mySettings.getSwitchName(), mySettings.getCygwinBash(), null); // Display compiler info in console (if any) myProject.getService(ORToolWindowManager.class).shouldShowToolWindows(); } private void createExternalLibraryDependency(@NotNull Project project, @NotNull String switchName, String opamLocation) { if (switchName.isEmpty()) { return; } LibraryTable projectLibraryTable = LibraryTablesRegistrar.getInstance().getLibraryTable(project); LibraryTable.ModifiableModel projectLibraryTableModel = projectLibraryTable.getModifiableModel(); String libraryName = "switch:" + switchName; // Remove existing lib Library oldLibrary = projectLibraryTableModel.getLibraryByName(libraryName); VirtualFile opamRootCandidate = VirtualFileManager.getInstance().findFileByNioPath(Path.of(opamLocation, switchName)); if (opamRootCandidate != null && opamRootCandidate.exists() && opamRootCandidate.isValid()) { Library library = oldLibrary == null ? projectLibraryTableModel.createLibrary(libraryName, OclLibraryKind.INSTANCE) : null; Library.ModifiableModel libraryModel = library == null ? null : library.getModifiableModel(); if (libraryModel != null) { OclLibraryType libraryType = (OclLibraryType) LibraryType.findByKind(OclLibraryKind.INSTANCE); LibraryRootsComponentDescriptor rootsComponentDescriptor = libraryType.createLibraryRootsComponentDescriptor(); List orderRoots = RootDetectionUtil.detectRoots(Collections.singleton(opamRootCandidate), myRootPanel, project, rootsComponentDescriptor); for (OrderRoot orderRoot : orderRoots) { libraryModel.addRoot(orderRoot.getFile(), orderRoot.getType()); } } ApplicationManager.getApplication().invokeAndWait(() -> WriteAction.run(() -> { if (libraryModel != null) { libraryModel.commit(); projectLibraryTableModel.commit(); } // Find module that contains dune config root file Map duneContentRoots = Platform.findModulesFor(project, DunePlatform.DUNE_PROJECT_FILENAME); for (Module module : duneContentRoots.keySet()) { ModuleRootModificationUtil.updateModel(module, moduleModel -> { // Remove all libraries entries that are of type Ocaml moduleModel.orderEntries().forEach(entry -> { Library entryLibrary = (entry instanceof LibraryOrderEntry) ? ((LibraryOrderEntry) entry).getLibrary() : null; PersistentLibraryKind entryLibraryKind = (entryLibrary instanceof LibraryBridge) ? ((LibraryBridge) entryLibrary).getKind() : null; if (entryLibraryKind instanceof OclLibraryKind) { moduleModel.removeOrderEntry(entry); } return true; }); // Add the new lib as order entry moduleModel.addLibraryEntry(library == null ? oldLibrary : library); }); } })); } } @Override public boolean isModified() { // General boolean isFormatOnSaveModified = f_generalIsFormatOnSave.isSelected() != mySettings.isFormatOnSaveEnabled(); boolean isFormatWidthColumnsModified = !f_generalFormatWidthColumns.getText().equals(mySettings.getFormatColumnWidth()); boolean isUseSuperErrorModified = myUseSuperErrorsCheckBox.isSelected() != mySettings.isUseSuperErrors(); // Bs boolean isBsEnabledModified = f_bsIsEnabled.isSelected() != mySettings.isBsEnabled(); boolean isBsPlatformLocationModified = !f_bsPlatformLocation.getText().equals(mySettings.getBsPlatformLocation()); // Opam boolean isOpamLocationModified = myOpamConfigurationTab.isOpamLocationModified(mySettings.getOpamLocation()); boolean isOpamSwitchModified = myOpamConfigurationTab.isOpamSwitchModified(mySettings.getSwitchName()); // Esy boolean isEsyExecutableModified = !f_esyExecutable.getText().equals(mySettings.getEsyExecutable()); return isFormatOnSaveModified || isFormatWidthColumnsModified || isUseSuperErrorModified || isBsEnabledModified || isBsPlatformLocationModified || isOpamLocationModified || isOpamSwitchModified || isEsyExecutableModified; } @Override public void reset() { // General f_generalIsFormatOnSave.setSelected(mySettings.isFormatOnSaveEnabled()); f_generalFormatWidthColumns.setText(mySettings.getFormatColumnWidth()); myUseSuperErrorsCheckBox.setSelected(mySettings.isUseSuperErrors()); // BuckleScript f_bsIsEnabled.setSelected(mySettings.isBsEnabled()); f_bsPlatformLocation.setText(mySettings.getBsPlatformLocation()); // Opam myCygwinBash = mySettings.getCygwinBash(); myIsWsl = mySettings.isWsl(); myOpamConfigurationTab.setOpamLocation(mySettings.getOpamLocation()); myOpamConfigurationTab.setDetectionText(); myOpamConfigurationTab.createSwitch(mySettings.getOpamLocation(), mySettings.getSwitchName()); // Esy f_esyExecutable.setText(mySettings.getEsyExecutable()); } private void createGeneralTab() { } private void createBsTab() { Project project = mySettings.getProject(); f_bsPlatformLocation.addBrowseFolderListener(BS_PLATFORM_LOCATION_LABEL, null, project, FileChooserDescriptorFactory.createSingleFolderDescriptor()); } private void createEsyTab() { Project project = mySettings.getProject(); f_esyExecutable.addBrowseFolderListener(ESY_EXECUTABLE_LABEL, null, project, FileChooserDescriptorFactory.createSingleFileOrExecutableAppDescriptor()); } private static @NotNull String sanitizeInput(@NotNull TextFieldWithBrowseButton textFieldWithBrowseButton) { return sanitizeInput(textFieldWithBrowseButton.getText()); } private static @NotNull String sanitizeInput(@NotNull String input) { return input.trim(); } } ================================================ FILE: src/main/java/com/reason/ide/settings/OpamConfigurationTab.form ================================================
================================================ FILE: src/main/java/com/reason/ide/settings/OpamConfigurationTab.java ================================================ package com.reason.ide.settings; import com.intellij.openapi.application.*; import com.intellij.openapi.fileChooser.*; import com.intellij.openapi.project.*; import com.intellij.openapi.ui.*; import com.intellij.openapi.vfs.*; import com.intellij.openapi.vfs.impl.wsl.*; import com.intellij.util.ui.*; import com.reason.comp.ocaml.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; import javax.swing.*; import javax.swing.table.*; import java.awt.event.*; import java.nio.file.*; import java.util.*; public class OpamConfigurationTab { private static final String[] EMPTY_COLUMNS = {}; private JPanel myRootPanel; private TextFieldWithBrowseButton myOpamLocation; private JLabel myDetectionLabel; private JComboBox mySwitchSelect; private JTable myOpamLibraries; private boolean myIsWsl = false; private String myCygwinBash; private final List myEnv = new ArrayList<>(); public void createComponent(@Nullable Project project, @NotNull String switchName) { TextBrowseFolderListener browseListener = new TextBrowseFolderListener(FileChooserDescriptorFactory.createSingleFolderDescriptor(), project) { @Override protected void onFileChosen(@NotNull VirtualFile chosenDir) { super.onFileChosen(chosenDir); detectSwitchSystem(chosenDir); setDetectionText(); createSwitch(chosenDir.getPath(), switchName); } }; FocusListener focusListener = getFocusListener(switchName); myOpamLocation.getTextField().addFocusListener(focusListener); myOpamLocation.addBrowseFolderListener(browseListener); mySwitchSelect.addItemListener(itemEvent -> { if (itemEvent.getStateChange() == ItemEvent.SELECTED) { String version = (String) itemEvent.getItem(); clearEnv(); listLibraries(version); } }); myOpamLibraries.setBorder(BorderFactory.createLineBorder(JBUI.CurrentTheme.DefaultTabs.borderColor())); listLibraries(switchName); } private @NotNull FocusListener getFocusListener(@NotNull String switchName) { final String[] previousOpamLocation = new String[1]; return new FocusListener() { @Override public void focusGained(FocusEvent e) { previousOpamLocation[0] = myOpamLocation.getText(); } @Override public void focusLost(FocusEvent e) { String path = myOpamLocation.getText(); String oldPath = previousOpamLocation[0]; if (!path.equals(oldPath)) { VirtualFile chosenDir = VirtualFileManager.getInstance().findFileByNioPath(Path.of(path)); if (chosenDir == null) { createSwitch("", switchName); clearEnv(); } else { detectSwitchSystem(chosenDir); setDetectionText(); createSwitch(chosenDir.getPath(), switchName); } } } }; } private void detectSwitchSystem(@NotNull VirtualFile dir) { myIsWsl = dir.getPath().replace("/", "\\").startsWith(WslConstants.UNC_PREFIX); myCygwinBash = null; if (!myIsWsl && Platform.isWindows()) { // cygwin VirtualFile binDir = findBinary(dir); if (binDir != null && binDir.isValid()) { VirtualFile opam = binDir.findChild("bash.exe"); if (opam != null && opam.isValid()) { myCygwinBash = opam.getPath(); } } } } void setDetectionText() { if (myCygwinBash != null) { myDetectionLabel.setText("Cygwin detected"); } else if (myIsWsl) { myDetectionLabel.setText("WSL detected"); } else { myDetectionLabel.setText(""); } } void createSwitch(@NotNull String opamLocation, @NotNull String switchName) { ApplicationManager.getApplication() .getService(OpamProcess.class) .listSwitch(opamLocation, myCygwinBash, opamSwitches -> { boolean switchEnabled = opamSwitches != null && !opamSwitches.isEmpty(); mySwitchSelect.removeAllItems(); mySwitchSelect.setEnabled(switchEnabled); if (switchEnabled) { boolean useOpamSelection = switchName.isEmpty(); //System.out.println("Add: [" + Joiner.join(", ", opamSwitches) + "]"); for (OpamProcess.OpamSwitch opamSwitch : opamSwitches) { mySwitchSelect.addItem(opamSwitch.name()); if (opamSwitch.isSelected() && useOpamSelection) { mySwitchSelect.setSelectedIndex(mySwitchSelect.getItemCount() - 1); } } if (!useOpamSelection) { mySwitchSelect.setSelectedItem(switchName); } } else { clearEnv(); } }); } private void listLibraries(@NotNull String version) { ApplicationManager.getApplication().getService(OpamProcess.class) .list(myOpamLocation.getText(), version, myCygwinBash, libs -> { myEnv.clear(); if (libs != null) { myEnv.addAll(libs); } myOpamLibraries.setModel(createDataModel()); }); } void clearEnv() { myEnv.clear(); myOpamLibraries.setModel(createDataModel()); } @NotNull private AbstractTableModel createDataModel() { return new AbstractTableModel() { @Override public int getRowCount() { return myEnv.size(); } @Override public int getColumnCount() { return 3; } @Override public @NotNull Object getValueAt(int rowIndex, int columnIndex) { String[] columns = rowIndex < getRowCount() ? myEnv.get(rowIndex) : EMPTY_COLUMNS; return columns.length <= columnIndex ? "" : columns[columnIndex]; } }; } private VirtualFile findBinary(@Nullable VirtualFile dir) { if (dir == null) { return null; } VirtualFile child = dir.findChild("bin"); if (child != null) { return child; } return findBinary(dir.getParent()); } public TextFieldWithBrowseButton getOpamLocation() { return myOpamLocation; } public void setOpamLocation(@NotNull String opamLocation) { myOpamLocation.setText(opamLocation); } public @Nullable String getSelectedSwitch() { return (String) mySwitchSelect.getSelectedItem(); } public boolean isWsl() { return myIsWsl; } public @Nullable String getCygwinBash() { return myCygwinBash; } public boolean isOpamLocationModified(String opamLocation) { return !myOpamLocation.getText().equals(opamLocation); } public boolean isOpamSwitchModified(@NotNull String switchName) { return !switchName.equals(mySwitchSelect.getSelectedItem()); } } ================================================ FILE: src/main/java/com/reason/ide/spellcheckers/ORSpellCheckerStrategy.java ================================================ package com.reason.ide.spellcheckers; import com.intellij.lang.injection.*; import com.intellij.psi.*; import com.intellij.psi.impl.source.tree.*; import com.intellij.spellchecker.tokenizer.*; import com.reason.lang.core.psi.impl.*; import org.jetbrains.annotations.*; /** * Handling the Generic SpellcheckingStrategy * * @see SpellcheckingStrategy */ public class ORSpellCheckerStrategy extends SpellcheckingStrategy { @Override public @NotNull Tokenizer getTokenizer(@Nullable PsiElement element) { if (element == null || element instanceof PsiWhiteSpace) { // skip whitespace return EMPTY_TOKENIZER; } // optimization if (element.getClass() == LeafPsiElement.class) { return EMPTY_TOKENIZER; } // skip other languages if (element instanceof PsiLanguageInjectionHost && InjectedLanguageManager.getInstance(element.getProject()).getInjectedPsiFiles(element) != null) { return EMPTY_TOKENIZER; } // handle comments if (element instanceof PsiComment) { return myCommentTokenizer; } // literals if (element instanceof RPsiLiteralString) { return TEXT_TOKENIZER; } // Named elements if (element instanceof RPsiUpperSymbol || element instanceof RPsiLowerSymbol) { return TEXT_TOKENIZER; } return EMPTY_TOKENIZER; // skip everything else } } ================================================ FILE: src/main/java/com/reason/ide/spellcheckers/OclSpellCheckerStrategy.java ================================================ package com.reason.ide.spellcheckers; import com.intellij.psi.*; import com.intellij.spellchecker.tokenizer.*; import org.jetbrains.annotations.*; public class OclSpellCheckerStrategy extends ORSpellCheckerStrategy { @Override public @NotNull Tokenizer getTokenizer(@Nullable PsiElement element) { return super.getTokenizer(element); } } ================================================ FILE: src/main/java/com/reason/ide/spellcheckers/ResSpellCheckerStrategy.java ================================================ package com.reason.ide.spellcheckers; import com.intellij.psi.*; import com.intellij.spellchecker.tokenizer.*; import org.jetbrains.annotations.*; public class ResSpellCheckerStrategy extends ORSpellCheckerStrategy { @Override public @NotNull Tokenizer getTokenizer(@Nullable PsiElement element) { return super.getTokenizer(element); } } ================================================ FILE: src/main/java/com/reason/ide/spellcheckers/RmlSpellCheckerStrategy.java ================================================ package com.reason.ide.spellcheckers; import com.intellij.psi.*; import com.intellij.spellchecker.tokenizer.*; import org.jetbrains.annotations.*; public class RmlSpellCheckerStrategy extends ORSpellCheckerStrategy { @Override public @NotNull Tokenizer getTokenizer(@Nullable PsiElement element) { return super.getTokenizer(element); } } ================================================ FILE: src/main/java/com/reason/ide/structure/NestedFunctionsFilter.java ================================================ package com.reason.ide.structure; import com.intellij.icons.*; import com.intellij.ide.util.treeView.smartTree.*; import org.jetbrains.annotations.*; import javax.swing.*; public class NestedFunctionsFilter implements Filter { @Override public boolean isVisible(TreeElement treeNode) { if (treeNode instanceof StructureViewElement viewElement) { return viewElement.getLevel() < 2; } return true; } @Override public boolean isReverted() { return true; } @Override public @NotNull ActionPresentation getPresentation() { return new ActionPresentation() { @Override public @NotNull @Nls(capitalization = Nls.Capitalization.Sentence) String getText() { return "Show nested expressions"; } @Override public @Nls(capitalization = Nls.Capitalization.Sentence) @NotNull String getDescription() { return "Show nested expressions"; } @Override public @NotNull Icon getIcon() { return AllIcons.General.InspectionsEye; } }; } @Override public @NotNull String getName() { return "ShowNestedExpressions"; } } ================================================ FILE: src/main/java/com/reason/ide/structure/ORStructureViewModel.java ================================================ package com.reason.ide.structure; import com.intellij.ide.structureView.*; import com.intellij.ide.util.treeView.smartTree.*; import com.intellij.psi.*; import com.reason.ide.files.*; import org.jetbrains.annotations.*; public class ORStructureViewModel extends StructureViewModelBase implements com.intellij.ide.structureView.StructureViewModel.ElementInfoProvider { ORStructureViewModel(@NotNull PsiFile psiFile) { super(psiFile, new StructureViewElement(psiFile, 1)); } public @NotNull Sorter[] getSorters() { return new Sorter[]{Sorter.ALPHA_SORTER}; } @Override public @NotNull Filter[] getFilters() { return new Filter[]{new NestedFunctionsFilter(), new ShowVariableFilter()}; } @Override public boolean isAlwaysShowsPlus(StructureViewTreeElement element) { return false; } @Override public boolean isAlwaysLeaf(StructureViewTreeElement element) { return element instanceof RmlFile || element instanceof ResFile || element instanceof OclFile || element instanceof MlyFile || element instanceof MlgFile || element instanceof DuneFile; } } ================================================ FILE: src/main/java/com/reason/ide/structure/ShowVariableFilter.java ================================================ package com.reason.ide.structure; import com.intellij.icons.*; import com.intellij.ide.util.treeView.smartTree.*; import com.reason.lang.core.psi.*; import org.jetbrains.annotations.*; import javax.swing.*; public class ShowVariableFilter implements Filter { @Override public boolean isVisible(TreeElement treeNode) { if (treeNode instanceof StructureViewElement viewElement && viewElement.getElement() instanceof RPsiVar varElement) { return varElement.isFunction(); } return true; } @Override public boolean isReverted() { return true; } @Override public @NotNull ActionPresentation getPresentation() { return new ActionPresentation() { @Override public @NotNull @Nls(capitalization = Nls.Capitalization.Sentence) String getText() { return "Show variables"; } @Override public @Nls(capitalization = Nls.Capitalization.Sentence) @NotNull String getDescription() { return "Show variables"; } @Override public @NotNull Icon getIcon() { return AllIcons.General.InspectionsEye; } }; } @Override public @NotNull String getName() { return "ShowVariables"; } } ================================================ FILE: src/main/java/com/reason/ide/structure/StructureViewElement.java ================================================ package com.reason.ide.structure; import com.intellij.ide.structureView.*; import com.intellij.ide.util.treeView.smartTree.*; import com.intellij.navigation.*; import com.intellij.psi.*; import com.intellij.util.*; import com.reason.ide.*; import com.reason.ide.files.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import org.jetbrains.annotations.*; import javax.swing.*; import java.util.*; public class StructureViewElement implements StructureViewTreeElement, SortableTreeElement { private final PsiElement myElement; private final PsiElement myViewElement; private final int myLevel; private final boolean myNavigateToViewElement; StructureViewElement(@NotNull PsiElement element, int level) { this(null, element, false, level); } private StructureViewElement(@Nullable PsiElement viewElement, @NotNull PsiElement element, boolean navigateToViewElement, int level) { myViewElement = viewElement; myElement = element; myNavigateToViewElement = navigateToViewElement; myLevel = level; } @Override public @NotNull Object getValue() { return myViewElement == null ? myElement : myViewElement; } public PsiElement getElement() { return myElement; } int getLevel() { return myLevel; } @Override public void navigate(boolean requestFocus) { if (myElement instanceof NavigationItem) { NavigationItem targetElement = (NavigationItem) (myNavigateToViewElement ? myViewElement : myElement); assert targetElement != null; targetElement.navigate(requestFocus); } } @Override public boolean canNavigate() { return myElement instanceof NavigationItem && ((NavigationItem) myElement).canNavigate(); } @Override public boolean canNavigateToSource() { return myElement instanceof NavigationItem && ((NavigationItem) myElement).canNavigateToSource(); } @Override public @NotNull String getAlphaSortKey() { String name = null; PsiElement element = myViewElement == null ? myElement : myViewElement; if (element instanceof PsiNamedElement) { name = ((PsiNamedElement) element).getName(); } else if (element instanceof RPsiInclude) { name = ((RPsiInclude) element).getIncludePath(); } else if (element instanceof RPsiOpen) { name = ((RPsiOpen) element).getPath(); } return name == null ? "" : name; } @Override public @NotNull ItemPresentation getPresentation() { if (myViewElement != null) { return new ItemPresentation() { @Override public String getPresentableText() { return myViewElement.getText(); } @Override public @Nullable String getLocationString() { if (myElement instanceof RPsiLet && ((RPsiLet) myElement).isDeconstruction()) { return ""; } return myElement instanceof PsiNamedElement ? ((PsiNamedElement) myElement).getName() : ""; } @Override public @Nullable Icon getIcon(boolean unused) { return PsiIconUtil.getIconFromProviders(myElement, 0); } }; } if (myElement instanceof NavigationItem) { ItemPresentation presentation = ((NavigationItem) myElement).getPresentation(); if (presentation != null) { return presentation; } } return new ItemPresentation() { @Override public @NotNull String getPresentableText() { if (myElement instanceof PsiNamedElement namedElement) { String name = namedElement.getName(); if (name != null) { return name; } } return "Unknown presentation for element " + myElement.getText(); } @Override public @NotNull String getLocationString() { return ""; } @Override public @Nullable Icon getIcon(boolean unused) { return PsiIconUtil.getIconFromProviders(myElement, 0); } }; } @Override public @NotNull TreeElement[] getChildren() { List treeElements = null; // File if (myElement instanceof FileBase || myElement instanceof DuneFile || myElement instanceof MlgFile || myElement instanceof MllFile || myElement instanceof MlyFile) { treeElements = new ArrayList<>(); myElement.acceptChildren(new ElementVisitor(treeElements, myLevel)); if (!treeElements.isEmpty()) { return treeElements.toArray(new TreeElement[0]); } } // Lang else if (myElement instanceof RPsiInnerModule) { treeElements = buildModuleStructure((RPsiInnerModule) myElement); } else if (myElement instanceof RPsiFunctor) { treeElements = buildFunctorStructure((RPsiFunctor) myElement); } else if (myElement instanceof RPsiType) { treeElements = buildTypeStructure((RPsiType) myElement); } else if (myElement instanceof RPsiClass) { treeElements = buildClassStructure((RPsiClass) myElement); } else if (myElement instanceof RPsiLet) { treeElements = buildLetStructure((RPsiLet) myElement); } // Dune else if (myElement instanceof RPsiDuneStanza) { treeElements = buildStanzaStructure((RPsiDuneStanza) myElement); } if (treeElements != null && !treeElements.isEmpty()) { return treeElements.toArray(new TreeElement[0]); } return EMPTY_ARRAY; } private @NotNull List buildModuleStructure(@NotNull RPsiInnerModule moduleElement) { List treeElements = new ArrayList<>(); RPsiModuleSignature moduleSignature = moduleElement.getModuleSignature(); if (moduleSignature != null) { RPsiUpperSymbol nameIdentifier = moduleSignature.getNameIdentifier(); if (nameIdentifier != null) { // module type of ... treeElements.add(new StructureViewElement(moduleSignature, myLevel + 1)); } else { // sig ... end moduleSignature.acceptChildren(new ElementVisitor(treeElements, myLevel)); } } if (moduleSignature == null) { PsiElement body = moduleElement.getBody(); if (body != null) { body.acceptChildren(new ElementVisitor(treeElements, myLevel)); } } // Process body if there is a signature if (moduleSignature != null) { PsiElement rootElement = moduleElement.getBody(); if (rootElement != null) { treeElements.add(new StructureModuleImplView(rootElement)); } } return treeElements; } private @NotNull List buildFunctorStructure(@NotNull RPsiFunctor functor) { List treeElements = new ArrayList<>(); PsiElement binding = functor.getBody(); if (binding != null) { binding.acceptChildren(new ElementVisitor(treeElements, myLevel)); } return treeElements; } private @NotNull List buildTypeStructure(@NotNull RPsiType type) { List treeElements = new ArrayList<>(); PsiElement binding = type.getBinding(); if (binding != null) { RPsiRecord record = ORUtil.findImmediateFirstChildOfClass(binding, RPsiRecord.class); if (record != null) { binding.acceptChildren(new ElementVisitor(treeElements, myLevel)); } } return treeElements; } private @NotNull List buildClassStructure(@NotNull RPsiClass classElement) { List treeElements = new ArrayList<>(); PsiElement rootElement = classElement.getClassBody(); if (rootElement != null) { rootElement.acceptChildren(new ElementVisitor(treeElements, myLevel)); } return treeElements; } private @NotNull List buildStanzaStructure(@NotNull RPsiDuneStanza stanza) { List treeElements = new ArrayList<>(); PsiElement rootElement = ORUtil.findImmediateFirstChildOfClass(stanza, RPsiDuneFields.class); if (rootElement != null) { rootElement.acceptChildren(new ElementVisitor(treeElements, myLevel)); } return treeElements; } private @Nullable List buildLetStructure(@NotNull RPsiLet let) { List treeElements = null; PsiElement rootElement = let.isFunction() ? let.getFunction() : let.getBinding(); if (rootElement instanceof RPsiFunction) { rootElement = ((RPsiFunction) rootElement).getBody(); } if (rootElement != null) { treeElements = new ArrayList<>(); rootElement.acceptChildren(new ElementVisitor(treeElements, myLevel + 1)); } return treeElements; } static class ElementVisitor extends PsiElementVisitor { private final List myTreeElements; private final int myElementLevel; ElementVisitor(List elements, int elementLevel) { myTreeElements = elements; myElementLevel = elementLevel; } @Override public void visitElement(@NotNull PsiElement element) { switch (element) { case RPsiStructuredElement structuredElement when myElementLevel < 3 -> { if (structuredElement.canBeDisplayed()) { if (element instanceof RPsiLet let) { if (let.isScopeIdentifier()) { // it's a tuple! add each element of the tuple separately. for (PsiElement child : let.getScopeChildren()) { if (child instanceof RPsiLowerSymbol || child instanceof RPsiLowerName) { myTreeElements.add(new StructureViewElement(child, element, true, myElementLevel)); } } return; } PsiElement letElement = let.isFunction() ? let.getFunction() : let.getBinding(); PsiElement letBinding = letElement instanceof RPsiFunction f ? f.getBody() : letElement; PsiElement firstChild = letBinding != null ? letBinding.getFirstChild() : null; if (firstChild instanceof RPsiObject psiObject) { myTreeElements.add(new StructureObjectView(psiObject, let.getName())); return; } } myTreeElements.add(new StructureViewElement(element, myElementLevel)); } } case RPsiRecord record when myElementLevel < 2 -> { for (RPsiRecordField field : record.getFields()) { myTreeElements.add(new StructureViewElement(field, myElementLevel)); } } case RPsiScopedExpr ignored when myElementLevel < 2 -> { List children = ORUtil.findImmediateChildrenOfClass(element, RPsiStructuredElement.class); for (RPsiStructuredElement child : children) { if (child.canBeDisplayed()) { myTreeElements.add(new StructureViewElement(child, myElementLevel)); } } } default -> { } } } } static abstract class CustomStructureView implements StructureViewTreeElement, SortableTreeElement { final PsiElement myRootElement; CustomStructureView(PsiElement rootElement) { myRootElement = rootElement; } @Override public @NotNull TreeElement[] getChildren() { List treeElements = new ArrayList<>(); myRootElement.acceptChildren(new ElementVisitor(treeElements, 1)); return treeElements.toArray(new TreeElement[0]); } @Override public Object getValue() { return myRootElement; } @Override public void navigate(boolean requestFocus) { } } static class StructureObjectView extends CustomStructureView { private final String myName; StructureObjectView(PsiElement rootElement, String name) { super(rootElement); myName = name; } @Override public @NotNull ItemPresentation getPresentation() { return new ItemPresentation() { @Override public @NotNull String getPresentableText() { return myName; } @Override public @NotNull Icon getIcon(boolean b) { return ORIcons.CLASS; } }; } @Override public @NotNull String getAlphaSortKey() { return myName; } } static class StructureModuleImplView extends CustomStructureView { StructureModuleImplView(PsiElement rootElement) { super(rootElement); } @Override public @NotNull ItemPresentation getPresentation() { return new ItemPresentation() { @Override public @NotNull String getPresentableText() { return "(impl)"; } @Nullable @Override public String getLocationString() { return null; } @Nullable @Override public Icon getIcon(boolean unused) { return null; } }; } @NotNull @Override public String getAlphaSortKey() { return "zzzImplementation"; } } } ================================================ FILE: src/main/java/com/reason/ide/structure/StructureViewFactory.java ================================================ package com.reason.ide.structure; import com.intellij.ide.structureView.StructureViewBuilder; import com.intellij.ide.structureView.TreeBasedStructureViewBuilder; import com.intellij.lang.PsiStructureViewFactory; import com.intellij.openapi.editor.Editor; import com.intellij.psi.PsiFile; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; public class StructureViewFactory implements PsiStructureViewFactory { @Override public @Nullable StructureViewBuilder getStructureViewBuilder(@NotNull PsiFile psiFile) { return new TreeBasedStructureViewBuilder() { @Override public @NotNull com.intellij.ide.structureView.StructureViewModel createStructureViewModel(@Nullable Editor editor) { return new ORStructureViewModel(psiFile); } }; } } ================================================ FILE: src/main/java/com/reason/ide/template/OCamlBaseLiveTemplateContextType.java ================================================ package com.reason.ide.template; import com.intellij.codeInsight.template.*; import com.intellij.psi.*; import com.reason.ide.files.*; import org.jetbrains.annotations.*; /** * Every "*.ml" kind of file is considered to be a part of the OCaml context */ public class OCamlBaseLiveTemplateContextType extends TemplateContextType { protected OCamlBaseLiveTemplateContextType() { super("Ocaml"); } public OCamlBaseLiveTemplateContextType(String presentableName) { super(presentableName); } @Override public boolean isInContext(@NotNull TemplateActionContext templateActionContext) { PsiFile file = templateActionContext.getFile(); return file instanceof OclFile || file instanceof OclInterfaceFile; } protected boolean evaluateContext(@NotNull TemplateActionContext templateActionContext, boolean inComment) { PsiFile file = templateActionContext.getFile(); int offset = templateActionContext.getStartOffset(); PsiElement element = file.findElementAt(offset); return (element instanceof PsiComment) == inComment; } } ================================================ FILE: src/main/java/com/reason/ide/template/OCamlCodeLiveTemplateContextType.java ================================================ package com.reason.ide.template; import com.intellij.codeInsight.template.*; import org.jetbrains.annotations.*; public class OCamlCodeLiveTemplateContextType extends OCamlBaseLiveTemplateContextType { public OCamlCodeLiveTemplateContextType() { super("OCaml expression"); } @Override public boolean isInContext(@NotNull TemplateActionContext templateActionContext) { if (super.isInContext(templateActionContext)) { return evaluateContext(templateActionContext, false); } return false; } } ================================================ FILE: src/main/java/com/reason/ide/template/OCamlCommentLiveTemplateContextType.java ================================================ package com.reason.ide.template; import com.intellij.codeInsight.template.*; import org.jetbrains.annotations.*; public class OCamlCommentLiveTemplateContextType extends OCamlBaseLiveTemplateContextType { public OCamlCommentLiveTemplateContextType() { super("OCaml comment"); } @Override public boolean isInContext(@NotNull TemplateActionContext templateActionContext) { if (super.isInContext(templateActionContext)) { return evaluateContext(templateActionContext, true); } return false; } } ================================================ FILE: src/main/java/com/reason/ide/template/ResBaseLiveTemplateContextType.java ================================================ package com.reason.ide.template; import com.intellij.codeInsight.template.*; import com.intellij.psi.*; import com.reason.ide.files.*; import org.jetbrains.annotations.*; public class ResBaseLiveTemplateContextType extends TemplateContextType { public ResBaseLiveTemplateContextType() { super("Rescript"); } @Override public boolean isInContext(@NotNull TemplateActionContext context) { PsiFile file = context.getFile(); return file instanceof ResFile || file instanceof ResInterfaceFile; } } ================================================ FILE: src/main/java/com/reason/ide/template/RmlBaseLiveTemplateContextType.java ================================================ package com.reason.ide.template; import com.intellij.codeInsight.template.*; import com.intellij.psi.*; import com.reason.ide.files.*; import org.jetbrains.annotations.*; public class RmlBaseLiveTemplateContextType extends TemplateContextType { public RmlBaseLiveTemplateContextType() { super("ReasonML"); } @Override public boolean isInContext(@NotNull TemplateActionContext context) { PsiFile file = context.getFile(); return file instanceof RmlFile || file instanceof RmlInterfaceFile; } } ================================================ FILE: src/main/java/com/reason/ide/testAssistant/GotoTestDataAction.java ================================================ package com.reason.ide.testAssistant; import com.intellij.notification.*; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.editor.*; import com.intellij.openapi.project.*; import com.intellij.openapi.ui.popup.*; import com.intellij.psi.*; import com.intellij.psi.search.*; import com.intellij.ui.awt.*; import com.reason.ide.files.*; import com.reason.ide.search.*; import com.reason.ide.search.index.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; import java.util.*; import static com.intellij.notification.NotificationType.*; import static com.intellij.openapi.application.ApplicationManager.*; public class GotoTestDataAction extends AnAction { @Override public void actionPerformed(@NotNull AnActionEvent e) { Project project = e.getProject(); if (project != null) { DataContext dataContext = e.getDataContext(); List fileNames = findRelatedFiles(project, dataContext); if (fileNames.isEmpty()) { Notifications.Bus.notify( new ORNotification("testdata", "Found no testdata files", "Cannot find related files", INFORMATION), project); return; } Editor editor = e.getData(CommonDataKeys.EDITOR); JBPopupFactory popupFactory = JBPopupFactory.getInstance(); RelativePoint point = editor == null ? popupFactory.guessBestPopupLocation(dataContext) : popupFactory.guessBestPopupLocation(editor); TestDataNavigationHandler.navigate(point, fileNames, project); } } private @NotNull List findRelatedFiles(@NotNull Project project, @NotNull DataContext context) { PsiFile file = context.getData(CommonDataKeys.PSI_FILE); if (file instanceof FileBase) { FileModuleIndexService topModulesIndex = getApplication().getService(FileModuleIndexService.class); GlobalSearchScope scope = GlobalSearchScope.projectScope(project); FileModuleData relatedData; String[] tokens = splitModuleName(((FileBase) file).getModuleName()); if (tokens.length == 1) { List relatedModuleData = topModulesIndex.getTopModuleData(tokens[0] + "_test", scope); relatedData = relatedModuleData.isEmpty() ? null : relatedModuleData.iterator().next(); if (relatedData == null) { relatedModuleData = topModulesIndex.getTopModuleData(tokens[0] + "_spec", scope); relatedData = relatedModuleData.isEmpty() ? null : relatedModuleData.iterator().next(); } } else { List relatedModuleData = topModulesIndex.getTopModuleData(tokens[0], scope); relatedData = relatedModuleData.isEmpty() ? null : relatedModuleData.iterator().next(); } if (relatedData != null) { return Collections.singletonList(relatedData.getPath()); } } return Collections.emptyList(); } private @NotNull String[] splitModuleName(@NotNull String moduleName) { int underscoreIndex = moduleName.lastIndexOf("_"); return 0 < underscoreIndex ? new String[]{moduleName.substring(0, underscoreIndex), moduleName.substring(underscoreIndex + 1)} : new String[]{moduleName}; } } ================================================ FILE: src/main/java/com/reason/ide/testAssistant/TestDataNavigationHandler.java ================================================ package com.reason.ide.testAssistant; import com.intellij.codeInsight.daemon.*; import com.intellij.openapi.fileEditor.*; import com.intellij.openapi.project.*; import com.intellij.openapi.vfs.*; import com.intellij.ui.awt.*; import com.reason.lang.core.psi.impl.*; import org.jetbrains.annotations.*; import java.awt.event.*; import java.util.*; public class TestDataNavigationHandler implements GutterIconNavigationHandler { static void navigate(@NotNull RelativePoint point, @NotNull List paths, @NotNull Project project) { if (!paths.isEmpty()) { VirtualFile virtualFile = LocalFileSystem.getInstance().refreshAndFindFileByPath(paths.get(0)); if (virtualFile != null) { new OpenFileDescriptor(project, virtualFile).navigate(true); } } } @Override public void navigate(MouseEvent e, RPsiFunction element) { } } ================================================ FILE: src/main/java/com/reason/lang/CommonPsiParser.java ================================================ package com.reason.lang; import com.intellij.lang.*; import com.intellij.psi.tree.*; import org.jetbrains.annotations.*; public abstract class CommonPsiParser implements PsiParser { public static final int PARSE_MAX_TIME = 1_000; // 1s protected final boolean myIsSafe; protected CommonPsiParser(boolean isSafe) { myIsSafe = isSafe; } @Override public @NotNull ASTNode parse(@NotNull IElementType elementType, @NotNull PsiBuilder builder) { //builder.setDebugMode(false); // RELEASE: debug mode is false PsiBuilder.Marker r = builder.mark(); ORParser state = getORParser(builder); state.parse(); // if we have a scope at last position in a file, without SEMI, we need to handle it here if (!state.empty()) { state.clear(); } // end stream if (!builder.eof()) { builder.mark().error("Unexpected token"); while (!builder.eof()) { builder.advanceLexer(); } } r.done(elementType); return builder.getTreeBuilt(); } protected abstract ORParser getORParser(@NotNull PsiBuilder builder); } ================================================ FILE: src/main/java/com/reason/lang/Marker.java ================================================ package com.reason.lang; import com.intellij.lang.*; import com.intellij.psi.tree.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; // Wrapper around the PsiBuilder.Marker to keep its status public class Marker { enum Status { unset, atom, hold, done, dropped } private final PsiBuilder myBuilder; private final PsiBuilder.Marker myMark; private final int myOffset; private ORCompositeType myCompositeElementType; private IElementType myScopeElementType; private IElementType myAtomType; private Status myStatus = Status.unset; private Marker(@NotNull PsiBuilder builder, @NotNull PsiBuilder.Marker mark, @NotNull ORCompositeType compositeElementType, @Nullable IElementType scopeTokenElementType) { myBuilder = builder; myMark = mark; myOffset = builder.getCurrentOffset(); myCompositeElementType = compositeElementType; myScopeElementType = scopeTokenElementType; } public static @NotNull Marker mark(@NotNull PsiBuilder builder, @NotNull ORCompositeType compositeElementType) { return new Marker(builder, builder.mark(), compositeElementType, null); } public static @NotNull Marker atom(@NotNull PsiBuilder builder, @NotNull ORCompositeType atomElementType) { Marker marker = new Marker(builder, builder.mark(), atomElementType, null); marker.myAtomType = builder.getTokenType(); marker.myStatus = Status.atom; return marker; } public static @NotNull Marker precede(@NotNull Marker mark, @NotNull ORCompositeType compositeType) { PsiBuilder.Marker precede = mark.myMark.precede(); return new Marker(mark.myBuilder, precede, compositeType, null); } public static Marker duplicate(Marker marker) { Marker newMarker = new Marker(marker.myBuilder, marker.myBuilder.mark(), marker.myCompositeElementType, marker.myScopeElementType); newMarker.myStatus = marker.myStatus; if (marker.isDone()) { newMarker.done(); } else if (marker.isDropped()) { newMarker.drop(); } return newMarker; } public int getLength() { return myBuilder.getCurrentOffset() - myOffset; } public boolean isEmpty() { return getLength() == 0; } public void end() { done(); myScopeElementType = null; } private void done() { if (myCompositeElementType instanceof IElementType) { if (myStatus == Status.unset) { if (myCompositeElementType.toString().startsWith("D_")) { myMark.drop(); myStatus = Status.dropped; } else { myMark.done((IElementType) myCompositeElementType); myStatus = Status.done; } } else if (myStatus == Status.atom && myAtomType != null) { myMark.collapse(myAtomType); myStatus = Status.done; } } if (myStatus != Status.done && myStatus != Status.dropped) { myMark.drop(); myStatus = Status.dropped; } } public void drop() { if (myStatus == Status.unset) { myMark.drop(); myStatus = Status.dropped; } } public void hold() { if (myStatus == Status.unset) { myStatus = Status.hold; } } public void updateCompositeType(@NotNull ORCompositeType compositeType) { myCompositeElementType = compositeType; } boolean isCompositeIn(@NotNull ORCompositeType... compositeType) { for (ORCompositeType composite : compositeType) { if (myCompositeElementType == composite) { return true; } } return false; } void setScopeType(@NotNull IElementType scopeType) { myScopeElementType = scopeType; } public void updateScope(@Nullable IElementType scopeToken) { myScopeElementType = scopeToken; } public boolean isScopeToken(ORTokenElementType scopeType) { return myScopeElementType == scopeType; } public @Nullable IElementType getScopeType() { return myScopeElementType; } public boolean hasScope() { return myScopeElementType != null; } public void rollbackTo() { if (myMark != null) { myMark.rollbackTo(); } } public boolean isCompositeType(@Nullable ORCompositeType elementType) { return myCompositeElementType == elementType; } public @NotNull ORCompositeType getCompositeType() { return myCompositeElementType; } public boolean isUnset() { return myStatus == Status.unset; } public boolean isDone() { return myStatus == Status.done; } public boolean isDropped() { return myStatus == Status.dropped; } public boolean isHold() { return myStatus == Status.hold; } public void resetStatus() { if (myStatus != Status.dropped && myStatus != Status.done) { myStatus = Status.unset; } } } ================================================ FILE: src/main/java/com/reason/lang/ModuleHelper.java ================================================ package com.reason.lang; import com.intellij.psi.*; import com.intellij.psi.util.*; import com.reason.lang.core.psi.impl.RPsiAnnotation; import com.reason.lang.core.psi.*; import org.jetbrains.annotations.*; import java.util.*; public class ModuleHelper { private ModuleHelper() { } public static boolean isComponent(@Nullable PsiElement element) { if (element == null) { return false; } PsiElement componentDef = null; RPsiLet makeDef = null; // JSX 3 // Try to find a React.component attribute for (RPsiAnnotation annotation : PsiTreeUtil.getStubChildrenOfTypeAsList(element, RPsiAnnotation.class)) { if ("@react.component".equals(annotation.getName())) { return true; } } // JSX 2 // Try to find if it's a proxy to a React class List externals = PsiTreeUtil.getStubChildrenOfTypeAsList(element, RPsiExternal.class); for (RPsiExternal external : externals) { RPsiSignature signature = external.getSignature(); String signatureText = signature == null ? null : signature.asText(ORLanguageProperties.cast(element.getLanguage())); if ("ReasonReact.reactClass".equals(signatureText)) { componentDef = external; break; } } // Try to find a make function and a component (if not a proxy) functions List expressions = PsiTreeUtil.getStubChildrenOfTypeAsList(element, RPsiLet.class); for (RPsiLet let : expressions) { if (componentDef == null && "component".equals(let.getName())) { componentDef = let; } else if (makeDef == null && "make".equals(let.getName())) { makeDef = let; } else if (componentDef != null && makeDef != null) { break; } } return componentDef != null && makeDef != null; } } ================================================ FILE: src/main/java/com/reason/lang/ORLanguageParser.java ================================================ package com.reason.lang; import com.intellij.lang.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; public abstract class ORLanguageParser extends ORParser { protected ORLanguageParser(@NotNull T types, @NotNull PsiBuilder builder, boolean verbose) { super(types, builder, verbose); } public @NotNull ORLanguageParser markParenthesisScope(boolean isDummy) { if (getTokenType() == myTypes.LPAREN) { if (isDummy) { markDummyScope(myTypes.C_SCOPED_EXPR, myTypes.LPAREN); } else { markScope(myTypes.C_SCOPED_EXPR, myTypes.LPAREN); } advance().markHolder(myTypes.H_COLLECTION_ITEM); } return this; } protected @Nullable WhitespaceSkippedCallback endJsxPropertyIfWhitespace() { return (type, start, end) -> { if (is(myTypes.C_TAG_PROPERTY) || (strictlyIn(myTypes.C_TAG_PROP_VALUE))) { if (isFound(myTypes.C_TAG_PROP_VALUE)) { popEndUntilFoundIndex().popEnd(); } popEnd(); setWhitespaceSkippedCallback(null); } }; } } ================================================ FILE: src/main/java/com/reason/lang/ORLanguageProperties.java ================================================ package com.reason.lang; import com.intellij.lang.*; import org.jetbrains.annotations.*; public interface ORLanguageProperties { static @Nullable ORLanguageProperties cast(@Nullable Language language) { return (language instanceof ORLanguageProperties) ? (ORLanguageProperties) language : null; } @NotNull String getParameterSeparator(); @NotNull String getFunctionSeparator(); @NotNull String getTemplateStart(); @NotNull String getTemplateEnd(); } ================================================ FILE: src/main/java/com/reason/lang/ORParser.java ================================================ package com.reason.lang; import com.intellij.lang.*; import com.intellij.psi.tree.*; import com.intellij.util.*; import com.reason.lang.core.type.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; import java.util.*; public abstract class ORParser { protected static final Log LOG = Log.create("parser"); protected final T myTypes; protected final boolean myVerbose; protected final PsiBuilder myBuilder; protected final LinkedList myMarkers = new LinkedList<>(); public boolean dontMove = false; private int myIndex; // found index when using in(..)/inAny(..) functions // rollback protection protected final boolean myIsSafe; private int myLatestRollbackIndex = -1; private int myRollbackCount = 0; protected ORParser(@NotNull T types, @NotNull PsiBuilder builder, boolean isSafe) { myTypes = types; myBuilder = builder; myIsSafe = isSafe; myVerbose = !isSafe; } public abstract void parse(); public @Nullable IElementType previousElementType(int step) { int pos = -1; int found = 0; IElementType elementType = myBuilder.rawLookup(pos); if (elementType != null && elementType != myTypes.WHITE_SPACE && elementType != myTypes.SINGLE_COMMENT && elementType != myTypes.MULTI_COMMENT) { found++; } while (elementType != null && found != step) { pos--; elementType = myBuilder.rawLookup(pos); if (elementType != null && elementType != myTypes.WHITE_SPACE && elementType != myTypes.SINGLE_COMMENT && elementType != myTypes.MULTI_COMMENT) { found++; } } return elementType; } public @Nullable Marker popEndUntilScope() { Marker latestKnownScope = null; if (!myMarkers.isEmpty()) { latestKnownScope = myMarkers.peek(); Marker marker = latestKnownScope; while (marker != null && !marker.hasScope()) { marker = pop(); if (marker != null) { marker.end(); latestKnownScope = marker; } marker = getLatestMarker(); } } return latestKnownScope; } public @Nullable Marker popEndUntilScopeToken(@NotNull IElementType scopeElementType) { Marker scope = null; if (!myMarkers.isEmpty()) { scope = myMarkers.peek(); while (scope != null && scope.getScopeType() != scopeElementType) { popEnd(); scope = getLatestMarker(); } } return scope; } public @Nullable Marker getLatestMarker() { return myMarkers.isEmpty() ? null : myMarkers.peek(); } public @Nullable Marker getCurrentMarker() { for (Marker marker : myMarkers) { if (!marker.isHold()) { return marker; } } return null; } public @Nullable Marker getActiveMarker() { // latest unset, not hold marker for (Marker marker : myMarkers) { if (marker.isUnset()) { return marker; } } return null; } public boolean is(@Nullable ORCompositeType composite) { return !myMarkers.isEmpty() && myMarkers.peek().isCompositeType(composite); } public boolean isCurrent(@Nullable ORCompositeType composite) { // skip done/drop/hold elements for (Marker marker : myMarkers) { if (marker.isUnset() && !marker.isHold()) { return marker.isCompositeType(composite); } } return false; } public boolean isDone(@Nullable ORCompositeType composite) { Marker marker = myMarkers.isEmpty() ? null : myMarkers.peek(); return marker != null && marker.isDone() && marker.isCompositeType(composite); } public boolean isRawParent(ORCompositeType composite) { boolean found = false; if (myMarkers.size() >= 2) { // current index is 0 found = myMarkers.get(1).isCompositeType(composite); } myIndex = found ? 1 : -1; return found; } public boolean isParent(ORCompositeType expectedType) { boolean found = false; int markersCount = myMarkers.size(); // find start int startIndex = 0; while (startIndex < markersCount && !myMarkers.get(startIndex).isUnset()) { startIndex++; } // find parent int parentIndex = startIndex + 1; while (parentIndex < markersCount && !found) { if (myMarkers.get(parentIndex).isUnset()) { found = true; } else { parentIndex++; } } // parent found, try type if (found && myMarkers.get(parentIndex).isCompositeType(expectedType)) { myIndex = parentIndex; return true; } myIndex = -1; return false; } public boolean isGrandParent(ORCompositeType expectedType) { boolean foundParent = false; boolean foundGrandParent = false; int markersCount = myMarkers.size(); // find start int startIndex = 0; while (startIndex < markersCount && !myMarkers.get(startIndex).isUnset()) { startIndex++; } // find parent int parentIndex = startIndex + 1; while (parentIndex < markersCount && !foundParent) { if (myMarkers.get(parentIndex).isUnset()) { foundParent = true; } else { parentIndex++; } } // if parent found, find grand parent int grandParentIndex = parentIndex + 1; if (foundParent) { while (grandParentIndex < markersCount && !foundGrandParent) { if (myMarkers.get(grandParentIndex).isUnset()) { foundGrandParent = true; } else { grandParentIndex++; } } } // grand parent found, try type if (foundGrandParent && myMarkers.get(grandParentIndex).isCompositeType(expectedType)) { myIndex = grandParentIndex; return true; } myIndex = -1; return false; } public boolean isPrevious(ORCompositeType expectedComposite, int index) { int size = myMarkers.size() - 1; if (index >= 0 && index < size) { return myMarkers.get(index + 1).isCompositeType(expectedComposite); } return false; } public boolean isRawGrandParent(@Nullable ORCompositeType composite) { if (myMarkers.size() >= 3) { return myMarkers.get(2).isCompositeType(composite); } return false; } public boolean in(ORCompositeType composite) { return in(composite, null, myMarkers.size(), false); } public boolean in(ORCompositeType composite, @Nullable ORCompositeType excluded) { return in(composite, excluded, myMarkers.size(), false); } public boolean in(ORCompositeType composite, @Nullable ORCompositeType excluded, int maxDepth, boolean useScope) { int size = myMarkers.size(); int stop = Math.min(size, maxDepth); for (int i = 0; i < stop; i++) { Marker marker = myMarkers.get(i); if (marker.isUnset() || marker.isHold()) { // not dropped or done if (marker.isCompositeType(excluded)) { myIndex = -1; return false; } if ((useScope && marker.hasScope()) || marker.isCompositeType(composite)) { myIndex = i; return true; } } } myIndex = -1; return false; } public boolean strictlyIn(ORCompositeType composite) { for (int i = 0; i < myMarkers.size(); i++) { Marker marker = myMarkers.get(i); if (marker.isUnset()) { // not dropped or done if (marker.isCompositeType(composite)) { myIndex = i; return true; } if (marker.hasScope()) { myIndex = -1; return false; } } } myIndex = -1; return false; } public boolean strictlyInAny(@NotNull ORCompositeType... composite) { for (int i = 0; i < myMarkers.size(); i++) { Marker marker = myMarkers.get(i); if (marker.isUnset() || marker.isHold()) { // not dropped or done if (marker.isCompositeIn(composite)) { myIndex = i; return true; } if (marker.hasScope()) { myIndex = -1; return false; } } } myIndex = -1; return false; } public @Nullable Marker find(int index) { return (0 <= index && index < myMarkers.size()) ? myMarkers.get(index) : null; } public int latestIndexOfCompositeAtMost(int maxIndex, @NotNull ORCompositeType... composites) { int max = Math.min(maxIndex, myMarkers.size()); for (int i = 0; i < max; i++) { Marker markerScope = myMarkers.get(i); if (markerScope.isCompositeIn(composites)) { return i; } } return -1; } public boolean inAny(ORCompositeType... composite) { int stop = myMarkers.size(); for (int i = 0; i < stop; i++) { Marker markerScope = myMarkers.get(i); if (markerScope.isCompositeIn(composite)) { myIndex = i; return true; } } myIndex = -1; return false; } public boolean inScopeOrAny(ORCompositeType... composite) { int stop = myMarkers.size(); for (int i = 0; i < stop; i++) { Marker markerScope = myMarkers.get(i); if (markerScope.hasScope() || markerScope.isCompositeIn(composite)) { myIndex = i; return true; } } myIndex = -1; return false; } public boolean isScope(@Nullable ORTokenElementType scopeType) { Marker latest = getLatestMarker(); return latest != null && latest.isScopeToken(scopeType); } public boolean isCurrentScope(@Nullable ORTokenElementType scopeType) { Marker latest = getCurrentMarker(); return latest != null && latest.isScopeToken(scopeType); } public boolean isScopeAtIndex(int pos, @Nullable ORTokenElementType scopeType) { Marker marker = find(pos); return marker != null && marker.isScopeToken(scopeType); } protected boolean isFoundScope(@Nullable ORTokenElementType expectedScope) { Marker found = find(myIndex); return found != null && found.isScopeToken(expectedScope); } public @NotNull ORParser mark(@NotNull ORCompositeType composite) { Marker mark = Marker.mark(myBuilder, composite); myMarkers.push(mark); return this; } public @NotNull ORParser markScope(@NotNull ORCompositeType composite, @Nullable IElementType scope) { mark(composite).updateScopeToken(scope); return this; } public @NotNull ORParser markDummyScope(@NotNull ORCompositeType composite, @NotNull ORTokenElementType scope) { markScope(composite, scope); Marker latest = myMarkers.peek(); if (latest != null) { latest.hold(); } return this; } public ORParser markHolder(@NotNull ORCompositeType compositeType) { Marker mark = Marker.mark(myBuilder, compositeType); mark.hold(); myMarkers.push(mark); return this; } protected ORParser markHolderBefore(int pos, @NotNull ORCompositeType compositeType) { markBefore(pos, compositeType); Marker mark = myMarkers.get(pos + 1); assert mark != null; mark.hold(); return this; } boolean empty() { return myMarkers.isEmpty(); } public @Nullable Marker pop() { return myMarkers.isEmpty() ? null : myMarkers.pop(); } void clear() { Marker scope = pop(); while (scope != null) { scope.end(); scope = pop(); } } public ORParser popIfHold() { if (isHold()) { popEnd(); } return this; } public @NotNull ORParser popEnd() { Marker scope = pop(); if (scope != null && !scope.isDone()) { scope.end(); } return this; } public void popEndUntilOneOf(@NotNull ORCompositeType... composites) { if (!myMarkers.isEmpty()) { Marker marker = myMarkers.peek(); while (marker != null && !ArrayUtil.contains(marker.getCompositeType(), composites)) { popEnd(); marker = getLatestMarker(); } } } @Nullable public Marker popEndUntilOneOfElementType(@NotNull ORTokenElementType... scopeElementTypes) { Marker marker = null; if (!myMarkers.isEmpty()) { marker = myMarkers.peek(); while (marker != null && !ArrayUtil.contains(marker.getScopeType(), scopeElementTypes)) { popEnd(); marker = getLatestMarker(); } } return marker; } @NotNull public ORParser popEndUntil(@NotNull ORCompositeType composite) { Marker marker = getLatestMarker(); while (marker != null && !marker.isCompositeType(composite)) { popEnd(); marker = getLatestMarker(); } return this; } public ORParser popEndUntilIndex(int index) { if (0 <= index && index <= myMarkers.size()) { for (int i = 0; i < index; i++) { popEnd(); } } return this; } public ORParser popEndUntilFoundIndex() { int index = myIndex; myIndex = -1; return popEndUntilIndex(index); } public boolean rawHasScope() { Marker marker = getLatestMarker(); return marker != null && marker.hasScope(); } protected boolean currentHasScope() { Marker current = getCurrentMarker(); return current != null && current.hasScope(); } public @NotNull ORParser updateLatestComposite(@NotNull ORCompositeType compositeElementType) { Marker marker = getLatestMarker(); if (marker != null && !marker.isDropped() && !marker.isDone()) { marker.updateCompositeType(compositeElementType); marker.resetStatus(); } return this; } public @NotNull ORParser updateComposite(@NotNull ORCompositeType compositeElementType) { Marker marker = getCurrentMarker(); if (marker != null && !marker.isDropped() && !marker.isDone()) { marker.updateCompositeType(compositeElementType); marker.resetStatus(); } return this; } public @NotNull ORParser updateToHolder() { Marker marker = getCurrentMarker(); if (marker != null) { marker.hold(); } return this; } public @NotNull ORParser advance() { myBuilder.advanceLexer(); dontMove = true; return this; } public @NotNull ORParser updateScopeToken(@Nullable IElementType token) { if (token != null) { Marker marker = getLatestMarker(); if (marker != null) { marker.setScopeType(token); } } return this; } @SuppressWarnings("UnusedReturnValue") public @NotNull ORParser wrapWith(@NotNull ORCompositeType compositeType) { mark(compositeType).advance(); myMarkers.getFirst().end(); return this; } public @NotNull ORParser wrapAtom(@NotNull ORCompositeType atomCompositeType) { Marker mark = Marker.atom(myBuilder, atomCompositeType); myMarkers.push(mark); advance(); mark.end(); return this; } @SuppressWarnings("unused") public void error(@NotNull String message) { //myBuilder.error("Plugin error! " + message); // RELEASE: comment that line } public @NotNull ORParser remapCurrentToken(ORTokenElementType elementType) { myBuilder.remapCurrentToken(elementType); return this; } public @NotNull ORParser setWhitespaceSkippedCallback(@Nullable WhitespaceSkippedCallback callback) { myBuilder.setWhitespaceSkippedCallback(callback); return this; } public @Nullable String getTokenText() { return myBuilder.getTokenText(); } public @Nullable IElementType rawLookup(int steps) { return myBuilder.rawLookup(steps); } public @Nullable IElementType lookAhead(int steps) { return myBuilder.lookAhead(steps); } public @Nullable IElementType getTokenType() { return myBuilder.getTokenType(); } public boolean isRoot() { return myMarkers.isEmpty(); } public boolean isHold() { Marker marker = getLatestMarker(); return marker != null && marker.isHold(); } public ORParser duplicateAtIndex(int pos) { if (0 <= pos) { Marker foundMarker = myMarkers.get(pos); Marker marker = Marker.precede(foundMarker, foundMarker.getCompositeType()); marker.updateScope(foundMarker.getScopeType()); myMarkers.add(pos + 1, marker); } return this; } public ORParser markBefore(int pos, ORCompositeType compositeType) { if (0 <= pos) { Marker scope = myMarkers.get(pos); Marker precedeScope = Marker.precede(scope, compositeType); myMarkers.add(pos + 1, precedeScope); } return this; } public @Nullable Marker getPrevious() { if (myMarkers.size() > 1) { return myMarkers.get(1); } return null; } //@SuppressWarnings("UnusedReturnValue") public @NotNull ORParser rollbackToIndexAndDrop(int pos) { for (int i = 0; i < pos; i++) { myMarkers.pop(); } myMarkers.pop().rollbackTo(); if (myVerbose) { System.out.println("rollbacked to: " + myBuilder.getCurrentOffset() + ", " + myBuilder.getTokenType() + "(" + myBuilder.getTokenText() + ")"); } dontMove = true; return this; } public @NotNull ORParser rollbackToIndex(int index) { if (myRollbackCount > 10) { //myBuilder.error("Plugin error! Too many rollbacks"); // RELEASE: comment that line return this; } for (int i = 0; i < index; i++) { myMarkers.pop(); } Marker foundMarker = myMarkers.pop(); foundMarker.rollbackTo(); // int rollbackIndex = myBuilder.getCurrentOffset(); if (rollbackIndex == myLatestRollbackIndex) { myRollbackCount++; } else { myLatestRollbackIndex = rollbackIndex; myRollbackCount = 1; } if (myVerbose) { System.out.println("rollback to index [#" + myRollbackCount + "]: " + rollbackIndex + ", " + myBuilder.getTokenType() + "(" + myBuilder.getTokenText() + ")"); } Marker marker = Marker.duplicate(foundMarker); myMarkers.push(marker); if (marker.hasScope()) { advance(); } dontMove = true; return this; } public @NotNull ORParser rollbackToFoundIndex() { rollbackToIndex(myIndex); myIndex = -1; return this; } public int getIndex() { return myIndex; } public boolean isFound(@Nullable ORCompositeType expectedType) { Marker scope = find(myIndex); return scope != null && scope.isCompositeType(expectedType); } public ORParser updateScopeToken(@Nullable Marker scope, @Nullable ORTokenElementType scopeToken) { if (scope != null) { scope.updateScope(scopeToken); } return this; } public boolean isAtIndex(int index, @NotNull ORCompositeType expectedComposite) { Marker marker = find(index); return marker != null && marker.isCompositeType(expectedComposite); } public @NotNull ORParser end() { Marker marker = getLatestMarker(); if (marker != null) { marker.updateScope(null); marker.end(); } return this; } public ORParser updateCompositeAt(int pos, @NotNull ORCompositeType compositeType) { if (0 <= pos && pos < myMarkers.size()) { Marker marker = myMarkers.get(pos); marker.updateCompositeType(compositeType); if (marker.isHold()) { marker.resetStatus(); } } return this; } public ORParser dropLatest() { Marker marker = getLatestMarker(); if (marker != null) { marker.drop(); } return this; } } ================================================ FILE: src/main/java/com/reason/lang/core/LiteralStringManipulator.java ================================================ package com.reason.lang.core; import com.intellij.openapi.util.*; import com.intellij.openapi.util.text.*; import com.intellij.psi.*; import com.intellij.util.*; import com.reason.lang.core.psi.impl.RPsiLiteralString; import org.jetbrains.annotations.*; // com.intellij.psi.impl.source.resolve.reference.impl.manipulators.StringLiteralManipulator public class LiteralStringManipulator extends AbstractElementManipulator { @Override public @Nullable RPsiLiteralString handleContentChange(@NotNull RPsiLiteralString element, @NotNull TextRange range, String newContent) throws IncorrectOperationException { String oldText = element.getText(); if (oldText.startsWith("\"")) { newContent = StringUtil.escapeStringCharacters(newContent); } else if (oldText.startsWith("'") && newContent.length() <= 1) { newContent = newContent.length() == 1 && newContent.charAt(0) == '\'' ? "\\'" : newContent; } else { throw new IncorrectOperationException("cannot handle content change for: " + oldText + ", expr: " + element); } String newText = oldText.substring(0, range.getStartOffset()) + newContent + oldText.substring(range.getEndOffset()); final PsiExpression newExpr = JavaPsiFacade.getElementFactory(element.getProject()).createExpressionFromText(newText, null); return (RPsiLiteralString) element.replace(newExpr); } @Override public @NotNull TextRange getRangeInElement(@NotNull RPsiLiteralString element) { return getValueRange(element); } @NotNull public static TextRange getValueRange(@NotNull RPsiLiteralString expression) { return new TextRange(1, Math.max(1, expression.getTextLength() - 1)); } } ================================================ FILE: src/main/java/com/reason/lang/core/ORCodeFactory.java ================================================ package com.reason.lang.core; import com.intellij.lang.*; import com.intellij.openapi.project.*; import com.intellij.psi.*; import com.reason.ide.files.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import com.reason.lang.reason.*; import org.jetbrains.annotations.*; public class ORCodeFactory { private ORCodeFactory() { } @Nullable public static RPsiUpperSymbol createModuleName(@NotNull Project project, @NotNull String name) { FileBase file = createFileFromText(project, RmlLanguage.INSTANCE, "module " + name + " = {};"); RPsiInnerModule module = ORUtil.findImmediateFirstChildOfClass(file, RPsiInnerModule.class); return ORUtil.findImmediateFirstChildOfClass(module, RPsiUpperSymbol.class); } @Nullable public static RPsiLowerSymbol createLetName(@NotNull Project project, @NotNull String name) { FileBase file = createFileFromText(project, RmlLanguage.INSTANCE, "let " + name + " = 1;"); RPsiLet let = ORUtil.findImmediateFirstChildOfClass(file, RPsiLet.class); return ORUtil.findImmediateFirstChildOfClass(let, RPsiLowerSymbol.class); } @Nullable public static RPsiLowerSymbol createTypeName(@NotNull Project project, @NotNull String name) { FileBase file = createFileFromText(project, RmlLanguage.INSTANCE, "type " + name + ";"); RPsiType type = ORUtil.findImmediateFirstChildOfClass(file, RPsiType.class); return ORUtil.findImmediateFirstChildOfClass(type, RPsiLowerSymbol.class); } @Nullable public static PsiElement createExpression(@NotNull Project project, @NotNull Language language, @NotNull String expression) { FileBase file = createFileFromText(project, language, expression); return file.getFirstChild(); } @NotNull public static FileBase createFileFromText(@NotNull Project project, @NotNull Language language, @NotNull String text) { return (FileBase) PsiFileFactory.getInstance(project).createFileFromText("Dummy", language, text); } } ================================================ FILE: src/main/java/com/reason/lang/core/ORUtil.java ================================================ package com.reason.lang.core; import com.intellij.lang.*; import com.intellij.navigation.*; import com.intellij.openapi.util.io.*; import com.intellij.psi.*; import com.intellij.psi.tree.*; import com.intellij.psi.util.*; import com.reason.ide.files.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import com.reason.lang.core.type.*; import com.reason.lang.ocaml.*; import com.reason.lang.reason.*; import com.reason.lang.rescript.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; import java.util.*; public class ORUtil { private static final String[] EMPTY_PATH = new String[0]; private ORUtil() { } @NotNull public static String moduleNameToFileName(@NotNull String name) { if (name.isEmpty()) { return name; } return name.substring(0, 1).toLowerCase(Locale.getDefault()) + name.substring(1); } @NotNull public static String fileNameToModuleName(@NotNull String filename) { String nameWithoutExtension = FileUtilRt.getNameWithoutExtension(filename); if (nameWithoutExtension.isEmpty()) { return ""; } return nameWithoutExtension.substring(0, 1).toUpperCase(Locale.getDefault()) + nameWithoutExtension.substring(1); } @Nullable public static PsiElement prevSibling(@NotNull PsiElement element) { // previous sibling without considering whitespace PsiElement prevSibling = element.getPrevSibling(); while (prevSibling != null) { ASTNode prevNode = prevSibling.getNode(); if (prevNode == null || prevNode.getElementType() != TokenType.WHITE_SPACE) { break; } prevSibling = prevSibling.getPrevSibling(); } return prevSibling; } @Nullable public static PsiElement prevPrevSibling(@NotNull PsiElement element) { PsiElement prevElement = prevSibling(element); return prevElement == null ? null : prevSibling(prevElement); } @NotNull public static List prevAnnotations(@NotNull PsiElement element) { List annotations = new ArrayList<>(); PsiElement prevSibling = prevSibling(element); while (prevSibling instanceof RPsiAnnotation) { annotations.add((RPsiAnnotation) prevSibling); prevSibling = prevSibling(prevSibling); } return annotations; } public static @Nullable PsiElement nextSiblingWithTokenType(@NotNull PsiElement root, @NotNull IElementType elementType) { PsiElement found = null; PsiElement sibling = root.getNextSibling(); while (sibling != null) { if (sibling.getNode().getElementType() == elementType) { found = sibling; sibling = null; } else { sibling = sibling.getNextSibling(); } } return found; } public static @NotNull String getTextUntilTokenType(@NotNull PsiElement root, @Nullable IElementType elementType) { StringBuilder text = new StringBuilder(root.getText()); PsiElement sibling = root.getNextSibling(); while (sibling != null) { if (sibling.getNode().getElementType() == elementType) { sibling = null; } else { text.append(sibling.getText()); sibling = sibling.getNextSibling(); } } return text.toString().trim(); } public static @NotNull String getTextUntilClass(@NotNull PsiElement root, @Nullable Class clazz) { StringBuilder text = new StringBuilder(root.getText()); PsiElement sibling = root.getNextSibling(); while (sibling != null) { if (clazz != null && sibling.getClass().isAssignableFrom(clazz)) { sibling = null; } else { text.append(sibling.getText()); sibling = sibling.getNextSibling(); } } return text.toString().trim(); } /* x M1.M2.x */ public static @NotNull String getLongIdent(@Nullable PsiElement root) { StringBuilder text = new StringBuilder(root == null ? "" : root.getText()); ORLangTypes types = root == null ? null : ORUtil.getTypes(root.getLanguage()); PsiElement sibling = root == null ? null : root.getNextSibling(); while (sibling != null) { IElementType type = sibling.getNode().getElementType(); if (type == types.DOT || type == types.UIDENT || type == types.LIDENT || type == types.A_MODULE_NAME || type == types.A_UPPER_TAG_NAME || type == types.A_LOWER_TAG_NAME) { text.append(sibling.getText()); sibling = PsiTreeUtil.nextLeaf(sibling); } else { sibling = null; } } return text.toString(); } @NotNull public static ASTNode nextSiblingNode(@NotNull ASTNode node) { ASTNode nextSibling = node.getTreeNext(); while (nextSibling.getElementType() == TokenType.WHITE_SPACE) { nextSibling = nextSibling.getTreeNext(); } return nextSibling; } public static @Nullable PsiElement nextSibling(@Nullable PsiElement element) { if (element == null) { return null; } PsiElement nextSibling = element.getNextSibling(); while ((nextSibling instanceof PsiWhiteSpace)) { nextSibling = nextSibling.getNextSibling(); } return nextSibling; } public static @NotNull List findImmediateChildrenOfClass(@Nullable PsiElement element, @NotNull Class clazz) { if (element == null) { return Collections.emptyList(); } return PsiTreeUtil.getStubChildrenOfTypeAsList(element, clazz); } @NotNull public static List findImmediateChildrenOfType(@Nullable PsiElement element, @NotNull IElementType elementType) { PsiElement child = element == null ? null : element.getFirstChild(); if (child == null) { return Collections.emptyList(); } List result = new ArrayList<>(); while (child != null) { if (child.getNode().getElementType() == elementType) { result.add(child); } child = child.getNextSibling(); } return result; } @NotNull public static List findImmediateChildrenOfType(@Nullable PsiElement element, @NotNull ORCompositeType elementType) { return findImmediateChildrenOfType(element, (IElementType) elementType); } @Nullable public static PsiElement findImmediateFirstChildOfType(@Nullable PsiElement element, @NotNull IElementType elementType) { Collection children = findImmediateChildrenOfType(element, elementType); return children.isEmpty() ? null : children.iterator().next(); } public static @Nullable PsiElement findImmediateLastChildOfType(@Nullable PsiElement element, @NotNull IElementType elementType) { Collection children = findImmediateChildrenOfType(element, elementType); Iterator it = children.iterator(); PsiElement child = null; while (it.hasNext()) { child = it.next(); } return child; } @Nullable public static PsiElement findImmediateFirstChildOfType(@Nullable PsiElement element, @NotNull ORCompositeType elementType) { return findImmediateFirstChildOfType(element, (IElementType) elementType); } @Nullable public static PsiElement findImmediateLastChildOfType(@Nullable PsiElement element, @NotNull ORCompositeType elementType) { return findImmediateLastChildOfType(element, (IElementType) elementType); } @Nullable public static T findImmediateFirstChildOfClass(@Nullable PsiElement element, @NotNull Class clazz) { PsiElement child = element == null ? null : element.getFirstChild(); while (child != null) { if (clazz.isInstance(child)) { return clazz.cast(child); } child = child.getNextSibling(); } return null; } @Nullable public static T findImmediateLastChildOfClass(@Nullable PsiElement element, @NotNull Class clazz) { PsiElement child = element == null ? null : element.getFirstChild(); T found = null; while (child != null) { if (clazz.isInstance(child)) { //noinspection unchecked found = (T) child; } child = child.getNextSibling(); } return found; } public static @Nullable PsiElement findImmediateFirstChildOfAnyClass(@NotNull PsiElement element, @NotNull Class... clazz) { PsiElement child = element.getFirstChild(); while (child != null) { if (!(child instanceof PsiWhiteSpace)) { for (Class aClazz : clazz) { if (aClazz.isInstance(child)) { return child; } } } child = child.getNextSibling(); } return null; } public static @NotNull String[] getQualifiedPath(@NotNull PsiElement element) { String path = ""; PsiElement parent = element.getParent(); while (parent != null) { if (parent instanceof RPsiParameterReference) { PsiElement parameters = parent.getParent(); PsiElement functionCall = parameters == null ? null : parameters.getParent(); if (parameters instanceof RPsiParameters && (functionCall instanceof RPsiFunctionCall || functionCall instanceof RPsiFunctorCall)) { int index = ((RPsiParameters) parameters).getParametersList().indexOf(parent); if (index >= 0) { path = ((NavigationItem) functionCall).getName() + "[" + index + "]" + (path.isEmpty() ? "" : "." + path); } parent = functionCall.getParent(); } else { parent = parameters; } } else if (parent instanceof RPsiQualifiedPathElement) { if (parent instanceof PsiNameIdentifierOwner && ((PsiNameIdentifierOwner) parent).getNameIdentifier() == element) { String[] parentPath = ((RPsiQualifiedPathElement) parent).getPath(); return parentPath == null ? EMPTY_PATH : parentPath; } return (((PsiQualifiedNamedElement) parent).getQualifiedName() + (path.isEmpty() ? "" : "." + path)).split("\\."); } else { if (parent instanceof PsiNameIdentifierOwner) { String parentName = ((PsiNamedElement) parent).getName(); if (parentName != null && !parentName.isEmpty()) { path = parentName + (path.isEmpty() ? "" : "." + path); } } parent = parent.getParent(); } } try { PsiFile containingFile = element.getContainingFile(); String fileName = containingFile instanceof FileBase ? ((FileBase) containingFile).getModuleName() : containingFile.getName(); return (fileName + (path.isEmpty() ? "" : "." + path)).split("\\."); } catch (PsiInvalidElementAccessException e) { return path.split("\\."); } } @NotNull public static String getQualifiedName(@NotNull PsiNamedElement element) { String qualifiedPath = Joiner.join(".", getQualifiedPath(element)); String name = element.getName(); if (name == null) { String nullifier = element instanceof RPsiModuleSignature ? "" : "."; // anonymous signature type is ok return qualifiedPath + nullifier; } return qualifiedPath + "." + name; } @NotNull public static ORLangTypes getTypes(@NotNull Language language) { return language == ResLanguage.INSTANCE ? ResTypes.INSTANCE : language == RmlLanguage.INSTANCE ? RmlTypes.INSTANCE : OclTypes.INSTANCE; } @Nullable public static String computeAlias(@Nullable PsiElement rootElement, @NotNull Language language, boolean lowerAccepted) { boolean isALias = true; PsiElement currentElement = rootElement; ORLangTypes types = getTypes(language); StringBuilder aliasName = new StringBuilder(); IElementType elementType = currentElement == null ? null : currentElement.getNode().getElementType(); while (elementType != null && elementType != types.SEMI && elementType != types.EOL) { if (elementType != TokenType.WHITE_SPACE && elementType != types.A_MODULE_NAME && elementType != types.DOT) { // if last term is lower symbol, and we accept lower symbol, then it's an alias if ((elementType != types.LIDENT && elementType != types.A_VARIANT_NAME) || currentElement.getNextSibling() != null || !lowerAccepted) { isALias = false; break; } } if (elementType != TokenType.WHITE_SPACE) { aliasName.append(currentElement.getText()); } currentElement = currentElement.getNextSibling(); elementType = currentElement == null ? null : currentElement.getNode().getElementType(); } return isALias ? aliasName.toString() : null; } public static @NotNull List findPreviousSiblingsOrParentOfClass(@NotNull PsiElement element, @NotNull Class clazz) { List result = new ArrayList<>(); PsiElement previous = element.getPrevSibling(); PsiElement prevSibling = previous == null ? element.getParent() : previous; while (prevSibling != null) { if (clazz.isInstance(prevSibling)) { //noinspection unchecked result.add((T) prevSibling); } previous = prevSibling.getPrevSibling(); prevSibling = previous == null ? prevSibling.getParent() : previous; } return result; } public static @Nullable T findImmediateNamedChildOfClass(@Nullable PsiElement element, @NotNull Class clazz, @NotNull String name) { return ORUtil.findImmediateChildrenOfClass(element, clazz).stream().filter(item -> name.equals(item.getName())).findFirst().orElse(null); } public static boolean isPrevType(PsiElement root, ORTokenElementType elementType) { PsiElement prevSibling = ORUtil.prevSibling(root); IElementType prevType = prevSibling == null ? null : prevSibling.getNode().getElementType(); return prevType != null && prevType == elementType; } public static boolean isInterfaceFile(@Nullable PsiElement element) { PsiFile file = element != null ? element.getContainingFile() : null; return file instanceof FileBase fileBase && fileBase.isInterface(); } public static boolean inInterface(@Nullable PsiElement element) { PsiElement parent = PsiTreeUtil.getStubOrPsiParent(element); if (parent instanceof RPsiModuleSignature) { return true; } else if (parent instanceof RPsiModuleBinding moduleBinding) { boolean interfaceFile = isInterfaceFile(moduleBinding); if (interfaceFile) { return true; } PsiElement bindingParent = PsiTreeUtil.getStubOrPsiParent(moduleBinding); if (bindingParent instanceof RPsiInnerModule innerModule) { return innerModule.isModuleType(); } } return isInterfaceFile(element); } public static @Nullable IElementType getNodeType(@Nullable PsiElement element) { ASTNode node = element != null ? element.getNode() : null; return node != null ? node.getElementType() : null; } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/RPsiClass.java ================================================ package com.reason.lang.core.psi; import com.intellij.psi.*; import com.reason.lang.core.psi.impl.*; import com.reason.lang.core.stub.*; import org.jetbrains.annotations.*; import java.util.*; public interface RPsiClass extends RPsiQualifiedPathElement, NavigatablePsiElement, RPsiStructuredElement, PsiNameIdentifierOwner, StubBasedPsiElement { @Nullable PsiElement getClassBody(); @NotNull Collection getFields(); @NotNull Collection getMethods(); @NotNull Collection getParameters(); @Nullable RPsiClassConstructor getConstructor(); @Nullable RPsiClassInitializer getInitializer(); } ================================================ FILE: src/main/java/com/reason/lang/core/psi/RPsiClassMethod.java ================================================ package com.reason.lang.core.psi; import com.intellij.psi.*; import com.reason.lang.core.stub.*; import org.jetbrains.annotations.*; public interface RPsiClassMethod extends RPsiQualifiedPathElement, NavigatablePsiElement, PsiNameIdentifierOwner, RPsiStructuredElement, StubBasedPsiElement { @Nullable RPsiSignature getSignature(); } ================================================ FILE: src/main/java/com/reason/lang/core/psi/RPsiConditional.java ================================================ package com.reason.lang.core.psi; import com.intellij.psi.PsiElement; import com.reason.lang.core.psi.impl.RPsiBinaryCondition; import org.jetbrains.annotations.Nullable; public interface RPsiConditional { @Nullable RPsiBinaryCondition getCondition(); @Nullable PsiElement getThenExpression(); @Nullable PsiElement getElseExpression(); } ================================================ FILE: src/main/java/com/reason/lang/core/psi/RPsiException.java ================================================ package com.reason.lang.core.psi; import com.intellij.psi.*; import com.reason.lang.core.stub.*; import org.jetbrains.annotations.*; public interface RPsiException extends NavigatablePsiElement, RPsiStructuredElement, PsiNameIdentifierOwner, RPsiQualifiedPathElement, StubBasedPsiElement { @Nullable String getAlias(); } ================================================ FILE: src/main/java/com/reason/lang/core/psi/RPsiExternal.java ================================================ package com.reason.lang.core.psi; import com.intellij.psi.*; import com.reason.lang.core.stub.*; import org.jetbrains.annotations.*; public interface RPsiExternal extends RPsiQualifiedPathElement, RPsiSignatureElement, RPsiStructuredElement, PsiNameIdentifierOwner, StubBasedPsiElement { boolean isFunction(); @NotNull String getExternalName(); } ================================================ FILE: src/main/java/com/reason/lang/core/psi/RPsiField.java ================================================ package com.reason.lang.core.psi; import com.reason.lang.core.psi.impl.*; import org.jetbrains.annotations.*; public interface RPsiField extends RPsiQualifiedPathElement { @Nullable RPsiFieldValue getValue(); } ================================================ FILE: src/main/java/com/reason/lang/core/psi/RPsiFunctor.java ================================================ package com.reason.lang.core.psi; import com.intellij.psi.*; import com.reason.lang.core.psi.impl.*; import com.reason.lang.core.stub.*; import org.jetbrains.annotations.*; import java.util.*; public interface RPsiFunctor extends PsiNameIdentifierOwner, RPsiModule, StubBasedPsiElement { @NotNull List getParameters(); @Nullable RPsiFunctorResult getReturnType(); @NotNull List getConstraints(); } ================================================ FILE: src/main/java/com/reason/lang/core/psi/RPsiInclude.java ================================================ package com.reason.lang.core.psi; import com.intellij.psi.*; import com.reason.lang.core.stub.*; import org.jetbrains.annotations.*; public interface RPsiInclude extends RPsiStructuredElement, StubBasedPsiElement { String[] getQualifiedPath(); @NotNull String getIncludePath(); boolean useFunctor(); } ================================================ FILE: src/main/java/com/reason/lang/core/psi/RPsiInferredType.java ================================================ package com.reason.lang.core.psi; import com.intellij.psi.*; import org.jetbrains.annotations.*; public interface RPsiInferredType extends PsiElement { void setInferredType(@NotNull RPsiSignature inferredType); @Nullable RPsiSignature getInferredType(); boolean hasInferredType(); } ================================================ FILE: src/main/java/com/reason/lang/core/psi/RPsiInnerModule.java ================================================ package com.reason.lang.core.psi; import com.intellij.psi.*; import com.reason.lang.core.psi.impl.*; import com.reason.lang.core.stub.*; import org.jetbrains.annotations.*; import java.util.*; public interface RPsiInnerModule extends RPsiModule, StubBasedPsiElement, NavigatablePsiElement, PsiNameIdentifierOwner { @Nullable String getAlias(); @Nullable RPsiUnpack getUnpack(); @Nullable RPsiUpperSymbol getAliasSymbol(); boolean isComponent(); boolean isFunctorCall(); @Nullable RPsiFunctorCall getFunctorCall(); boolean isModuleType(); @Nullable RPsiModuleSignature getModuleSignature(); @Nullable PsiElement getBody(); @NotNull List getConstraints(); } ================================================ FILE: src/main/java/com/reason/lang/core/psi/RPsiLanguageConverter.java ================================================ package com.reason.lang.core.psi; import com.reason.lang.*; import org.jetbrains.annotations.*; public interface RPsiLanguageConverter { @NotNull String asText(@Nullable ORLanguageProperties language); } ================================================ FILE: src/main/java/com/reason/lang/core/psi/RPsiLet.java ================================================ package com.reason.lang.core.psi; import com.intellij.psi.*; import com.reason.lang.core.psi.impl.*; import com.reason.lang.core.stub.*; import org.jetbrains.annotations.*; import java.util.*; public interface RPsiLet extends RPsiVar, RPsiSignatureElement, RPsiInferredType, RPsiQualifiedPathElement, NavigatablePsiElement, RPsiStructuredElement, PsiNameIdentifierOwner, StubBasedPsiElement { @Nullable RPsiLetBinding getBinding(); @Nullable RPsiFunction getFunction(); boolean isComponent(); boolean isRecord(); boolean isJsObject(); boolean isScopeIdentifier(); @Nullable String getAlias(); @NotNull Collection getScopeChildren(); boolean isDeconstruction(); @NotNull List getDeconstructedElements(); boolean isPrivate(); @Nullable RPsiFirstClass getFirstClassModule(); } ================================================ FILE: src/main/java/com/reason/lang/core/psi/RPsiModule.java ================================================ package com.reason.lang.core.psi; import com.intellij.psi.*; import org.jetbrains.annotations.*; /** * Common interface to file-based modules and inner modules */ public interface RPsiModule extends RPsiQualifiedPathElement, RPsiStructuredElement { @Nullable String getModuleName(); @Nullable PsiElement getBody(); boolean isComponent(); @Nullable PsiElement getMakeFunction(); } ================================================ FILE: src/main/java/com/reason/lang/core/psi/RPsiOCamlInjection.java ================================================ package com.reason.lang.core.psi; import com.intellij.extapi.psi.*; import com.intellij.lang.*; import com.intellij.navigation.*; import com.reason.ide.*; import com.reason.lang.core.psi.*; import org.jetbrains.annotations.*; import javax.swing.*; public class RPsiOCamlInjection extends ASTWrapperPsiElement implements RPsiStructuredElement { public RPsiOCamlInjection(@NotNull ASTNode node) { super(node); } // region RPsiStructuredElement @Override public @Nullable ItemPresentation getPresentation() { return new ItemPresentation() { @Override public @NotNull String getPresentableText() { return "OCaml"; } @Override public @NotNull Icon getIcon(boolean unused) { return ORIcons.OCAML; } }; } // endregion } ================================================ FILE: src/main/java/com/reason/lang/core/psi/RPsiOpen.java ================================================ package com.reason.lang.core.psi; import com.intellij.psi.*; import com.reason.lang.core.stub.*; import org.jetbrains.annotations.*; public interface RPsiOpen extends RPsiStructuredElement, StubBasedPsiElement { @NotNull String getPath(); boolean useFunctor(); } ================================================ FILE: src/main/java/com/reason/lang/core/psi/RPsiParameterDeclaration.java ================================================ package com.reason.lang.core.psi; import com.intellij.psi.*; import com.reason.lang.core.psi.impl.*; import com.reason.lang.core.stub.*; import org.jetbrains.annotations.*; public interface RPsiParameterDeclaration extends PsiNameIdentifierOwner, RPsiQualifiedPathElement, RPsiSignatureElement, RPsiLanguageConverter, StubBasedPsiElement { @Nullable RPsiDefaultValue getDefaultValue(); boolean isOptional(); boolean isNamed(); } ================================================ FILE: src/main/java/com/reason/lang/core/psi/RPsiQualifiedPathElement.java ================================================ package com.reason.lang.core.psi; import com.intellij.psi.*; import org.jetbrains.annotations.*; /** * A {@link PsiQualifiedNamedElement} with extra access to the element path as array of strings. */ public interface RPsiQualifiedPathElement extends PsiQualifiedNamedElement { @Nullable String[] getPath(); } ================================================ FILE: src/main/java/com/reason/lang/core/psi/RPsiRecordField.java ================================================ package com.reason.lang.core.psi; import com.intellij.psi.*; import com.reason.lang.core.stub.*; public interface RPsiRecordField extends RPsiField, PsiNameIdentifierOwner, RPsiQualifiedPathElement, NavigatablePsiElement, RPsiSignatureElement, StubBasedPsiElement { } ================================================ FILE: src/main/java/com/reason/lang/core/psi/RPsiSignature.java ================================================ package com.reason.lang.core.psi; import com.intellij.psi.*; import org.jetbrains.annotations.*; import java.util.*; public interface RPsiSignature extends PsiElement, RPsiLanguageConverter { boolean isFunction(); @NotNull List getItems(); } ================================================ FILE: src/main/java/com/reason/lang/core/psi/RPsiSignatureElement.java ================================================ package com.reason.lang.core.psi; import com.intellij.psi.*; import org.jetbrains.annotations.*; public interface RPsiSignatureElement extends PsiElement { @Nullable RPsiSignature getSignature(); } ================================================ FILE: src/main/java/com/reason/lang/core/psi/RPsiSignatureItem.java ================================================ package com.reason.lang.core.psi; import com.intellij.psi.*; import org.jetbrains.annotations.*; public interface RPsiSignatureItem extends PsiNamedElement, RPsiLanguageConverter { boolean isNamedItem(); @Nullable String getName(); boolean isOptional(); @Nullable PsiElement getSignature(); @Nullable PsiElement getDefaultValue(); } ================================================ FILE: src/main/java/com/reason/lang/core/psi/RPsiSignatureUtil.java ================================================ package com.reason.lang.core.psi; import com.intellij.psi.*; import com.reason.lang.*; import org.jetbrains.annotations.*; public final class RPsiSignatureUtil { private RPsiSignatureUtil() { } public static @NotNull String getSignature(@Nullable PsiElement element, @Nullable ORLanguageProperties toLang) { if (element instanceof RPsiExternal) { RPsiSignature signature = ((RPsiExternal) element).getSignature(); return signature == null ? "" : signature.asText(toLang); } else if (element instanceof RPsiLet let) { RPsiSignature signature = let.hasInferredType() ? let.getInferredType() : let.getSignature(); return signature == null ? "" : signature.asText(toLang); } else if (element instanceof RPsiVal) { RPsiSignature signature = ((RPsiVal) element).getSignature(); return signature == null ? "" : signature.asText(toLang); } else if (element instanceof RPsiInnerModule) { String qName = ((RPsiInnerModule) element).getQualifiedName(); return qName == null ? "" : qName; } return ""; } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/RPsiStructuredElement.java ================================================ package com.reason.lang.core.psi; import com.intellij.psi.PsiElement; /** Indicates that the element can be displayed in the structure view */ public interface RPsiStructuredElement extends PsiElement { default boolean canBeDisplayed() { return true; } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/RPsiType.java ================================================ package com.reason.lang.core.psi; import com.intellij.psi.*; import com.reason.lang.core.psi.impl.*; import com.reason.lang.core.stub.*; import org.jetbrains.annotations.*; import java.util.*; public interface RPsiType extends PsiNameIdentifierOwner, RPsiQualifiedPathElement, NavigatablePsiElement, RPsiStructuredElement, StubBasedPsiElement { @Nullable PsiElement getBinding(); @NotNull Collection getVariants(); boolean isJsObject(); boolean isRecord(); @Nullable RPsiParameters getParameters(); @NotNull Collection getJsObjectFields(); @NotNull Collection getRecordFields(); boolean isAbstract(); } ================================================ FILE: src/main/java/com/reason/lang/core/psi/RPsiVal.java ================================================ package com.reason.lang.core.psi; import com.intellij.psi.*; import com.reason.lang.core.stub.*; public interface RPsiVal extends RPsiVar, RPsiQualifiedPathElement, RPsiSignatureElement, NavigatablePsiElement, RPsiStructuredElement, StubBasedPsiElement { } ================================================ FILE: src/main/java/com/reason/lang/core/psi/RPsiVar.java ================================================ package com.reason.lang.core.psi; import com.intellij.psi.*; import com.reason.lang.core.psi.impl.*; import org.jetbrains.annotations.*; import java.util.*; /** * A RPsiLet or a RPsiVar are used to declare a variable. They both implement RPsiVar. */ public interface RPsiVar extends RPsiQualifiedPathElement, PsiNameIdentifierOwner { boolean isFunction(); @NotNull Collection getJsObjectFields(); @NotNull Collection getRecordFields(); /** * @return true if the name is an underscore `_` or unit `()` */ boolean isAnonymous(); } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/ORASTFactory.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.lang.*; import com.intellij.psi.impl.source.tree.*; import com.intellij.psi.tree.*; import com.reason.lang.core.type.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; import static com.intellij.codeInsight.completion.CompletionUtilCore.*; public class ORASTFactory extends ASTFactory { private final T myTypes; public ORASTFactory(T types) { myTypes = types; } @Override public @Nullable CompositeElement createComposite(@NotNull IElementType type) { if (type == myTypes.C_ASSERT_STMT) { return new RPsiAssert(myTypes, type); } if (type == myTypes.C_ARRAY) { return new RPsiArray(myTypes, type); } if (type == myTypes.C_LET_BINDING) { return new RPsiLetBinding(myTypes, type); } if (type == myTypes.C_DEFAULT_VALUE) { return new RPsiDefaultValue(myTypes, type); } if (type == myTypes.C_SIG_EXPR) { return new RPsiSignatureImpl(myTypes, type); } if (type == myTypes.C_SIG_ITEM) { return new RPsiSignatureItemImpl(myTypes, type); } if (type == myTypes.C_TAG) { return new RPsiTag(myTypes, type); } if (type == myTypes.C_TAG_BODY) { return new RPsiTagBody(myTypes, type); } if (type == myTypes.C_TAG_CLOSE) { return new RPsiTagClose(myTypes, type); } if (type == myTypes.C_TAG_PROPERTY) { return new RPsiTagProperty(myTypes, type); } if (type == myTypes.C_TAG_PROP_VALUE) { return new RPsiTagPropertyValue(myTypes, type); } if (type == myTypes.C_TAG_START) { return new RPsiTagStart(myTypes, type); } if (type == myTypes.C_FIELD_VALUE) { return new RPsiFieldValue(myTypes, type); } if (type == myTypes.C_FUN_EXPR) { return new RPsiFunSwitch(myTypes, type); } if (type == myTypes.C_FUNCTION_BODY) { return new RPsiFunctionBody(myTypes, type); } if (type == myTypes.C_FUNCTION_CALL) { return new RPsiFunctionCall(myTypes, type); } if (type == myTypes.C_FUNCTION_EXPR) { return new RPsiFunction(myTypes, type); } if (type == myTypes.C_CUSTOM_OPERATOR || type == myTypes.C_SCOPED_EXPR || type == myTypes.C_IF_THEN_ELSE || type == myTypes.C_DO_LOOP) { return new RPsiScopedExpr(myTypes, type); } if (type == myTypes.C_FIRST_CLASS) { return new RPsiFirstClass(myTypes, type); } if (type == myTypes.C_FOR_LOOP) { return new RPsiForLoop(myTypes, type); } if (type == myTypes.C_LOCAL_OPEN) { return new RPsiLocalOpen(myTypes, type); } if (type == myTypes.C_PARAM || type == myTypes.C_NAMED_PARAM) { return new RPsiParameterReference(myTypes, type); } if (type == myTypes.C_PARAMETERS || type == myTypes.C_VARIANT_CONSTRUCTOR) { return new RPsiParameters(myTypes, type); } if (type == myTypes.C_PATTERN_MATCH_BODY) { return new RPsiPatternMatchBody(myTypes, type); } if (type == myTypes.C_PATTERN_MATCH_EXPR) { return new RPsiPatternMatch(myTypes, type); } if (type == myTypes.C_JS_OBJECT) { return new RPsiJsObject(myTypes, type); } if (type == myTypes.C_DECONSTRUCTION) { return new RPsiDeconstruction(myTypes, type); } if (type == myTypes.C_TYPE_BINDING) { return new RPsiTypeBinding(myTypes, type); } if (type == myTypes.C_RECORD_EXPR) { return new RPsiRecord(myTypes, type); } if (type == myTypes.C_METHOD_CALL) { return new RPsiMethodCall(myTypes, type); } if (type == myTypes.C_MODULE_BINDING) { return new RPsiModuleBinding(myTypes, type); } if (type == myTypes.C_MODULE_SIGNATURE) { return new RPsiModuleSignature(myTypes, type); } if (type == myTypes.C_ANNOTATION) { return new RPsiAnnotation(myTypes, type); } if (type == myTypes.C_MACRO_BODY) { return new RPsiMacroBody(myTypes, type); } if (type == myTypes.C_FUNCTOR_CALL) { return new RPsiFunctorCall(myTypes, type); } if (type == myTypes.C_CONSTRAINTS) { return new RPsiConstraints(myTypes, type); } if (type == myTypes.C_TYPE_CONSTRAINT) { return new RPsiTypeConstraint(myTypes, type); } if (type == myTypes.C_GUARD) { return new RPsiGuard(myTypes, type); } if (type == myTypes.C_IF) { return new RPsiIfStatement(myTypes, type); } if (type == myTypes.C_OBJECT) { return new RPsiObject(myTypes, type); } if (type == myTypes.C_OPTION) { return new RPsiOption(myTypes, type); } if (type == myTypes.C_INHERIT) { return new RPsiInherit(myTypes, type); } if (type == myTypes.C_CLASS_CONSTR) { return new RPsiClassConstructor(myTypes, type); } if (type == myTypes.C_CLASS_FIELD) { return new RPsiClassField(myTypes, type); } if (type == myTypes.C_CLASS_INITIALIZER) { return new RPsiClassInitializer(myTypes, type); } if (type == myTypes.C_SWITCH_BODY) { return new RPsiSwitchBody(myTypes, type); } if (type == myTypes.C_SWITCH_EXPR || type == myTypes.C_MATCH_EXPR) { return new RPsiSwitch(myTypes, type); } if (type == myTypes.C_FUNCTOR_BINDING) { return new RPsiFunctorBinding(myTypes, type); } if (type == myTypes.C_FUNCTOR_RESULT) { return new RPsiFunctorResult(myTypes, type); } if (type == myTypes.C_BINARY_CONDITION) { return new RPsiBinaryCondition(myTypes, type); } if (type == myTypes.C_TERNARY) { return new RPsiTernary(myTypes, type); } if (type == myTypes.C_MIXIN_FIELD) { return new RPsiMixinField(myTypes, type); } if (type == myTypes.C_LET_ATTR) { return new RPsiLetAttribute(myTypes, type); } if (type == myTypes.C_CLOSED_VARIANT) { return new RPsiPolyVariantConstraint(myTypes, type); } if (type == myTypes.C_MACRO_EXPR) { return new RPsiMacro(myTypes, type); } if (type == myTypes.C_INTERPOLATION_EXPR) { return new RPsiInterpolation(myTypes, type); } if (type == myTypes.C_INTERPOLATION_REF) { return new RPsiInterpolationReference(myTypes, type); } if (type == myTypes.C_TRY_EXPR) { return new RPsiTry(myTypes, type); } if (type == myTypes.C_TRY_HANDLERS) { return new CompositePsiElement(type) { }; } if (type == myTypes.C_TRY_HANDLER) { return new RPsiTryHandler(myTypes, type); } if (type == myTypes.C_TRY_HANDLER_BODY) { return new RPsiTryHandlerBody(myTypes, type); } if (type == myTypes.C_TRY_BODY) { return new RPsiTryBody(myTypes, type); } if (type == myTypes.C_TUPLE) { return new RPsiTuple(myTypes, type); } if (type == myTypes.C_ML_INTERPOLATOR) { return new RPsiMultiLineInterpolator(myTypes, type); } if (type == myTypes.C_DIRECTIVE) { return new RPsiDirective(myTypes, type); } if (type == myTypes.C_SOME) { return new RPsiOptionValue(myTypes, type); } if (type == myTypes.C_STRUCT_EXPR) { return new RPsiStruct(myTypes, type); } if (type == myTypes.C_UNIT) { return new RPsiUnit(myTypes, type); } if (type == myTypes.C_UNPACK) { return new RPsiUnpack(myTypes, type); } if (type == myTypes.C_WHILE) { return new RPsiWhile(myTypes, type); } // Generic if (type == myTypes.C_INTERPOLATION_PART || type == myTypes.C_TYPE_VARIABLE) { return new CompositePsiElement(type) { }; } if (type == myTypes.C_OPEN_VARIANT) { return new RPsiPolyVariantConstraint(myTypes, type); } if (type == myTypes.C_MACRO_NAME) { return new RPsiMacroName(myTypes, type); } if (type == myTypes.C_LOWER_NAME) { return new RPsiLowerName(myTypes, type); } return null; } @Override public @Nullable LeafElement createLeaf(@NotNull IElementType type, @NotNull CharSequence text) { if (type == myTypes.UIDENT || type == myTypes.A_VARIANT_NAME || type == myTypes.A_MODULE_NAME || type == myTypes.A_EXCEPTION_NAME) { if (!DUMMY_IDENTIFIER_TRIMMED.contentEquals(text)) { return new RPsiUpperSymbol(myTypes, type, text); } } if (type == myTypes.POLY_VARIANT) { return StringUtil.isUpper(text.charAt(1)) ? new RPsiUpperSymbol(myTypes, type, text) : new RPsiLowerSymbol(myTypes, type, text); } if (type == myTypes.A_UPPER_TAG_NAME) { return new RPsiUpperTagName(myTypes, type, text); } if (type == myTypes.LIDENT) { return new RPsiLowerSymbol(myTypes, type, text); } if (type == myTypes.PROPERTY_NAME) { return new RPsiLeafPropertyName(type, text); } if (type == myTypes.STRING_VALUE) { return new RPsiLiteralString(myTypes, type, text); } return super.createLeaf(type, text); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiAnnotation.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.psi.*; import com.intellij.psi.tree.*; import com.intellij.util.*; import com.reason.lang.core.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; public class RPsiAnnotation extends ORCompositePsiElement implements PsiNameIdentifierOwner { protected RPsiAnnotation(@NotNull ORLangTypes types, @NotNull IElementType elementType) { super(types, elementType); } @Override public @Nullable PsiElement getNameIdentifier() { return ORUtil.findImmediateFirstChildOfClass(this, RPsiMacroName.class); } @Override public @Nullable String getName() { PsiElement identifier = getNameIdentifier(); return identifier == null ? null : identifier.getText(); } @Override public @NotNull PsiElement setName(@NotNull String name) throws IncorrectOperationException { return this; } public @Nullable PsiElement getValue() { RPsiScopedExpr expr = ORUtil.findImmediateFirstChildOfClass(this, RPsiScopedExpr.class); if (expr != null) { return ORUtil.nextSibling(expr.getFirstChild()); } return null; } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiArray.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.psi.tree.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; public class RPsiArray extends ORCompositePsiElement { protected RPsiArray(@NotNull ORLangTypes types, @NotNull IElementType elementType) { super(types, elementType); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiAssert.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.psi.*; import com.intellij.psi.tree.*; import com.intellij.psi.util.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; public class RPsiAssert extends ORCompositePsiElement { protected RPsiAssert(@NotNull ORLangTypes types, @NotNull IElementType elementType) { super(types, elementType); } public @Nullable PsiElement getAssertion() { return PsiTreeUtil.skipWhitespacesForward(getFirstChild()); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiBinaryCondition.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.psi.tree.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; public class RPsiBinaryCondition extends ORCompositePsiElement { protected RPsiBinaryCondition(@NotNull ORLangTypes types, @NotNull IElementType elementType) { super(types, elementType); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiClassConstructor.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.psi.tree.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; public class RPsiClassConstructor extends ORCompositePsiElement { protected RPsiClassConstructor(@NotNull ORLangTypes types, @NotNull IElementType elementType) { super(types, elementType); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiClassField.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.navigation.*; import com.intellij.psi.*; import com.intellij.psi.tree.*; import com.intellij.psi.util.*; import com.intellij.util.*; import com.reason.ide.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; import javax.swing.*; public class RPsiClassField extends ORCompositePsiElement implements NavigatablePsiElement, PsiNameIdentifierOwner, RPsiStructuredElement { protected RPsiClassField(@NotNull ORLangTypes types, @NotNull IElementType elementType) { super(types, elementType); } @Override public @Nullable PsiElement getNameIdentifier() { return PsiTreeUtil.findChildOfType(this, RPsiLowerSymbol.class); } @Override public String getName() { PsiElement nameIdentifier = getNameIdentifier(); return nameIdentifier == null ? "" : nameIdentifier.getText(); } @Override public @Nullable PsiElement setName(@NotNull String name) throws IncorrectOperationException { return null; } public ItemPresentation getPresentation() { return new ItemPresentation() { @Nullable @Override public String getPresentableText() { return getName(); } @Nullable @Override public String getLocationString() { return null; } @NotNull @Override public Icon getIcon(boolean unused) { return ORIcons.VAL; } }; } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiClassImpl.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.lang.*; import com.intellij.navigation.*; import com.intellij.psi.*; import com.intellij.psi.stubs.*; import com.intellij.psi.util.*; import com.intellij.util.*; import com.reason.ide.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.stub.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; import javax.swing.*; import java.util.*; public class RPsiClassImpl extends RPsiTokenStub implements RPsiClass { // region Constructors public RPsiClassImpl(@NotNull ORLangTypes types, @NotNull ASTNode node) { super(types, node); } public RPsiClassImpl(@NotNull ORLangTypes types, @NotNull RsiClassStub stub, @NotNull IStubElementType nodeType) { super(types, stub, nodeType); } // endregion // region PsiNamedElement public @Nullable PsiElement getNameIdentifier() { return findChildByClass(RPsiLowerSymbol.class); } @Override public @Nullable String getName() { PsiElement nameIdentifier = getNameIdentifier(); return nameIdentifier == null ? "" : nameIdentifier.getText(); } @Override public @NotNull PsiElement setName(@NotNull String name) throws IncorrectOperationException { return this; } // endregion //region PsiQualifiedName @Override public @Nullable String[] getPath() { RsiClassStub stub = getGreenStub(); if (stub != null) { return stub.getPath(); } return ORUtil.getQualifiedPath(this); } @Override public @NotNull String getQualifiedName() { RsiClassStub stub = getGreenStub(); if (stub != null) { return stub.getQualifiedName(); } return ORUtil.getQualifiedName(this); } //endregion @Override public @Nullable PsiElement getClassBody() { return PsiTreeUtil.findChildOfType(this, RPsiObject.class); } @Override public @NotNull Collection getFields() { return PsiTreeUtil.findChildrenOfType(getClassBody(), RPsiClassField.class); } @Override public @NotNull Collection getMethods() { return PsiTreeUtil.findChildrenOfType(getClassBody(), RPsiClassMethod.class); } @Override public @NotNull Collection getParameters() { return PsiTreeUtil.findChildrenOfType(this, RPsiParameters.class); } @Override public @Nullable RPsiClassConstructor getConstructor() { return findChildByClass(RPsiClassConstructor.class); } @Override public @Nullable RPsiClassInitializer getInitializer() { PsiElement body = getClassBody(); return body != null ? ORUtil.findImmediateFirstChildOfClass(body, RPsiClassInitializer.class) : null; } public ItemPresentation getPresentation() { return new ItemPresentation() { @Override public @Nullable String getPresentableText() { return getName(); } @Override public @Nullable String getLocationString() { return null; } @Override public @NotNull Icon getIcon(boolean unused) { return ORIcons.CLASS; } }; } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiClassInitializer.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.psi.tree.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; public class RPsiClassInitializer extends ORCompositePsiElement { protected RPsiClassInitializer(@NotNull ORLangTypes types, @NotNull IElementType elementType) { super(types, elementType); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiClassMethodImpl.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.lang.*; import com.intellij.navigation.*; import com.intellij.psi.*; import com.intellij.psi.stubs.*; import com.intellij.util.*; import com.reason.ide.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.stub.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; import javax.swing.*; public class RPsiClassMethodImpl extends RPsiTokenStub implements RPsiClassMethod { // region Constructors public RPsiClassMethodImpl(@NotNull ORLangTypes types, @NotNull ASTNode node) { super(types, node); } public RPsiClassMethodImpl(@NotNull ORLangTypes types, @NotNull RsiClassMethodStub stub, @NotNull IStubElementType nodeType) { super(types, stub, nodeType); } // endregion @Override public @Nullable PsiElement getNameIdentifier() { return ORUtil.findImmediateFirstChildOfClass(this, RPsiLowerSymbol.class); } //region PsiQualifiedName @Override public @NotNull String getQualifiedName() { RsiClassMethodStub stub = getGreenStub(); if (stub != null) { return stub.getQualifiedName(); } return ORUtil.getQualifiedName(this); } @Override public @NotNull String[] getPath() { RsiClassMethodStub stub = getGreenStub(); if (stub != null) { return stub.getPath(); } return ORUtil.getQualifiedPath(this); } //endregion @Override public String getName() { PsiElement nameIdentifier = getNameIdentifier(); return nameIdentifier == null ? "" : nameIdentifier.getText(); } @Override public @Nullable PsiElement setName(@NotNull String name) throws IncorrectOperationException { return null; } public @Nullable RPsiSignature getSignature() { return ORUtil.findImmediateFirstChildOfClass(this, RPsiSignature.class); } public ItemPresentation getPresentation() { return new ItemPresentation() { @Override public @Nullable String getPresentableText() { return getName(); } @Override public @Nullable String getLocationString() { RPsiSignature signature = getSignature(); return signature == null ? null : signature.getText(); } @Override public @NotNull Icon getIcon(boolean unused) { return ORIcons.METHOD; } }; } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiConstraints.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.psi.tree.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; public class RPsiConstraints extends ORCompositePsiElement { protected RPsiConstraints(@NotNull ORLangTypes types, @NotNull IElementType elementType) { super(types, elementType); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiDeconstruction.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.psi.*; import com.intellij.psi.tree.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; import java.util.*; public class RPsiDeconstruction extends ORCompositePsiElement { protected RPsiDeconstruction(@NotNull ORLangTypes types, @NotNull IElementType elementType) { super(types, elementType); } public @NotNull List getDeconstructedElements() { List result = new ArrayList<>(); for (PsiElement child : getChildren()) { if (child instanceof RPsiDeconstruction) { // nested deconstructions result.addAll(((RPsiDeconstruction) child).getDeconstructedElements()); } else { IElementType elementType = child.getNode().getElementType(); if (elementType != myTypes.LPAREN && elementType != myTypes.RPAREN && elementType != myTypes.LBRACE && elementType != myTypes.RBRACE && elementType != myTypes.COMMA && elementType != myTypes.SEMI && elementType != myTypes.UNDERSCORE && !(child instanceof PsiWhiteSpace)) { result.add(child); } } } return result; } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiDefaultValue.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.psi.tree.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; public class RPsiDefaultValue extends ORCompositePsiElement { public RPsiDefaultValue(@NotNull ORLangTypes types, @NotNull IElementType elementType) { super(types, elementType); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiDirective.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.psi.tree.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; public class RPsiDirective extends ORCompositePsiElement { protected RPsiDirective(@NotNull ORLangTypes types, @NotNull IElementType elementType) { super(types, elementType); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiDuneField.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.navigation.*; import com.intellij.psi.*; import com.intellij.psi.tree.*; import com.intellij.psi.util.*; import com.intellij.util.*; import com.reason.ide.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.type.*; import com.reason.lang.dune.*; import org.jetbrains.annotations.*; import javax.swing.*; public class RPsiDuneField extends ORCompositePsiElement implements PsiNameIdentifierOwner, RPsiStructuredElement { public RPsiDuneField(@NotNull DuneTypes types, @NotNull IElementType elementType) { super(types, elementType); } @Override public @Nullable PsiElement getNameIdentifier() { PsiElement firstChild = getFirstChild(); PsiElement nextSibling = firstChild.getNextSibling(); return nextSibling != null && nextSibling.getNode().getElementType() == myTypes.ATOM ? nextSibling : null; } @Override public @Nullable String getName() { PsiElement identifier = getNameIdentifier(); return identifier == null ? null : identifier.getText(); } @Override public @Nullable PsiElement setName(@NotNull String name) throws IncorrectOperationException { return null; } public @NotNull String getValue() { PsiElement name = ORUtil.findImmediateFirstChildOfType(this, DuneTypes.INSTANCE.ATOM); PsiElement nextLeaf = name == null ? null : PsiTreeUtil.nextVisibleLeaf(name); return nextLeaf == null ? "" : nextLeaf.getText(); // might be not enough } @Override public @NotNull ItemPresentation getPresentation() { return new ItemPresentation() { @Override public @NotNull String getPresentableText() { String name = getName(); return name == null ? "unknown" : name; } @Override public @Nullable String getLocationString() { return null; } @Override public @NotNull Icon getIcon(boolean unused) { return ORIcons.OBJECT; } }; } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiDuneFields.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.psi.tree.*; import com.reason.lang.core.type.*; import com.reason.lang.dune.*; import org.jetbrains.annotations.*; public class RPsiDuneFields extends ORCompositePsiElement { public RPsiDuneFields(@NotNull DuneTypes types, @NotNull IElementType elementType) { super(types, elementType); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiDuneSExpr.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.psi.tree.*; import com.reason.lang.core.type.*; import com.reason.lang.dune.*; import org.jetbrains.annotations.*; public class RPsiDuneSExpr extends ORCompositePsiElement { public RPsiDuneSExpr(@NotNull DuneTypes types, @NotNull IElementType elementType) { super(types, elementType); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiDuneStanza.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.navigation.*; import com.intellij.psi.*; import com.intellij.psi.tree.*; import com.intellij.util.*; import com.reason.ide.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.type.*; import com.reason.lang.dune.*; import org.jetbrains.annotations.*; import javax.swing.*; import java.util.*; public class RPsiDuneStanza extends ORCompositePsiElement implements PsiNameIdentifierOwner, RPsiStructuredElement { public RPsiDuneStanza(@NotNull DuneTypes types, @NotNull IElementType elementType) { super(types, elementType); } @Override public @Nullable PsiElement getNameIdentifier() { PsiElement firstChild = getFirstChild(); PsiElement nextSibling = firstChild.getNextSibling(); return nextSibling != null && nextSibling.getNode().getElementType() == myTypes.ATOM ? nextSibling : null; } @Override public @Nullable String getName() { PsiElement identifier = getNameIdentifier(); return identifier == null ? null : identifier.getText(); } @Override public @Nullable PsiElement setName(@NotNull String name) throws IncorrectOperationException { return null; } public @NotNull Collection getFields() { RPsiDuneFields fields = ORUtil.findImmediateFirstChildOfClass(this, RPsiDuneFields.class); return ORUtil.findImmediateChildrenOfClass(fields, RPsiDuneField.class); } public @Nullable RPsiDuneField getField(@NotNull String name) { for (RPsiDuneField field : getFields()) { if (name.equals(field.getName())) { return field; } } return null; } @Nullable @Override public ItemPresentation getPresentation() { return new ItemPresentation() { @Override public @NotNull String getPresentableText() { String name = getName(); return name == null ? "unknown" : name; } @Override public @Nullable String getLocationString() { return null; } @Override public @NotNull Icon getIcon(boolean unused) { return ORIcons.OBJECT; } }; } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiDuneVar.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.psi.tree.*; import com.reason.lang.core.type.*; import com.reason.lang.dune.*; import org.jetbrains.annotations.*; public class RPsiDuneVar extends ORCompositePsiElement { public RPsiDuneVar(@NotNull DuneTypes types, @NotNull IElementType elementType) { super(types, elementType); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiExceptionImpl.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.lang.*; import com.intellij.navigation.*; import com.intellij.psi.*; import com.intellij.psi.stubs.*; import com.intellij.util.*; import com.reason.ide.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.stub.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; import javax.swing.*; public class RPsiExceptionImpl extends RPsiTokenStub implements RPsiException { // region Constructors public RPsiExceptionImpl(@NotNull ORLangTypes types, @NotNull ASTNode node) { super(types, node); } public RPsiExceptionImpl(@NotNull ORLangTypes types, @NotNull PsiExceptionStub stub, @NotNull IStubElementType nodeType) { super(types, stub, nodeType); } // endregion //region PsiNamedElement @Override public @Nullable PsiElement getNameIdentifier() { return ORUtil.findImmediateFirstChildOfType(this, myTypes.A_EXCEPTION_NAME); } @Override public @Nullable String getName() { PsiExceptionStub stub = getGreenStub(); if (stub != null) { return stub.getName(); } PsiElement nameIdentifier = getNameIdentifier(); return nameIdentifier == null ? null : nameIdentifier.getText(); } @Override public @NotNull PsiElement setName(@NotNull String name) throws IncorrectOperationException { return this; } //endregion //region PsiQualifiedPathName @Override public @Nullable String[] getPath() { PsiExceptionStub stub = getGreenStub(); if (stub != null) { return stub.getPath(); } return ORUtil.getQualifiedPath(this); } @Override public @NotNull String getQualifiedName() { PsiExceptionStub stub = getGreenStub(); if (stub != null) { return stub.getQualifiedName(); } return ORUtil.getQualifiedName(this); } //endregion @Override public int getTextOffset() { PsiElement id = getNameIdentifier(); return id == null ? 0 : id.getTextOffset(); } @Override public @Nullable String getAlias() { PsiExceptionStub stub = getGreenStub(); if (stub != null) { return stub.getQualifiedName(); } PsiElement eq = findChildByType(myTypes.EQ); return eq == null ? null : ORUtil.computeAlias(eq.getNextSibling(), getLanguage(), false); } @Override public ItemPresentation getPresentation() { return new ItemPresentation() { @Override public @Nullable String getPresentableText() { return getName(); } @Override public @Nullable String getLocationString() { return null; } @Override public @NotNull Icon getIcon(boolean unused) { return ORIcons.EXCEPTION; } }; } @Override public @NotNull String toString() { return "RPsiException:" + getName(); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiExternalImpl.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.lang.*; import com.intellij.navigation.*; import com.intellij.psi.*; import com.intellij.psi.stubs.*; import com.intellij.psi.tree.*; import com.intellij.psi.util.*; import com.intellij.util.*; import com.reason.ide.*; import com.reason.lang.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.stub.*; import com.reason.lang.core.type.*; import com.reason.lang.rescript.*; import org.jetbrains.annotations.*; import javax.swing.*; import java.util.*; public class RPsiExternalImpl extends RPsiTokenStub implements RPsiExternal { // region Constructors public RPsiExternalImpl(@NotNull ORLangTypes types, @NotNull ASTNode node) { super(types, node); } public RPsiExternalImpl(@NotNull ORLangTypes types, @NotNull PsiExternalStub stub, @NotNull IStubElementType nodeType) { super(types, stub, nodeType); } // endregion // region PsiNamedElement @Override public @Nullable PsiElement getNameIdentifier() { RPsiScopedExpr operatorOverride = findChildByClass(RPsiScopedExpr.class); if (operatorOverride != null) { return operatorOverride; } return findChildByClass(RPsiLowerSymbol.class); } @Override public String getName() { PsiExternalStub stub = getGreenStub(); if (stub != null) { return stub.getName(); } PsiElement nameIdentifier = getNameIdentifier(); if (nameIdentifier == null) { if (myTypes == ResTypes.INSTANCE) { // overridden operators start with backslash and string PsiElement nexElement = ORUtil.nextSibling(getFirstChild()); IElementType nextElementType = nexElement != null ? nexElement.getNode().getElementType() : null; if (nextElementType == myTypes.BACKSLASH) { PsiElement nextNextElement = ORUtil.nextSibling(nexElement); if (nextNextElement instanceof RPsiLiteralString) { return nextNextElement.getText(); } } } return "unknown"; } return nameIdentifier.getText(); } @Override public @NotNull PsiElement setName(@NotNull String name) throws IncorrectOperationException { return this; } // endregion //region PsiQualifiedName @Override public @NotNull String[] getPath() { PsiExternalStub stub = getGreenStub(); if (stub != null) { return stub.getPath(); } return ORUtil.getQualifiedPath(this); } @Override public @NotNull String getQualifiedName() { PsiExternalStub stub = getGreenStub(); if (stub != null) { return stub.getQualifiedName(); } return ORUtil.getQualifiedName(this); } //endregion @Override public int getTextOffset() { PsiElement id = getNameIdentifier(); return id == null ? 0 : id.getTextOffset(); } @Override public @Nullable RPsiSignature getSignature() { return findChildByClass(RPsiSignature.class); } private @NotNull String getRealName() { PsiElement name = findChildByType(myTypes.STRING_VALUE); return name == null ? "" : name.getText(); } @Override public boolean isFunction() { PsiExternalStub stub = getGreenStub(); if (stub != null) { return stub.isFunction(); } RPsiSignature signature = PsiTreeUtil.findChildOfType(this, RPsiSignature.class); return signature != null && signature.isFunction(); } @Override public @NotNull String getExternalName() { PsiElement eq = ORUtil.findImmediateFirstChildOfType(this, myTypes.EQ); if (eq != null) { PsiElement next = ORUtil.nextSiblingWithTokenType(eq, myTypes.STRING_VALUE); if (next != null) { String text = next.getText(); return 2 < text.length() ? text.substring(1, text.length() - 1) : ""; } } return ""; } @Override public ItemPresentation getPresentation() { return new ItemPresentation() { @Override public @Nullable String getPresentableText() { String aliasName = getName(); String realName = getRealName(); if (!realName.isEmpty()) { String realNameText = realName.substring(1, realName.length() - 1); if (!Objects.equals(aliasName, realNameText)) { aliasName += " (" + realNameText + ")"; } } return aliasName; } @Override public @Nullable String getLocationString() { RPsiSignature signature = getSignature(); return signature == null ? null : signature.asText(ORLanguageProperties.cast(getLanguage())); } @Override public Icon getIcon(boolean unused) { return ORIcons.EXTERNAL; } }; } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiFieldValue.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.psi.tree.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; public class RPsiFieldValue extends ORCompositePsiElement { protected RPsiFieldValue(@NotNull ORLangTypes types, @NotNull IElementType elementType) { super(types, elementType); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiFirstClass.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.psi.tree.*; import com.reason.lang.core.*; import com.reason.lang.core.type.*; import com.reason.lang.ocaml.*; import org.jetbrains.annotations.*; public class RPsiFirstClass extends ORCompositePsiElement { protected RPsiFirstClass(@NotNull ORLangTypes types, @NotNull IElementType elementType) { super(types, elementType); } public @Nullable RPsiUpperSymbol getFirstClassModuleSymbol() { if (myTypes == OclTypes.INSTANCE) { return ORUtil.findImmediateFirstChildOfClass(this, RPsiUpperSymbol.class); } RPsiScopedExpr scope = ORUtil.findImmediateLastChildOfClass(this, RPsiScopedExpr.class); return scope != null ? ORUtil.findImmediateLastChildOfClass(scope, RPsiUpperSymbol.class) : null; } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiForLoop.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.psi.tree.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; public class RPsiForLoop extends ORCompositePsiElement { protected RPsiForLoop(@NotNull ORLangTypes types, @NotNull IElementType elementType) { super(types, elementType); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiFunSwitch.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.psi.tree.*; import com.reason.lang.core.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; import java.util.*; public class RPsiFunSwitch extends ORCompositePsiElement { protected RPsiFunSwitch(@NotNull ORLangTypes types, @NotNull IElementType elementType) { super(types, elementType); } public @NotNull List getPatterns() { return ORUtil.findImmediateChildrenOfClass(this, RPsiPatternMatch.class); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiFunction.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.psi.tree.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; import java.util.*; public class RPsiFunction extends ORCompositePsiElement { protected RPsiFunction(@NotNull ORLangTypes types, @NotNull IElementType elementType) { super(types, elementType); } public @NotNull List getParameters() { RPsiParameters parameters = ORUtil.findImmediateFirstChildOfClass(this, RPsiParameters.class); return ORUtil.findImmediateChildrenOfClass(parameters, RPsiParameterDeclaration.class); } public @Nullable RPsiFunctionBody getBody() { return ORUtil.findImmediateFirstChildOfClass(this, RPsiFunctionBody.class); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiFunctionBody.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.psi.tree.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; public class RPsiFunctionBody extends ORCompositePsiElement { protected RPsiFunctionBody(@NotNull ORLangTypes types, @NotNull IElementType elementType) { super(types, elementType); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiFunctionCall.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.psi.tree.*; import com.reason.lang.core.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; import java.util.*; public class RPsiFunctionCall extends ORCompositePsiElement { protected RPsiFunctionCall(@NotNull ORLangTypes types, @NotNull IElementType elementType) { super(types, elementType); } @Override public @NotNull String getName() { RPsiLowerSymbol name = ORUtil.findImmediateFirstChildOfClass(this, RPsiLowerSymbol.class); return name == null ? "" : name.getText(); } public @NotNull List getParameters() { return ORUtil.findImmediateChildrenOfClass( ORUtil.findImmediateFirstChildOfClass(this, RPsiParameters.class), RPsiParameterReference.class); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiFunctorBinding.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.psi.tree.IElementType; import com.reason.lang.core.type.*; import org.jetbrains.annotations.NotNull; public class RPsiFunctorBinding extends ORCompositePsiElement { protected RPsiFunctorBinding(@NotNull ORLangTypes types, @NotNull IElementType elementType) { super(types, elementType); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiFunctorCall.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.psi.tree.*; import com.intellij.psi.util.*; import com.reason.lang.core.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; import java.util.*; import static java.util.Collections.*; public class RPsiFunctorCall extends ORCompositePsiElement { protected RPsiFunctorCall(@NotNull ORLangTypes types, @NotNull IElementType elementType) { super(types, elementType); } public @Nullable RPsiUpperSymbol getReferenceIdentifier() { return ORUtil.findImmediateLastChildOfClass(this, RPsiUpperSymbol.class); } @Override public int getTextOffset() { RPsiUpperSymbol id = getReferenceIdentifier(); return id == null ? 0 : id.getTextOffset(); } @Override public @NotNull String getName() { RPsiUpperSymbol name = getReferenceIdentifier(); return name == null ? "" : name.getText(); } public @NotNull List getParameters() { RPsiParameters params = PsiTreeUtil.findChildOfType(this, RPsiParameters.class); return params == null ? emptyList() : ORUtil.findImmediateChildrenOfClass(params, RPsiParameterReference.class); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiFunctorImpl.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.lang.*; import com.intellij.navigation.*; import com.intellij.psi.*; import com.intellij.psi.stubs.*; import com.intellij.util.*; import com.reason.ide.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.stub.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; import javax.swing.*; import java.util.*; public class RPsiFunctorImpl extends RPsiTokenStub implements RPsiFunctor { // region Constructors public RPsiFunctorImpl(@NotNull ORLangTypes types, @NotNull ASTNode node) { super(types, node); } public RPsiFunctorImpl(@NotNull ORLangTypes types, @NotNull PsiModuleStub stub, @NotNull IStubElementType nodeType) { super(types, stub, nodeType); } // endregion // region PsiNamedElement @Override public @Nullable PsiElement getNameIdentifier() { return ORUtil.findImmediateFirstChildOfClass(this, RPsiUpperSymbol.class); } @Override public int getTextOffset() { PsiElement id = getNameIdentifier(); return id == null ? 0 : id.getTextOffset(); } @Override public @Nullable String getName() { PsiModuleStub stub = getGreenStub(); if (stub != null) { return stub.getName(); } PsiElement nameIdentifier = getNameIdentifier(); return nameIdentifier == null ? null : nameIdentifier.getText(); } @Override public @NotNull PsiElement setName(@NotNull String newName) throws IncorrectOperationException { PsiElement id = getNameIdentifier(); PsiElement newId = ORCodeFactory.createModuleName(getProject(), newName); if (id != null && newId != null) { id.replace(newId); } return this; } // endregion //region PsiQualifiedName @Override public @Nullable String[] getPath() { PsiModuleStub stub = getGreenStub(); if (stub != null) { return stub.getPath(); } return ORUtil.getQualifiedPath(this); } @Override public @NotNull String getQualifiedName() { PsiModuleStub stub = getGreenStub(); if (stub != null) { return stub.getQualifiedName(); } return ORUtil.getQualifiedName(this); } //endregion @Override public boolean isComponent() { return false; } @Override public @Nullable PsiElement getMakeFunction() { return null; } @Override public @Nullable PsiElement getBody() { return ORUtil.findImmediateFirstChildOfClass(this, RPsiFunctorBinding.class); } @Override public @NotNull String getModuleName() { String name = getName(); return name == null ? "" : name; } @Override public @NotNull List getParameters() { return ORUtil.findImmediateChildrenOfClass( ORUtil.findImmediateFirstChildOfClass(this, RPsiParameters.class), RPsiParameterDeclaration.class); } @Override public @Nullable RPsiFunctorResult getReturnType() { RPsiFunctorResult result = ORUtil.findImmediateFirstChildOfClass(this, RPsiFunctorResult.class); if (result == null) { // maybe it is a signature only RPsiModuleSignature signature = ORUtil.findImmediateFirstChildOfClass(this, RPsiModuleSignature.class); result = signature == null ? null : ORUtil.findImmediateFirstChildOfClass(signature, RPsiFunctorResult.class); } return result; } @Override public @NotNull List getConstraints() { RPsiConstraints constraints = ORUtil.findImmediateFirstChildOfClass(this, RPsiConstraints.class); if (constraints == null) { PsiElement colon = ORUtil.findImmediateFirstChildOfType(this, myTypes.COLON); PsiElement element = ORUtil.nextSibling(colon); constraints = element instanceof RPsiConstraints ? (RPsiConstraints) element : ORUtil.findImmediateFirstChildOfClass(element, RPsiConstraints.class); } return constraints == null ? Collections.emptyList() : ORUtil.findImmediateChildrenOfClass(constraints, RPsiTypeConstraint.class); } public ItemPresentation getPresentation() { return new ItemPresentation() { @Override public @Nullable String getPresentableText() { return getName(); } @Override public @Nullable String getLocationString() { return null; } @Override public @NotNull Icon getIcon(boolean unused) { return ORIcons.FUNCTOR; } }; } @Override public String toString() { return super.toString() + ":" + getModuleName(); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiFunctorResult.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.psi.tree.*; import com.reason.lang.core.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; public class RPsiFunctorResult extends ORCompositePsiElement { protected RPsiFunctorResult(@NotNull ORLangTypes types, @NotNull IElementType elementType) { super(types, elementType); } public @Nullable RPsiUpperSymbol getModuleType() { return ORUtil.findImmediateLastChildOfClass(this, RPsiUpperSymbol.class); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiGuard.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.psi.tree.*; import com.reason.lang.core.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; public class RPsiGuard extends ORCompositePsiElement { protected RPsiGuard(@NotNull ORLangTypes types, @NotNull IElementType elementType) { super(types, elementType); } @Nullable public RPsiBinaryCondition getCondition() { return ORUtil.findImmediateFirstChildOfClass(this, RPsiBinaryCondition.class); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiIfStatement.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.psi.*; import com.intellij.psi.tree.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; public class RPsiIfStatement extends ORCompositePsiElement implements RPsiConditional { protected RPsiIfStatement(@NotNull ORLangTypes types, @NotNull IElementType elementType) { super(types, elementType); } @Nullable public RPsiBinaryCondition getCondition() { return ORUtil.findImmediateFirstChildOfClass(this, RPsiBinaryCondition.class); } @Override public @Nullable PsiElement getThenExpression() { return ORUtil.findImmediateFirstChildOfType(this, myTypes.C_IF_THEN_ELSE); } @Override public @Nullable PsiElement getElseExpression() { return ORUtil.findImmediateLastChildOfType(this, myTypes.C_IF_THEN_ELSE); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiIncludeImpl.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.lang.*; import com.intellij.navigation.*; import com.intellij.psi.*; import com.intellij.psi.stubs.*; import com.intellij.psi.tree.*; import com.intellij.psi.util.*; import com.intellij.util.xml.model.gotosymbol.*; import com.reason.ide.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.stub.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; /** * Note: can’t implement getReference here */ public class RPsiIncludeImpl extends RPsiTokenStub implements RPsiInclude { // region Constructors public RPsiIncludeImpl(@NotNull ORLangTypes types, @NotNull ASTNode node) { super(types, node); } public RPsiIncludeImpl(@NotNull ORLangTypes types, @NotNull PsiIncludeStub stub, @NotNull IStubElementType nodeType) { super(types, stub, nodeType); } // endregion @Override public String[] getQualifiedPath() { PsiIncludeStub stub = getGreenStub(); if (stub != null) { return stub.getQualifiedPath(); } return ORUtil.getQualifiedPath(this); } @Override public @NotNull String getIncludePath() { PsiIncludeStub stub = getGreenStub(); if (stub != null) { return stub.getIncludePath(); } PsiElement firstModule = ORUtil.findImmediateFirstChildOfType(this, myTypes.A_MODULE_NAME); RPsiFunctorCall functorCall = ORUtil.findImmediateFirstChildOfClass(this, RPsiFunctorCall.class); if (functorCall != null) { String path = ""; if (firstModule != null) { path = ORUtil.getTextUntilTokenType(firstModule, (IElementType) myTypes.C_FUNCTOR_CALL); } return path + functorCall.getName(); } PsiElement firstChild = PsiTreeUtil.skipWhitespacesForward(getFirstChild()); return firstChild == null || firstChild instanceof RPsiModule ? "" : ORUtil.getTextUntilClass(firstChild, RPsiConstraints.class); } @Override public boolean useFunctor() { PsiElement firstChild = ORUtil.findImmediateFirstChildOfClass(this, RPsiFunctorCall.class); return firstChild != null; } @Override public ItemPresentation getPresentation() { return new GoToSymbolProvider.BaseNavigationItem(this, getIncludePath(), ORIcons.INCLUDE); } @Override public boolean canBeDisplayed() { return !(getParent() instanceof RPsiFunctionBody); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiInherit.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.psi.*; import com.intellij.psi.tree.*; import com.reason.lang.core.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; import java.util.*; import static java.util.Collections.emptyList; public class RPsiInherit extends ORCompositePsiElement { protected RPsiInherit(@NotNull ORLangTypes types, @NotNull IElementType elementType) { super(types, elementType); } public @Nullable PsiElement getClassTypeIdentifier() { return ORUtil.findImmediateFirstChildOfClass(this, RPsiLowerSymbol.class); } public @NotNull List getParameters() { RPsiParameters parameters = ORUtil.findImmediateFirstChildOfClass(this, RPsiParameters.class); return parameters != null ? parameters.getParametersList() : emptyList(); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiInnerModuleImpl.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.lang.*; import com.intellij.navigation.*; import com.intellij.psi.*; import com.intellij.psi.stubs.*; import com.intellij.util.*; import com.reason.ide.*; import com.reason.ide.files.*; import com.reason.lang.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.stub.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; import javax.swing.*; import java.util.*; public class RPsiInnerModuleImpl extends RPsiTokenStub implements RPsiInnerModule, PsiNameIdentifierOwner { // region Constructors public RPsiInnerModuleImpl(@NotNull ORLangTypes types, @NotNull ASTNode node) { super(types, node); } public RPsiInnerModuleImpl(@NotNull ORLangTypes types, @NotNull PsiModuleStub stub, @NotNull IStubElementType nodeType) { super(types, stub, nodeType); } // endregion // region NamedElement public @Nullable PsiElement getNameIdentifier() { return findChildByClass(RPsiUpperSymbol.class); } @Override public int getTextOffset() { PsiElement id = getNameIdentifier(); return id == null ? 0 : id.getTextOffset(); } @Override public @Nullable String getName() { PsiModuleStub stub = getGreenStub(); if (stub != null) { return stub.getName(); } PsiElement nameIdentifier = getNameIdentifier(); return nameIdentifier == null ? null : nameIdentifier.getText(); } @Override public @NotNull PsiElement setName(@NotNull String newName) throws IncorrectOperationException { PsiElement id = getNameIdentifier(); PsiElement newId = ORCodeFactory.createModuleName(getProject(), newName); if (id != null && newId != null) { id.replace(newId); } return this; } // endregion //region PsiQualifiedPath @Override public @Nullable String[] getPath() { PsiModuleStub stub = getGreenStub(); if (stub != null) { return stub.getPath(); } return ORUtil.getQualifiedPath(this); } @Override public @NotNull String getQualifiedName() { PsiModuleStub stub = getGreenStub(); if (stub != null) { return stub.getQualifiedName(); } return ORUtil.getQualifiedName(this); } //endregion @Override public @NotNull String getModuleName() { String name = getName(); return name == null ? "" : name; } @Override public @Nullable RPsiFunctorCall getFunctorCall() { return ORUtil.findImmediateFirstChildOfClass(getBody(), RPsiFunctorCall.class); } @Override public @Nullable PsiElement getBody() { return ORUtil.findImmediateFirstChildOfAnyClass(this, RPsiModuleBinding.class); } @Override public @NotNull List getConstraints() { RPsiConstraints constraints = ORUtil.findImmediateFirstChildOfClass(this, RPsiConstraints.class); return ORUtil.findImmediateChildrenOfClass(constraints, RPsiTypeConstraint.class); } @Override public @Nullable RPsiModuleSignature getModuleSignature() { PsiElement child = ORUtil.findImmediateFirstChildOfAnyClass(this, RPsiModuleSignature.class, RPsiScopedExpr.class); if (child instanceof RPsiScopedExpr) { child = ORUtil.findImmediateFirstChildOfClass(child, RPsiModuleSignature.class); } return child instanceof RPsiModuleSignature ? (RPsiModuleSignature) child : null; } @Override public boolean isModuleType() { PsiModuleStub stub = getGreenStub(); if (stub != null) { return stub.isModuleType(); } PsiElement psiElement = ORUtil.nextSibling(getFirstChild()); return psiElement != null && psiElement.getNode().getElementType() == myTypes.TYPE; } @Override public boolean isComponent() { PsiModuleStub stub = getGreenStub(); if (stub != null) { return stub.isComponent(); } return ModuleHelper.isComponent(getBody()); } @Override public @Nullable PsiElement getMakeFunction() { PsiElement make = ORUtil.findImmediateNamedChildOfClass(getBody(), RPsiLet.class, "make"); if (make == null) { make = ORUtil.findImmediateNamedChildOfClass(getBody(), RPsiExternal.class, "make"); } return make; } @Override public boolean isFunctorCall() { PsiModuleStub stub = getGreenStub(); if (stub != null) { return stub.isFunctorCall(); } return ORUtil.findImmediateFirstChildOfType(getBody(), myTypes.C_FUNCTOR_CALL) != null; } public @Nullable RPsiUpperSymbol getAliasSymbol() { RPsiModuleBinding binding = ORUtil.findImmediateFirstChildOfClass(this, RPsiModuleBinding.class); return binding == null ? null : ORUtil.findImmediateLastChildOfClass(binding, RPsiUpperSymbol.class); } @Override public @Nullable String getAlias() { PsiModuleStub stub = getGreenStub(); if (stub != null) { return stub.getAlias(); } RPsiModuleBinding binding = ORUtil.findImmediateFirstChildOfClass(this, RPsiModuleBinding.class); if (binding != null) { return ORUtil.computeAlias(binding.getFirstChild(), getLanguage(), false); } return null; } @Override public @Nullable RPsiUnpack getUnpack() { return ORUtil.findImmediateFirstChildOfClass(this, RPsiUnpack.class); } public ItemPresentation getPresentation() { //RPsiModuleSignature moduleSignature = getModuleSignature(); return new ItemPresentation() { @Override public @Nullable String getPresentableText() { return getName(); } @Override public @NotNull String getLocationString() { return getQualifiedName(); } @Override public @NotNull Icon getIcon(boolean unused) { return isModuleType() ? ORIcons.MODULE_TYPE : ((FileBase) getContainingFile()).isInterface() ? ORIcons.INNER_MODULE_INTF : ORIcons.INNER_MODULE; } }; } @Override public String toString() { return super.toString() + ":" + getModuleName(); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiInterpolation.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.psi.tree.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; public class RPsiInterpolation extends ORCompositePsiElement { protected RPsiInterpolation(@NotNull ORLangTypes types, @NotNull IElementType elementType) { super(types, elementType); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiInterpolationReference.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.psi.tree.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; public class RPsiInterpolationReference extends ORCompositePsiElement { protected RPsiInterpolationReference(@NotNull ORLangTypes types, @NotNull IElementType elementType) { super(types, elementType); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiJsObject.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.lang.*; import com.intellij.psi.*; import com.intellij.psi.tree.*; import com.reason.lang.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.type.*; import com.reason.lang.ocaml.*; import org.jetbrains.annotations.*; import java.util.*; public class RPsiJsObject extends ORCompositePsiElement implements RPsiLanguageConverter { protected RPsiJsObject(@NotNull ORLangTypes types, @NotNull IElementType elementType) { super(types, elementType); } public @NotNull List getFields() { return ORUtil.findImmediateChildrenOfClass(this, RPsiObjectField.class); } @Override public @NotNull String asText(@Nullable ORLanguageProperties toLang) { StringBuilder convertedText = null; Language fromLang = getLanguage(); if (fromLang != toLang) { convertedText = new StringBuilder(); boolean firstField = true; if (toLang == OclLanguage.INSTANCE) { // Convert to OCaml convertedText.append("<"); for (PsiElement element : getChildren()) { if (element instanceof RPsiObjectField) { if (firstField) { firstField = false; } else { convertedText.append("; "); } convertedText.append(((RPsiObjectField) element).asText(toLang)); } } convertedText.append("> Js.t"); } else { // Convert from OCaml convertedText.append("{. "); for (PsiElement element : getChildren()) { if (element instanceof RPsiObjectField) { if (firstField) { firstField = false; } else { convertedText.append(", "); } convertedText.append(((RPsiObjectField) element).asText(toLang)); } } convertedText.append(" }"); } } return convertedText == null ? getText() : convertedText.toString(); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiLeafPropertyName.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.psi.*; import com.intellij.psi.impl.source.tree.*; import com.intellij.psi.tree.*; import com.reason.ide.search.reference.*; import com.reason.lang.core.*; import org.jetbrains.annotations.*; public class RPsiLeafPropertyName extends LeafPsiElement { public RPsiLeafPropertyName(@NotNull IElementType type, CharSequence text) { super(type, text); } @Override public PsiReference getReference() { return new ORPsiPropertyNameReference(this, ORUtil.getTypes(getLanguage())); } @Override public @NotNull String toString() { return "RPsiLeafPropertyName"; } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiLetAttribute.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.psi.*; import com.intellij.psi.tree.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; public class RPsiLetAttribute extends ORCompositePsiElement { protected RPsiLetAttribute(@NotNull ORLangTypes types, @NotNull IElementType elementType) { super(types, elementType); } @Nullable String getValue() { PsiElement nextSibling = getFirstChild().getNextSibling(); return nextSibling == null ? null : nextSibling.getText(); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiLetBinding.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.psi.tree.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; public class RPsiLetBinding extends ORCompositePsiElement { public RPsiLetBinding(@NotNull ORLangTypes types, @NotNull IElementType elementType) { super(types, elementType); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiLetImpl.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.lang.*; import com.intellij.navigation.*; import com.intellij.psi.*; import com.intellij.psi.stubs.*; import com.intellij.psi.tree.*; import com.intellij.psi.util.*; import com.intellij.util.*; import com.reason.ide.*; import com.reason.lang.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.stub.*; import com.reason.lang.core.type.*; import com.reason.lang.ocaml.*; import org.jetbrains.annotations.*; import javax.swing.*; import java.util.*; import static java.util.Collections.*; public class RPsiLetImpl extends RPsiTokenStub implements RPsiLet { private RPsiSignature myInferredType; // region Constructors public RPsiLetImpl(@NotNull ORLangTypes types, @NotNull ASTNode node) { super(types, node); } public RPsiLetImpl(@NotNull ORLangTypes types, @NotNull PsiLetStub stub, @NotNull IStubElementType nodeType) { super(types, stub, nodeType); } // endregion // region PsiNamedElement public @Nullable PsiElement getNameIdentifier() { return ORUtil.findImmediateFirstChildOfAnyClass(this, RPsiLowerSymbol.class, RPsiScopedExpr.class, RPsiLiteralString.class/*rescript custom operator*/, RPsiUnit.class); } @Override public @Nullable String getName() { PsiLetStub stub = getGreenStub(); if (stub != null) { return stub.getName(); } PsiElement nameIdentifier = getNameIdentifier(); IElementType nameType = nameIdentifier == null ? null : nameIdentifier.getNode().getElementType(); return nameType == null || nameType == myTypes.UNDERSCORE || nameType == myTypes.C_UNIT ? null : nameIdentifier.getText(); } @Override public @NotNull PsiElement setName(@NotNull String name) throws IncorrectOperationException { PsiElement id = getNameIdentifier(); PsiElement newId = ORCodeFactory.createLetName(getProject(), name); // deconstruction ??? if (id != null && newId != null) { id.replace(newId); } return this; } // endregion @Override public int getTextOffset() { PsiElement id = getNameIdentifier(); return id == null ? 0 : id.getTextOffset(); } //region PsiQualifiedName @Override public @NotNull String[] getPath() { PsiLetStub stub = getGreenStub(); if (stub != null) { return stub.getPath(); } return ORUtil.getQualifiedPath(this); } @Override public @NotNull String getQualifiedName() { PsiLetStub stub = getGreenStub(); if (stub != null) { return stub.getQualifiedName(); } return ORUtil.getQualifiedName(this); } //endregion @Override public @Nullable RPsiLetBinding getBinding() { return findChildByClass(RPsiLetBinding.class); } @Override public boolean isScopeIdentifier() { return ORUtil.findImmediateFirstChildOfClass(this, RPsiDeconstruction.class) != null; } @Override public @NotNull Collection getScopeChildren() { Collection result = new ArrayList<>(); PsiElement scope = ORUtil.findImmediateFirstChildOfClass(this, RPsiDeconstruction.class); if (scope != null) { for (PsiElement element : scope.getChildren()) { if (element.getNode().getElementType() != myTypes.COMMA) { result.add(element); } } } return result; } @Override public @Nullable String getAlias() { PsiLetStub stub = getGreenStub(); if (stub != null) { return stub.getAlias(); } PsiElement binding = getBinding(); if (binding != null) { return ORUtil.computeAlias(binding.getFirstChild(), getLanguage(), true); } return null; } @Override public @Nullable RPsiSignature getSignature() { RPsiSignature signature = findChildByClass(RPsiSignature.class); if (signature == null && myTypes == OclTypes.INSTANCE) { // maybe an OCaml first class module RPsiFirstClass firstClassModule = getFirstClassModule(); return PsiTreeUtil.getChildOfType(firstClassModule, RPsiModuleSignature.class); } return signature; } @Override public boolean isFunction() { PsiLetStub stub = getGreenStub(); if (stub != null) { return stub.isFunction(); } RPsiSignature inferredType = getInferredType(); if (inferredType != null) { return inferredType.isFunction(); } else { RPsiSignature signature = getSignature(); if (signature != null) { return signature.isFunction(); } } RPsiLetBinding binding = findChildByClass(RPsiLetBinding.class); return binding != null && binding.getFirstChild() instanceof RPsiFunction; } public @Nullable RPsiFunction getFunction() { RPsiLetBinding binding = getBinding(); if (binding != null) { PsiElement child = binding.getFirstChild(); if (child instanceof RPsiFunction) { return (RPsiFunction) child; } } return null; } @Override public boolean isComponent() { PsiLetStub stub = getGreenStub(); if (stub != null) { return stub.isComponent(); } if ("make".equals(getName())) { List annotations = ORUtil.prevAnnotations(this); return annotations.stream().anyMatch(annotation -> { String name = annotation.getName(); return name != null && name.contains("react.component"); }); } return false; } @Override public boolean isRecord() { return findChildByClass(RPsiRecord.class) != null; } @Override public boolean isJsObject() { RPsiLetBinding binding = getBinding(); return binding != null && binding.getFirstChild() instanceof RPsiJsObject; } @Override public @NotNull Collection getJsObjectFields() { RPsiLetBinding binding = getBinding(); PsiElement firstChild = binding == null ? null : binding.getFirstChild(); RPsiJsObject jsObject = firstChild instanceof RPsiJsObject ? ((RPsiJsObject) firstChild) : null; return jsObject == null ? emptyList() : jsObject.getFields(); } @Override public @NotNull Collection getRecordFields() { RPsiLetBinding binding = getBinding(); PsiElement firstChild = binding != null ? binding.getFirstChild() : null; RPsiRecord record = firstChild instanceof RPsiRecord ? ((RPsiRecord) firstChild) : null; return record != null ? record.getFields() : emptyList(); } private boolean isRecursive() { // Find first element after the LET PsiElement firstChild = getFirstChild(); PsiElement sibling = firstChild.getNextSibling(); if (sibling instanceof PsiWhiteSpace) { sibling = sibling.getNextSibling(); } return sibling != null && "rec".equals(sibling.getText()); } // region Inferred type @Override public @Nullable RPsiSignature getInferredType() { return myInferredType; } @Override public void setInferredType(@NotNull RPsiSignature inferredType) { myInferredType = inferredType; } @Override public boolean hasInferredType() { return myInferredType != null; } // endregion @Override public boolean isPrivate() { RPsiLetAttribute attribute = ORUtil.findImmediateFirstChildOfClass(this, RPsiLetAttribute.class); String value = attribute == null ? null : attribute.getValue(); return value != null && value.equals("private"); } @Override public @Nullable RPsiFirstClass getFirstClassModule() { return ORUtil.findImmediateFirstChildOfClass(this, RPsiFirstClass.class); } @Override public boolean isAnonymous() { return getName() == null; } @Override public boolean isDeconstruction() { return ORUtil.findImmediateFirstChildOfAnyClass(this, RPsiDeconstruction.class) != null; } @Override public @NotNull List getDeconstructedElements() { PsiElement child = ORUtil.findImmediateFirstChildOfAnyClass(this, RPsiDeconstruction.class); return child instanceof RPsiDeconstruction deconstruction ? deconstruction.getDeconstructedElements() : emptyList(); } // region RPsiStructuredElement @Override public boolean canBeDisplayed() { PsiElement nameIdentifier = getNameIdentifier(); if (nameIdentifier instanceof RPsiUnit) { return false; } if (nameIdentifier != null) { return true; } PsiElement deconstruction = ORUtil.findImmediateFirstChildOfType(this, myTypes.C_DECONSTRUCTION); if (deconstruction != null) { return true; } PsiElement underscore = ORUtil.findImmediateFirstChildOfType(this, myTypes.UNDERSCORE); if (underscore != null) { return false; } RPsiScopedExpr scope = ORUtil.findImmediateFirstChildOfClass(this, RPsiScopedExpr.class); return scope != null && !scope.isEmpty(); } @Nullable @Override public ItemPresentation getPresentation() { final RPsiLet let = this; return new ItemPresentation() { @NotNull @Override public String getPresentableText() { PsiElement letValueName = getNameIdentifier(); if (letValueName == null) { RPsiScopedExpr scope = ORUtil.findImmediateFirstChildOfClass(let, RPsiScopedExpr.class); return scope == null || scope.isEmpty() ? "_" : scope.getText(); } String letName = letValueName.getText(); if (isFunction()) { return letName + (isRecursive() ? " (rec)" : ""); } return letName; } @Override public @Nullable String getLocationString() { RPsiSignature signature = hasInferredType() ? getInferredType() : getSignature(); return (signature == null ? null : signature.asText(ORLanguageProperties.cast(getLanguage()))); } @Override public @NotNull Icon getIcon(boolean unused) { return isFunction() ? ORIcons.FUNCTION : ORIcons.LET; } }; } // endregion @Override public String toString() { return "RPsiLet: " + getName(); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiLiteralString.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.psi.*; import com.intellij.psi.impl.source.tree.*; import com.intellij.psi.tree.*; import com.reason.ide.search.reference.*; import com.reason.lang.core.type.*; import com.reason.lang.rescript.*; import org.jetbrains.annotations.*; public class RPsiLiteralString extends LeafPsiElement { private final ORLangTypes myTypes; public RPsiLiteralString(@NotNull ORLangTypes types, @NotNull IElementType type, CharSequence text) { super(type, text); myTypes = types; } @Override public @Nullable PsiReference getReference() { return myTypes instanceof ResTypes ? new ORPsiLiteralStringReference(this, myTypes) : null; } @Override public @NotNull String toString() { return "RPsiLiteralString"; } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiLocalOpen.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.psi.tree.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; public class RPsiLocalOpen extends ORCompositePsiElement { protected RPsiLocalOpen(@NotNull ORLangTypes types, @NotNull IElementType elementType) { super(types, elementType); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiLowerName.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.openapi.util.*; import com.intellij.psi.*; import com.intellij.psi.tree.*; import com.intellij.util.*; import com.reason.lang.core.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; public class RPsiLowerName extends ORCompositePsiElement implements PsiNamedElement, PsiNameIdentifierOwner { // region Constructors protected RPsiLowerName(@NotNull ORLangTypes types, @NotNull IElementType elementType) { super(types, elementType); } // endregion @Override public @Nullable PsiElement getNameIdentifier() { return ORUtil.findImmediateFirstChildOfAnyClass(this, RPsiLowerSymbol.class); } @Override public @Nullable String getName() { PsiElement nameIdentifier = getNameIdentifier(); return nameIdentifier != null ? nameIdentifier.getText() : null; } @Override public PsiElement setName(@NlsSafe @NotNull String s) throws IncorrectOperationException { return null; } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiLowerSymbol.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.psi.impl.source.tree.*; import com.intellij.psi.tree.*; import com.reason.ide.search.reference.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; public class RPsiLowerSymbol extends LeafPsiElement { protected final ORLangTypes myTypes; // region Constructors public RPsiLowerSymbol(@NotNull ORLangTypes types, @NotNull IElementType tokenType, @NotNull CharSequence text) { super(tokenType, text); myTypes = types; } // endregion @Override public @NotNull ORPsiLowerSymbolReference getReference() { return new ORPsiLowerSymbolReference(this, myTypes); } @Override public @NotNull String toString() { return "RPsiLowerSymbol:" + getElementType() + " (" + getText() + ")"; } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiMacro.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.lang.*; import com.intellij.psi.*; import com.intellij.psi.tree.*; import com.reason.lang.core.*; import com.reason.lang.core.type.*; import com.reason.lang.reason.*; import org.jetbrains.annotations.*; public class RPsiMacro extends ORCompositePsiElement { protected RPsiMacro(@NotNull ORLangTypes types, @NotNull IElementType elementType) { super(types, elementType); } @Override public @Nullable String getName() { PsiElement name = ORUtil.findImmediateFirstChildOfClass(this, RPsiMacroName.class); return name == null ? null : name.getText(); } public @Nullable RPsiMacroBody getContent() { return ORUtil.findImmediateFirstChildOfClass(this, RPsiMacroBody.class); } public boolean isRoot() { PsiElement name = ORUtil.findImmediateFirstChildOfClass(this, RPsiMacroName.class); if (name == null) { return false; } if (getLanguage() == RmlLanguage.INSTANCE) { PsiElement firstChild = getFirstChild(); ASTNode node = firstChild == null ? null : firstChild.getNode(); return node != null && node.getElementType() != RmlTypes.INSTANCE.LBRACKET; } return name.getText().startsWith("%%"); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiMacroBody.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.json.psi.impl.*; import com.intellij.lang.*; import com.intellij.openapi.util.*; import com.intellij.psi.*; import com.intellij.psi.impl.source.tree.*; import com.intellij.psi.tree.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; public class RPsiMacroBody extends ORCompositePsiElement implements PsiLanguageInjectionHost { protected RPsiMacroBody(@NotNull ORLangTypes types, @NotNull IElementType elementType) { super(types, elementType); } @Override public boolean isValidHost() { return true; } @Override public @NotNull PsiLanguageInjectionHost updateText(@NotNull String text) { ASTNode valueNode = getNode().getFirstChildNode(); if (valueNode instanceof LeafElement) { ((LeafElement) valueNode).replaceWithText(text); } return this; } @Override public @NotNull LiteralTextEscaper createLiteralTextEscaper() { return new JSStringLiteralEscaper(this) { @Override protected boolean isRegExpLiteral() { return false; } }; } public @Nullable TextRange getMacroTextRange() { ASTNode firstChildNode = getNode().getFirstChildNode(); IElementType elementType = firstChildNode == null ? null : firstChildNode.getElementType(); if (elementType == myTypes.STRING_VALUE || elementType == myTypes.ML_STRING_VALUE || elementType == myTypes.C_INTERPOLATION_EXPR) { int max = getTextLength() - 1; if (1 <= max) { return new TextRange(1, max); } } else { int max = getTextLength() - 2; if (2 <= max) { return new TextRange(2, max); } } return null; } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiMacroName.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.psi.tree.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; public class RPsiMacroName extends ORCompositePsiElement { protected RPsiMacroName(@NotNull ORLangTypes types, @NotNull IElementType elementType) { super(types, elementType); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiMethodCall.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.psi.tree.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; public class RPsiMethodCall extends ORCompositePsiElement { protected RPsiMethodCall(@NotNull ORLangTypes types, @NotNull IElementType elementType) { super(types, elementType); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiMixinField.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.psi.tree.IElementType; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; public class RPsiMixinField extends ORCompositePsiElement { protected RPsiMixinField(@NotNull ORLangTypes types, @NotNull IElementType elementType) { super(types, elementType); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiModuleBinding.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.psi.tree.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; public class RPsiModuleBinding extends ORCompositePsiElement { public RPsiModuleBinding(@NotNull ORLangTypes types, @NotNull IElementType elementType) { super(types, elementType); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiModuleSignature.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.navigation.*; import com.intellij.psi.*; import com.intellij.psi.tree.*; import com.reason.ide.*; import com.reason.lang.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.type.*; import com.reason.lang.rescript.*; import org.jetbrains.annotations.*; import javax.swing.*; import java.util.*; import static java.util.Collections.*; /* module M : Signature = ... module M : { /...signature/ } = ... */ public class RPsiModuleSignature extends ORCompositePsiElement implements RPsiSignature { public static final String[] EMPTY_PATH = new String[0]; protected RPsiModuleSignature(@NotNull ORLangTypes types, @NotNull IElementType elementType) { super(types, elementType); } public @Nullable RPsiUpperSymbol getNameIdentifier() { if (myTypes == ResTypes.INSTANCE) { RPsiScopedExpr scope = ORUtil.findImmediateFirstChildOfClass(this, RPsiScopedExpr.class); PsiElement rootElement = scope != null ? scope : this; return ORUtil.findImmediateLastChildOfClass(rootElement, RPsiUpperSymbol.class); } return ORUtil.findImmediateLastChildOfClass(this, RPsiUpperSymbol.class); } @Override public @Nullable String getName() { // used when default usage PsiElement nameIdentifier = getNameIdentifier(); return nameIdentifier != null ? nameIdentifier.getText() : null; } public @NotNull String getQName() { // used when first class module signature String text; if (myTypes == ResTypes.INSTANCE) { RPsiScopedExpr scope = ORUtil.findImmediateFirstChildOfClass(this, RPsiScopedExpr.class); text = scope != null ? scope.getInnerText() : ""; } else { RPsiUpperSymbol firstModuleIdentifier = ORUtil.findImmediateFirstChildOfClass(this, RPsiUpperSymbol.class); text = ORUtil.getLongIdent(firstModuleIdentifier); } return text; } @Override public @NotNull ItemPresentation getPresentation() { return new ItemPresentation() { @Override public @NotNull String getPresentableText() { String name = getName(); return name != null ? name : ""; } @Override public @NotNull String getLocationString() { return ""; } @Override public @NotNull Icon getIcon(boolean unused) { return ORIcons.MODULE_TYPE; } }; } @Override public @NotNull String asText(@Nullable ORLanguageProperties language) { return getText(); } @Override public boolean isFunction() { return false; } @Override public @NotNull List getItems() { return emptyList(); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiMultiLineInterpolator.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.psi.tree.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; public class RPsiMultiLineInterpolator extends ORCompositePsiElement { protected RPsiMultiLineInterpolator(@NotNull ORLangTypes types, @NotNull IElementType elementType) { super(types, elementType); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiObject.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.lang.*; import com.intellij.psi.tree.*; import com.reason.lang.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.type.*; import com.reason.lang.ocaml.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; import java.util.*; import java.util.stream.*; public class RPsiObject extends ORCompositePsiElement implements RPsiLanguageConverter { protected RPsiObject(@NotNull ORLangTypes types, @NotNull IElementType elementType) { super(types, elementType); } @Override public @NotNull String asText(@Nullable ORLanguageProperties toLang) { StringBuilder convertedText = null; Language fromLang = getLanguage(); if (fromLang != toLang) { if (toLang != null && toLang != OclLanguage.INSTANCE) { convertedText = new StringBuilder(); convertedText.append("{. "); List conversions = getFields().stream().map(item -> item.asText(toLang)).collect(Collectors.toList()); convertedText.append(Joiner.join(toLang.getParameterSeparator(), conversions)); convertedText.append(" }"); } } return convertedText == null ? getText() : convertedText.toString(); } public @NotNull List getFields() { return ORUtil.findImmediateChildrenOfClass(this, RPsiObjectField.class); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiObjectField.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.lang.*; import com.intellij.psi.*; import com.intellij.psi.stubs.*; import com.intellij.util.*; import com.reason.lang.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.stub.*; import com.reason.lang.core.type.*; import com.reason.lang.ocaml.*; import org.jetbrains.annotations.*; public class RPsiObjectField extends RPsiTokenStub implements RPsiField, PsiNameIdentifierOwner, RPsiLanguageConverter, RPsiQualifiedPathElement, RPsiSignatureElement, StubBasedPsiElement { // region Constructors public RPsiObjectField(@NotNull ORLangTypes types, @NotNull ASTNode node) { super(types, node); } public RPsiObjectField(@NotNull ORLangTypes types, @NotNull PsiObjectFieldStub stub, @NotNull IStubElementType nodeType) { super(types, stub, nodeType); } // endregion @Override public @Nullable PsiElement getNameIdentifier() { return getFirstChild(); } @Override public @NotNull String getName() { PsiObjectFieldStub stub = getGreenStub(); if (stub != null) { String name = stub.getName(); return name == null ? "" : name; } PsiElement nameElement = getNameIdentifier(); return nameElement == null ? "" : nameElement.getText().replaceAll("\"", ""); } @Override public @Nullable PsiElement setName(@NotNull String name) throws IncorrectOperationException { return null; } //region PsiQualifiedName @Override public @NotNull String[] getPath() { PsiObjectFieldStub stub = getGreenStub(); if (stub != null) { return stub.getPath(); } return ORUtil.getQualifiedPath(this); } @Override public @NotNull String getQualifiedName() { PsiObjectFieldStub stub = getGreenStub(); if (stub != null) { return stub.getQualifiedName(); } return ORUtil.getQualifiedName(this); } //endregion @Override public @Nullable RPsiSignature getSignature() { return ORUtil.findImmediateFirstChildOfClass(this, RPsiSignature.class); } @Override public @NotNull String asText(@Nullable ORLanguageProperties toLang) { StringBuilder convertedText = null; Language fromLang = getLanguage(); if (fromLang != toLang) { if (toLang != OclLanguage.INSTANCE) { convertedText = new StringBuilder(); // Convert from OCaml to Reason PsiElement nameIdentifier = getNameIdentifier(); if (nameIdentifier == null) { convertedText.append(getText()); } else { RPsiFieldValue fieldValue = getValue(); PsiElement value; if (fieldValue == null) { value = ORUtil.findImmediateFirstChildOfClass(this, RPsiSignature.class); } else { value = fieldValue.getFirstChild(); } String valueAsText = ""; if (value instanceof RPsiLanguageConverter) { valueAsText = ((RPsiLanguageConverter) value).asText(toLang); } else if (value != null) { valueAsText = value.getText(); } convertedText.append(nameIdentifier.getText()).append(":").append(valueAsText); } } } return convertedText == null ? getText() : convertedText.toString(); } @Nullable public RPsiFieldValue getValue() { return ORUtil.findImmediateFirstChildOfClass(this, RPsiFieldValue.class); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiOpenImpl.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.lang.*; import com.intellij.navigation.*; import com.intellij.psi.*; import com.intellij.psi.stubs.*; import com.intellij.psi.tree.*; import com.intellij.psi.util.*; import com.intellij.util.xml.model.gotosymbol.*; import com.reason.ide.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.stub.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; /** * Note: can’t implement getReference here */ public class RPsiOpenImpl extends RPsiTokenStub implements RPsiOpen { // region Constructors public RPsiOpenImpl(@NotNull ORLangTypes types, @NotNull ASTNode node) { super(types, node); } public RPsiOpenImpl(@NotNull ORLangTypes types, @NotNull PsiOpenStub stub, @NotNull IStubElementType nodeType) { super(types, stub, nodeType); } // endregion public @NotNull String getPath() { PsiOpenStub stub = getGreenStub(); if (stub != null) { String openPath = stub.getOpenPath(); return openPath == null ? "" : openPath; } PsiElement firstModule = ORUtil.findImmediateFirstChildOfType(this, myTypes.A_MODULE_NAME); RPsiFunctorCall functorCall = ORUtil.findImmediateFirstChildOfClass(this, RPsiFunctorCall.class); if (functorCall != null) { String path = ""; if (firstModule != null) { path = ORUtil.getTextUntilTokenType(firstModule, (IElementType) myTypes.C_FUNCTOR_CALL); } return path + functorCall.getName(); } // Skip `let` and `open` PsiElement firstChild = getFirstChild(); if (firstChild != null && firstChild.getNode().getElementType() == myTypes.LET) { // `let open` in OCaml firstChild = ORUtil.nextSibling(firstChild); } // Skip force open PsiElement child = PsiTreeUtil.skipWhitespacesForward(firstChild); if (child != null && child.getNode().getElementType() == myTypes.EXCLAMATION_MARK) { child = PsiTreeUtil.skipWhitespacesForward(child); } return child == null ? "" : ORUtil.getTextUntilTokenType(child, null); } @Override public boolean useFunctor() { PsiElement firstChild = ORUtil.findImmediateFirstChildOfClass(this, RPsiFunctorCall.class); return firstChild != null; } @Override public boolean canBeDisplayed() { return !(getParent() instanceof RPsiFunctionBody); } @Override public @NotNull ItemPresentation getPresentation() { return new GoToSymbolProvider.BaseNavigationItem(this, getPath(), ORIcons.OPEN); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiOption.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.lang.*; import com.intellij.psi.*; import com.intellij.psi.tree.*; import com.reason.lang.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.type.*; import com.reason.lang.ocaml.*; import org.jetbrains.annotations.*; public class RPsiOption extends ORCompositePsiElement implements RPsiLanguageConverter { protected RPsiOption(@NotNull ORLangTypes types, @NotNull IElementType elementType) { super(types, elementType); } @Override public @NotNull String asText(@Nullable ORLanguageProperties toLang) { StringBuilder convertedText = null; Language fromLang = getLanguage(); if (fromLang != toLang && toLang != null) { convertedText = new StringBuilder(); if (toLang == OclLanguage.INSTANCE) { // Convert from Reason/Rescript to OCaml RPsiScopedExpr scope = ORUtil.findImmediateFirstChildOfClass(this, RPsiScopedExpr.class); if (scope != null) { String scopeText = scope.getText(); convertedText.append(scopeText, 1, scopeText.length() - 1).append(" option"); } } else if (fromLang == OclLanguage.INSTANCE) { // Convert from OCaml PsiElement[] children = getChildren(); if (children.length > 2) { int lastChild = children[children.length - 2] instanceof PsiWhiteSpace ? children.length - 3 : children.length - 2; convertedText.append("option"); convertedText.append(toLang.getTemplateStart()); for (int i = 0; i <= lastChild; i++) { PsiElement child = children[i]; IElementType childElementType = child.getNode().getElementType(); if (childElementType != myTypes.OPTION) { convertedText.append(child.getText()); } } convertedText.append(toLang.getTemplateEnd()); } else { convertedText.append(getText()); } } else { convertedText.append(getText()); } } return convertedText == null ? getText() : convertedText.toString(); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiOptionValue.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.psi.tree.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; public class RPsiOptionValue extends ORCompositePsiElement { protected RPsiOptionValue(@NotNull ORLangTypes types, @NotNull IElementType elementType) { super(types, elementType); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiParameterDeclarationImpl.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.lang.*; import com.intellij.psi.*; import com.intellij.psi.stubs.*; import com.intellij.psi.tree.*; import com.intellij.psi.util.*; import com.intellij.util.*; import com.reason.lang.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.stub.*; import com.reason.lang.core.type.*; import com.reason.lang.ocaml.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; import java.util.*; public class RPsiParameterDeclarationImpl extends RPsiTokenStub implements RPsiParameterDeclaration { // region Constructors public RPsiParameterDeclarationImpl(@NotNull ORLangTypes types, @NotNull ASTNode node) { super(types, node); } public RPsiParameterDeclarationImpl(@NotNull ORLangTypes types, @NotNull PsiParameterDeclarationStub stub, @NotNull IStubElementType nodeType) { super(types, stub, nodeType); } // endregion //region PsiNamedElement public @Nullable PsiElement getNameIdentifier() { PsiElement parent = getParent(); PsiElement grandParent = parent == null ? null : parent.getParent(); if (grandParent instanceof RPsiFunctionCall || grandParent instanceof RPsiFunctorCall) { return null; } PsiElement identifier = getFirstChild(); IElementType elementType = identifier == null ? null : identifier.getNode().getElementType(); if (elementType == myTypes.TILDE || elementType == myTypes.LPAREN || elementType == myTypes.QUESTION_MARK) { PsiElement nextSibling = identifier.getNextSibling(); IElementType nextElementType = nextSibling == null ? null : nextSibling.getNode().getElementType(); identifier = nextElementType == myTypes.LPAREN ? nextSibling.getNextSibling() : nextSibling; } return identifier; } @Override public @Nullable String getName() { PsiParameterDeclarationStub stub = getGreenStub(); if (stub != null) { return stub.getName(); } PsiElement identifier = getNameIdentifier(); if (identifier != null) { return identifier.getText(); } PsiElement parent = getParent(); if (parent instanceof RPsiParameters) { List parameters = ((RPsiParameters) parent).getParametersList(); int i = 0; for (PsiElement parameter : parameters) { if (parameter == this) { PsiElement prevSibling = ORUtil.prevSibling(parent); return (prevSibling == null ? "" : prevSibling.getText()) + "[" + i + "]"; } i++; } } return null; } @Override public @NotNull PsiElement setName(@NotNull String name) throws IncorrectOperationException { return this; } //endregion //region PsiQualifiedName @Override public @Nullable String[] getPath() { PsiParameterDeclarationStub stub = getGreenStub(); if (stub != null) { return stub.getPath(); } PsiQualifiedNamedElement qualifiedParent = PsiTreeUtil.getParentOfType(this, PsiQualifiedNamedElement.class); String qName = qualifiedParent == null ? null : qualifiedParent.getQualifiedName(); return qName == null ? null : qName.split("\\."); } @Override public @NotNull String getQualifiedName() { PsiParameterDeclarationStub stub = getGreenStub(); if (stub != null) { return stub.getQualifiedName(); } String name = getName(); String[] path = getPath(); return Joiner.join(".", path) + "[" + name + "]"; } //endregion @Override public @Nullable RPsiSignature getSignature() { return PsiTreeUtil.findChildOfType(this, RPsiSignature.class); } @Override public @Nullable RPsiDefaultValue getDefaultValue() { return ORUtil.findImmediateFirstChildOfClass(this, RPsiDefaultValue.class); } @Override public boolean isOptional() { return getDefaultValue() != null; } public boolean isNamed() { if (getLanguage() == OclLanguage.INSTANCE) { // a signature ? return ORUtil.findImmediateFirstChildOfClass(this, RPsiSignature.class) != null; } PsiElement firstChild = getFirstChild(); return firstChild != null && firstChild.getNode().getElementType() == myTypes.TILDE; } @Override public @NotNull String asText(@Nullable ORLanguageProperties toLang) { StringBuilder convertedText = null; Language fromLang = getLanguage(); if (fromLang != toLang && isNamed()) { if (fromLang == OclLanguage.INSTANCE) { convertedText = new StringBuilder(); convertedText.append("~").append(getName()); RPsiSignature signature = getSignature(); if (signature != null) { convertedText.append(":").append(signature.asText(toLang)); } } } return convertedText == null ? getText() : convertedText.toString(); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiParameterReference.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.psi.*; import com.intellij.psi.tree.*; import com.reason.lang.core.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; public class RPsiParameterReference extends ORCompositePsiElement { protected RPsiParameterReference(@NotNull ORLangTypes types, @NotNull IElementType elementType) { super(types, elementType); } @Override public String getName() { PsiElement nameIdentifier = null; PsiElement firstChild = getFirstChild(); if (firstChild != null && firstChild.getNode().getElementType() == myTypes.TILDE) { nameIdentifier = ORUtil.nextSiblingWithTokenType(firstChild, myTypes.LIDENT); } return nameIdentifier == null ? null : nameIdentifier.getText(); } public @Nullable PsiElement getValue() { return ORUtil.findImmediateFirstChildOfClass(this, RPsiDefaultValue.class); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiParameters.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.psi.*; import com.intellij.psi.tree.*; import com.reason.lang.core.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; import java.util.*; public class RPsiParameters extends ORCompositePsiElement implements PsiElement { protected RPsiParameters(@NotNull ORLangTypes types, @NotNull IElementType elementType) { super(types, elementType); } public @NotNull List getParametersList() { PsiElement parent = getParent(); boolean isCall = parent instanceof RPsiFunctionCall || parent instanceof RPsiFunctorCall || parent instanceof RPsiInherit; return ORUtil.findImmediateChildrenOfType(this, isCall ? myTypes.C_PARAM : myTypes.C_PARAM_DECLARATION); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiPatternMatch.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.psi.tree.*; import com.reason.lang.core.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; public class RPsiPatternMatch extends ORCompositePsiElement { protected RPsiPatternMatch(@NotNull ORLangTypes types, @NotNull IElementType elementType) { super(types, elementType); } public @Nullable RPsiPatternMatchBody getBody() { return ORUtil.findImmediateFirstChildOfClass(this, RPsiPatternMatchBody.class); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiPatternMatchBody.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.psi.tree.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; public class RPsiPatternMatchBody extends ORCompositePsiElement { protected RPsiPatternMatchBody(@NotNull ORLangTypes types, @NotNull IElementType elementType) { super(types, elementType); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiPolyVariantConstraint.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.psi.*; import com.intellij.psi.tree.*; import com.reason.lang.core.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; public class RPsiPolyVariantConstraint extends ORCompositePsiElement { protected RPsiPolyVariantConstraint(@NotNull ORLangTypes types, @NotNull IElementType elementType) { super(types, elementType); } public boolean isOpen() { PsiElement bracket = ORUtil.findImmediateFirstChildOfType(this, myTypes.LBRACKET); PsiElement open = bracket == null ? null : bracket.getNextSibling(); return open != null && open.getNode().getElementType() == myTypes.GT; } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiRecord.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.psi.*; import com.intellij.psi.tree.*; import com.intellij.util.containers.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; import java.util.*; public class RPsiRecord extends ORCompositePsiElement { protected RPsiRecord(@NotNull ORLangTypes types, @NotNull IElementType elementType) { super(types, elementType); } public @NotNull List getFields() { PsiElement[] children = getChildren(); if (children.length == 0) { return ContainerUtil.emptyList(); } List result = new ArrayList<>(children.length); for (PsiElement child : children) { if (child instanceof RPsiRecordField) { result.add((RPsiRecordField) child); } } return result; } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiRecordFieldImpl.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.lang.*; import com.intellij.navigation.*; import com.intellij.psi.*; import com.intellij.psi.stubs.*; import com.intellij.psi.util.*; import com.intellij.util.*; import com.reason.ide.*; import com.reason.lang.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.stub.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; import javax.swing.*; public class RPsiRecordFieldImpl extends RPsiTokenStub implements RPsiRecordField { // region Constructors public RPsiRecordFieldImpl(@NotNull ORLangTypes types, @NotNull ASTNode node) { super(types, node); } public RPsiRecordFieldImpl(@NotNull ORLangTypes types, @NotNull RsiRecordFieldStub stub, @NotNull IStubElementType nodeType) { super(types, stub, nodeType); } // endregion // region PsiNamedElement @Override public @Nullable PsiElement getNameIdentifier() { return ORUtil.findImmediateFirstChildOfClass(this, RPsiLowerSymbol.class); } @Override public @Nullable String getName() { RsiRecordFieldStub stub = getGreenStub(); if (stub != null) { return stub.getName(); } PsiElement nameElement = getNameIdentifier(); return nameElement == null ? "" : nameElement.getText().replaceAll("\"", ""); } @Override public @NotNull PsiElement setName(@NotNull String name) throws IncorrectOperationException { return this; } // endregion //region PsiQualifiedName @Override public @NotNull String[] getPath() { RsiRecordFieldStub stub = getGreenStub(); if (stub != null) { return stub.getPath(); } return ORUtil.getQualifiedPath(this); } @Override public @NotNull String getQualifiedName() { RsiRecordFieldStub stub = getGreenStub(); if (stub != null) { return stub.getQualifiedName(); } return ORUtil.getQualifiedName(this); } //endregion @Override public @Nullable RPsiSignature getSignature() { return PsiTreeUtil.findChildOfType(this, RPsiSignature.class); } @Override public @Nullable RPsiFieldValue getValue() { return ORUtil.findImmediateFirstChildOfClass(this, RPsiFieldValue.class); } @Override public ItemPresentation getPresentation() { return new ItemPresentation() { @Override public @Nullable String getPresentableText() { return getName(); } @Override public @Nullable String getLocationString() { RPsiSignature signature = getSignature(); return signature == null ? null : signature.asText(ORLanguageProperties.cast(getLanguage())); } @Override public @NotNull Icon getIcon(boolean unused) { return ORIcons.VAL; } }; } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiScopedExpr.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.psi.*; import com.intellij.psi.tree.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; public class RPsiScopedExpr extends ORCompositePsiElement { protected RPsiScopedExpr(@NotNull ORLangTypes types, @NotNull IElementType elementType) { super(types, elementType); } public boolean isEmpty() { PsiElement firstChild = getFirstChild(); IElementType firstType = firstChild == null ? null : firstChild.getNode().getElementType(); if (firstType == myTypes.LPAREN) { assert firstChild != null; PsiElement secondChild = firstChild.getNextSibling(); IElementType secondType = secondChild == null ? null : secondChild.getNode().getElementType(); return secondType == myTypes.RPAREN; } return false; } public @NotNull String getInnerText() { return getText().substring(1, getTextLength() - 1); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiSignatureImpl.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.psi.tree.*; import com.reason.lang.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.type.*; import com.reason.lang.reason.*; import com.reason.lang.rescript.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; import java.util.*; import java.util.stream.*; public class RPsiSignatureImpl extends ORCompositePsiElement implements RPsiSignature { RPsiSignatureImpl(@NotNull ORLangTypes types, @NotNull IElementType elementType) { super(types, elementType); } @Override public boolean isFunction() { return getItems().size() > 1; } @Override public @NotNull String asText(@Nullable ORLanguageProperties toLang) { List items = getItems(); if (items.isEmpty()) { return ""; } boolean reason = toLang == RmlLanguage.INSTANCE || toLang == ResLanguage.INSTANCE; boolean isFunction = 1 < items.size(); String signatureText; if (toLang == null || toLang.equals(getLanguage())) { signatureText = getText(); } else { StringBuilder sb = new StringBuilder(); List conversions = items.stream().map(item -> item.asText(toLang)).collect(Collectors.toList()); String result = conversions.remove(items.size() - 1); if (isFunction) { if (reason && 1 < conversions.size()) { sb.append("("); } sb.append(Joiner.join(toLang.getParameterSeparator(), conversions)); if (reason && 1 < conversions.size()) { sb.append(")"); } sb.append(toLang.getFunctionSeparator()); } sb.append(result); signatureText = sb.toString(); } String text = signatureText.replaceAll("\\s+", " "); if (toLang == ResLanguage.INSTANCE) { text = text .replaceAll("< ", "<") .replaceAll(", >", ">"); } return text .replaceAll("\\( ", "(") .replaceAll(", \\)", ")"); } @Override public @NotNull List getItems() { return ORUtil.findImmediateChildrenOfClass(this, RPsiSignatureItem.class); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiSignatureItemImpl.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.openapi.util.*; import com.intellij.psi.*; import com.intellij.psi.tree.*; import com.intellij.util.*; import com.reason.lang.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; public class RPsiSignatureItemImpl extends ORCompositePsiElement implements RPsiSignatureItem { protected RPsiSignatureItemImpl(@NotNull ORLangTypes types, @NotNull IElementType elementType) { super(types, elementType); } public @Nullable RPsiParameterDeclaration getNamedParam() { RPsiParameterDeclaration parameter = ORUtil.findImmediateFirstChildOfClass(this, RPsiParameterDeclaration.class); return parameter != null && parameter.isNamed() ? parameter : null; } public @Nullable String getName() { RPsiParameterDeclaration param = getNamedParam(); return param == null ? null : param.getName(); } @Override public PsiElement setName(@NlsSafe @NotNull String name) throws IncorrectOperationException { return this; } @Override public boolean isNamedItem() { return getNamedParam() != null; } @Override public boolean isOptional() { RPsiParameterDeclaration namedParam = getNamedParam(); return namedParam != null && namedParam.isOptional(); } @Override public @Nullable PsiElement getSignature() { RPsiParameterDeclaration param = getNamedParam(); return param == null ? null : param.getSignature(); } @Override public PsiElement getDefaultValue() { RPsiParameterDeclaration param = getNamedParam(); return param == null ? null : param.getDefaultValue(); } @Override public @NotNull String asText(@Nullable ORLanguageProperties toLang) { PsiElement firstChild = getFirstChild(); if (firstChild instanceof RPsiLanguageConverter) { return ((RPsiLanguageConverter) firstChild).asText(toLang); } return getText(); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiStruct.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.psi.tree.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; public class RPsiStruct extends ORCompositePsiElement { protected RPsiStruct(@NotNull ORLangTypes types, @NotNull IElementType elementType) { super(types, elementType); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiSwitch.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.psi.tree.*; import com.intellij.psi.util.*; import com.reason.lang.core.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; import java.util.*; public class RPsiSwitch extends ORCompositePsiElement { protected RPsiSwitch(@NotNull ORLangTypes types, @NotNull IElementType elementType) { super(types, elementType); } public @Nullable RPsiBinaryCondition getCondition() { return PsiTreeUtil.findChildOfType(this, RPsiBinaryCondition.class); } public @NotNull List getPatterns() { RPsiSwitchBody scope = ORUtil.findImmediateFirstChildOfClass(this, RPsiSwitchBody.class); return ORUtil.findImmediateChildrenOfClass(scope == null ? this : scope, RPsiPatternMatch.class); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiSwitchBody.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.psi.tree.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; public class RPsiSwitchBody extends ORCompositePsiElement { protected RPsiSwitchBody(@NotNull ORLangTypes types, @NotNull IElementType elementType) { super(types, elementType); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiTag.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.psi.tree.*; import com.reason.lang.core.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; import java.util.*; public class RPsiTag extends ORCompositePsiElement { protected RPsiTag(@NotNull ORLangTypes types, @NotNull IElementType elementType) { super(types, elementType); } @Override public @Nullable String getName() { RPsiTagStart tagStart = ORUtil.findImmediateFirstChildOfClass(this, RPsiTagStart.class); return tagStart == null ? null : ORUtil.getLongIdent(tagStart.getFirstChild().getNextSibling()); } public @NotNull List getProperties() { return ORUtil.findImmediateChildrenOfClass(getFirstChild(/*tag_start*/), RPsiTagProperty.class); } public @Nullable RPsiTagBody getBody() { return ORUtil.findImmediateFirstChildOfClass(this, RPsiTagBody.class); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiTagBody.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.psi.tree.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; public class RPsiTagBody extends ORCompositePsiElement { protected RPsiTagBody(@NotNull ORLangTypes types, @NotNull IElementType elementType) { super(types, elementType); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiTagClose.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.psi.tree.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; public class RPsiTagClose extends ORCompositePsiElement { protected RPsiTagClose(@NotNull ORLangTypes types, @NotNull IElementType elementType) { super(types, elementType); } @Override public @NotNull String toString() { return "RPsiTagClose: " + getName(); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiTagProperty.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.psi.*; import com.intellij.psi.tree.*; import com.reason.lang.core.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; public class RPsiTagProperty extends ORCompositePsiElement { // region Constructors protected RPsiTagProperty(@NotNull ORLangTypes types, @NotNull IElementType elementType) { super(types, elementType); } // endregion @Nullable private PsiElement getNameElement() { return ORUtil.findImmediateFirstChildOfType(this, myTypes.PROPERTY_NAME); } @NotNull public String getName() { PsiElement nameElement = getNameElement(); return nameElement == null ? "" : nameElement.getText(); } @Nullable public PsiElement getValue() { PsiElement eq = ORUtil.nextSiblingWithTokenType(getFirstChild(), myTypes.EQ); return eq == null ? null : eq.getNextSibling(); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiTagPropertyValue.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.psi.tree.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; public class RPsiTagPropertyValue extends ORCompositePsiElement { // region Constructors protected RPsiTagPropertyValue(@NotNull ORLangTypes types, @NotNull IElementType elementType) { super(types, elementType); } // endregion } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiTagStart.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.psi.*; import com.intellij.psi.tree.*; import com.intellij.util.*; import com.reason.lang.core.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; import java.util.*; public class RPsiTagStart extends ORCompositePsiElement implements PsiNameIdentifierOwner { protected RPsiTagStart(@NotNull ORLangTypes types, @NotNull IElementType elementType) { super(types, elementType); } @Override public @Nullable PsiElement getNameIdentifier() { PsiElement lastTag = null; PsiElement element = getFirstChild(); IElementType elementType = element == null ? null : element.getNode().getElementType(); while (elementType == myTypes.A_UPPER_TAG_NAME || elementType == myTypes.A_LOWER_TAG_NAME || elementType == myTypes.DOT || elementType == myTypes.LT) { if (elementType != myTypes.DOT && elementType != myTypes.LT) { lastTag = element; } element = element.getNextSibling(); elementType = element == null ? null : element.getNode().getElementType(); } return lastTag; } @Override public @Nullable String getName() { PsiElement nameIdentifier = getNameIdentifier(); return nameIdentifier == null ? null : nameIdentifier.getText(); } @Override public @Nullable PsiElement setName(@NotNull String name) throws IncorrectOperationException { return null; } public @NotNull List getProperties() { return ORUtil.findImmediateChildrenOfClass(this, RPsiTagProperty.class); } @Override public String toString() { return "RPsiTagStart: " + getName(); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiTernary.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.psi.*; import com.intellij.psi.tree.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; public class RPsiTernary extends ORCompositePsiElement implements RPsiConditional { protected RPsiTernary(@NotNull ORLangTypes types, @NotNull IElementType elementType) { super(types, elementType); } @Override public @Nullable RPsiBinaryCondition getCondition() { return ORUtil.findImmediateFirstChildOfClass(this, RPsiBinaryCondition.class); } @Override public @Nullable PsiElement getThenExpression() { PsiElement element = ORUtil.findImmediateFirstChildOfType(this, myTypes.QUESTION_MARK); return element == null ? null : ORUtil.nextSibling(element); } @Override public @Nullable PsiElement getElseExpression() { PsiElement element = ORUtil.findImmediateFirstChildOfType(this, myTypes.COLON); return element == null ? null : ORUtil.nextSibling(element); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiTokenStub.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.extapi.psi.*; import com.intellij.lang.*; import com.intellij.psi.*; import com.intellij.psi.stubs.*; import org.jetbrains.annotations.*; public class RPsiTokenStub> extends StubBasedPsiElementBase { @NotNull protected final T myTypes; public RPsiTokenStub(@NotNull T types, @NotNull ASTNode node) { super(node); myTypes = types; } public RPsiTokenStub(@NotNull T types, @NotNull S stub, @NotNull IStubElementType nodeType) { super(stub, nodeType); myTypes = types; } @Override public String toString() { String className = getClass().getSimpleName(); boolean isImpl = className.endsWith("Impl"); return (isImpl ? className.substring(0, className.length() - 4) : className); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiTry.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.psi.*; import com.intellij.psi.tree.*; import com.reason.lang.core.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; import java.util.*; public class RPsiTry extends ORCompositePsiElement { protected RPsiTry(@NotNull ORLangTypes types, @NotNull IElementType elementType) { super(types, elementType); } public @Nullable RPsiTryBody getBody() { return ORUtil.findImmediateFirstChildOfClass(this, RPsiTryBody.class); } public @Nullable List getHandlers() { PsiElement scopedElement = ORUtil.findImmediateFirstChildOfType(this, (IElementType) myTypes.C_TRY_HANDLERS); return ORUtil.findImmediateChildrenOfClass(scopedElement, RPsiTryHandler.class); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiTryBody.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.psi.tree.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; public class RPsiTryBody extends ORCompositePsiElement { protected RPsiTryBody(@NotNull ORLangTypes types, @NotNull IElementType elementType) { super(types, elementType); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiTryHandler.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.psi.tree.*; import com.reason.lang.core.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; public class RPsiTryHandler extends ORCompositePsiElement { protected RPsiTryHandler(@NotNull ORLangTypes types, @NotNull IElementType elementType) { super(types, elementType); } public @Nullable RPsiTryHandlerBody getBody() { return ORUtil.findImmediateFirstChildOfClass(this, RPsiTryHandlerBody.class); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiTryHandlerBody.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.psi.tree.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; public class RPsiTryHandlerBody extends ORCompositePsiElement { protected RPsiTryHandlerBody(@NotNull ORLangTypes types, @NotNull IElementType elementType) { super(types, elementType); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiTuple.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.psi.tree.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; public class RPsiTuple extends ORCompositePsiElement { protected RPsiTuple(@NotNull ORLangTypes types, @NotNull IElementType elementType) { super(types, elementType); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiTypeBinding.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.psi.tree.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; public class RPsiTypeBinding extends ORCompositePsiElement { protected RPsiTypeBinding(@NotNull ORLangTypes types, @NotNull IElementType elementType) { super(types, elementType); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiTypeConstraint.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.psi.tree.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; public class RPsiTypeConstraint extends ORCompositePsiElement { protected RPsiTypeConstraint(@NotNull ORLangTypes types, @NotNull IElementType elementType) { super(types, elementType); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiTypeImpl.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.lang.*; import com.intellij.navigation.*; import com.intellij.psi.*; import com.intellij.psi.stubs.*; import com.intellij.psi.util.*; import com.intellij.util.*; import com.reason.ide.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.stub.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; import javax.swing.*; import java.util.*; import static java.util.Collections.*; public class RPsiTypeImpl extends RPsiTokenStub implements RPsiType { // region Constructors public RPsiTypeImpl(@NotNull ORLangTypes types, @NotNull ASTNode node) { super(types, node); } public RPsiTypeImpl(@NotNull ORLangTypes types, @NotNull PsiTypeStub stub, @NotNull IStubElementType nodeType) { super(types, stub, nodeType); } // endregion // region PsiNamedElement @Override public @Nullable PsiElement getNameIdentifier() { return findChildByClass(RPsiLowerSymbol.class); } @Override public @Nullable String getName() { PsiTypeStub stub = getGreenStub(); if (stub != null) { return stub.getName(); } PsiElement constrName = getNameIdentifier(); return constrName == null ? "" : constrName.getText(); } @Override public @NotNull PsiElement setName(@NotNull String name) throws IncorrectOperationException { PsiElement id = getNameIdentifier(); PsiElement newId = ORCodeFactory.createLetName(getProject(), name); if (id != null && newId != null) { id.replace(newId); } return this; } // endregion @Override public int getTextOffset() { PsiElement id = getNameIdentifier(); return id == null ? 0 : id.getTextOffset(); } //region PsiQualifiedPathName @Override public @Nullable String[] getPath() { PsiTypeStub stub = getGreenStub(); if (stub != null) { return stub.getPath(); } return ORUtil.getQualifiedPath(this); } @Override public @NotNull String getQualifiedName() { PsiTypeStub stub = getGreenStub(); if (stub != null) { return stub.getQualifiedName(); } return ORUtil.getQualifiedName(this); } //endregion @Override public boolean isAbstract() { PsiTypeStub stub = getGreenStub(); if (stub != null) { return stub.isAbstract(); } return getBinding() == null; } @Override public boolean isJsObject() { PsiTypeStub stub = getGreenStub(); if (stub != null) { return stub.isJsObject(); } RPsiTypeBinding binding = getBinding(); return binding != null && binding.getFirstChild() instanceof RPsiJsObject; } @Override public boolean isRecord() { PsiTypeStub stub = getGreenStub(); if (stub != null) { return stub.isRecord(); } RPsiTypeBinding binding = getBinding(); return binding != null && binding.getFirstChild() instanceof RPsiRecord; } @Override public @Nullable RPsiParameters getParameters() { return ORUtil.findImmediateFirstChildOfClass(this, RPsiParameters.class); } @Override public @NotNull List getJsObjectFields() { RPsiTypeBinding binding = getBinding(); PsiElement firstChild = binding == null ? null : binding.getFirstChild(); RPsiJsObject jsObject = firstChild instanceof RPsiJsObject ? ((RPsiJsObject) firstChild) : null; return jsObject == null ? emptyList() : jsObject.getFields(); } @Override public @NotNull List getRecordFields() { RPsiTypeBinding binding = getBinding(); PsiElement firstChild = binding == null ? null : binding.getFirstChild(); RPsiRecord record = firstChild instanceof RPsiRecord ? ((RPsiRecord) firstChild) : null; return record == null ? emptyList() : record.getFields(); } @Override public @Nullable RPsiTypeBinding getBinding() { return findChildByClass(RPsiTypeBinding.class); } @Override public @NotNull Collection getVariants() { RPsiTypeBinding binding = getBinding(); if (binding != null) { return PsiTreeUtil.findChildrenOfType(binding, RPsiVariantDeclaration.class); } return emptyList(); } @Override public ItemPresentation getPresentation() { return new ItemPresentation() { @Override public String getPresentableText() { return getName(); } @Override public @Nullable String getLocationString() { return null; } @Override public Icon getIcon(boolean unused) { return ORIcons.TYPE; } }; } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiUnit.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.psi.tree.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; public class RPsiUnit extends ORCompositePsiElement { protected RPsiUnit(@NotNull ORLangTypes types, @NotNull IElementType elementType) { super(types, elementType); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiUnpack.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.psi.tree.*; import com.reason.lang.core.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; public class RPsiUnpack extends ORCompositePsiElement { protected RPsiUnpack(@NotNull ORLangTypes types, @NotNull IElementType elementType) { super(types, elementType); } public @Nullable RPsiModuleSignature getSignature() { return ORUtil.findImmediateFirstChildOfClass(this, RPsiModuleSignature.class); } public @Nullable RPsiUpperSymbol getModuleReference() { return ORUtil.findImmediateLastChildOfClass(getSignature(), RPsiUpperSymbol.class); } public @Nullable RPsiLowerSymbol getFirstClassSymbol() { return ORUtil.findImmediateFirstChildOfClass(this, RPsiLowerSymbol.class); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiUpperSymbol.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.psi.impl.source.tree.*; import com.intellij.psi.tree.*; import com.reason.ide.search.reference.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; public class RPsiUpperSymbol extends LeafPsiElement { protected final ORLangTypes myTypes; // region Constructors public RPsiUpperSymbol(@NotNull ORLangTypes types, @NotNull IElementType tokenType, CharSequence text) { super(tokenType, text); myTypes = types; } // endregion @Override public @NotNull ORPsiUpperSymbolReference getReference() { return new ORPsiUpperSymbolReference(this, myTypes); } @Override public String toString() { return "RPsiUpperSymbol:" + getElementType() + " (" + getText() + ")"; } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiUpperTagName.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.psi.tree.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; public class RPsiUpperTagName extends RPsiUpperSymbol { public RPsiUpperTagName(@NotNull ORLangTypes types, @NotNull IElementType tokenType, CharSequence text) { super(types, tokenType, text); } @Override public @NotNull String toString() { return "RPsiUpperTagName:" + getText(); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiValImpl.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.lang.*; import com.intellij.navigation.*; import com.intellij.psi.*; import com.intellij.psi.stubs.*; import com.intellij.psi.util.*; import com.intellij.util.*; import com.reason.ide.*; import com.reason.lang.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.stub.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; import javax.swing.*; import java.util.*; public class RPsiValImpl extends RPsiTokenStub implements RPsiVal { // region Constructors public RPsiValImpl(@NotNull ORLangTypes types, @NotNull ASTNode node) { super(types, node); } public RPsiValImpl(@NotNull ORLangTypes types, @NotNull PsiValStub stub, @NotNull IStubElementType nodeType) { super(types, stub, nodeType); } // endregion // region PsiNamedElement public @Nullable PsiElement getNameIdentifier() { return ORUtil.findImmediateFirstChildOfAnyClass(this, RPsiLowerSymbol.class, RPsiScopedExpr.class); } @Override public @Nullable String getName() { PsiValStub stub = getGreenStub(); if (stub != null) { return stub.getName(); } PsiElement nameIdentifier = getNameIdentifier(); return nameIdentifier != null ? nameIdentifier.getText() : null; } @Override public @NotNull PsiElement setName(@NotNull String name) throws IncorrectOperationException { return this; } // endregion //region PsiQualifiedName @Override public @NotNull String[] getPath() { PsiValStub stub = getGreenStub(); if (stub != null) { return stub.getPath(); } return ORUtil.getQualifiedPath(this); } @Override public @NotNull String getQualifiedName() { PsiValStub stub = getGreenStub(); if (stub != null) { return stub.getQualifiedName(); } return ORUtil.getQualifiedName(this); } //endregion @Override public int getTextOffset() { PsiElement id = getNameIdentifier(); return id == null ? 0 : id.getTextOffset(); } @Override public boolean isFunction() { PsiValStub stub = getGreenStub(); if (stub != null) { return stub.isFunction(); } RPsiSignature signature = getSignature(); return signature != null && signature.isFunction(); } @Override public @NotNull Collection getJsObjectFields() { PsiElement firstChild = getFirstChild(); RPsiJsObject jsObject = firstChild instanceof RPsiJsObject ? ((RPsiJsObject) firstChild) : null; return ORUtil.findImmediateChildrenOfClass(jsObject, RPsiObjectField.class); } @Override public @NotNull Collection getRecordFields() { return PsiTreeUtil.findChildrenOfType(this, RPsiRecordField.class); } @Override public @Nullable RPsiSignature getSignature() { return findChildByClass(RPsiSignature.class); } @Override public boolean isAnonymous() { return getName() == null; } @Override public ItemPresentation getPresentation() { return new ItemPresentation() { @Override public @NotNull String getPresentableText() { String name = getName(); return name == null ? "" : name; } @Override public @Nullable String getLocationString() { RPsiSignature signature = getSignature(); return signature == null ? null : signature.asText(ORLanguageProperties.cast(getLanguage())); } @Override public @NotNull Icon getIcon(boolean unused) { return isFunction() ? ORIcons.FUNCTION : ORIcons.VAL; } }; } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiVariantDeclaration.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.lang.*; import com.intellij.psi.*; import com.intellij.psi.stubs.*; import com.intellij.util.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.stub.*; import com.reason.lang.core.type.*; import com.reason.lang.rescript.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; import java.util.*; import static java.util.Collections.*; public class RPsiVariantDeclaration extends RPsiTokenStub implements PsiNameIdentifierOwner, RPsiQualifiedPathElement, StubBasedPsiElement { // region Constructors public RPsiVariantDeclaration(@NotNull ORLangTypes types, @NotNull ASTNode node) { super(types, node); } public RPsiVariantDeclaration(@NotNull ORLangTypes types, @NotNull PsiVariantDeclarationStub stub, @NotNull IStubElementType nodeType) { super(types, stub, nodeType); } // endregion public boolean isPolyVariant() { PsiElement id = getNameIdentifier(); return id != null && id.getNode().getElementType() == myTypes.POLY_VARIANT; } //region PsiNamedElement @Override public @Nullable PsiElement getNameIdentifier() { return getFirstChild(); } @Override public int getTextOffset() { PsiElement id = getNameIdentifier(); return id == null ? 0 : id.getTextOffset(); } @Override public @Nullable String getName() { PsiVariantDeclarationStub stub = getGreenStub(); if (stub != null) { return stub.getName(); } PsiElement id = getNameIdentifier(); String name = id != null ? id.getText() : ""; if (id != null && isPolyVariant()) { return myTypes == ResTypes.INSTANCE ? name : "#" + name.substring(1); } return name; } @Override public @NotNull PsiElement setName(@NotNull String name) throws IncorrectOperationException { return this; } //endregion //region PsiQualifiedName @Override public @NotNull String[] getPath() { PsiVariantDeclarationStub stub = getGreenStub(); if (stub != null) { return stub.getPath(); } String[] path = ORUtil.getQualifiedPath(this); // We do not include type in the variant path String[] result = new String[path.length - 1]; System.arraycopy(path, 0, result, 0, path.length - 1); return result; } @Override public @NotNull String getQualifiedName() { PsiVariantDeclarationStub stub = getGreenStub(); if (stub != null) { return stub.getQualifiedName(); } String[] path = getPath(); return Joiner.join(".", path) + "." + getName(); } //endregion @Nullable public PsiElement getVariant() { return ORUtil.findImmediateFirstChildOfClass(this, RPsiUpperSymbol.class); } @NotNull public List getParametersList() { RPsiParameters parameters = ORUtil.findImmediateFirstChildOfClass(this, RPsiParameters.class); return parameters == null ? emptyList() : ORUtil.findImmediateChildrenOfClass(parameters, RPsiParameterDeclaration.class); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/impl/RPsiWhile.java ================================================ package com.reason.lang.core.psi.impl; import com.intellij.psi.tree.*; import com.reason.lang.core.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; public class RPsiWhile extends ORCompositePsiElement { protected RPsiWhile(@NotNull ORLangTypes types, @NotNull IElementType elementType) { super(types, elementType); } public @Nullable RPsiBinaryCondition getCondition() { return ORUtil.findImmediateFirstChildOfClass(this, RPsiBinaryCondition.class); } public @Nullable RPsiScopedExpr getBody() { return ORUtil.findImmediateFirstChildOfClass(this, RPsiScopedExpr.class); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/ocamlgrammar/RPsiGrammarArgument.java ================================================ package com.reason.lang.core.psi.ocamlgrammar; import com.intellij.extapi.psi.*; import com.intellij.lang.*; import com.intellij.navigation.*; import com.intellij.psi.*; import com.reason.ide.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import com.reason.lang.ocamlgrammar.*; import org.jetbrains.annotations.*; import javax.swing.*; public class RPsiGrammarArgument extends ASTWrapperPsiElement implements RPsiStructuredElement { public RPsiGrammarArgument(@NotNull ASTNode node) { super(node); } @Override public @NotNull ItemPresentation getPresentation() { return new ItemPresentation() { @Override public String getPresentableText() { PsiElement ident = ORUtil.findImmediateFirstChildOfType(RPsiGrammarArgument.this, OclGrammarTypes.INSTANCE.IDENT); return "Argument " + (ident == null ? "" : ident.getText()); } @Override public Icon getIcon(boolean unused) { return ORIcons.OBJECT; } }; } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/ocamlgrammar/RPsiGrammarGrammar.java ================================================ package com.reason.lang.core.psi.ocamlgrammar; import com.intellij.extapi.psi.*; import com.intellij.lang.*; import com.intellij.navigation.*; import com.intellij.psi.*; import com.reason.ide.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import com.reason.lang.ocamlgrammar.*; import org.jetbrains.annotations.*; import javax.swing.*; public class RPsiGrammarGrammar extends ASTWrapperPsiElement implements RPsiStructuredElement { public RPsiGrammarGrammar(@NotNull ASTNode node) { super(node); } @Override public @NotNull ItemPresentation getPresentation() { return new ItemPresentation() { @Override public String getPresentableText() { PsiElement ident = ORUtil.findImmediateFirstChildOfType(RPsiGrammarGrammar.this, OclGrammarTypes.INSTANCE.IDENT); return "Grammar " + (ident == null ? "" : ident.getText()); } @Override public Icon getIcon(boolean unused) { return ORIcons.OBJECT; } }; } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/ocamlgrammar/RPsiGrammarTactic.java ================================================ package com.reason.lang.core.psi.ocamlgrammar; import com.intellij.extapi.psi.*; import com.intellij.lang.*; import com.intellij.navigation.*; import com.intellij.psi.*; import com.reason.ide.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import com.reason.lang.ocamlgrammar.*; import org.jetbrains.annotations.*; import javax.swing.*; public class RPsiGrammarTactic extends ASTWrapperPsiElement implements RPsiStructuredElement { public RPsiGrammarTactic(@NotNull ASTNode node) { super(node); } @Override public @NotNull ItemPresentation getPresentation() { return new ItemPresentation() { @Override public String getPresentableText() { PsiElement ident = ORUtil.findImmediateFirstChildOfType(RPsiGrammarTactic.this, OclGrammarTypes.INSTANCE.IDENT); return "Tactic " + (ident == null ? "" : ident.getText()); } @Override public Icon getIcon(boolean unused) { return ORIcons.OBJECT; } }; } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/ocamlgrammar/RPsiGrammarVernac.java ================================================ package com.reason.lang.core.psi.ocamlgrammar; import com.intellij.extapi.psi.*; import com.intellij.lang.*; import com.intellij.navigation.*; import com.intellij.psi.*; import com.reason.ide.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import com.reason.lang.ocamlgrammar.*; import org.jetbrains.annotations.*; import javax.swing.*; public class RPsiGrammarVernac extends ASTWrapperPsiElement implements RPsiStructuredElement { public RPsiGrammarVernac(@NotNull ASTNode node) { super(node); } @Override public @NotNull ItemPresentation getPresentation() { return new ItemPresentation() { @Override public String getPresentableText() { PsiElement ident = ORUtil.findImmediateFirstChildOfType(RPsiGrammarVernac.this, OclGrammarTypes.INSTANCE.IDENT); return "Vernac " + (ident == null ? "" : ident.getText()); } @Override public Icon getIcon(boolean unused) { return ORIcons.OBJECT; } }; } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/ocamllex/RPsiLexLet.java ================================================ package com.reason.lang.core.psi.ocamllex; import com.intellij.extapi.psi.*; import com.intellij.lang.*; import com.intellij.navigation.*; import com.intellij.psi.*; import com.intellij.util.*; import com.reason.ide.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import com.reason.lang.ocamllex.*; import org.jetbrains.annotations.*; import javax.swing.*; public class RPsiLexLet extends ASTWrapperPsiElement implements RPsiStructuredElement, PsiNameIdentifierOwner { public RPsiLexLet(@NotNull ASTNode node) { super(node); } // region PsiNamedElement @Override public @Nullable PsiElement getNameIdentifier() { return ORUtil.findImmediateFirstChildOfType(this, OclLexTypes.INSTANCE.IDENT); } @Override public @Nullable String getName() { PsiElement nameIdentifier = getNameIdentifier(); return nameIdentifier == null ? null : nameIdentifier.getText(); } @Override public @NotNull PsiElement setName(@NotNull String name) throws IncorrectOperationException { return this; } // endregion // region RPsiStructuredElement @Override public @NotNull ItemPresentation getPresentation() { return new ItemPresentation() { @Override public @NotNull String getPresentableText() { String name = getName(); return name == null ? "unknown" : name; } @Override public @NotNull Icon getIcon(boolean unused) { return ORIcons.LET; } }; } // endregion } ================================================ FILE: src/main/java/com/reason/lang/core/psi/ocamllex/RPsiLexPattern.java ================================================ package com.reason.lang.core.psi.ocamllex; import com.intellij.extapi.psi.*; import com.intellij.lang.*; import org.jetbrains.annotations.*; public class RPsiLexPattern extends ASTWrapperPsiElement { public RPsiLexPattern(@NotNull ASTNode node) { super(node); } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/ocamllex/RPsiLexRule.java ================================================ package com.reason.lang.core.psi.ocamllex; import com.intellij.extapi.psi.*; import com.intellij.lang.*; import com.intellij.navigation.*; import com.intellij.psi.*; import com.intellij.util.*; import com.reason.ide.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import com.reason.lang.ocamllex.*; import org.jetbrains.annotations.*; import javax.swing.*; public class RPsiLexRule extends ASTWrapperPsiElement implements RPsiStructuredElement, PsiNameIdentifierOwner { public RPsiLexRule(@NotNull ASTNode node) { super(node); } // region PsiNamedElement @Override public @Nullable PsiElement getNameIdentifier() { return ORUtil.findImmediateFirstChildOfType(this, OclLexTypes.INSTANCE.IDENT); } @Override public @Nullable String getName() { PsiElement nameIdentifier = getNameIdentifier(); return nameIdentifier == null ? null : nameIdentifier.getText(); } @Override public @NotNull PsiElement setName(@NotNull String name) throws IncorrectOperationException { return this; } // endregion // region RPsiStructuredElement @Override public @NotNull ItemPresentation getPresentation() { return new ItemPresentation() { @Override public @NotNull String getPresentableText() { String name = getName(); return name == null ? "unknown" : name; } @Override public @NotNull Icon getIcon(boolean unused) { return ORIcons.FUNCTION; } }; } // endregion } ================================================ FILE: src/main/java/com/reason/lang/core/psi/ocamlyacc/RPsiYaccDeclaration.java ================================================ package com.reason.lang.core.psi.ocamlyacc; import com.intellij.navigation.*; import com.intellij.psi.*; import com.reason.ide.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import com.reason.lang.ocamlyacc.*; import org.jetbrains.annotations.*; import com.intellij.lang.ASTNode; import com.intellij.extapi.psi.ASTWrapperPsiElement; import javax.swing.*; public class RPsiYaccDeclaration extends ASTWrapperPsiElement implements RPsiStructuredElement { public RPsiYaccDeclaration(@NotNull ASTNode node) { super(node); } @Override public @NotNull ItemPresentation getPresentation() { return new ItemPresentation() { @Override public String getPresentableText() { PsiElement firstChild = getFirstChild(); String type = firstChild.getText().substring(1); PsiElement sibling = ORUtil.nextSibling(firstChild); if (sibling != null && sibling.getNode().getElementType() == OclYaccTypes.INSTANCE.IDENT) { return type + " " + sibling.getText(); } return type; } @Override public Icon getIcon(boolean unused) { return ORIcons.TYPE; } }; } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/ocamlyacc/RPsiYaccHeader.java ================================================ package com.reason.lang.core.psi.ocamlyacc; import com.intellij.extapi.psi.*; import com.intellij.lang.*; import com.intellij.navigation.*; import com.reason.ide.*; import com.reason.lang.core.psi.*; import org.jetbrains.annotations.*; import javax.swing.*; public class RPsiYaccHeader extends ASTWrapperPsiElement implements RPsiStructuredElement { public RPsiYaccHeader(@NotNull ASTNode node) { super(node); } @Override public @NotNull ItemPresentation getPresentation() { return new ItemPresentation() { @Override public String getPresentableText() { return "Header"; } @Override public Icon getIcon(boolean unused) { return ORIcons.OCAML; } }; } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/ocamlyacc/RPsiYaccRule.java ================================================ package com.reason.lang.core.psi.ocamlyacc; import com.intellij.navigation.*; import com.reason.ide.*; import com.reason.lang.core.psi.*; import org.jetbrains.annotations.*; import com.intellij.lang.ASTNode; import com.intellij.extapi.psi.ASTWrapperPsiElement; import javax.swing.*; public class RPsiYaccRule extends ASTWrapperPsiElement implements RPsiStructuredElement { public RPsiYaccRule(@NotNull ASTNode node) { super(node); } @Override public @NotNull ItemPresentation getPresentation() { return new ItemPresentation() { @Override public String getPresentableText() { return getFirstChild().getText(); } @Override public Icon getIcon(boolean unused) { return ORIcons.FUNCTION; } }; } } ================================================ FILE: src/main/java/com/reason/lang/core/psi/ocamlyacc/RPsiYaccRuleBody.java ================================================ package com.reason.lang.core.psi.ocamlyacc; import com.intellij.extapi.psi.*; import com.intellij.lang.*; import org.jetbrains.annotations.*; public class RPsiYaccRuleBody extends ASTWrapperPsiElement { public RPsiYaccRuleBody(@NotNull ASTNode node) { super(node); } } ================================================ FILE: src/main/java/com/reason/lang/core/stub/OclFileStub.java ================================================ package com.reason.lang.core.stub; import com.intellij.psi.stubs.*; import com.intellij.psi.tree.*; import com.reason.ide.files.*; import com.reason.lang.core.stub.type.*; import org.jetbrains.annotations.*; public class OclFileStub extends PsiFileStubImpl { public OclFileStub(FileBase file) { super(file); } @Override public @NotNull IStubFileElementType getType() { return OclFileStubElementType.INSTANCE; } } ================================================ FILE: src/main/java/com/reason/lang/core/stub/OclStubBasedElementTypes.java ================================================ package com.reason.lang.core.stub; import com.intellij.psi.stubs.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import com.reason.lang.core.stub.type.*; import com.reason.lang.ocaml.*; public interface OclStubBasedElementTypes { IStubElementType C_OPEN = new PsiOpenStubElementType("C_OPEN", OclLanguage.INSTANCE); IStubElementType C_INCLUDE = new PsiIncludeStubElementType("C_INCLUDE", OclLanguage.INSTANCE); IStubElementType C_MODULE_DECLARATION = new PsiInnerModuleStubElementType("C_MODULE_DECLARATION", OclLanguage.INSTANCE); IStubElementType C_FUNCTOR_DECLARATION = new PsiFunctorModuleStubElementType("C_FUNCTOR_DECLARATION", OclLanguage.INSTANCE); IStubElementType C_CLASS_DECLARATION = new RPsiClassStubElementType("C_CLASS_DECLARATION", OclLanguage.INSTANCE); IStubElementType C_CLASS_METHOD = new RPsiClassMethodStubElementType("C_CLASS_METHOD", OclLanguage.INSTANCE); IStubElementType C_EXCEPTION_DECLARATION = new PsiExceptionStubElementType("C_EXCEPTION_DECLARATION", OclLanguage.INSTANCE); IStubElementType C_TYPE_DECLARATION = new PsiTypeStubElementType("C_TYPE_DECLARATION", OclLanguage.INSTANCE); IStubElementType C_EXTERNAL_DECLARATION = new PsiExternalStubElementType("C_EXTERNAL_DECLARATION", OclLanguage.INSTANCE); IStubElementType C_LET_DECLARATION = new PsiLetStubElementType("C_LET_DECLARATION", OclLanguage.INSTANCE); IStubElementType C_VAL_DECLARATION = new PsiValStubElementType("C_VAL_DECLARATION", OclLanguage.INSTANCE); IStubElementType C_VARIANT_DECLARATION = new PsiVariantStubElementType("C_VARIANT_DECLARATION", OclLanguage.INSTANCE); IStubElementType C_PARAM_DECLARATION = new PsiParameterDeclarationStubElementType("C_PARAM_DECLARATION", OclLanguage.INSTANCE); IStubElementType C_RECORD_FIELD = new RPsiRecordFieldStubElementType("C_RECORD_FIELD", OclLanguage.INSTANCE); IStubElementType C_OBJECT_FIELD = new PsiObjectFieldStubElementType("C_OBJECT_FIELD", OclLanguage.INSTANCE); } ================================================ FILE: src/main/java/com/reason/lang/core/stub/PsiExceptionStub.java ================================================ package com.reason.lang.core.stub; import com.intellij.psi.stubs.*; import com.intellij.util.io.*; import com.reason.lang.core.psi.*; import org.jetbrains.annotations.*; public class PsiExceptionStub extends PsiQualifiedNameStub { private final String myAlias; public PsiExceptionStub(@Nullable StubElement parent, @NotNull IStubElementType elementType, @Nullable String name, @NotNull String[] path, @Nullable String alias) { super(parent, elementType, name, path); myAlias = alias; } public PsiExceptionStub(@Nullable StubElement parent, @NotNull IStubElementType elementType, @Nullable StringRef name, @NotNull String[] path, @Nullable String alias) { super(parent, elementType, name, path); myAlias = alias; } public @Nullable String getAlias() { return myAlias; } } ================================================ FILE: src/main/java/com/reason/lang/core/stub/PsiExternalStub.java ================================================ package com.reason.lang.core.stub; import com.intellij.psi.stubs.*; import com.intellij.util.io.*; import com.reason.lang.core.psi.*; import org.jetbrains.annotations.*; public class PsiExternalStub extends PsiQualifiedNameStub { private final boolean myIsFunction; public PsiExternalStub(@Nullable StubElement parent, @NotNull IStubElementType elementType, @Nullable String name, @NotNull String[] path, boolean isFunction) { super(parent, elementType, name, path); myIsFunction = isFunction; } public PsiExternalStub(@Nullable StubElement parent, @NotNull IStubElementType elementType, @Nullable StringRef name, @NotNull String[] path, boolean isFunction) { super(parent, elementType, name, path); myIsFunction = isFunction; } public boolean isFunction() { return myIsFunction; } } ================================================ FILE: src/main/java/com/reason/lang/core/stub/PsiIncludeStub.java ================================================ package com.reason.lang.core.stub; import com.intellij.psi.stubs.*; import com.intellij.util.io.*; import com.reason.lang.core.psi.*; import org.jetbrains.annotations.*; public class PsiIncludeStub extends StubBase { @Nullable private final StringRef myFileModule; private final String myIncludePath; private final String[] myQualifiedPath; public PsiIncludeStub(@Nullable StubElement parent, @NotNull IStubElementType elementType, @Nullable StringRef fileModule, String includePath, String[] qualifiedPath) { super(parent, elementType); myFileModule = fileModule; myIncludePath = includePath; myQualifiedPath = qualifiedPath; } public PsiIncludeStub(@Nullable StubElement parent, @NotNull IStubElementType elementType, @Nullable String fileModule, String includePath, String[] qualifiedPath) { super(parent, elementType); myFileModule = StringRef.fromString(fileModule); myIncludePath = includePath; myQualifiedPath = qualifiedPath; } public String getIncludePath() { return myIncludePath; } public String[] getQualifiedPath() { return myQualifiedPath; } @Nullable public String getFileModule() { return myFileModule != null ? myFileModule.getString() : null; } } ================================================ FILE: src/main/java/com/reason/lang/core/stub/PsiLetStub.java ================================================ package com.reason.lang.core.stub; import com.intellij.psi.stubs.*; import com.intellij.util.io.*; import com.reason.lang.core.psi.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; import java.util.*; import java.util.stream.*; public class PsiLetStub extends NamedStubBase { private final String[] myPath; private final List myQnames; private final String myAlias; private final boolean myIsFunction; private final boolean myIsComponent; private final List myDeconstructionNames; public PsiLetStub(StubElement parent, @NotNull IStubElementType elementType, String name, String[] path, String alias, boolean isFunction, boolean isComponent, @NotNull List deconstructionNames) { super(parent, elementType, name); myPath = path; myAlias = alias; myIsFunction = isFunction; myIsComponent = isComponent; myDeconstructionNames = deconstructionNames; String joinedPath = Joiner.join(".", path); if (deconstructionNames.isEmpty()) { myQnames = List.of(joinedPath + "." + name); } else { myQnames = deconstructionNames.stream().map(dname -> joinedPath + "." + dname).collect(Collectors.toList()); } } public PsiLetStub(StubElement parent, @NotNull IStubElementType elementType, StringRef name, String[] path, String alias, boolean isFunction, boolean isComponent, @NotNull List deconstructionNames) { super(parent, elementType, name); myPath = path; myAlias = alias; myIsFunction = isFunction; myIsComponent = isComponent; myDeconstructionNames = deconstructionNames; String joinedPath = Joiner.join(".", path); if (deconstructionNames.isEmpty()) { myQnames = List.of(joinedPath + "." + name); } else { myQnames = deconstructionNames.stream().map(dname -> joinedPath + "." + dname).collect(Collectors.toList()); } } public String[] getPath() { return myPath; } public @NotNull String getQualifiedName() { return myQnames.get(0); } public String getAlias() { return myAlias; } public boolean isFunction() { return myIsFunction; } public boolean isComponent() { return myIsComponent; } public @NotNull List getDeconstructionNames() { return myDeconstructionNames; } public @NotNull List getQualifiedNames() { return myQnames; } } ================================================ FILE: src/main/java/com/reason/lang/core/stub/PsiModuleStub.java ================================================ package com.reason.lang.core.stub; import com.intellij.psi.stubs.*; import com.intellij.util.io.*; import com.reason.lang.core.psi.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; public class PsiModuleStub extends NamedStubBase { private final String[] myPath; private final String myQname; private final String myAlias; private final boolean myIsComponent; private final boolean myIsModuleType; private final boolean myIsTopLevel; private final boolean myIsFunctorCall; private final String myReturnTypeName; public PsiModuleStub(StubElement parent, @NotNull IStubElementType elementType, @Nullable String name, @Nullable String[] path, @Nullable String namespace, String alias, boolean isComponent, boolean isModuleType, boolean isTopLevel, boolean isFunctorCall, @Nullable String returnTypeName) { super(parent, elementType, name); myPath = path; myQname = namespace == null ? (path != null && path.length > 0 ? Joiner.join(".", path) + "." + name : name) : namespace; myAlias = alias; myIsComponent = isComponent; myIsModuleType = isModuleType; myIsTopLevel = isTopLevel; myIsFunctorCall = isFunctorCall; myReturnTypeName = returnTypeName; } public PsiModuleStub(StubElement parent, @NotNull IStubElementType elementType, @Nullable StringRef name, @Nullable String[] path, @Nullable String namespace, String alias, boolean isComponent, boolean isModuleType, boolean isTopLevel, boolean isFunctorCall, @Nullable StringRef returnTypeNameRef) { super(parent, elementType, name); myPath = path; myQname = namespace == null ? path != null && path.length > 0 ? Joiner.join(".", path) + "." + name : "" + name : namespace; myAlias = alias; myIsComponent = isComponent; myIsModuleType = isModuleType; myIsTopLevel = isTopLevel; myIsFunctorCall = isFunctorCall; myReturnTypeName = returnTypeNameRef != null ? returnTypeNameRef.getString() : null; } public @Nullable String[] getPath() { return myPath; } public @NotNull String getQualifiedName() { return myQname; } public String getAlias() { return myAlias; } public boolean isComponent() { return myIsComponent; } public boolean isModuleType() { return myIsModuleType; } public boolean isTopLevel() { return myIsTopLevel; } public boolean isFunctorCall() { return myIsFunctorCall; } public String getSignatureName() { return myReturnTypeName; } } ================================================ FILE: src/main/java/com/reason/lang/core/stub/PsiObjectFieldStub.java ================================================ package com.reason.lang.core.stub; import com.intellij.psi.stubs.*; import com.intellij.util.io.*; import com.reason.lang.core.psi.impl.*; import org.jetbrains.annotations.*; public class PsiObjectFieldStub extends PsiQualifiedNameStub { public PsiObjectFieldStub(StubElement parent, @NotNull IStubElementType elementType, @Nullable String name, @NotNull String[] path) { super(parent, elementType, name, path); } public PsiObjectFieldStub(StubElement parent, @NotNull IStubElementType elementType, @Nullable StringRef name, @NotNull String[] path) { super(parent, elementType, name, path); } } ================================================ FILE: src/main/java/com/reason/lang/core/stub/PsiOpenStub.java ================================================ package com.reason.lang.core.stub; import com.intellij.psi.stubs.*; import com.reason.lang.core.psi.*; import org.jetbrains.annotations.*; public class PsiOpenStub extends StubBase { private final String myOpenPath; public PsiOpenStub(@Nullable StubElement parent, @NotNull IStubElementType elementType, @Nullable String openPath) { super(parent, elementType); myOpenPath = openPath; } public @Nullable String getOpenPath() { return myOpenPath; } } ================================================ FILE: src/main/java/com/reason/lang/core/stub/PsiParameterDeclarationStub.java ================================================ package com.reason.lang.core.stub; import com.intellij.psi.stubs.*; import com.intellij.util.io.*; import com.reason.lang.core.psi.*; import org.jetbrains.annotations.*; public class PsiParameterDeclarationStub extends NamedStubBase { private final String[] myPath; private final String myQname; private final boolean myIsNamed; public PsiParameterDeclarationStub(StubElement parent, @NotNull IStubElementType elementType, String name, String[] path, String qName, boolean isNamed) { super(parent, elementType, name); myPath = path; myQname = qName; myIsNamed = isNamed; } public PsiParameterDeclarationStub(StubElement parent, @NotNull IStubElementType elementType, StringRef name, String[] path, String qname, boolean isNamed) { super(parent, elementType, name); myPath = path; myQname = qname; myIsNamed = isNamed; } public String[] getPath() { return myPath; } public String getQualifiedName() { return myQname; } public boolean isNamed() { return myIsNamed; } } ================================================ FILE: src/main/java/com/reason/lang/core/stub/PsiQualifiedNameStub.java ================================================ package com.reason.lang.core.stub; import com.intellij.psi.*; import com.intellij.psi.stubs.*; import com.intellij.util.io.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; public abstract class PsiQualifiedNameStub extends NamedStubBase { private final @NotNull String[] myPath; private final String myQname; PsiQualifiedNameStub(@Nullable StubElement parent, @NotNull IStubElementType elementType, @Nullable String name, @NotNull String[] path) { super(parent, elementType, name); myPath = path; myQname = Joiner.join(".", myPath) + "." + name; } PsiQualifiedNameStub(@Nullable StubElement parent, @NotNull IStubElementType elementType, @Nullable StringRef name, @NotNull String[] path) { super(parent, elementType, name); myPath = path; myQname = Joiner.join(".", myPath) + "." + name; } public @NotNull String[] getPath() { return myPath; } public @NotNull String getQualifiedName() { return myQname; } } ================================================ FILE: src/main/java/com/reason/lang/core/stub/PsiTypeStub.java ================================================ package com.reason.lang.core.stub; import com.intellij.psi.stubs.*; import com.intellij.util.io.*; import com.reason.lang.core.psi.*; import org.jetbrains.annotations.*; public class PsiTypeStub extends PsiQualifiedNameStub { private final boolean myAbstract; private final boolean myJsObject; private final boolean myRecord; public PsiTypeStub(@Nullable StubElement parent, @NotNull IStubElementType elementType, @Nullable String name, @NotNull String[] path, boolean isAbstract, boolean isJsObject, boolean record) { super(parent, elementType, name, path); myAbstract = isAbstract; myJsObject = isJsObject; myRecord = record; } public PsiTypeStub(@Nullable StubElement parent, @NotNull IStubElementType elementType, @Nullable StringRef name, @NotNull String[] path, boolean isAbstract, boolean isJsObject, boolean isRecord) { super(parent, elementType, name, path); myAbstract = isAbstract; myJsObject = isJsObject; myRecord = isRecord; } public boolean isAbstract() { return myAbstract; } public boolean isJsObject() { return myJsObject; } public boolean isRecord() { return myRecord; } } ================================================ FILE: src/main/java/com/reason/lang/core/stub/PsiValStub.java ================================================ package com.reason.lang.core.stub; import com.intellij.psi.stubs.*; import com.intellij.util.io.*; import com.reason.lang.core.psi.*; import org.jetbrains.annotations.*; public class PsiValStub extends PsiQualifiedNameStub { private final boolean myIsFunction; public PsiValStub(@Nullable StubElement parent, @NotNull IStubElementType elementType, @Nullable String name, @NotNull String[] path, boolean isFunction) { super(parent, elementType, name, path); myIsFunction = isFunction; } public PsiValStub(@Nullable StubElement parent, @NotNull IStubElementType elementType, @Nullable StringRef name, @NotNull String[] path, boolean isFunction) { super(parent, elementType, name, path); myIsFunction = isFunction; } public boolean isFunction() { return myIsFunction; } } ================================================ FILE: src/main/java/com/reason/lang/core/stub/PsiVariantDeclarationStub.java ================================================ package com.reason.lang.core.stub; import com.intellij.psi.stubs.*; import com.intellij.util.io.*; import com.reason.lang.core.psi.impl.*; import org.jetbrains.annotations.*; public class PsiVariantDeclarationStub extends PsiQualifiedNameStub { public PsiVariantDeclarationStub(@Nullable StubElement parent, @NotNull IStubElementType elementType, @Nullable String name, @NotNull String[] path) { super(parent, elementType, name, path); } public PsiVariantDeclarationStub(@Nullable StubElement parent, @NotNull IStubElementType elementType, @Nullable StringRef name, @NotNull String[] path) { super(parent, elementType, name, path); } } ================================================ FILE: src/main/java/com/reason/lang/core/stub/ResFileStub.java ================================================ package com.reason.lang.core.stub; import com.intellij.psi.stubs.*; import com.intellij.psi.tree.*; import com.reason.ide.files.*; import com.reason.lang.core.stub.type.*; import org.jetbrains.annotations.*; public class ResFileStub extends PsiFileStubImpl { private final boolean myIsComponent; public ResFileStub(FileBase file, boolean isComponent) { super(file); myIsComponent = isComponent; } @Override public @NotNull IStubFileElementType getType() { return ResFileStubElementType.INSTANCE; } public boolean isComponent() { return myIsComponent; } } ================================================ FILE: src/main/java/com/reason/lang/core/stub/ResStubBasedElementTypes.java ================================================ package com.reason.lang.core.stub; import com.intellij.psi.stubs.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import com.reason.lang.core.stub.type.*; import com.reason.lang.rescript.*; public interface ResStubBasedElementTypes { IStubElementType C_FUNCTOR_DECLARATION = new PsiFunctorModuleStubElementType("C_FUNCTOR_DECLARATION", ResLanguage.INSTANCE); IStubElementType C_MODULE_DECLARATION = new PsiInnerModuleStubElementType("C_MODULE_DECLARATION", ResLanguage.INSTANCE); IStubElementType C_CLASS_DECLARATION = new RPsiClassStubElementType("C_CLASS_DECLARATION", ResLanguage.INSTANCE); IStubElementType C_CLASS_METHOD = new RPsiClassMethodStubElementType("C_CLASS_METHOD", ResLanguage.INSTANCE); IStubElementType C_EXCEPTION_DECLARATION = new PsiExceptionStubElementType("C_EXCEPTION_DECLARATION", ResLanguage.INSTANCE); IStubElementType C_TYPE_DECLARATION = new PsiTypeStubElementType("C_TYPE_DECLARATION", ResLanguage.INSTANCE); IStubElementType C_EXTERNAL_DECLARATION = new PsiExternalStubElementType("C_EXTERNAL_DECLARATION", ResLanguage.INSTANCE); IStubElementType C_LET_DECLARATION = new PsiLetStubElementType("C_LET_DECLARATION", ResLanguage.INSTANCE); IStubElementType C_RECORD_FIELD = new RPsiRecordFieldStubElementType("C_RECORD_FIELD", ResLanguage.INSTANCE); IStubElementType C_OBJECT_FIELD = new PsiObjectFieldStubElementType("C_OBJECT_FIELD", ResLanguage.INSTANCE); IStubElementType C_VAL_DECLARATION = new PsiValStubElementType("C_VAL_DECLARATION", ResLanguage.INSTANCE); IStubElementType C_VARIANT_DECLARATION = new PsiVariantStubElementType("C_VARIANT_DECLARATION", ResLanguage.INSTANCE); IStubElementType C_INCLUDE = new PsiIncludeStubElementType("C_INCLUDE", ResLanguage.INSTANCE); IStubElementType C_OPEN = new PsiOpenStubElementType("C_OPEN", ResLanguage.INSTANCE); IStubElementType C_PARAM_DECLARATION = new PsiParameterDeclarationStubElementType("C_PARAM_DECLARATION", ResLanguage.INSTANCE); } ================================================ FILE: src/main/java/com/reason/lang/core/stub/RmlFileStub.java ================================================ package com.reason.lang.core.stub; import com.intellij.psi.stubs.*; import com.intellij.psi.tree.*; import com.reason.ide.files.*; import com.reason.lang.core.stub.type.*; import org.jetbrains.annotations.*; public class RmlFileStub extends PsiFileStubImpl { private final boolean myIsComponent; public RmlFileStub(FileBase file, boolean isComponent) { super(file); myIsComponent = isComponent; } @Override public @NotNull IStubFileElementType getType() { return RmlFileStubElementType.INSTANCE; } public boolean isComponent() { return myIsComponent; } } ================================================ FILE: src/main/java/com/reason/lang/core/stub/RmlStubBasedElementTypes.java ================================================ package com.reason.lang.core.stub; import com.intellij.psi.stubs.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import com.reason.lang.core.stub.type.*; import com.reason.lang.reason.*; public interface RmlStubBasedElementTypes { IStubElementType C_FUNCTOR_DECLARATION = new PsiFunctorModuleStubElementType("C_FUNCTOR_DECLARATION", RmlLanguage.INSTANCE); IStubElementType C_MODULE_DECLARATION = new PsiInnerModuleStubElementType("C_MODULE_DECLARATION", RmlLanguage.INSTANCE); IStubElementType C_CLASS_DECLARATION = new RPsiClassStubElementType("C_CLASS_DECLARATION", RmlLanguage.INSTANCE); IStubElementType C_CLASS_METHOD = new RPsiClassMethodStubElementType("C_CLASS_METHOD", RmlLanguage.INSTANCE); IStubElementType C_EXCEPTION_DECLARATION = new PsiExceptionStubElementType("C_EXCEPTION_DECLARATION", RmlLanguage.INSTANCE); IStubElementType C_TYPE_DECLARATION = new PsiTypeStubElementType("C_TYPE_DECLARATION", RmlLanguage.INSTANCE); IStubElementType C_EXTERNAL_DECLARATION = new PsiExternalStubElementType("C_EXTERNAL_DECLARATION", RmlLanguage.INSTANCE); IStubElementType C_LET_DECLARATION = new PsiLetStubElementType("C_LET_DECLARATION", RmlLanguage.INSTANCE); IStubElementType C_VAL_DECLARATION = new PsiValStubElementType("C_VAL_DECLARATION", RmlLanguage.INSTANCE); IStubElementType C_RECORD_FIELD = new RPsiRecordFieldStubElementType("C_RECORD_FIELD", RmlLanguage.INSTANCE); IStubElementType C_OBJECT_FIELD = new PsiObjectFieldStubElementType("C_OBJECT_FIELD", RmlLanguage.INSTANCE); IStubElementType C_VARIANT_DECLARATION = new PsiVariantStubElementType("C_VARIANT_DECLARATION", RmlLanguage.INSTANCE); IStubElementType C_INCLUDE = new PsiIncludeStubElementType("C_INCLUDE", RmlLanguage.INSTANCE); IStubElementType C_OPEN = new PsiOpenStubElementType("C_OPEN", RmlLanguage.INSTANCE); IStubElementType C_PARAM_DECLARATION = new PsiParameterDeclarationStubElementType("C_PARAM_DECLARATION", RmlLanguage.INSTANCE); } ================================================ FILE: src/main/java/com/reason/lang/core/stub/RsiClassMethodStub.java ================================================ package com.reason.lang.core.stub; import com.intellij.psi.stubs.*; import com.intellij.util.io.*; import com.reason.lang.core.psi.*; import org.jetbrains.annotations.*; public class RsiClassMethodStub extends PsiQualifiedNameStub { public RsiClassMethodStub(@Nullable StubElement parent, @NotNull IStubElementType elementType, @Nullable String name, @NotNull String[] path) { super(parent, elementType, name, path); } public RsiClassMethodStub(@Nullable StubElement parent, @NotNull IStubElementType elementType, @Nullable StringRef name, @NotNull String[] path) { super(parent, elementType, name, path); } } ================================================ FILE: src/main/java/com/reason/lang/core/stub/RsiClassStub.java ================================================ package com.reason.lang.core.stub; import com.intellij.psi.stubs.*; import com.intellij.util.io.*; import com.reason.lang.core.psi.*; import org.jetbrains.annotations.*; public class RsiClassStub extends PsiQualifiedNameStub { public RsiClassStub(@Nullable StubElement parent, @NotNull IStubElementType elementType, @Nullable String name, @NotNull String[] path) { super(parent, elementType, name, path); } public RsiClassStub(@Nullable StubElement parent, @NotNull IStubElementType elementType, @Nullable StringRef name, @NotNull String[] path) { super(parent, elementType, name, path); } } ================================================ FILE: src/main/java/com/reason/lang/core/stub/RsiRecordFieldStub.java ================================================ package com.reason.lang.core.stub; import com.intellij.psi.stubs.*; import com.intellij.util.io.*; import com.reason.lang.core.psi.*; import org.jetbrains.annotations.*; public class RsiRecordFieldStub extends PsiQualifiedNameStub { public RsiRecordFieldStub(StubElement parent, @NotNull IStubElementType elementType, @Nullable String name, @NotNull String[] path) { super(parent, elementType, name, path); } public RsiRecordFieldStub(StubElement parent, @NotNull IStubElementType elementType, @Nullable StringRef name, @NotNull String[] path) { super(parent, elementType, name, path); } } ================================================ FILE: src/main/java/com/reason/lang/core/stub/type/ORStubElementType.java ================================================ package com.reason.lang.core.stub.type; import com.intellij.lang.*; import com.intellij.psi.*; import com.intellij.psi.stubs.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; /** Some causes of bugs include:
  • leaf/composite PSI mismatch (thanks Vladimir for pointing that out!)
  • LightStubBuilder not working in the same way as the normal one would (by building stubs by AST)
  • errors in incremental parsing (e.g. IReparseableElementType#isParsable incorrectly returning true on broken code, when full reparse would produce a different result)
  • asymmetric stub serialization/deserialization code
  • lexer/parser/AST/PSI/stubs depending on something more than the file's content: other file, project settings, etc
*/ public abstract class ORStubElementType, PsiT extends PsiElement> extends IStubElementType implements ORCompositeType { static final String[] EMPTY_PATH = new String[0]; ORStubElementType(@NotNull String debugName, @Nullable Language language) { super(debugName, language); } public abstract @NotNull PsiElement createPsi(ASTNode node); } ================================================ FILE: src/main/java/com/reason/lang/core/stub/type/ORStubVersions.java ================================================ package com.reason.lang.core.stub.type; public class ORStubVersions { private ORStubVersions() { } public static final int INCLUDE = 5; public static final int CLASS = 3; public static final int CLASS_METHOD = 1; public static final int LET = 17; public static final int EXCEPTION = 8; public static final int EXTERNAL = 10; public static final int MODULE = 28; public static final int OBJECT_FIELD = 3; public static final int OPEN = 3; public static final int PARAMETER = 7; public static final int RECORD_FIELD = 6; public static final int TYPE = 12; public static final int VAL = 13; public static final int VARIANT = 9; public static final int OCL_FILE = 10; public static final int RES_FILE = 10; public static final int RML_FILE = 10; } ================================================ FILE: src/main/java/com/reason/lang/core/stub/type/OclFileStubElementType.java ================================================ package com.reason.lang.core.stub.type; import com.intellij.psi.*; import com.intellij.psi.stubs.*; import com.intellij.psi.tree.*; import com.reason.ide.files.*; import com.reason.lang.core.stub.*; import com.reason.lang.ocaml.*; import org.jetbrains.annotations.*; public class OclFileStubElementType extends IStubFileElementType { public static final IStubFileElementType INSTANCE = new OclFileStubElementType(); private OclFileStubElementType() { super("OCAML_FILE", OclLanguage.INSTANCE); } @Override public @NotNull StubBuilder getBuilder() { return new DefaultStubBuilder() { @Override protected @NotNull PsiFileStub createStubForFile(@NotNull PsiFile file) { if (file instanceof OclFile) { return new OclFileStub((OclFile) file); } else if (file instanceof OclInterfaceFile) { return new OclFileStub((OclInterfaceFile) file); } return new PsiFileStubImpl<>(file); } }; } @Override public int getStubVersion() { return ORStubVersions.OCL_FILE; } @Override public void serialize(@NotNull OclFileStub stub, @NotNull StubOutputStream dataStream) { } @Override public @NotNull OclFileStub deserialize(@NotNull StubInputStream dataStream, StubElement parentStub) { return new OclFileStub(null); } @Override public @NotNull String getExternalId() { return "ocaml.FILE"; } } ================================================ FILE: src/main/java/com/reason/lang/core/stub/type/PsiExceptionStubElementType.java ================================================ package com.reason.lang.core.stub.type; import com.intellij.lang.*; import com.intellij.psi.stubs.*; import com.intellij.util.io.*; import com.reason.ide.search.index.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import com.reason.lang.core.stub.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; import java.io.*; public class PsiExceptionStubElementType extends ORStubElementType { public PsiExceptionStubElementType(@NotNull String name, @NotNull Language language) { super(name, language); } public @NotNull RPsiException createPsi(@NotNull PsiExceptionStub stub) { return new RPsiExceptionImpl(ORTypesUtil.getInstance(getLanguage()), stub, this); } public @NotNull RPsiException createPsi(@NotNull ASTNode node) { return new RPsiExceptionImpl(ORTypesUtil.getInstance(getLanguage()), node); } public @NotNull PsiExceptionStub createStub(@NotNull RPsiException psi, @Nullable StubElement parentStub) { String[] path = psi.getPath(); return new PsiExceptionStub(parentStub, this, psi.getName(), path == null ? EMPTY_PATH : path, psi.getAlias()); } public void serialize(@NotNull PsiExceptionStub stub, @NotNull StubOutputStream dataStream) throws IOException { dataStream.writeName(stub.getName()); SerializerUtil.writePath(dataStream, stub.getPath()); dataStream.writeName(stub.getAlias()); } public @NotNull PsiExceptionStub deserialize(@NotNull StubInputStream dataStream, @Nullable StubElement parentStub) throws IOException { StringRef name = dataStream.readName(); String[] path = SerializerUtil.readPath(dataStream); StringRef alias = dataStream.readName(); return new PsiExceptionStub(parentStub, this, name, path == null ? EMPTY_PATH : path, alias == null ? null : alias.getString()); } public void indexStub(@NotNull PsiExceptionStub stub, @NotNull IndexSink sink) { String name = stub.getName(); if (name != null) { sink.occurrence(IndexKeys.EXCEPTIONS, name); } String fqn = stub.getQualifiedName(); sink.occurrence(IndexKeys.EXCEPTIONS_FQN, fqn.hashCode()); } public @NotNull String getExternalId() { return getLanguage().getID() + "." + super.toString(); } } ================================================ FILE: src/main/java/com/reason/lang/core/stub/type/PsiExternalStubElementType.java ================================================ package com.reason.lang.core.stub.type; import com.intellij.lang.*; import com.intellij.psi.stubs.*; import com.intellij.util.io.*; import com.reason.ide.search.index.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import com.reason.lang.core.stub.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; import java.io.*; public class PsiExternalStubElementType extends ORStubElementType { public PsiExternalStubElementType(@NotNull String name, @Nullable Language language) { super(name, language); } public @NotNull RPsiExternalImpl createPsi(@NotNull PsiExternalStub stub) { return new RPsiExternalImpl(ORTypesUtil.getInstance(getLanguage()), stub, this); } public @NotNull RPsiExternalImpl createPsi(@NotNull ASTNode node) { return new RPsiExternalImpl(ORTypesUtil.getInstance(getLanguage()), node); } public @NotNull PsiExternalStub createStub(@NotNull RPsiExternal psi, @Nullable StubElement parentStub) { String[] path = psi.getPath(); return new PsiExternalStub(parentStub, this, psi.getName(), path == null ? EMPTY_PATH : path, psi.isFunction()); } public void serialize(@NotNull PsiExternalStub stub, @NotNull StubOutputStream dataStream) throws IOException { dataStream.writeName(stub.getName()); SerializerUtil.writePath(dataStream, stub.getPath()); dataStream.writeBoolean(stub.isFunction()); } public @NotNull PsiExternalStub deserialize(@NotNull StubInputStream dataStream, @Nullable StubElement parentStub) throws IOException { StringRef name = dataStream.readName(); String[] path = SerializerUtil.readPath(dataStream); boolean isFunction = dataStream.readBoolean(); return new PsiExternalStub(parentStub, this, name, path == null ? EMPTY_PATH : path, isFunction); } public void indexStub(@NotNull PsiExternalStub stub, @NotNull IndexSink sink) { String name = stub.getName(); if (name != null) { sink.occurrence(IndexKeys.EXTERNALS, name); int fqnHash = stub.getQualifiedName().hashCode(); sink.occurrence(IndexKeys.EXTERNALS_FQN, fqnHash); } } public @NotNull String getExternalId() { return getLanguage().getID() + "." + super.toString(); } } ================================================ FILE: src/main/java/com/reason/lang/core/stub/type/PsiFunctorModuleStubElementType.java ================================================ package com.reason.lang.core.stub.type; import com.intellij.lang.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import com.reason.lang.core.stub.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; public class PsiFunctorModuleStubElementType extends PsiModuleStubElementType { public PsiFunctorModuleStubElementType(@NotNull String name, @NotNull Language language) { super(name, language); } public @NotNull RPsiFunctor createPsi(@NotNull PsiModuleStub stub) { return new RPsiFunctorImpl(ORTypesUtil.getInstance(getLanguage()), stub, this); } public @NotNull RPsiFunctor createPsi(@NotNull ASTNode node) { return new RPsiFunctorImpl(ORTypesUtil.getInstance(getLanguage()), node); } } ================================================ FILE: src/main/java/com/reason/lang/core/stub/type/PsiIncludeStubElementType.java ================================================ package com.reason.lang.core.stub.type; import com.intellij.lang.*; import com.intellij.psi.*; import com.intellij.psi.stubs.*; import com.intellij.util.io.*; import com.reason.ide.files.*; import com.reason.ide.search.index.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import com.reason.lang.core.stub.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; import java.io.*; public class PsiIncludeStubElementType extends ORStubElementType { public PsiIncludeStubElementType(@NotNull String name, @Nullable Language language) { super(name, language); } @Override public @NotNull RPsiInclude createPsi(@NotNull PsiIncludeStub stub) { return new RPsiIncludeImpl(ORTypesUtil.getInstance(getLanguage()), stub, this); } @Override public @NotNull PsiElement createPsi(@NotNull ASTNode node) { return new RPsiIncludeImpl(ORTypesUtil.getInstance(getLanguage()), node); } @Override public @NotNull PsiIncludeStub createStub(@NotNull RPsiInclude psi, @Nullable StubElement parentStub) { return new PsiIncludeStub(parentStub, this, ((FileBase) psi.getContainingFile()).getModuleName(), psi.getIncludePath(), psi.getQualifiedPath()); } @Override public void serialize(@NotNull PsiIncludeStub stub, @NotNull StubOutputStream dataStream) throws IOException { dataStream.writeName(stub.getFileModule()); dataStream.writeUTFFast(stub.getIncludePath()); SerializerUtil.writePath(dataStream, stub.getQualifiedPath()); } @Override public @NotNull PsiIncludeStub deserialize(@NotNull StubInputStream dataStream, @Nullable StubElement parentStub) throws IOException { StringRef fileModule = dataStream.readName(); String includePath = dataStream.readUTFFast(); String[] qualifiedPath = SerializerUtil.readPath(dataStream); return new PsiIncludeStub(parentStub, this, fileModule, includePath, qualifiedPath); } @Override public void indexStub(@NotNull PsiIncludeStub stub, @NotNull IndexSink sink) { sink.occurrence(IndexKeys.INCLUDES, stub.getIncludePath()); } @Override public @NotNull String getExternalId() { return getLanguage().getID() + "." + super.toString(); } } ================================================ FILE: src/main/java/com/reason/lang/core/stub/type/PsiInnerModuleStubElementType.java ================================================ package com.reason.lang.core.stub.type; import com.intellij.lang.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import com.reason.lang.core.stub.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; public class PsiInnerModuleStubElementType extends PsiModuleStubElementType implements ORCompositeType { public PsiInnerModuleStubElementType(@NotNull String name, @Nullable Language language) { super(name, language); } public @NotNull RPsiInnerModule createPsi(@NotNull ASTNode node) { return new RPsiInnerModuleImpl(ORTypesUtil.getInstance(getLanguage()), node); } public @NotNull RPsiInnerModule createPsi(@NotNull PsiModuleStub stub) { return new RPsiInnerModuleImpl(ORTypesUtil.getInstance(getLanguage()), stub, this); } } ================================================ FILE: src/main/java/com/reason/lang/core/stub/type/PsiLetStubElementType.java ================================================ package com.reason.lang.core.stub.type; import com.intellij.lang.*; import com.intellij.psi.*; import com.intellij.psi.stubs.*; import com.intellij.util.io.*; import com.reason.ide.search.index.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import com.reason.lang.core.stub.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; import java.io.*; import java.util.*; public class PsiLetStubElementType extends ORStubElementType { public PsiLetStubElementType(@NotNull String name, @Nullable Language language) { super(name, language); } public @NotNull RPsiLetImpl createPsi(@NotNull PsiLetStub stub) { return new RPsiLetImpl(ORTypesUtil.getInstance(getLanguage()), stub, this); } @Override public @NotNull PsiElement createPsi(@NotNull ASTNode node) { return new RPsiLetImpl(ORTypesUtil.getInstance(getLanguage()), node); } public @NotNull PsiLetStub createStub(@NotNull RPsiLet psi, @Nullable StubElement parentStub) { List deconstructedNames = new ArrayList<>(); if (psi.isDeconstruction()) { List elements = psi.getDeconstructedElements(); for (PsiElement element : elements) { if (element instanceof RPsiLowerSymbol) { deconstructedNames.add(element.getText()); } } } return new PsiLetStub(parentStub, this, psi.getName(), psi.getPath(), psi.getAlias(), psi.isFunction(), psi.isComponent(), deconstructedNames); } public void serialize(@NotNull PsiLetStub stub, @NotNull StubOutputStream dataStream) throws IOException { dataStream.writeName(stub.getName()); SerializerUtil.writePath(dataStream, stub.getPath()); dataStream.writeBoolean(stub.isFunction()); dataStream.writeBoolean(stub.isComponent()); List deconstructionNames = stub.getDeconstructionNames(); dataStream.writeByte(deconstructionNames.size()); if (!deconstructionNames.isEmpty()) { for (String name : deconstructionNames) { dataStream.writeUTFFast(name); } } String alias = stub.getAlias(); dataStream.writeBoolean(alias != null); if (alias != null) { dataStream.writeUTFFast(stub.getAlias()); } } public @NotNull PsiLetStub deserialize(@NotNull StubInputStream dataStream, @Nullable StubElement parentStub) throws IOException { StringRef name = dataStream.readName(); String[] path = SerializerUtil.readPath(dataStream); boolean isFunction = dataStream.readBoolean(); boolean isComponent = dataStream.readBoolean(); List deconstructionNames = new ArrayList<>(); byte namesCount = dataStream.readByte(); if (namesCount > 0) { for (int i = 0; i < namesCount; i++) { deconstructionNames.add(dataStream.readUTFFast()); } } String alias = null; boolean isAlias = dataStream.readBoolean(); if (isAlias) { alias = dataStream.readUTFFast(); } return new PsiLetStub(parentStub, this, name, path, alias, isFunction, isComponent, deconstructionNames); } public void indexStub(@NotNull PsiLetStub stub, @NotNull IndexSink sink) { List deconstructionNames = stub.getDeconstructionNames(); if (deconstructionNames.isEmpty()) { // Normal let String fqn = stub.getQualifiedName(); sink.occurrence(IndexKeys.LETS_FQN, fqn.hashCode()); if (stub.isComponent()) { sink.occurrence(IndexKeys.LETS_COMP_FQN, fqn.hashCode()); } } else { // Deconstruction for (String fqn : stub.getQualifiedNames()) { sink.occurrence(IndexKeys.LETS_FQN, fqn.hashCode()); } } } public @NotNull String getExternalId() { return getLanguage().getID() + "." + super.toString(); } } ================================================ FILE: src/main/java/com/reason/lang/core/stub/type/PsiModuleStubElementType.java ================================================ package com.reason.lang.core.stub.type; import com.intellij.lang.*; import com.intellij.psi.stubs.*; import com.intellij.util.io.*; import com.reason.ide.search.index.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import com.reason.lang.core.stub.*; import org.jetbrains.annotations.*; import java.io.*; public abstract class PsiModuleStubElementType extends ORStubElementType { protected PsiModuleStubElementType(@NotNull String name, @Nullable Language language) { super(name, language); } @NotNull public PsiModuleStub createStub(@NotNull RPsiModule psi, StubElement parentStub) { boolean isFunctorCall = false; boolean isModuleType = false; String alias = null; String signatureName = null; if (psi instanceof RPsiInnerModule innerModule) { isModuleType = innerModule.isModuleType(); isFunctorCall = innerModule.isFunctorCall(); alias = innerModule.getAlias(); RPsiModuleSignature moduleSignature = innerModule.getModuleSignature(); signatureName = moduleSignature != null ? moduleSignature.getName() : null; } return new PsiModuleStub(parentStub, this, psi.getName(), psi.getPath(), null, alias, psi.isComponent(), isModuleType, false, isFunctorCall, signatureName); } public void serialize(@NotNull PsiModuleStub stub, @NotNull StubOutputStream dataStream) throws IOException { dataStream.writeName(stub.getName()); SerializerUtil.writePath(dataStream, stub.getPath()); dataStream.writeBoolean(stub.isComponent()); dataStream.writeBoolean(stub.isModuleType()); dataStream.writeBoolean(stub.isTopLevel()); dataStream.writeBoolean(stub.isFunctorCall()); String alias = stub.getAlias(); dataStream.writeBoolean(alias != null); if (alias != null) { dataStream.writeUTFFast(alias); } String returnTypeName = stub.getSignatureName(); dataStream.writeBoolean(returnTypeName != null); if (returnTypeName != null) { dataStream.writeName(returnTypeName); } } public @NotNull PsiModuleStub deserialize(@NotNull StubInputStream dataStream, StubElement parentStub) throws IOException { StringRef moduleName = dataStream.readName(); String[] path = SerializerUtil.readPath(dataStream); boolean isComponent = dataStream.readBoolean(); boolean isModuleType = dataStream.readBoolean(); boolean isTopLevel = dataStream.readBoolean(); boolean isFunctorCall = dataStream.readBoolean(); String alias = null; boolean isAlias = dataStream.readBoolean(); if (isAlias) { alias = dataStream.readUTFFast(); } StringRef returnTypeName = null; if (dataStream.readBoolean()) { returnTypeName = dataStream.readName(); } return new PsiModuleStub(parentStub, this, moduleName, path, null, alias, isComponent, isModuleType, isTopLevel, isFunctorCall, returnTypeName); } public void indexStub(@NotNull PsiModuleStub stub, @NotNull IndexSink sink) { String name = stub.getName(); if (name != null) { sink.occurrence(IndexKeys.MODULES, name); int fqnHash = stub.getQualifiedName().hashCode(); sink.occurrence(IndexKeys.MODULES_FQN, fqnHash); String signatureName = stub.getSignatureName(); if (signatureName != null) { sink.occurrence(IndexKeys.MODULES_SIGNATURE, signatureName); } } } @NotNull public String getExternalId() { return getLanguage().getID() + "." + super.toString(); } } ================================================ FILE: src/main/java/com/reason/lang/core/stub/type/PsiObjectFieldStubElementType.java ================================================ package com.reason.lang.core.stub.type; import com.intellij.lang.*; import com.intellij.psi.stubs.*; import com.intellij.util.io.*; import com.reason.ide.search.index.*; import com.reason.lang.core.psi.impl.*; import com.reason.lang.core.stub.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; import java.io.*; public class PsiObjectFieldStubElementType extends ORStubElementType { public PsiObjectFieldStubElementType(@NotNull String name, @NotNull Language language) { super(name, language); } public @NotNull RPsiObjectField createPsi(@NotNull PsiObjectFieldStub stub) { return new RPsiObjectField(ORTypesUtil.getInstance(getLanguage()), stub, this); } public @NotNull RPsiObjectField createPsi(@NotNull ASTNode node) { return new RPsiObjectField(ORTypesUtil.getInstance(getLanguage()), node); } public @NotNull PsiObjectFieldStub createStub(@NotNull RPsiObjectField psi, @Nullable StubElement parentStub) { return new PsiObjectFieldStub(parentStub, this, psi.getName(), psi.getPath()); } public void serialize(@NotNull final PsiObjectFieldStub stub, @NotNull StubOutputStream dataStream) throws IOException { dataStream.writeName(stub.getName()); SerializerUtil.writePath(dataStream, stub.getPath()); } public @NotNull PsiObjectFieldStub deserialize(@NotNull StubInputStream dataStream, StubElement parentStub) throws IOException { StringRef name = dataStream.readName(); String[] path = SerializerUtil.readPath(dataStream); return new PsiObjectFieldStub(parentStub, this, name, path == null ? EMPTY_PATH : path); } public void indexStub(@NotNull PsiObjectFieldStub stub, @NotNull IndexSink sink) { String name = stub.getName(); if (name != null) { sink.occurrence(IndexKeys.OBJECT_FIELDS, name); } } public @NotNull String getExternalId() { return getLanguage().getID() + "." + super.toString(); } } ================================================ FILE: src/main/java/com/reason/lang/core/stub/type/PsiOpenStubElementType.java ================================================ package com.reason.lang.core.stub.type; import com.intellij.lang.*; import com.intellij.psi.*; import com.intellij.psi.stubs.*; import com.reason.ide.search.index.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import com.reason.lang.core.stub.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; import java.io.*; public class PsiOpenStubElementType extends ORStubElementType { public PsiOpenStubElementType(@NotNull String name, @Nullable Language language) { super(name, language); } @Override public @NotNull RPsiOpen createPsi(@NotNull PsiOpenStub stub) { return new RPsiOpenImpl(ORTypesUtil.getInstance(getLanguage()), stub, this); } @Override public @NotNull PsiElement createPsi(@NotNull ASTNode node) { return new RPsiOpenImpl(ORTypesUtil.getInstance(getLanguage()), node); } @Override public @NotNull PsiOpenStub createStub(@NotNull RPsiOpen psi, @Nullable StubElement parentStub) { return new PsiOpenStub(parentStub, this, psi.getPath()); } @Override public void serialize(@NotNull PsiOpenStub stub, @NotNull StubOutputStream dataStream) throws IOException { String openPath = stub.getOpenPath(); dataStream.writeUTFFast(openPath == null ? "" : openPath); } @Override public @NotNull PsiOpenStub deserialize(@NotNull StubInputStream dataStream, @Nullable StubElement parentStub) throws IOException { String openPath = dataStream.readUTFFast(); return new PsiOpenStub(parentStub, this, openPath); } @Override public void indexStub(@NotNull PsiOpenStub stub, @NotNull IndexSink sink) { String openPath = stub.getOpenPath(); if (openPath != null) { sink.occurrence(IndexKeys.OPENS, openPath); } } @Override public @NotNull String getExternalId() { return getLanguage().getID() + "." + super.toString(); } } ================================================ FILE: src/main/java/com/reason/lang/core/stub/type/PsiParameterDeclarationStubElementType.java ================================================ package com.reason.lang.core.stub.type; import com.intellij.lang.*; import com.intellij.psi.stubs.*; import com.intellij.util.io.*; import com.reason.ide.search.index.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import com.reason.lang.core.stub.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; import java.io.*; public class PsiParameterDeclarationStubElementType extends ORStubElementType { public PsiParameterDeclarationStubElementType(@NotNull String name, @NotNull Language language) { super(name, language); } @NotNull public RPsiParameterDeclaration createPsi(@NotNull PsiParameterDeclarationStub stub) { ORLangTypes types = ORTypesUtil.getInstance(getLanguage()); return new RPsiParameterDeclarationImpl(types, stub, this); } @NotNull public RPsiParameterDeclaration createPsi(@NotNull ASTNode node) { ORLangTypes types = ORTypesUtil.getInstance(getLanguage()); return new RPsiParameterDeclarationImpl(types, node); } @NotNull public PsiParameterDeclarationStub createStub(@NotNull RPsiParameterDeclaration psi, StubElement parentStub) { return new PsiParameterDeclarationStub(parentStub, this, psi.getName(), psi.getPath(), psi.getQualifiedName(), psi.isNamed()); } public void serialize(@NotNull PsiParameterDeclarationStub stub, @NotNull StubOutputStream dataStream) throws IOException { dataStream.writeName(stub.getName()); SerializerUtil.writePath(dataStream, stub.getPath()); dataStream.writeUTFFast(stub.getQualifiedName()); dataStream.writeBoolean(stub.isNamed()); } @NotNull public PsiParameterDeclarationStub deserialize(@NotNull StubInputStream dataStream, StubElement parentStub) throws IOException { StringRef name = dataStream.readName(); String[] path = SerializerUtil.readPath(dataStream); String qname = dataStream.readUTFFast(); boolean isNamed = dataStream.readBoolean(); return new PsiParameterDeclarationStub(parentStub, this, name, path, qname, isNamed); } public void indexStub(@NotNull PsiParameterDeclarationStub stub, @NotNull IndexSink sink) { String name = stub.getName(); if (name != null) { sink.occurrence(IndexKeys.PARAMETERS, name); } String fqn = stub.getQualifiedName(); if (fqn != null) { sink.occurrence(IndexKeys.PARAMETERS_FQN, fqn.hashCode()); } } @NotNull public String getExternalId() { return getLanguage().getID() + "." + super.toString(); } } ================================================ FILE: src/main/java/com/reason/lang/core/stub/type/PsiTypeStubElementType.java ================================================ package com.reason.lang.core.stub.type; import com.intellij.lang.*; import com.intellij.psi.stubs.*; import com.intellij.util.io.*; import com.reason.ide.search.index.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import com.reason.lang.core.stub.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; import java.io.*; public class PsiTypeStubElementType extends ORStubElementType { public PsiTypeStubElementType(@NotNull String name, @NotNull Language language) { super(name, language); } public @NotNull RPsiTypeImpl createPsi(@NotNull PsiTypeStub stub) { return new RPsiTypeImpl(ORTypesUtil.getInstance(getLanguage()), stub, this); } public @NotNull RPsiTypeImpl createPsi(@NotNull ASTNode node) { return new RPsiTypeImpl(ORTypesUtil.getInstance(getLanguage()), node); } public @NotNull PsiTypeStub createStub(@NotNull RPsiType psi, @Nullable StubElement parentStub) { String[] path = psi.getPath(); return new PsiTypeStub(parentStub, this, psi.getName(), path == null ? EMPTY_PATH : path, psi.isAbstract(), psi.isJsObject(), psi.isRecord()); } public void serialize(@NotNull PsiTypeStub stub, @NotNull StubOutputStream dataStream) throws IOException { dataStream.writeName(stub.getName()); SerializerUtil.writePath(dataStream, stub.getPath()); dataStream.writeBoolean(stub.isAbstract()); dataStream.writeBoolean(stub.isJsObject()); dataStream.writeBoolean(stub.isRecord()); } public @NotNull PsiTypeStub deserialize(@NotNull StubInputStream dataStream, @Nullable StubElement parentStub) throws IOException { StringRef name = dataStream.readName(); String[] path = SerializerUtil.readPath(dataStream); boolean isAbstract = dataStream.readBoolean(); boolean isJsObject = dataStream.readBoolean(); boolean isRecord = dataStream.readBoolean(); return new PsiTypeStub(parentStub, this, name, path == null ? EMPTY_PATH : path, isAbstract, isJsObject, isRecord); } public void indexStub(@NotNull PsiTypeStub stub, @NotNull IndexSink sink) { String name = stub.getName(); if (name != null) { sink.occurrence(IndexKeys.TYPES, name); } String fqn = stub.getQualifiedName(); sink.occurrence(IndexKeys.TYPES_FQN, fqn.hashCode()); } public @NotNull String getExternalId() { return getLanguage().getID() + "." + super.toString(); } } ================================================ FILE: src/main/java/com/reason/lang/core/stub/type/PsiValStubElementType.java ================================================ package com.reason.lang.core.stub.type; import com.intellij.lang.*; import com.intellij.psi.stubs.*; import com.intellij.util.io.*; import com.reason.ide.search.index.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import com.reason.lang.core.stub.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; import java.io.*; public class PsiValStubElementType extends ORStubElementType { public PsiValStubElementType(@NotNull String name, @Nullable Language language) { super(name, language); } public @NotNull RPsiValImpl createPsi(@NotNull ASTNode node) { return new RPsiValImpl(ORTypesUtil.getInstance(getLanguage()), node); } public @NotNull RPsiValImpl createPsi(@NotNull PsiValStub stub) { return new RPsiValImpl(ORTypesUtil.getInstance(getLanguage()), stub, this); } public @NotNull PsiValStub createStub(@NotNull RPsiVal psi, @Nullable StubElement parentStub) { String[] path = psi.getPath(); return new PsiValStub(parentStub, this, psi.getName(), path == null ? EMPTY_PATH : path, psi.isFunction()); } public void serialize(@NotNull PsiValStub stub, @NotNull StubOutputStream dataStream) throws IOException { dataStream.writeName(stub.getName()); SerializerUtil.writePath(dataStream, stub.getPath()); dataStream.writeBoolean(stub.isFunction()); } public @NotNull PsiValStub deserialize(@NotNull StubInputStream dataStream, @Nullable StubElement parentStub) throws IOException { StringRef name = dataStream.readName(); String[] path = SerializerUtil.readPath(dataStream); boolean isFunction = dataStream.readBoolean(); return new PsiValStub(parentStub, this, name, path == null ? EMPTY_PATH : path, isFunction); } public void indexStub(@NotNull PsiValStub stub, @NotNull IndexSink sink) { String fqn = stub.getQualifiedName(); sink.occurrence(IndexKeys.VALS_FQN, fqn.hashCode()); } public @NotNull String getExternalId() { return getLanguage().getID() + "." + super.toString(); } } ================================================ FILE: src/main/java/com/reason/lang/core/stub/type/PsiVariantStubElementType.java ================================================ package com.reason.lang.core.stub.type; import com.intellij.lang.*; import com.intellij.psi.stubs.*; import com.intellij.util.io.*; import com.reason.ide.search.index.*; import com.reason.lang.core.psi.impl.*; import com.reason.lang.core.stub.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; import java.io.*; public class PsiVariantStubElementType extends ORStubElementType { public PsiVariantStubElementType(@NotNull String name, @Nullable Language language) { super(name, language); } public @NotNull RPsiVariantDeclaration createPsi(@NotNull PsiVariantDeclarationStub stub) { return new RPsiVariantDeclaration(ORTypesUtil.getInstance(getLanguage()), stub, this); } public @NotNull RPsiVariantDeclaration createPsi(@NotNull ASTNode node) { return new RPsiVariantDeclaration(ORTypesUtil.getInstance(getLanguage()), node); } public @NotNull PsiVariantDeclarationStub createStub(@NotNull RPsiVariantDeclaration psi, @Nullable StubElement parentStub) { return new PsiVariantDeclarationStub(parentStub, this, psi.getName(), psi.getPath()); } public void serialize(@NotNull PsiVariantDeclarationStub stub, @NotNull StubOutputStream dataStream) throws IOException { dataStream.writeName(stub.getName()); SerializerUtil.writePath(dataStream, stub.getPath()); } public @NotNull PsiVariantDeclarationStub deserialize(@NotNull StubInputStream dataStream, StubElement parentStub) throws IOException { StringRef name = dataStream.readName(); String[] path = SerializerUtil.readPath(dataStream); return new PsiVariantDeclarationStub(parentStub, this, name, path == null ? EMPTY_PATH : path); } public void indexStub(@NotNull PsiVariantDeclarationStub stub, @NotNull IndexSink sink) { String fqn = stub.getQualifiedName(); sink.occurrence(IndexKeys.VARIANTS_FQN, fqn.hashCode()); } public @NotNull String getExternalId() { return getLanguage().getID() + "." + super.toString(); } } ================================================ FILE: src/main/java/com/reason/lang/core/stub/type/RPsiClassMethodStubElementType.java ================================================ package com.reason.lang.core.stub.type; import com.intellij.lang.*; import com.intellij.psi.stubs.*; import com.intellij.util.io.*; import com.reason.ide.search.index.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import com.reason.lang.core.stub.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; import java.io.*; public class RPsiClassMethodStubElementType extends ORStubElementType { public RPsiClassMethodStubElementType(@NotNull String name, @Nullable Language language) { super(name, language); } public @NotNull RPsiClassMethodImpl createPsi(@NotNull RsiClassMethodStub stub) { return new RPsiClassMethodImpl(ORTypesUtil.getInstance(getLanguage()), stub, this); } public @NotNull RPsiClassMethodImpl createPsi(@NotNull ASTNode node) { return new RPsiClassMethodImpl(ORTypesUtil.getInstance(getLanguage()), node); } public @NotNull RsiClassMethodStub createStub(@NotNull RPsiClassMethod psi, @Nullable StubElement parentStub) { String[] path = psi.getPath(); return new RsiClassMethodStub(parentStub, this, psi.getName(), path == null ? EMPTY_PATH : path); } public void serialize(@NotNull RsiClassMethodStub stub, @NotNull StubOutputStream dataStream) throws IOException { dataStream.writeName(stub.getName()); SerializerUtil.writePath(dataStream, stub.getPath()); } public @NotNull RsiClassMethodStub deserialize(@NotNull StubInputStream dataStream, @Nullable StubElement parentStub) throws IOException { StringRef name = dataStream.readName(); String[] path = SerializerUtil.readPath(dataStream); return new RsiClassMethodStub(parentStub, this, name, path == null ? EMPTY_PATH : path); } public void indexStub(@NotNull RsiClassMethodStub stub, @NotNull IndexSink sink) { String fqn = stub.getQualifiedName(); sink.occurrence(IndexKeys.CLASS_METHODS_FQN, fqn.hashCode()); } public @NotNull String getExternalId() { return getLanguage().getID() + "." + super.toString(); } } ================================================ FILE: src/main/java/com/reason/lang/core/stub/type/RPsiClassStubElementType.java ================================================ package com.reason.lang.core.stub.type; import com.intellij.lang.*; import com.intellij.psi.stubs.*; import com.intellij.util.io.*; import com.reason.ide.search.index.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import com.reason.lang.core.stub.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; import java.io.*; public class RPsiClassStubElementType extends ORStubElementType { public RPsiClassStubElementType(@NotNull String name, @Nullable Language language) { super(name, language); } public @NotNull RPsiClassImpl createPsi(@NotNull RsiClassStub stub) { return new RPsiClassImpl(ORTypesUtil.getInstance(getLanguage()), stub, this); } public @NotNull RPsiClassImpl createPsi(@NotNull ASTNode node) { return new RPsiClassImpl(ORTypesUtil.getInstance(getLanguage()), node); } public @NotNull RsiClassStub createStub(@NotNull RPsiClass psi, @Nullable StubElement parentStub) { String[] path = psi.getPath(); return new RsiClassStub(parentStub, this, psi.getName(), path == null ? EMPTY_PATH : path); } public void serialize(@NotNull RsiClassStub stub, @NotNull StubOutputStream dataStream) throws IOException { dataStream.writeName(stub.getName()); SerializerUtil.writePath(dataStream, stub.getPath()); } public @NotNull RsiClassStub deserialize(@NotNull StubInputStream dataStream, @Nullable StubElement parentStub) throws IOException { StringRef name = dataStream.readName(); String[] path = SerializerUtil.readPath(dataStream); return new RsiClassStub(parentStub, this, name, path == null ? EMPTY_PATH : path); } public void indexStub(@NotNull RsiClassStub stub, @NotNull IndexSink sink) { String fqn = stub.getQualifiedName(); sink.occurrence(IndexKeys.CLASSES_FQN, fqn.hashCode()); } public @NotNull String getExternalId() { return getLanguage().getID() + "." + super.toString(); } } ================================================ FILE: src/main/java/com/reason/lang/core/stub/type/RPsiRecordFieldStubElementType.java ================================================ package com.reason.lang.core.stub.type; import com.intellij.lang.*; import com.intellij.psi.stubs.*; import com.intellij.util.io.*; import com.reason.ide.search.index.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import com.reason.lang.core.stub.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; import java.io.*; public class RPsiRecordFieldStubElementType extends ORStubElementType { public RPsiRecordFieldStubElementType(@NotNull String name, @NotNull Language language) { super(name, language); } public @NotNull RPsiRecordFieldImpl createPsi(@NotNull RsiRecordFieldStub stub) { return new RPsiRecordFieldImpl(ORTypesUtil.getInstance(getLanguage()), stub, this); } public @NotNull RPsiRecordFieldImpl createPsi(@NotNull ASTNode node) { return new RPsiRecordFieldImpl(ORTypesUtil.getInstance(getLanguage()), node); } public @NotNull RsiRecordFieldStub createStub(@NotNull RPsiRecordField psi, @Nullable StubElement parentStub) { String[] path = psi.getPath(); return new RsiRecordFieldStub(parentStub, this, psi.getName(), path != null ? path : new String[0]); } public void serialize(@NotNull final RsiRecordFieldStub stub, @NotNull StubOutputStream dataStream) throws IOException { dataStream.writeName(stub.getName()); SerializerUtil.writePath(dataStream, stub.getPath()); } public @NotNull RsiRecordFieldStub deserialize(@NotNull StubInputStream dataStream, @Nullable StubElement parentStub) throws IOException { StringRef name = dataStream.readName(); String[] path = SerializerUtil.readPath(dataStream); return new RsiRecordFieldStub(parentStub, this, name, path == null ? EMPTY_PATH : path); } public void indexStub(@NotNull RsiRecordFieldStub stub, @NotNull IndexSink sink) { String name = stub.getName(); if (name != null) { sink.occurrence(IndexKeys.RECORD_FIELDS, name); } } public @NotNull String getExternalId() { return getLanguage().getID() + "." + super.toString(); } } ================================================ FILE: src/main/java/com/reason/lang/core/stub/type/ResFileStubElementType.java ================================================ package com.reason.lang.core.stub.type; import com.intellij.psi.PsiFile; import com.intellij.psi.StubBuilder; import com.intellij.psi.stubs.*; import com.intellij.psi.tree.IStubFileElementType; import com.reason.ide.files.*; import com.reason.lang.core.stub.ResFileStub; import com.reason.lang.rescript.ResLanguage; import java.io.*; import org.jetbrains.annotations.NotNull; public class ResFileStubElementType extends IStubFileElementType { public static final IStubFileElementType INSTANCE = new ResFileStubElementType(); private ResFileStubElementType() { super("RESCRIPT_FILE", ResLanguage.INSTANCE); } @Override public @NotNull StubBuilder getBuilder() { return new DefaultStubBuilder() { @Override protected @NotNull PsiFileStub createStubForFile(@NotNull PsiFile file) { if (file instanceof ResFile) { return new ResFileStub((ResFile) file, ((ResFile) file).isComponent()); } else if (file instanceof ResInterfaceFile) { return new ResFileStub((ResInterfaceFile) file, ((ResInterfaceFile) file).isComponent()); } return new PsiFileStubImpl<>(file); } }; } @Override public int getStubVersion() { return ORStubVersions.RES_FILE; } @Override public void serialize(@NotNull ResFileStub stub, @NotNull StubOutputStream dataStream) throws IOException { dataStream.writeBoolean(stub.isComponent()); } @Override public @NotNull ResFileStub deserialize(@NotNull StubInputStream dataStream, StubElement parentStub) throws IOException { return new ResFileStub(null, dataStream.readBoolean()); } @Override public @NotNull String getExternalId() { return "rescript.FILE"; } } ================================================ FILE: src/main/java/com/reason/lang/core/stub/type/RmlFileStubElementType.java ================================================ package com.reason.lang.core.stub.type; import com.intellij.psi.*; import com.intellij.psi.stubs.*; import com.intellij.psi.tree.*; import com.reason.ide.files.*; import com.reason.lang.core.stub.*; import com.reason.lang.reason.*; import org.jetbrains.annotations.*; import java.io.*; public class RmlFileStubElementType extends IStubFileElementType { public static final IStubFileElementType INSTANCE = new RmlFileStubElementType(); private RmlFileStubElementType() { super("REASON_FILE", RmlLanguage.INSTANCE); } @Override public @NotNull StubBuilder getBuilder() { return new DefaultStubBuilder() { @Override protected @NotNull PsiFileStub createStubForFile(@NotNull PsiFile file) { if (file instanceof RmlFile) { return new RmlFileStub((RmlFile) file, ((RmlFile) file).isComponent()); } else if (file instanceof RmlInterfaceFile) { return new RmlFileStub((RmlInterfaceFile) file, ((RmlInterfaceFile) file).isComponent()); } return new PsiFileStubImpl<>(file); } }; } @Override public int getStubVersion() { return ORStubVersions.RML_FILE; } @Override public void serialize(@NotNull RmlFileStub stub, @NotNull StubOutputStream dataStream) throws IOException { dataStream.writeBoolean(stub.isComponent()); } @NotNull @Override public RmlFileStub deserialize(@NotNull StubInputStream dataStream, StubElement parentStub) throws IOException { return new RmlFileStub(null, dataStream.readBoolean()); } @NotNull @Override public String getExternalId() { return "reason.FILE"; } } ================================================ FILE: src/main/java/com/reason/lang/core/stub/type/SerializerUtil.java ================================================ package com.reason.lang.core.stub.type; import com.intellij.psi.stubs.*; import org.jetbrains.annotations.*; import java.io.*; public class SerializerUtil { private SerializerUtil() { } static void writePath(@NotNull StubOutputStream dataStream, @Nullable String[] path) throws IOException { if (path == null) { dataStream.writeByte(0); } else { dataStream.writeByte(path.length); for (String name : path) { dataStream.writeUTFFast(name == null ? "" : name); } } } static @Nullable String[] readPath(@NotNull StubInputStream dataStream) throws IOException { String[] path = null; byte namesCount = dataStream.readByte(); if (namesCount > 0) { path = new String[namesCount]; for (int i = 0; i < namesCount; i++) { path[i] = dataStream.readUTFFast(); } } return path; } } ================================================ FILE: src/main/java/com/reason/lang/core/type/ORCompositeElementType.java ================================================ package com.reason.lang.core.type; import com.intellij.lang.Language; import com.intellij.psi.tree.IElementType; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; public class ORCompositeElementType extends IElementType implements ORCompositeType { public ORCompositeElementType(@NotNull @NonNls String debugName, @NotNull Language language) { super(debugName, language); } } ================================================ FILE: src/main/java/com/reason/lang/core/type/ORCompositePsiElement.java ================================================ package com.reason.lang.core.type; import com.intellij.psi.impl.source.tree.*; import com.intellij.psi.tree.*; import org.jetbrains.annotations.*; public abstract class ORCompositePsiElement extends CompositePsiElement { protected final @NotNull T myTypes; protected ORCompositePsiElement(@NotNull T types, @NotNull IElementType elementType) { super(elementType); myTypes = types; } @Override public String toString() { String className = getClass().getSimpleName(); boolean isImpl = className.endsWith("Impl"); return (isImpl ? className.substring(0, className.length() - 4) : className); } } ================================================ FILE: src/main/java/com/reason/lang/core/type/ORCompositeType.java ================================================ package com.reason.lang.core.type; public interface ORCompositeType {} ================================================ FILE: src/main/java/com/reason/lang/core/type/ORLangTypes.java ================================================ package com.reason.lang.core.type; public abstract class ORLangTypes extends ORTypes { // Stubbed elements public ORCompositeType C_CLASS_DECLARATION; public ORCompositeType C_CLASS_METHOD; public ORCompositeType C_EXCEPTION_DECLARATION; public ORCompositeType C_EXTERNAL_DECLARATION; public ORCompositeType C_FUNCTOR_DECLARATION; public ORCompositeType C_INCLUDE; public ORCompositeType C_LET_DECLARATION; public ORCompositeType C_MODULE_DECLARATION; public ORCompositeType C_OBJECT_FIELD; public ORCompositeType C_OPEN; public ORCompositeType C_PARAM_DECLARATION; public ORCompositeType C_RECORD_FIELD; public ORCompositeType C_TYPE_DECLARATION; public ORCompositeType C_VAL_DECLARATION; public ORCompositeType C_VARIANT_DECLARATION; // Composite element types public ORCompositeType C_ANNOTATION; public ORCompositeType C_ARRAY; public ORCompositeType C_ASSERT_STMT; public ORCompositeType C_BINARY_CONDITION; public ORCompositeType C_CLASS_CONSTR; public ORCompositeType C_CLASS_FIELD; public ORCompositeType C_CLASS_INITIALIZER; public ORCompositeType C_CLOSED_VARIANT; public ORCompositeType C_CONSTRAINTS; public ORCompositeType C_TYPE_CONSTRAINT; public ORCompositeType C_CUSTOM_OPERATOR; public ORCompositeType C_DECONSTRUCTION; public ORCompositeType C_DEFAULT_VALUE; public ORCompositeType C_DIRECTIVE; public ORCompositeType C_DO_LOOP; public ORCompositeType C_FIELD_VALUE; public ORCompositeType C_FIRST_CLASS; public ORCompositeType C_FOR_LOOP; public ORCompositeType C_FUN_EXPR; public ORCompositeType C_FUNCTION_BODY; public ORCompositeType C_FUNCTION_CALL; public ORCompositeType C_FUNCTION_EXPR; public ORCompositeType C_FUNCTOR_BINDING; public ORCompositeType C_FUNCTOR_CALL; public ORCompositeType C_FUNCTOR_RESULT; public ORCompositeType C_GUARD; public ORCompositeType C_IF; public ORCompositeType C_IF_THEN_ELSE; public ORCompositeType C_INHERIT; public ORCompositeType C_LET_ATTR; public ORCompositeType C_LET_BINDING; public ORCompositeType C_LOCAL_OPEN; public ORCompositeType C_TYPE_VARIABLE; public ORCompositeType C_MACRO_EXPR; public ORCompositeType C_MACRO_NAME; public ORCompositeType C_MACRO_BODY; public ORCompositeType C_METHOD_CALL; public ORCompositeType C_MODULE_BINDING; public ORCompositeType C_MODULE_SIGNATURE; public ORCompositeType C_JS_OBJECT; public ORCompositeType C_MATCH_EXPR; public ORCompositeType C_MIXIN_FIELD; public ORCompositeType C_ML_INTERPOLATOR; public ORCompositeType C_NAMED_PARAM; public ORCompositeType C_NONE; public ORCompositeType C_OBJECT; public ORCompositeType C_OPEN_VARIANT; public ORCompositeType C_OPTION; public ORCompositeType C_LOWER_NAME; public ORCompositeType C_PARAM; public ORCompositeType C_PARAMETERS; public ORCompositeType C_PATTERN_MATCH_BODY; public ORCompositeType C_PATTERN_MATCH_EXPR; public ORCompositeType C_SCOPED_EXPR; public ORCompositeType C_SIG_EXPR; public ORCompositeType C_SIG_ITEM; public ORCompositeType C_SOME; public ORCompositeType C_TAG; public ORCompositeType C_TAG_PROP_VALUE; public ORCompositeType C_TAG_BODY; public ORCompositeType C_INTERPOLATION_EXPR; public ORCompositeType C_INTERPOLATION_PART; public ORCompositeType C_INTERPOLATION_REF; public ORCompositeType C_TAG_START; public ORCompositeType C_TAG_CLOSE; public ORCompositeType C_TAG_PROPERTY; public ORCompositeType C_TERNARY; public ORCompositeType C_TUPLE; public ORCompositeType C_RECORD_EXPR; public ORCompositeType C_SWITCH_EXPR; public ORCompositeType C_SWITCH_BODY; public ORCompositeType C_STRUCT_EXPR; public ORCompositeType C_TRY_EXPR; public ORCompositeType C_TRY_BODY; public ORCompositeType C_TRY_HANDLERS; public ORCompositeType C_TRY_HANDLER; public ORCompositeType C_TRY_HANDLER_BODY; public ORCompositeType C_TYPE_BINDING; public ORCompositeType C_UNIT; public ORCompositeType C_UNPACK; public ORCompositeType C_VARIANT_CONSTRUCTOR; public ORCompositeType C_WHILE; // Atom types public ORCompositeType CA_LOWER_SYMBOL; public ORCompositeType CA_UPPER_SYMBOL; public ORTokenElementType A_LOWER_TAG_NAME; public ORTokenElementType A_UPPER_TAG_NAME; public ORTokenElementType A_VARIANT_NAME; public ORTokenElementType A_MODULE_NAME; public ORTokenElementType A_EXCEPTION_NAME; // Dummy types public ORCompositeType H_ATOM; public ORCompositeType H_PLACE_HOLDER; public ORCompositeType H_COLLECTION_ITEM; public ORCompositeType H_NAMED_PARAM_DECLARATION; // Token element types public ORTokenElementType BOOL_VALUE; public ORTokenElementType STRING_VALUE; public ORTokenElementType FLOAT_VALUE; public ORTokenElementType CHAR_VALUE; public ORTokenElementType INT_VALUE; public ORTokenElementType ASYNC; public ORTokenElementType AWAIT; public ORTokenElementType AND; public ORTokenElementType ASSERT; public ORTokenElementType BACKSLASH; public ORTokenElementType BEGIN; public ORTokenElementType CATCH; public ORTokenElementType CLASS; public ORTokenElementType CONSTRAINT; public ORTokenElementType DIRECTIVE_IF; public ORTokenElementType DIRECTIVE_ELSE; public ORTokenElementType DIRECTIVE_ELIF; public ORTokenElementType DIRECTIVE_END; public ORTokenElementType DIRECTIVE_ENDIF; public ORTokenElementType DO; public ORTokenElementType DONE; public ORTokenElementType DOWNTO; public ORTokenElementType ELSE; public ORTokenElementType END; public ORTokenElementType EXCEPTION; public ORTokenElementType EXTERNAL; public ORTokenElementType FOR; public ORTokenElementType FUN; public ORTokenElementType FUNCTION; public ORTokenElementType FUNCTOR; public ORTokenElementType IF; public ORTokenElementType IN; public ORTokenElementType INCLUDE; public ORTokenElementType INHERIT; public ORTokenElementType INITIALIZER; public ORTokenElementType LAZY; public ORTokenElementType LET; public ORTokenElementType LIST; public ORTokenElementType L_AND; public ORTokenElementType OP_STRUCT_DIFF; public ORTokenElementType L_OR; public ORTokenElementType MODULE; public ORTokenElementType MUTABLE; public ORTokenElementType NEW; public ORTokenElementType NONREC; public ORTokenElementType OBJECT; public ORTokenElementType OF; public ORTokenElementType OPEN; public ORTokenElementType OR; public ORTokenElementType PIPE_FIRST; public ORTokenElementType PUB; public ORTokenElementType PRI; public ORTokenElementType REC; public ORTokenElementType SIG; public ORTokenElementType STRING_CONCAT; public ORTokenElementType STRUCT; public ORTokenElementType SWITCH; public ORTokenElementType THEN; public ORTokenElementType TO; public ORTokenElementType TRY; public ORTokenElementType TYPE; public ORTokenElementType UNPACK; // rescript public ORTokenElementType VAL; public ORTokenElementType VIRTUAL; public ORTokenElementType WHEN; public ORTokenElementType WHILE; public ORTokenElementType WITH; public ORTokenElementType RAW; public ORTokenElementType PROPERTY_NAME; public ORTokenElementType SHARPSHARP; public ORTokenElementType ARROBASE; public ORTokenElementType ARROBASE_2; public ORTokenElementType ARROBASE_3; public ORTokenElementType ARROW; public ORTokenElementType AS; public ORTokenElementType BACKTICK; public ORTokenElementType CARRET; public ORTokenElementType COLON; public ORTokenElementType COMMA; public ORTokenElementType DIFF; public ORTokenElementType LT_OR_EQUAL; public ORTokenElementType GT_OR_EQUAL; public ORTokenElementType DOLLAR; public ORTokenElementType DOT; public ORTokenElementType DOTDOTDOT; public ORTokenElementType NOT_EQ; public ORTokenElementType NOT_EQEQ; public ORTokenElementType EOL; public ORTokenElementType EQ; public ORTokenElementType EQEQ; public ORTokenElementType EQEQEQ; public ORTokenElementType EXCLAMATION_MARK; public ORTokenElementType TYPE_ARGUMENT; public ORTokenElementType GT; public ORTokenElementType LARRAY; public ORTokenElementType LBRACE; public ORTokenElementType LBRACKET; public ORTokenElementType LIDENT; public ORTokenElementType LPAREN; public ORTokenElementType LT; public ORTokenElementType MATCH; public ORTokenElementType MINUS; public ORTokenElementType MINUSDOT; public ORTokenElementType NONE; public ORTokenElementType OPTION; public ORTokenElementType POLY_VARIANT; public ORTokenElementType PIPE; public ORTokenElementType PIPE_FORWARD; public ORTokenElementType PLUS; public ORTokenElementType PERCENT; public ORTokenElementType PLUSDOT; public ORTokenElementType QUESTION_MARK; public ORTokenElementType SINGLE_QUOTE; public ORTokenElementType DOUBLE_QUOTE; public ORTokenElementType RAISE; public ORTokenElementType RARRAY; public ORTokenElementType RBRACE; public ORTokenElementType RBRACKET; public ORTokenElementType REF; public ORTokenElementType RPAREN; public ORTokenElementType SEMI; public ORTokenElementType SHARP; public ORTokenElementType SHORTCUT; public ORTokenElementType SLASH; public ORTokenElementType SLASH_2; public ORTokenElementType SLASHDOT; public ORTokenElementType SOME; public ORTokenElementType STAR; public ORTokenElementType STARDOT; public ORTokenElementType TAG_AUTO_CLOSE; public ORTokenElementType TAG_LT_SLASH; public ORTokenElementType TILDE; public ORTokenElementType UIDENT; public ORTokenElementType UNIT; public ORTokenElementType RECORD; public ORTokenElementType ASR; public ORTokenElementType LAND; public ORTokenElementType LOR; public ORTokenElementType LSL; public ORTokenElementType LSR; public ORTokenElementType LXOR; public ORTokenElementType METHOD; public ORTokenElementType MOD; public ORTokenElementType PRIVATE; public ORTokenElementType COLON_EQ; public ORTokenElementType COLON_GT; public ORTokenElementType DOTDOT; public ORTokenElementType SEMISEMI; public ORTokenElementType GT_BRACKET; public ORTokenElementType GT_BRACE; public ORTokenElementType LEFT_ARROW; public ORTokenElementType RIGHT_ARROW; public ORTokenElementType AMPERSAND; public ORTokenElementType BRACKET_GT; public ORTokenElementType BRACKET_LT; public ORTokenElementType BRACE_LT; public ORTokenElementType ML_STRING_VALUE; public ORTokenElementType ML_STRING_OPEN; public ORTokenElementType ML_STRING_CLOSE; public ORTokenElementType JS_STRING_OPEN; public ORTokenElementType JS_STRING_CLOSE; public ORTokenElementType UNDERSCORE; } ================================================ FILE: src/main/java/com/reason/lang/core/type/ORTokenElementType.java ================================================ package com.reason.lang.core.type; import com.intellij.lang.Language; import com.intellij.psi.tree.IElementType; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; public class ORTokenElementType extends IElementType { public ORTokenElementType(@NotNull @NonNls String debugName, @NotNull Language language) { super(debugName, language); } } ================================================ FILE: src/main/java/com/reason/lang/core/type/ORTypes.java ================================================ package com.reason.lang.core.type; import com.intellij.psi.*; import com.intellij.psi.tree.*; public abstract class ORTypes { public final IElementType WHITE_SPACE = TokenType.WHITE_SPACE; public ORTokenElementType SINGLE_COMMENT; public ORTokenElementType MULTI_COMMENT; } ================================================ FILE: src/main/java/com/reason/lang/core/type/ORTypesUtil.java ================================================ package com.reason.lang.core.type; import com.intellij.lang.Language; import com.reason.lang.rescript.ResLanguage; import com.reason.lang.rescript.ResTypes; import com.reason.lang.ocaml.OclLanguage; import com.reason.lang.ocaml.OclTypes; import com.reason.lang.reason.RmlTypes; import org.jetbrains.annotations.NotNull; public final class ORTypesUtil { private ORTypesUtil() { } public static @NotNull ORLangTypes getInstance(@NotNull Language language) { if (language instanceof OclLanguage) { return OclTypes.INSTANCE; } if (language instanceof ResLanguage) { return ResTypes.INSTANCE; } return RmlTypes.INSTANCE; } } ================================================ FILE: src/main/java/com/reason/lang/doc/ORDocConverter.java ================================================ package com.reason.lang.doc; import com.intellij.codeInsight.documentation.*; import com.intellij.lang.documentation.*; import com.intellij.lexer.*; import com.intellij.openapi.util.text.*; import com.intellij.psi.*; import com.intellij.psi.tree.*; import org.jetbrains.annotations.*; import java.io.*; import java.util.*; /** * see: * {@link DocumentationManagerUtil} * {@link DocumentationMarkup} * {@link DocumentationManagerProtocol} */ public abstract class ORDocConverter { protected static final HtmlChunk SPACE_CHUNK = HtmlChunk.text(" "); public abstract @NotNull HtmlBuilder convert(@Nullable PsiElement element, @NotNull String text); protected IElementType skipWhiteSpace(@NotNull FlexLexer lexer) throws IOException { IElementType elementType = lexer.advance(); while (elementType != null && elementType == TokenType.WHITE_SPACE) { elementType = lexer.advance(); } return elementType; } @NotNull protected String extractRaw(int startOffset, int endOffset, @NotNull CharSequence text) { return ((String) text).substring(startOffset, text.length() - endOffset); } @NotNull protected String extract(int startOffset, int endOffset, @NotNull CharSequence text) { return ((String) text).substring(startOffset, text.length() - endOffset).trim(); } protected static @NotNull List trimEndChildren(@NotNull List children) { if (!children.isEmpty()) { int lastIndex = children.size() - 1; HtmlChunk lastChunk = children.get(lastIndex); if (lastChunk == SPACE_CHUNK) { children.remove(lastIndex); return trimEndChildren(children); } } return children; } public static class ORDocHtmlBuilder { public final HtmlBuilder myBuilder = new HtmlBuilder(); public final List myChildren = new ArrayList<>(); public void appendChildren(boolean wrap) { if (!myChildren.isEmpty()) { trimEndChildren(myChildren); if (!myChildren.isEmpty()) { if (wrap) { myBuilder.append(HtmlChunk.p().children(myChildren)); } else { for (HtmlChunk chunk : myChildren) { myBuilder.append(chunk); } } myChildren.clear(); } } } public void addSpace() { if (!myChildren.isEmpty()) { int lastIndex = myChildren.size() - 1; HtmlChunk lastChunk = myChildren.get(lastIndex); if (lastChunk != SPACE_CHUNK) { myChildren.add(SPACE_CHUNK); } } } public void addChild(HtmlChunk.Element element) { myChildren.add(element); } } public static class ORDocSectionsBuilder extends ORDocHtmlBuilder { public String myTag = ""; public HtmlChunk.Element myHeaderCell = null; public void addHeaderCell(@NotNull String tag) { myHeaderCell = DocumentationMarkup.SECTION_HEADER_CELL.child(HtmlChunk.text(StringUtil.toTitleCase(tag) + ":").wrapWith("p")); myChildren.add(HtmlChunk.raw("

")); myTag = tag; } public void addSection() { HtmlChunk contentCell = DocumentationMarkup.SECTION_CONTENT_CELL.children(trimEndChildren(myChildren)); myBuilder.append(HtmlChunk.tag("tr").children(myHeaderCell, contentCell)); myHeaderCell = null; myTag = ""; myChildren.clear(); } } } ================================================ FILE: src/main/java/com/reason/lang/doc/ocaml/ODocLexer.java ================================================ /* The following code was generated by JFlex 1.7.0 tweaked for IntelliJ platform */ package com.reason.lang.doc.ocaml; import com.intellij.psi.tree.IElementType; import com.intellij.lexer.FlexLexer; import static com.intellij.psi.TokenType.*; @SuppressWarnings("ALL") /** * This class is a scanner generated by * JFlex 1.7.0 * from the specification file odoc.flex */ public class ODocLexer implements FlexLexer { /** 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 INITIAL = 2; public static final int IN_CODE = 4; public static final int IN_PRE = 6; public static final int IN_MARKUP = 8; /** * 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 }; /** * Translates characters to character classes * Chosen bits are [9, 6, 6] * Total runtime size is 1568 bytes */ public static int ZZ_CMAP(int ch) { return ZZ_CMAP_A[(ZZ_CMAP_Y[ZZ_CMAP_Z[ch>>12]|((ch>>6)&0x3f)]<<6)|(ch&0x3f)]; } /* The ZZ_CMAP_Z table has 272 entries */ static final char ZZ_CMAP_Z[] = zzUnpackCMap( "\1\0\1\100\1\200\u010d\100"); /* The ZZ_CMAP_Y table has 192 entries */ static final char ZZ_CMAP_Y[] = zzUnpackCMap( "\1\0\1\1\1\2\175\3\1\4\77\3"); /* The ZZ_CMAP_A table has 320 entries */ static final char ZZ_CMAP_A[] = zzUnpackCMap( "\11\0\1\3\1\1\1\27\1\30\1\2\22\0\1\3\1\20\6\0\1\7\1\11\1\10\2\0\1\24\2\0\12"+ "\4\1\14\5\0\1\25\32\5\1\12\1\0\1\26\3\0\1\5\1\15\2\5\1\17\3\5\1\16\2\5\1\22"+ "\2\5\1\21\5\5\1\23\5\5\1\13\1\0\1\6\7\0\1\27\242\0\2\27\26\0"); /** * Translates DFA states to action switch labels. */ private static final int [] ZZ_ACTION = zzUnpackAction(); private static final String ZZ_ACTION_PACKED_0 = "\5\0\1\1\1\2\2\3\1\4\1\5\1\2\1\6"+ "\1\7\1\6\1\10\1\2\2\11\1\12\1\13\1\6"+ "\1\11\1\14\1\0\1\15\1\16\1\17\1\0\1\20"+ "\1\21\1\22\1\23\2\0\1\24\1\25\1\26\1\27"+ "\1\16\1\30\1\31\1\32"; private static int [] zzUnpackAction() { int [] result = new int[43]; 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\31\0\62\0\113\0\144\0\175\0\226\0\175"+ "\0\257\0\310\0\175\0\341\0\372\0\175\0\u0113\0\226"+ "\0\u012c\0\175\0\u0145\0\175\0\175\0\175\0\u015e\0\175"+ "\0\u0177\0\175\0\u0190\0\175\0\u01a9\0\175\0\175\0\175"+ "\0\175\0\u01c2\0\u01db\0\u01f4\0\u012c\0\175\0\175\0\u020d"+ "\0\175\0\u0226\0\u023f"; private static int [] zzUnpackRowMap() { int [] result = new int[43]; 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(); 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 = "\31\6\1\7\1\10\1\11\1\12\2\7\1\13\1\14"+ "\1\15\1\7\1\16\1\17\1\20\10\7\1\21\2\7"+ "\1\12\2\22\1\23\7\22\1\24\13\22\1\25\2\26"+ "\2\22\1\23\23\22\1\27\2\26\2\22\1\23\3\22"+ "\1\30\20\22\2\26\31\0\1\7\3\0\2\7\1\0"+ "\1\7\1\0\1\7\2\0\14\7\2\0\1\10\32\0"+ "\1\12\24\0\1\12\1\7\3\0\2\7\1\0\1\7"+ "\1\31\1\7\2\0\14\7\12\0\1\32\23\0\1\33"+ "\5\0\1\34\1\35\1\0\1\36\1\37\1\40\1\41"+ "\1\42\1\0\1\43\1\44\4\0\1\7\3\0\1\7"+ "\1\45\1\0\1\7\1\0\1\7\2\0\1\7\3\45"+ "\1\7\3\45\4\7\2\0\1\22\35\0\1\46\32\0"+ "\1\47\23\0\1\50\1\33\23\0\1\50\14\0\1\51"+ "\36\0\1\52\30\0\1\53\11\0\1\44\24\0\1\44"+ "\3\0\1\50\24\0\1\50\3\0\1\52\24\0\1\52"+ "\3\0\1\53\24\0\1\53"; private static int [] zzUnpackTrans() { int [] result = new int[600]; 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 aState */ private static final int [] ZZ_ATTRIBUTE = zzUnpackAttribute(); private static final String ZZ_ATTRIBUTE_PACKED_0 = "\5\0\1\11\1\1\1\11\2\1\1\11\2\1\1\11"+ "\3\1\1\11\1\1\3\11\1\1\1\11\1\0\1\11"+ "\1\1\1\11\1\0\4\11\2\0\2\1\2\11\1\1"+ "\1\11\2\1"; private static int [] zzUnpackAttribute() { int [] result = new int[43]; 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; /** * zzAtBOL == true <=> the scanner is currently at the beginning of a line */ private boolean zzAtBOL = true; /** zzAtEOF == true <=> the scanner is at the EOF */ private boolean zzAtEOF; /** denotes if the user-EOF-code has already been executed */ private boolean zzEOFDone; /* user code: */ private int tokenStartIndex; private int codeDepth; private IElementType tag; public ODocLexer() { this((java.io.Reader)null); } // Store the start index of a token private void tokenStart() { tokenStartIndex = zzStartRead; } // Set the start index of the token to the stored index private void tokenEnd() { zzStartRead = tokenStartIndex; } /** * Creates a new scanner * * @param in the java.io.Reader to read input from. */ public ODocLexer(java.io.Reader in) { this.zzReader = in; } /** * Unpacks the compressed character translation table. * * @param packed the packed character translation table * @return the unpacked character translation table */ private static char [] zzUnpackCMap(String packed) { int size = 0; for (int i = 0, length = packed.length(); i < length; i += 2) { size += packed.charAt(i); } char[] map = new char[size]; int i = 0; /* index in packed string */ int j = 0; /* index in unpacked array */ while (i < packed.length()) { int count = packed.charAt(i++); char value = packed.charAt(i++); do map[j++] = value; while (--count > 0); } return map; } 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; } /** * Contains user EOF-code, which will be executed exactly once, * when the end of file is reached */ private void zzDoEOF() { if (!zzEOFDone) { zzEOFDone = true; } } /** * 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/*, zzEndReadL*/); 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/*, zzEndReadL*/); zzCurrentPosL += Character.charCount(zzInput); } } int zzNext = zzTransL[ zzRowMapL[zzState] + ZZ_CMAP(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; zzDoEOF(); switch (zzLexicalState) { case IN_CODE: { yybegin(INITIAL); tokenEnd(); return OclDocTypes.CODE; } // fall though case 44: break; case IN_PRE: { yybegin(INITIAL); tokenEnd(); return OclDocTypes.PRE; } // fall though case 45: break; case IN_MARKUP: { yybegin(INITIAL); tokenEnd(); return tag; } // fall though case 46: break; default: return null; } } else { switch (zzAction < 0 ? zzAction : ZZ_ACTION[zzAction]) { case 1: { yybegin(INITIAL); yypushback(1); } // fall through case 27: break; case 2: { return OclDocTypes.ATOM; } // fall through case 28: break; case 3: { return OclDocTypes.NEW_LINE; } // fall through case 29: break; case 4: { return WHITE_SPACE; } // fall through case 30: break; case 5: { return OclDocTypes.RBRACE; } // fall through case 31: break; case 6: { return BAD_CHARACTER; } // fall through case 32: break; case 7: { yybegin(IN_CODE); codeDepth = 1; tokenStart(); } // fall through case 33: break; case 8: { return OclDocTypes.COLON; } // fall through case 34: break; case 9: { } // fall through case 35: break; case 10: { codeDepth += 1; } // fall through case 36: break; case 11: { codeDepth -= 1; if (codeDepth == 0) { yybegin(INITIAL); tokenEnd(); return OclDocTypes.CODE; } } // fall through case 37: break; case 12: { yybegin(INITIAL); tokenEnd(); return tag; } // fall through case 38: break; case 13: { return OclDocTypes.COMMENT_END; } // fall through case 39: break; case 14: { return OclDocTypes.SECTION; } // fall through case 40: break; case 15: { yybegin(IN_PRE); tokenStart(); } // fall through case 41: break; case 16: { yybegin(IN_MARKUP); tag = OclDocTypes.BOLD; tokenStart(); } // fall through case 42: break; case 17: { yybegin(IN_MARKUP); tag = OclDocTypes.ITALIC; tokenStart(); } // fall through case 43: break; case 18: { yybegin(IN_MARKUP); tag = OclDocTypes.EMPHASIS; tokenStart(); } // fall through case 44: break; case 19: { yybegin(IN_MARKUP); tag = OclDocTypes.CROSS_REF; tokenStart(); } // fall through case 45: break; case 20: { return OclDocTypes.LIST_ITEM_START; } // fall through case 46: break; case 21: { return OclDocTypes.TAG; } // fall through case 47: break; case 22: { yybegin(INITIAL); tokenEnd(); return OclDocTypes.PRE; } // fall through case 48: break; case 23: { return OclDocTypes.COMMENT_START; } // fall through case 49: break; case 24: { return OclDocTypes.LINK_START; } // fall through case 50: break; case 25: { return OclDocTypes.O_LIST; } // fall through case 51: break; case 26: { return OclDocTypes.U_LIST; } // fall through case 52: break; default: zzScanError(ZZ_NO_MATCH); } } } } } ================================================ FILE: src/main/java/com/reason/lang/doc/ocaml/OclDocConverter.java ================================================ package com.reason.lang.doc.ocaml; import com.intellij.lang.documentation.*; import com.intellij.openapi.util.text.*; import com.intellij.psi.*; import com.intellij.psi.tree.*; import com.intellij.util.containers.*; import com.reason.lang.doc.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; import java.io.*; /** * see: * {@link com.intellij.codeInsight.documentation.DocumentationManagerUtil} * {@link com.intellij.lang.documentation.DocumentationMarkup} * {@link com.intellij.codeInsight.documentation.DocumentationManagerProtocol} */ public class OclDocConverter extends ORDocConverter { private static final Log LOG = Log.create("odoc"); private final ODocLexer myLexer = new ODocLexer(); @NotNull public HtmlBuilder convert(@Nullable PsiElement element, @NotNull String text) { myLexer.reset(text, 0, text.length(), ODocLexer.YYINITIAL); Stack builders = new Stack<>(); builders.add(new ORDocHtmlBuilder()); ORDocHtmlBuilder currentBuilder = builders.peek(); boolean advanced = false; try { IElementType tokenType = myLexer.advance(); while (tokenType != null) { if (LOG.isTraceEnabled()) { LOG.trace(tokenType + " : " + myLexer.yytext()); } // We have not produced the sections table, and it's the end of the comment if (currentBuilder instanceof ORDocSectionsBuilder && tokenType == OclDocTypes.COMMENT_END) { ORDocSectionsBuilder sectionsBuilder = (ORDocSectionsBuilder) builders.pop(); currentBuilder = builders.peek(); trimEndChildren(sectionsBuilder.myChildren); sectionsBuilder.myChildren.add(HtmlChunk.raw("

")); sectionsBuilder.addSection(); currentBuilder.myBuilder.append(sectionsBuilder.myBuilder.wrapWith(DocumentationMarkup.SECTIONS_TABLE)); currentBuilder.myChildren.clear(); } //noinspection StatementWithEmptyBody if (tokenType == OclDocTypes.COMMENT_START || tokenType == OclDocTypes.COMMENT_END) { // skip } else if (tokenType == OclDocTypes.CODE) { String yyValue = extract(1, 1, myLexer.yytext()); currentBuilder.addChild(HtmlChunk.raw(DocumentationMarkup.GRAYED_START + yyValue + DocumentationMarkup.GRAYED_END).wrapWith("code")); } else if (tokenType == OclDocTypes.BOLD) { String yyValue = extract(2, 1, myLexer.yytext()); currentBuilder.addChild(HtmlChunk.text(yyValue).wrapWith("b")); } else if (tokenType == OclDocTypes.ITALIC) { String yyValue = extract(2, 1, myLexer.yytext()); currentBuilder.addChild(HtmlChunk.text(yyValue).italic()); } else if (tokenType == OclDocTypes.EMPHASIS) { String yyValue = extract(2, 1, myLexer.yytext()); currentBuilder.addChild(HtmlChunk.text(yyValue).wrapWith("em")); } else if (tokenType == OclDocTypes.PRE) { String yyValue = extractRaw(2, 2, myLexer.yytext()); currentBuilder.addChild(HtmlChunk.raw(yyValue).wrapWith("code").wrapWith("pre")); } else if (tokenType == OclDocTypes.O_LIST) { currentBuilder.appendChildren(true); TagHtmlBuilder listBuilder = new TagHtmlBuilder("ol"); builders.add(listBuilder); currentBuilder = listBuilder; } else if (tokenType == OclDocTypes.U_LIST) { currentBuilder.appendChildren(true); TagHtmlBuilder listBuilder = new TagHtmlBuilder("ul"); builders.add(listBuilder); currentBuilder = listBuilder; } else if (tokenType == OclDocTypes.LIST_ITEM_START) { currentBuilder.appendChildren(false); TagHtmlBuilder listBuilder = new TagHtmlBuilder("li"); builders.add(listBuilder); currentBuilder = listBuilder; } else if (tokenType == OclDocTypes.SECTION) { currentBuilder.appendChildren(true); String tag = "h" + extract(1, 0, myLexer.yytext()); TagHtmlBuilder listBuilder = new TagHtmlBuilder(tag); builders.add(listBuilder); currentBuilder = listBuilder; } else if (tokenType == OclDocTypes.RBRACE) { ORDocHtmlBuilder builder = builders.empty() ? null : builders.pop(); currentBuilder = builders.empty() ? currentBuilder : builders.peek(); if (builder instanceof TagHtmlBuilder tagBuilder) { tagBuilder.appendChildren(false); currentBuilder.addChild(tagBuilder.myBuilder.wrapWith(tagBuilder.myTag)); if (tagBuilder.myTag.startsWith("h")) { // a title currentBuilder.appendChildren(false); } } } else if (tokenType == OclDocTypes.LINK_START) { // consume url StringBuilder sbUrl = new StringBuilder(); tokenType = myLexer.advance(); while (tokenType != null && tokenType != OclDocTypes.RBRACE) { sbUrl.append(myLexer.yytext()); tokenType = myLexer.advance(); } if (tokenType == OclDocTypes.RBRACE) { tokenType = myLexer.advance(); // consume text StringBuilder sbText = new StringBuilder(); while (tokenType != null && tokenType != OclDocTypes.RBRACE) { if (tokenType != OclDocTypes.NEW_LINE) { sbText.append(myLexer.yytext()); } tokenType = myLexer.advance(); } if (tokenType == OclDocTypes.RBRACE) { currentBuilder.addChild(HtmlChunk.link(sbUrl.toString(), sbText.toString())); } } } else if (tokenType == OclDocTypes.TAG) { String yyValue = extract(1, 0, myLexer.yytext()); if (currentBuilder instanceof ORDocSectionsBuilder sectionsBuilder) { trimEndChildren(sectionsBuilder.myChildren); if (sectionsBuilder.myTag.equals(yyValue)) { sectionsBuilder.myChildren.add(HtmlChunk.raw("

")); } else { sectionsBuilder.myChildren.add(HtmlChunk.raw("

")); sectionsBuilder.addSection(); sectionsBuilder.addHeaderCell(yyValue); } tokenType = skipWhiteSpace(myLexer); advanced = true; } else { currentBuilder.appendChildren(true); ORDocSectionsBuilder sectionsBuilder = new ORDocSectionsBuilder(); sectionsBuilder.addHeaderCell(yyValue); tokenType = skipWhiteSpace(myLexer); advanced = true; builders.add(sectionsBuilder); currentBuilder = sectionsBuilder; } } else if (tokenType == OclDocTypes.NEW_LINE) { if (!(currentBuilder instanceof ORDocSectionsBuilder)) { // \n\n is a new line tokenType = myLexer.advance(); boolean spaceAfterNewLine = tokenType == TokenType.WHITE_SPACE; if (spaceAfterNewLine) { tokenType = skipWhiteSpace(myLexer); } if (tokenType == OclDocTypes.NEW_LINE) { currentBuilder.appendChildren(true); } else { currentBuilder.addSpace(); } advanced = true; } } else { String yyValue = myLexer.yytext().toString(); boolean isSpace = tokenType == TokenType.WHITE_SPACE; if (!(isSpace && currentBuilder.myChildren.isEmpty())) { currentBuilder.myChildren.add(isSpace ? SPACE_CHUNK : HtmlChunk.text(yyValue)); } } if (advanced) { advanced = false; } else { tokenType = myLexer.advance(); } } } catch (IOException e) { LOG.error("Error during ODoc parsing", e); } currentBuilder.appendChildren(true); if (LOG.isTraceEnabled()) { LOG.trace("HTML: " + currentBuilder.myBuilder); } return currentBuilder.myBuilder; } static class TagHtmlBuilder extends ORDocHtmlBuilder { final String myTag; TagHtmlBuilder(String tag) { myTag = tag; } } } ================================================ FILE: src/main/java/com/reason/lang/doc/ocaml/OclDocLanguage.java ================================================ package com.reason.lang.doc.ocaml; import com.intellij.lang.Language; public class OclDocLanguage extends Language { public static final OclDocLanguage INSTANCE = new OclDocLanguage(); private OclDocLanguage() { super("ODoc"); } } ================================================ FILE: src/main/java/com/reason/lang/doc/ocaml/OclDocTypes.java ================================================ package com.reason.lang.doc.ocaml; import com.intellij.psi.tree.*; public interface OclDocTypes { IElementType ATOM = new IElementType("ATOM", OclDocLanguage.INSTANCE); IElementType BOLD = new IElementType("BOLD", OclDocLanguage.INSTANCE); IElementType CODE = new IElementType("CODE", OclDocLanguage.INSTANCE); IElementType COLON = new IElementType("COLON", OclDocLanguage.INSTANCE); IElementType CROSS_REF = new IElementType("CROSS_REF", OclDocLanguage.INSTANCE); IElementType EMPHASIS = new IElementType("EMPHASIS", OclDocLanguage.INSTANCE); IElementType ITALIC = new IElementType("ITALIC", OclDocLanguage.INSTANCE); IElementType LINK_START = new IElementType("LINK_START", OclDocLanguage.INSTANCE); IElementType LIST_ITEM_START = new IElementType("LIST_ITEM_START", OclDocLanguage.INSTANCE); IElementType NEW_LINE = new IElementType("NEW_LINE", OclDocLanguage.INSTANCE); IElementType COMMENT_START = new IElementType("COMMENT_START", OclDocLanguage.INSTANCE); IElementType COMMENT_END = new IElementType("COMMENT_END", OclDocLanguage.INSTANCE); IElementType O_LIST = new IElementType("O_LIST", OclDocLanguage.INSTANCE); IElementType PRE = new IElementType("PRE", OclDocLanguage.INSTANCE); IElementType RBRACE = new IElementType("RBRACE", OclDocLanguage.INSTANCE); IElementType SECTION = new IElementType("SECTION", OclDocLanguage.INSTANCE); IElementType TAG = new IElementType("TAG", OclDocLanguage.INSTANCE); IElementType U_LIST = new IElementType("U_LIST", OclDocLanguage.INSTANCE); } ================================================ FILE: src/main/java/com/reason/lang/doc/ocaml/odoc.flex ================================================ package com.reason.lang.doc.ocaml; import com.intellij.psi.tree.IElementType; import com.intellij.lexer.FlexLexer; import static com.intellij.psi.TokenType.*; @SuppressWarnings("ALL") %% %unicode %public %class ODocLexer %implements FlexLexer %function advance %type IElementType %eof{ return; %eof} %{ private int tokenStartIndex; private int codeDepth; private IElementType tag; public ODocLexer() { this((java.io.Reader)null); } // Store the start index of a token private void tokenStart() { tokenStartIndex = zzStartRead; } // Set the start index of the token to the stored index private void tokenEnd() { zzStartRead = tokenStartIndex; } %} EOL=\n|\r|\r\n WHITE_SPACE_CHAR=[\ \t\f] WHITE_SPACE={WHITE_SPACE_CHAR}+ DIGIT=[0-9] DIGITS={DIGIT}+ TAG_CHARACTER = [a-zA-Z] INPUT_CHARACTER = [^\r\n\ \t\f\[\{\}\*] %state INITIAL %state IN_CODE %state IN_PRE %state IN_MARKUP %% { [^] { yybegin(INITIAL); yypushback(1); } } { "(**" { return OclDocTypes.COMMENT_START; } "*)" { return OclDocTypes.COMMENT_END; } "[" { yybegin(IN_CODE); codeDepth = 1; tokenStart(); } "{{:" { return OclDocTypes.LINK_START; } "{[" { yybegin(IN_PRE); tokenStart(); } "{b" { yybegin(IN_MARKUP); tag = OclDocTypes.BOLD; tokenStart(); } "{i" { yybegin(IN_MARKUP); tag = OclDocTypes.ITALIC; tokenStart(); } "{e" { yybegin(IN_MARKUP); tag = OclDocTypes.EMPHASIS; tokenStart(); } "{!" { yybegin(IN_MARKUP); tag = OclDocTypes.CROSS_REF; tokenStart(); } "{ol" {WHITE_SPACE}* { return OclDocTypes.O_LIST; } "{ul" {WHITE_SPACE}* { return OclDocTypes.U_LIST; } "{-" {WHITE_SPACE}* { return OclDocTypes.LIST_ITEM_START; } "{" {DIGITS} {WHITE_SPACE}* { return OclDocTypes.SECTION; } ":" { return OclDocTypes.COLON; } "}" { return OclDocTypes.RBRACE; } "@" {TAG_CHARACTER}+ { return OclDocTypes.TAG; } {WHITE_SPACE} { return WHITE_SPACE; } {EOL} { return OclDocTypes.NEW_LINE; } {INPUT_CHARACTER}+ { return OclDocTypes.ATOM; } } { "[" { codeDepth += 1; } "]" { codeDepth -= 1; if (codeDepth == 0) { yybegin(INITIAL); tokenEnd(); return OclDocTypes.CODE; } } . | {EOL} { } <> { yybegin(INITIAL); tokenEnd(); return OclDocTypes.CODE; } } { "]}" { yybegin(INITIAL); tokenEnd(); return OclDocTypes.PRE; } . | {EOL} { } <> { yybegin(INITIAL); tokenEnd(); return OclDocTypes.PRE; } } { "}" { yybegin(INITIAL); tokenEnd(); return tag; } . | {EOL} { } <> { yybegin(INITIAL); tokenEnd(); return tag; } } [^] { return BAD_CHARACTER; } ================================================ FILE: src/main/java/com/reason/lang/doc/reason/RDocLexer.java ================================================ /* The following code was generated by JFlex 1.7.0 tweaked for IntelliJ platform */ package com.reason.lang.doc.reason; import com.intellij.psi.tree.IElementType; import com.intellij.lexer.FlexLexer; import static com.intellij.psi.TokenType.*; @SuppressWarnings("ALL") /** * This class is a scanner generated by * JFlex 1.7.0 * from the specification file rdoc.flex */ public class RDocLexer implements FlexLexer { /** 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 INITIAL = 2; /** * 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 }; /** * Translates characters to character classes * Chosen bits are [8, 6, 7] * Total runtime size is 1040 bytes */ public static int ZZ_CMAP(int ch) { return ZZ_CMAP_A[ZZ_CMAP_Y[ZZ_CMAP_Z[ch>>13]|((ch>>7)&0x3f)]|(ch&0x7f)]; } /* The ZZ_CMAP_Z table has 136 entries */ static final char ZZ_CMAP_Z[] = zzUnpackCMap( "\1\0\207\100"); /* The ZZ_CMAP_Y table has 128 entries */ static final char ZZ_CMAP_Y[] = zzUnpackCMap( "\1\0\177\200"); /* The ZZ_CMAP_A table has 256 entries */ static final char ZZ_CMAP_A[] = zzUnpackCMap( "\11\0\1\3\1\1\1\0\1\3\1\2\22\0\1\3\11\0\1\5\4\0\1\6\20\0\1\7\32\4\6\0\32\4"+ "\205\0"); /** * Translates DFA states to action switch labels. */ private static final int [] ZZ_ACTION = zzUnpackAction(); private static final String ZZ_ACTION_PACKED_0 = "\2\0\1\1\1\2\2\3\1\4\1\5\2\2\1\6"+ "\1\0\1\7\1\10"; private static int [] zzUnpackAction() { int [] result = new int[14]; 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\10\0\20\0\30\0\20\0\40\0\50\0\60"+ "\0\70\0\100\0\20\0\110\0\100\0\20"; private static int [] zzUnpackRowMap() { int [] result = new int[14]; 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(); 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 = "\10\3\1\4\1\5\1\6\1\7\1\4\1\10\1\11"+ "\1\12\10\0\1\4\3\0\1\4\1\0\2\4\1\0"+ "\1\5\11\0\1\7\12\0\1\13\1\0\1\4\3\0"+ "\1\4\1\14\3\4\3\0\1\15\1\0\2\4\5\0"+ "\1\16\2\0"; private static int [] zzUnpackTrans() { int [] result = new int[80]; 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 aState */ private static final int [] ZZ_ATTRIBUTE = zzUnpackAttribute(); private static final String ZZ_ATTRIBUTE_PACKED_0 = "\2\0\1\11\1\1\1\11\5\1\1\11\1\0\1\1"+ "\1\11"; private static int [] zzUnpackAttribute() { int [] result = new int[14]; 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; /** * zzAtBOL == true <=> the scanner is currently at the beginning of a line */ private boolean zzAtBOL = true; /** zzAtEOF == true <=> the scanner is at the EOF */ private boolean zzAtEOF; /** denotes if the user-EOF-code has already been executed */ private boolean zzEOFDone; /* user code: */ private int tokenStartIndex; private int codeDepth; private IElementType tag; public RDocLexer() { this((java.io.Reader)null); } // Store the start index of a token private void tokenStart() { tokenStartIndex = zzStartRead; } // Set the start index of the token to the stored index private void tokenEnd() { zzStartRead = tokenStartIndex; } /** * Creates a new scanner * * @param in the java.io.Reader to read input from. */ public RDocLexer(java.io.Reader in) { this.zzReader = in; } /** * Unpacks the compressed character translation table. * * @param packed the packed character translation table * @return the unpacked character translation table */ private static char [] zzUnpackCMap(String packed) { int size = 0; for (int i = 0, length = packed.length(); i < length; i += 2) { size += packed.charAt(i); } char[] map = new char[size]; int i = 0; /* index in packed string */ int j = 0; /* index in unpacked array */ while (i < packed.length()) { int count = packed.charAt(i++); char value = packed.charAt(i++); do map[j++] = value; while (--count > 0); } return map; } 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; } /** * Contains user EOF-code, which will be executed exactly once, * when the end of file is reached */ private void zzDoEOF() { if (!zzEOFDone) { zzEOFDone = true; } } /** * 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/*, zzEndReadL*/); 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/*, zzEndReadL*/); zzCurrentPosL += Character.charCount(zzInput); } } int zzNext = zzTransL[ zzRowMapL[zzState] + ZZ_CMAP(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; zzDoEOF(); return null; } else { switch (zzAction < 0 ? zzAction : ZZ_ACTION[zzAction]) { case 1: { yybegin(INITIAL); yypushback(1); } // fall through case 9: break; case 2: { return RmlDocTypes.ATOM; } // fall through case 10: break; case 3: { return RmlDocTypes.NEW_LINE; } // fall through case 11: break; case 4: { return WHITE_SPACE; } // fall through case 12: break; case 5: { return BAD_CHARACTER; } // fall through case 13: break; case 6: { return RmlDocTypes.COMMENT_END; } // fall through case 14: break; case 7: { return RmlDocTypes.TAG; } // fall through case 15: break; case 8: { return RmlDocTypes.COMMENT_START; } // fall through case 16: break; default: zzScanError(ZZ_NO_MATCH); } } } } } ================================================ FILE: src/main/java/com/reason/lang/doc/reason/RmlDocConverter.java ================================================ package com.reason.lang.doc.reason; import com.intellij.lang.documentation.*; import com.intellij.openapi.util.text.*; import com.intellij.psi.*; import com.intellij.psi.tree.*; import com.intellij.util.containers.Stack; import com.reason.lang.doc.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; import java.io.*; public class RmlDocConverter extends ORDocConverter { private static final Log LOG = Log.create("rdoc"); private final RDocLexer myLexer = new RDocLexer(); public @NotNull HtmlBuilder convert(@Nullable PsiElement element, @NotNull String text) { myLexer.reset(text, 0, text.length(), RDocLexer.YYINITIAL); Stack builders = new Stack<>(); builders.add(new ORDocHtmlBuilder()); ORDocHtmlBuilder currentBuilder = builders.peek(); boolean advanced = false; try { IElementType tokenType = myLexer.advance(); while (tokenType != null) { if (LOG.isTraceEnabled()) { LOG.trace(tokenType + " : " + myLexer.yytext()); } // We have not produced the sections table, and it's the end of the comment if (currentBuilder instanceof ORDocSectionsBuilder && tokenType == RmlDocTypes.COMMENT_END) { ORDocSectionsBuilder sectionsBuilder = (ORDocSectionsBuilder) builders.pop(); currentBuilder = builders.peek(); trimEndChildren(sectionsBuilder.myChildren); sectionsBuilder.myChildren.add(HtmlChunk.raw("

")); sectionsBuilder.addSection(); currentBuilder.myBuilder.append(sectionsBuilder.myBuilder.wrapWith(DocumentationMarkup.SECTIONS_TABLE)); currentBuilder.myChildren.clear(); } //noinspection StatementWithEmptyBody if (tokenType == RmlDocTypes.COMMENT_START || tokenType == RmlDocTypes.COMMENT_END) { // skip } else if (tokenType == RmlDocTypes.TAG) { String yyValue = extract(1, 0, myLexer.yytext()); if (currentBuilder instanceof ORDocSectionsBuilder sectionsBuilder) { if (sectionsBuilder.myTag.equals(yyValue)) { trimEndChildren(sectionsBuilder.myChildren); sectionsBuilder.myChildren.add(HtmlChunk.raw("

")); tokenType = skipWhiteSpace(myLexer); if ("param".equals(yyValue)) { // add dash:: paramName - paramValue sectionsBuilder.myChildren.add(HtmlChunk.text(myLexer.yytext() + " -")); } else { advanced = true; } } else { trimEndChildren(sectionsBuilder.myChildren); sectionsBuilder.myChildren.add(HtmlChunk.raw("

")); sectionsBuilder.addSection(); sectionsBuilder.addHeaderCell(yyValue); tokenType = skipWhiteSpace(myLexer); advanced = true; } } else { currentBuilder.appendChildren(true); ORDocSectionsBuilder sectionsBuilder = new ORDocSectionsBuilder(); sectionsBuilder.addHeaderCell(yyValue); tokenType = skipWhiteSpace(myLexer); if ("param".equals(yyValue)) { // add dash:: paramName - paramValue sectionsBuilder.myChildren.add(HtmlChunk.text(myLexer.yytext() + " -")); } else { advanced = true; } builders.add(sectionsBuilder); currentBuilder = sectionsBuilder; } } else if (tokenType == RmlDocTypes.NEW_LINE) { if (!(currentBuilder instanceof ORDocSectionsBuilder)) { // \n\n is a new line tokenType = myLexer.advance(); boolean spaceAfterNewLine = tokenType == TokenType.WHITE_SPACE; if (spaceAfterNewLine) { tokenType = skipWhiteSpace(myLexer); } if (tokenType == RmlDocTypes.NEW_LINE) { currentBuilder.appendChildren(true); } else { currentBuilder.addSpace(); } advanced = true; } } else { String yyValue = myLexer.yytext().toString(); boolean isSpace = tokenType == TokenType.WHITE_SPACE; if (!(isSpace && currentBuilder.myChildren.isEmpty())) { if (currentBuilder instanceof ORDocSectionsBuilder && ((ORDocSectionsBuilder) currentBuilder).myHeaderCell == null) { // @param |>xxx<| yyyy ((ORDocSectionsBuilder) currentBuilder).myHeaderCell = DocumentationMarkup.SECTION_HEADER_CELL.child(HtmlChunk.text(yyValue)); } else { currentBuilder.myChildren.add(isSpace ? SPACE_CHUNK : HtmlChunk.text(yyValue)); } } } if (advanced) { advanced = false; } else { tokenType = myLexer.advance(); } } } catch (IOException e) { LOG.error("Error during RDoc parsing", e); } currentBuilder.appendChildren(true); if (LOG.isTraceEnabled()) { LOG.trace("HTML: " + currentBuilder.myBuilder); } return currentBuilder.myBuilder; } } ================================================ FILE: src/main/java/com/reason/lang/doc/reason/RmlDocLanguage.java ================================================ package com.reason.lang.doc.reason; import com.intellij.lang.*; public class RmlDocLanguage extends Language { public static final RmlDocLanguage INSTANCE = new RmlDocLanguage(); private RmlDocLanguage() { super("ReasonDoc"); } } ================================================ FILE: src/main/java/com/reason/lang/doc/reason/RmlDocTypes.java ================================================ package com.reason.lang.doc.reason; import com.intellij.psi.tree.*; public interface RmlDocTypes { IElementType ATOM = new IElementType("ATOM", RmlDocLanguage.INSTANCE); IElementType NEW_LINE = new IElementType("NEW_LINE", RmlDocLanguage.INSTANCE); IElementType COMMENT_START = new IElementType("COMMENT_START", RmlDocLanguage.INSTANCE); IElementType COMMENT_END = new IElementType("COMMENT_END", RmlDocLanguage.INSTANCE); IElementType TAG = new IElementType("TAG", RmlDocLanguage.INSTANCE); } ================================================ FILE: src/main/java/com/reason/lang/doc/reason/rdoc.flex ================================================ package com.reason.lang.doc.reason; import com.intellij.psi.tree.IElementType; import com.intellij.lexer.FlexLexer; import static com.intellij.psi.TokenType.*; @SuppressWarnings("ALL") %% %unicode %public %class RDocLexer %implements FlexLexer %function advance %type IElementType %eof{ return; %eof} %{ private int tokenStartIndex; private int codeDepth; private IElementType tag; public RDocLexer() { this((java.io.Reader)null); } // Store the start index of a token private void tokenStart() { tokenStartIndex = zzStartRead; } // Set the start index of the token to the stored index private void tokenEnd() { zzStartRead = tokenStartIndex; } %} EOL=\n|\r|\r\n WHITE_SPACE_CHAR=[\ \t\f] WHITE_SPACE={WHITE_SPACE_CHAR}+ TAG_CHARACTER = [a-zA-Z] INPUT_CHARACTER = [^\r\n\ \t\f\*] %state INITIAL %% { [^] { yybegin(INITIAL); yypushback(1); } } { "/**" { return RmlDocTypes.COMMENT_START; } "*/" { return RmlDocTypes.COMMENT_END; } "@" {TAG_CHARACTER}+ { return RmlDocTypes.TAG; } {WHITE_SPACE} { return WHITE_SPACE; } {EOL} { return RmlDocTypes.NEW_LINE; } {INPUT_CHARACTER}+ { return RmlDocTypes.ATOM; } } [^] { return BAD_CHARACTER; } ================================================ FILE: src/main/java/com/reason/lang/dune/Dune.flex ================================================ package com.reason.lang.dune; import com.intellij.psi.tree.IElementType; import com.intellij.lexer.FlexLexer; import static com.intellij.psi.TokenType.*; @SuppressWarnings("ALL") %% %{ private DuneTypes types; private int tokenStartIndex; private CharSequence quotedStringId; private int commentDepth; private int parenDepth; public DuneLexer(DuneTypes types) { this.types = types; } // Store the start index of a token private void tokenStart() { tokenStartIndex = zzStartRead; } // Set the start index of the token to the stored index private void tokenEnd() { zzStartRead = tokenStartIndex; } %} %public %class DuneLexer %implements FlexLexer %unicode %function advance %type IElementType EOL=\n|\r|\r\n WHITE_SPACE_CHAR=[\ \t\f]|{EOL} WHITE_SPACE={WHITE_SPACE_CHAR}+ NEWLINE=("\r"* "\n") ATOM=[A-Za-z_0-9'@&\^!\.\-/+\\] %state WAITING_VALUE %state INITIAL %state IN_STRING %state IN_ML_COMMENT %state IN_SEXPR_COMMENT %state IN_SL_COMMENT %% { [^] { yybegin(INITIAL); yypushback(1); } } { {WHITE_SPACE} { return WHITE_SPACE; } "\"" { yybegin(IN_STRING); tokenStart(); } "#|" { yybegin(IN_ML_COMMENT); commentDepth = 1; tokenStart(); } "#;" { yybegin(IN_SEXPR_COMMENT); parenDepth = 0; tokenStart(); } ";" { yybegin(IN_SL_COMMENT); tokenStart(); } "%{" { return types.VAR_START; } ">=" { return types.GTE; } "<=" { return types.LTE; } "}" { return types.VAR_END; } ":" { return types.COLON; } "(" { return types.LPAREN; } ")" { return types.RPAREN; } "=" { return types.EQUAL; } "<" { return types.LT; } ">" { return types.GT; } "#" { return types.SHARP; } {ATOM}+ { return types.ATOM; } } { "\"" { yybegin(INITIAL); tokenEnd(); return types.STRING; } "\\" { NEWLINE } ([ \t] *) { } "\\" [\\\'\"ntbr ] { } "\\" [0-9] [0-9] [0-9] { } "\\" "o" [0-3] [0-7] [0-7] { } "\\" "x" [0-9a-fA-F] [0-9a-fA-F] { } "\\" . { } { NEWLINE } { } . { } <> { yybegin(INITIAL); tokenEnd(); return types.STRING; } } { "#|" { commentDepth += 1; } "|#" { commentDepth -= 1; if(commentDepth == 0) { yybegin(INITIAL); tokenEnd(); return types.MULTI_COMMENT; } } . | {NEWLINE} { } <> { yybegin(INITIAL); tokenEnd(); return types.MULTI_COMMENT; } } { "(" { parenDepth += 1; } ")" { parenDepth -= 1; if(parenDepth == 0) { yybegin(INITIAL); tokenEnd(); return types.MULTI_COMMENT; } } . | {NEWLINE} { } <> { yybegin(INITIAL); tokenEnd(); return types.MULTI_COMMENT; } } { . {} {NEWLINE} { yybegin(INITIAL); tokenEnd(); return types.SINGLE_COMMENT; } <> { yybegin(INITIAL); tokenEnd(); return types.SINGLE_COMMENT; } } [^] { return BAD_CHARACTER; } ================================================ FILE: src/main/java/com/reason/lang/dune/DuneASTFactory.java ================================================ package com.reason.lang.dune; import com.intellij.lang.*; import com.intellij.psi.impl.source.tree.*; import com.intellij.psi.tree.*; import com.reason.lang.core.psi.impl.*; import org.jetbrains.annotations.*; class DuneASTFactory extends ASTFactory { private final DuneTypes myTypes; public DuneASTFactory() { myTypes = DuneTypes.INSTANCE; } @Override public @Nullable CompositeElement createComposite(@NotNull IElementType type) { if (type == DuneTypes.INSTANCE.C_FIELD) { return new RPsiDuneField(myTypes, type); } else if (type == DuneTypes.INSTANCE.C_FIELDS) { return new RPsiDuneFields(myTypes, type); } else if (type == myTypes.C_STANZA) { return new RPsiDuneStanza(myTypes, type); } else if (type == DuneTypes.INSTANCE.C_VAR) { return new RPsiDuneVar(myTypes, type); } else if (type == DuneTypes.INSTANCE.C_SEXPR) { return new RPsiDuneSExpr(myTypes, type); } return null; } @Override public @Nullable LeafElement createLeaf(@NotNull IElementType type, @NotNull CharSequence text) { return super.createLeaf(type, text); } } ================================================ FILE: src/main/java/com/reason/lang/dune/DuneLanguage.java ================================================ package com.reason.lang.dune; import com.intellij.lang.Language; public class DuneLanguage extends Language { public static final DuneLanguage INSTANCE = new DuneLanguage(); private DuneLanguage() { super("Dune"); } } ================================================ FILE: src/main/java/com/reason/lang/dune/DuneLexer.java ================================================ /* The following code was generated by JFlex 1.7.0 tweaked for IntelliJ platform */ package com.reason.lang.dune; import com.intellij.psi.tree.IElementType; import com.intellij.lexer.FlexLexer; import static com.intellij.psi.TokenType.*; @SuppressWarnings("ALL") /** * This class is a scanner generated by * JFlex 1.7.0 * from the specification file Dune.flex */ public class DuneLexer implements FlexLexer { /** 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 WAITING_VALUE = 2; public static final int INITIAL = 4; public static final int IN_STRING = 6; public static final int IN_ML_COMMENT = 8; public static final int IN_SEXPR_COMMENT = 10; public static final int IN_SL_COMMENT = 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 }; /** * Translates characters to character classes * Chosen bits are [9, 6, 6] * Total runtime size is 1568 bytes */ public static int ZZ_CMAP(int ch) { return ZZ_CMAP_A[(ZZ_CMAP_Y[ZZ_CMAP_Z[ch>>12]|((ch>>6)&0x3f)]<<6)|(ch&0x3f)]; } /* The ZZ_CMAP_Z table has 272 entries */ static final char ZZ_CMAP_Z[] = zzUnpackCMap( "\1\0\1\100\1\200\u010d\100"); /* The ZZ_CMAP_Y table has 192 entries */ static final char ZZ_CMAP_Y[] = zzUnpackCMap( "\1\0\1\1\1\2\175\3\1\4\77\3"); /* The ZZ_CMAP_A table has 320 entries */ static final char ZZ_CMAP_A[] = zzUnpackCMap( "\11\0\1\23\1\1\1\32\1\3\1\2\22\0\1\23\1\4\1\5\1\6\1\0\1\11\2\4\1\20\1\21\1"+ "\0\1\4\1\0\3\4\4\26\4\27\2\24\1\17\1\10\1\15\1\14\1\13\1\0\1\4\6\31\24\4\1"+ "\0\1\22\1\0\2\4\1\0\6\31\10\4\1\25\10\4\1\30\2\4\1\12\1\7\1\16\7\0\1\32\242"+ "\0\2\32\26\0"); /** * 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\1\3\1\4\1\5\1\6\1\7"+ "\1\2\1\10\1\11\1\12\1\13\1\14\1\15\1\16"+ "\1\17\1\2\1\20\3\17\1\21\1\22\1\23\1\2"+ "\1\24\1\25\1\26\1\27\1\30\1\0\1\17\1\0"+ "\3\17\1\31\1\32\5\0"; private static int [] zzUnpackAction() { int [] result = new int[50]; 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\33\0\66\0\121\0\154\0\207\0\242\0\275"+ "\0\275\0\330\0\363\0\275\0\u010e\0\275\0\u0129\0\u0144"+ "\0\275\0\u015f\0\275\0\275\0\275\0\275\0\275\0\u017a"+ "\0\275\0\u0195\0\u01b0\0\u01cb\0\275\0\275\0\275\0\u01e6"+ "\0\275\0\275\0\275\0\275\0\275\0\u017a\0\u0201\0\u021c"+ "\0\u0237\0\u0252\0\u026d\0\275\0\275\0\u01e6\0\u0288\0\u02a3"+ "\0\u02be\0\u02d9"; private static int [] zzUnpackRowMap() { int [] result = new int[50]; 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(); 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 = "\33\10\34\11\3\12\1\13\1\14\1\15\1\11\1\16"+ "\1\17\1\11\1\20\1\21\1\22\1\23\1\24\1\25"+ "\1\26\1\13\1\12\6\13\1\11\2\27\1\30\1\11"+ "\1\27\1\31\14\27\1\32\7\27\1\11\2\27\1\30"+ "\1\11\2\27\1\33\1\34\22\27\1\11\2\27\1\30"+ "\1\11\14\27\1\35\1\36\10\27\1\11\1\27\1\37"+ "\1\40\1\11\26\27\1\11\34\0\3\12\17\0\1\12"+ "\13\0\1\13\15\0\1\13\1\0\6\13\10\0\1\41"+ "\1\42\34\0\1\43\34\0\1\44\32\0\1\45\17\0"+ "\1\27\1\46\30\0\1\27\1\47\1\50\1\0\20\27"+ "\1\51\1\52\2\51\1\53\1\27\10\0\1\54\31\0"+ "\1\55\25\0\1\37\1\56\53\0\1\47\10\0\1\47"+ "\1\50\54\0\1\57\1\0\2\57\31\0\1\60\30\0"+ "\1\61\1\0\2\61\1\0\1\61\25\0\1\27\1\0"+ "\2\27\31\0\2\62\27\0\1\27\1\0\2\27\1\0"+ "\1\27\27\0\2\27\3\0"; private static int [] zzUnpackTrans() { int [] result = new int[756]; 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 aState */ private static final int [] ZZ_ATTRIBUTE = zzUnpackAttribute(); private static final String ZZ_ATTRIBUTE_PACKED_0 = "\7\0\2\11\2\1\1\11\1\1\1\11\2\1\1\11"+ "\1\1\5\11\1\1\1\11\3\1\3\11\1\1\5\11"+ "\1\0\1\1\1\0\3\1\2\11\5\0"; private static int [] zzUnpackAttribute() { int [] result = new int[50]; 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; /* user code: */ private DuneTypes types; private int tokenStartIndex; private CharSequence quotedStringId; private int commentDepth; private int parenDepth; public DuneLexer(DuneTypes types) { this.types = types; } // Store the start index of a token private void tokenStart() { tokenStartIndex = zzStartRead; } // Set the start index of the token to the stored index private void tokenEnd() { zzStartRead = tokenStartIndex; } /** * Creates a new scanner * * @param in the java.io.Reader to read input from. */ public DuneLexer(java.io.Reader in) { this.zzReader = in; } /** * Unpacks the compressed character translation table. * * @param packed the packed character translation table * @return the unpacked character translation table */ private static char [] zzUnpackCMap(String packed) { int size = 0; for (int i = 0, length = packed.length(); i < length; i += 2) { size += packed.charAt(i); } char[] map = new char[size]; int i = 0; /* index in packed string */ int j = 0; /* index in unpacked array */ while (i < packed.length()) { int count = packed.charAt(i++); char value = packed.charAt(i++); do map[j++] = value; while (--count > 0); } return map; } 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; 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/*, zzEndReadL*/); 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/*, zzEndReadL*/); zzCurrentPosL += Character.charCount(zzInput); } } int zzNext = zzTransL[ zzRowMapL[zzState] + ZZ_CMAP(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; switch (zzLexicalState) { case IN_STRING: { yybegin(INITIAL); tokenEnd(); return types.STRING; } // fall though case 51: break; case IN_ML_COMMENT: { yybegin(INITIAL); tokenEnd(); return types.MULTI_COMMENT; } // fall though case 52: break; case IN_SEXPR_COMMENT: { yybegin(INITIAL); tokenEnd(); return types.MULTI_COMMENT; } // fall though case 53: break; case IN_SL_COMMENT: { yybegin(INITIAL); tokenEnd(); return types.SINGLE_COMMENT; } // fall though case 54: break; default: return null; } } else { switch (zzAction < 0 ? zzAction : ZZ_ACTION[zzAction]) { case 1: { yybegin(INITIAL); yypushback(1); } // fall through case 27: break; case 2: { return BAD_CHARACTER; } // fall through case 28: break; case 3: { return WHITE_SPACE; } // fall through case 29: break; case 4: { return types.ATOM; } // fall through case 30: break; case 5: { yybegin(IN_STRING); tokenStart(); } // fall through case 31: break; case 6: { return types.SHARP; } // fall through case 32: break; case 7: { yybegin(IN_SL_COMMENT); tokenStart(); } // fall through case 33: break; case 8: { return types.GT; } // fall through case 34: break; case 9: { return types.EQUAL; } // fall through case 35: break; case 10: { return types.LT; } // fall through case 36: break; case 11: { return types.VAR_END; } // fall through case 37: break; case 12: { return types.COLON; } // fall through case 38: break; case 13: { return types.LPAREN; } // fall through case 39: break; case 14: { return types.RPAREN; } // fall through case 40: break; case 15: { } // fall through case 41: break; case 16: { yybegin(INITIAL); tokenEnd(); return types.STRING; } // fall through case 42: break; case 17: { parenDepth += 1; } // fall through case 43: break; case 18: { parenDepth -= 1; if(parenDepth == 0) { yybegin(INITIAL); tokenEnd(); return types.MULTI_COMMENT; } } // fall through case 44: break; case 19: { yybegin(INITIAL); tokenEnd(); return types.SINGLE_COMMENT; } // fall through case 45: break; case 20: { yybegin(IN_ML_COMMENT); commentDepth = 1; tokenStart(); } // fall through case 46: break; case 21: { yybegin(IN_SEXPR_COMMENT); parenDepth = 0; tokenStart(); } // fall through case 47: break; case 22: { return types.VAR_START; } // fall through case 48: break; case 23: { return types.GTE; } // fall through case 49: break; case 24: { return types.LTE; } // fall through case 50: break; case 25: { commentDepth += 1; } // fall through case 51: break; case 26: { commentDepth -= 1; if(commentDepth == 0) { yybegin(INITIAL); tokenEnd(); return types.MULTI_COMMENT; } } // fall through case 52: break; default: zzScanError(ZZ_NO_MATCH); } } } } } ================================================ FILE: src/main/java/com/reason/lang/dune/DuneParser.java ================================================ package com.reason.lang.dune; import com.intellij.lang.*; import com.intellij.psi.tree.*; import com.reason.lang.*; import org.jetbrains.annotations.*; public class DuneParser extends CommonPsiParser { DuneParser() { super(true); } @Override protected ORParser getORParser(@NotNull PsiBuilder builder) { return new DuneParserState(builder, !myIsSafe); } static class DuneParserState extends ORParser { protected DuneParserState(@NotNull PsiBuilder builder, boolean verbose) { super(DuneTypes.INSTANCE, builder, verbose); } @Override public void parse() { IElementType tokenType; while (!myBuilder.eof()) { tokenType = myBuilder.getTokenType(); if (tokenType == myTypes.ATOM) { parseAtom(); } // ( ... ) else if (tokenType == myTypes.LPAREN) { parseLParen(); } else if (tokenType == myTypes.RPAREN) { parseRParen(); } // %{ ... } else if (tokenType == myTypes.VAR_START) { parseVarStart(); } else if (tokenType == myTypes.VAR_END) { parseVarEnd(); } if (dontMove) { dontMove = false; } else { myBuilder.advanceLexer(); } } } private void parseAtom() { if (is(myTypes.C_STANZA)) { advance().mark(myTypes.C_FIELDS); } } private void parseLParen() { if (isRoot()) { markScope(myTypes.C_STANZA, myTypes.LPAREN); } else if (is(myTypes.C_FIELDS)) { markScope(myTypes.C_FIELD, myTypes.LPAREN); } else { markScope(myTypes.C_SEXPR, myTypes.LPAREN); } } private void parseRParen() { if (is(myTypes.C_FIELDS)) { popEnd(); } if (rawHasScope()) { advance().popEnd(); } else { error("Unbalanced parenthesis"); } } private void parseVarStart() { // |>%{<| ... } markScope(myTypes.C_VAR, myTypes.VAR_START); } private void parseVarEnd() { if (is(myTypes.C_VAR)) { // %{ ... |>}<| advance().popEnd(); } } } } ================================================ FILE: src/main/java/com/reason/lang/dune/DuneParserDefinition.java ================================================ package com.reason.lang.dune; import com.intellij.lang.*; import com.intellij.lexer.*; import com.intellij.openapi.project.*; import com.intellij.psi.*; import com.intellij.psi.tree.*; import com.reason.ide.files.*; import org.jetbrains.annotations.*; public class DuneParserDefinition implements ParserDefinition { private static final TokenSet WHITE_SPACES = TokenSet.create(DuneTypes.INSTANCE.WHITE_SPACE); private static final TokenSet COMMENTS = TokenSet.create(DuneTypes.INSTANCE.SINGLE_COMMENT, DuneTypes.INSTANCE.MULTI_COMMENT); private static final TokenSet STRINGS = TokenSet.create(DuneTypes.INSTANCE.STRING); private static final IFileElementType FILE = new IFileElementType(DuneLanguage.INSTANCE); @NotNull @Override public Lexer createLexer(Project project) { return new FlexAdapter(new DuneLexer(DuneTypes.INSTANCE)); } @NotNull public TokenSet getWhitespaceTokens() { return WHITE_SPACES; } @NotNull public TokenSet getCommentTokens() { return COMMENTS; } @NotNull public TokenSet getStringLiteralElements() { return STRINGS; } @NotNull public PsiParser createParser(final Project project) { return new DuneParser(); } @NotNull @Override public IFileElementType getFileNodeType() { return FILE; } @NotNull public PsiFile createFile(@NotNull FileViewProvider viewProvider) { return new DuneFile(viewProvider); } @NotNull public SpaceRequirements spaceExistenceTypeBetweenTokens(ASTNode left, ASTNode right) { return SpaceRequirements.MAY; } @NotNull public PsiElement createElement(@NotNull ASTNode node) { IElementType type = node.getElementType(); throw new IllegalArgumentException("Not a Rescript node: " + node + " (" + type + ", " + type.getLanguage() + ")"); } } ================================================ FILE: src/main/java/com/reason/lang/dune/DuneTypes.java ================================================ package com.reason.lang.dune; import com.reason.lang.core.type.*; public class DuneTypes extends ORTypes { public static final DuneTypes INSTANCE = new DuneTypes(); private DuneTypes() { } public final ORTokenElementType SINGLE_COMMENT = new ORTokenElementType("SINGLE_COMMENT", DuneLanguage.INSTANCE); public final ORTokenElementType MULTI_COMMENT = new ORTokenElementType("MULTI_COMMENT", DuneLanguage.INSTANCE); // Composite element types public final ORCompositeElementType C_FIELD = new ORCompositeElementType("Field", DuneLanguage.INSTANCE); public final ORCompositeElementType C_FIELDS = new ORCompositeElementType("Fields", DuneLanguage.INSTANCE); public final ORCompositeElementType C_STANZA = new ORCompositeElementType("Stanza", DuneLanguage.INSTANCE); public final ORCompositeElementType C_SEXPR = new ORCompositeElementType("S-expr", DuneLanguage.INSTANCE); public final ORCompositeElementType C_VAR = new ORCompositeElementType("Var", DuneLanguage.INSTANCE); // Token element types public final ORTokenElementType ATOM = new ORTokenElementType("ATOM", DuneLanguage.INSTANCE); public final ORTokenElementType COLON = new ORTokenElementType("COLON", DuneLanguage.INSTANCE); public final ORTokenElementType EQUAL = new ORTokenElementType("EQUAL", DuneLanguage.INSTANCE); public final ORTokenElementType GT = new ORTokenElementType("GT", DuneLanguage.INSTANCE); public final ORTokenElementType GTE = new ORTokenElementType("GTE", DuneLanguage.INSTANCE); public final ORTokenElementType LPAREN = new ORTokenElementType("LPAREN", DuneLanguage.INSTANCE); public final ORTokenElementType LT = new ORTokenElementType("LT", DuneLanguage.INSTANCE); public final ORTokenElementType LTE = new ORTokenElementType("LTE", DuneLanguage.INSTANCE); public final ORTokenElementType RPAREN = new ORTokenElementType("RPAREN", DuneLanguage.INSTANCE); public final ORTokenElementType SHARP = new ORTokenElementType("SHARP", DuneLanguage.INSTANCE); public final ORTokenElementType STRING = new ORTokenElementType("String", DuneLanguage.INSTANCE); public final ORTokenElementType VAR_END = new ORTokenElementType("VAR_END", DuneLanguage.INSTANCE); public final ORTokenElementType VAR_START = new ORTokenElementType("VAR_START", DuneLanguage.INSTANCE); } ================================================ FILE: src/main/java/com/reason/lang/extra/OclP4Language.java ================================================ package com.reason.lang.extra; import com.intellij.lang.Language; public class OclP4Language extends Language { public static final OclP4Language INSTANCE = new OclP4Language(); private OclP4Language() { super("OCamlP4"); } } ================================================ FILE: src/main/java/com/reason/lang/extra/OclP4ParserDefinition.java ================================================ package com.reason.lang.extra; import com.intellij.lang.ASTNode; import com.intellij.lang.Language; import com.intellij.lang.ParserDefinition; import com.intellij.lang.PsiParser; import com.intellij.lexer.Lexer; import com.intellij.openapi.project.Project; import com.intellij.psi.FileViewProvider; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.TokenType; import com.intellij.psi.tree.IElementType; import com.intellij.psi.tree.IFileElementType; import com.intellij.psi.tree.TokenSet; import com.reason.ide.files.Ml4File; import com.reason.lang.core.stub.type.ORStubElementType; import com.reason.lang.ocaml.OclLexer; import com.reason.lang.ocaml.OclParser; import com.reason.lang.ocaml.OclTypes; import org.jetbrains.annotations.NotNull; public class OclP4ParserDefinition implements ParserDefinition { private static final TokenSet WHITE_SPACES = TokenSet.create(TokenType.WHITE_SPACE); private static final TokenSet COMMENTS = TokenSet.create(OclTypes.INSTANCE.MULTI_COMMENT); private static final TokenSet STRINGS = TokenSet.create(OclTypes.INSTANCE.STRING_VALUE); private static final IFileElementType FILE = new IFileElementType(Language.findInstance(OclP4Language.class)); @NotNull @Override public Lexer createLexer(Project project) { return new OclLexer(); } @NotNull public TokenSet getWhitespaceTokens() { return WHITE_SPACES; } @NotNull public TokenSet getCommentTokens() { return COMMENTS; } @NotNull public TokenSet getStringLiteralElements() { return STRINGS; } @NotNull public PsiParser createParser(final Project project) { return new OclParser(true); } @NotNull @Override public IFileElementType getFileNodeType() { return FILE; } @NotNull public PsiFile createFile(@NotNull FileViewProvider viewProvider) { return new Ml4File(viewProvider); } @NotNull public ParserDefinition.SpaceRequirements spaceExistenceTypeBetweenTokens( ASTNode left, ASTNode right) { return ParserDefinition.SpaceRequirements.MAY; } @NotNull public PsiElement createElement(@NotNull ASTNode node) { IElementType type = node.getElementType(); if (type instanceof ORStubElementType) { //noinspection rawtypes return ((ORStubElementType) node.getElementType()).createPsi(node); } throw new IllegalArgumentException( "Not an OCaml node: " + node + " (" + type + ", " + type.getLanguage() + ")"); } } ================================================ FILE: src/main/java/com/reason/lang/ocaml/OCaml.flex ================================================ package com.reason.lang.ocaml; import com.intellij.psi.tree.*; import com.reason.lang.core.type.*; import static com.intellij.psi.TokenType.*; @SuppressWarnings("ALL") %% %{ public OCamlLexer() { this.types = OclTypes.INSTANCE; } private ORLangTypes types; private int tokenStartIndex; private CharSequence quotedStringId; private int commentDepth; private boolean inCommentString = false; // Store the start index of a token private void tokenStart() { tokenStartIndex = zzStartRead; } // Set the start index of the token to the stored index private void tokenEnd() { zzStartRead = tokenStartIndex; } %} %public %class OCamlLexer %implements com.intellij.lexer.FlexLexer %unicode %function advance %type IElementType EOL=\n|\r|\r\n WHITE_SPACE_CHAR=[\ \t\f]|{EOL} WHITE_SPACE={WHITE_SPACE_CHAR}+ NEWLINE=("\r"* "\n") LOWERCASE=[a-z_] UPPERCASE=[A-Z] IDENTCHAR=[A-Za-z_0-9'] DECIMAL=[0-9] DECIMAL_SEP=[0-9_] HEXA=[0-9A-Fa-f] HEXA_SEP=[0-9A-Fa-f_] OCTAL=[0-7] OCTAL_SEP=[0-7_] DECIMAL_LITERAL={DECIMAL} {DECIMAL_SEP}* HEXA_LITERAL="0" [xX] {HEXA} {HEXA_SEP}* OCT_LITERAL="0" [oO] {OCTAL} {OCTAL_SEP}* BIN_LITERAL="0" [bB] [0-1] [0-1_]* INT_LITERAL= {DECIMAL_LITERAL} | {HEXA_LITERAL} | {OCT_LITERAL} | {BIN_LITERAL} FLOAT_LITERAL={DECIMAL} {DECIMAL_SEP}* ("." {DECIMAL_SEP}* )? ([eE] [+-]? {DECIMAL} {DECIMAL_SEP}* )? HEXA_FLOAT_LITERAL="0" [xX] {HEXA} {HEXA_SEP}* ("." {HEXA_SEP}* )? ([pP] [+-]? {DECIMAL} {DECIMAL_SEP}* )? LITERAL_MODIFIER=[G-Zg-z] ESCAPE_BACKSLASH="\\\\" ESCAPE_SINGLE_QUOTE="\\'" ESCAPE_LF="\\n" ESCAPE_TAB="\\t" ESCAPE_BACKSPACE="\\b" ESCAPE_CR="\\r" ESCAPE_QUOTE="\\\"" ESCAPE_DECIMAL="\\" {DECIMAL} {DECIMAL} {DECIMAL} ESCAPE_HEXA="\\x" {HEXA} {HEXA} ESCAPE_OCTAL="\\o" [0-3] {OCTAL} {OCTAL} ESCAPE_CHAR= {ESCAPE_BACKSLASH} | {ESCAPE_SINGLE_QUOTE} | {ESCAPE_LF} | {ESCAPE_TAB} | {ESCAPE_BACKSPACE } | { ESCAPE_CR } | { ESCAPE_QUOTE } | {ESCAPE_DECIMAL} | {ESCAPE_HEXA} | {ESCAPE_OCTAL} %state INITIAL %state IN_STRING %state IN_OCAML_ML_COMMENT %state IN_DIRECTIVE %% { [^] { yybegin(INITIAL); yypushback(1); } } { {WHITE_SPACE} { return WHITE_SPACE; } // OCaml reserved keywords // https://v2.ocaml.org/manual/lex.html#sss:keywords "assert" { return types.ASSERT; } "and" { return types.AND; } "asr" { return types.ASR; } "as" { return types.AS; } "begin" { return types.BEGIN; } "class" { return types.CLASS; } "constraint" { return types.CONSTRAINT; } "downto" { return types.DOWNTO; } "done" { return types.DONE; } "do" { return types.DO; } "exception" { return types.EXCEPTION; } "external" { return types.EXTERNAL; } "else" { return types.ELSE; } "end" { return types.END; } "function" { return types.FUNCTION; } "functor" { return types.FUNCTOR; } "false" { return types.BOOL_VALUE; } "fun" { return types.FUN; } "for" { return types.FOR; } "initializer" { return types.INITIALIZER; } "include" { return types.INCLUDE; } "inherit" { return types.INHERIT; } "if" { return types.IF; } "in" { return types.IN; } "land" { return types.LAND; } "lazy" { return types.LAZY; } "lxor" { return types.LXOR; } "let" { return types.LET; } "lor" { return types.LOR; } "lsl" { return types.LSL; } "lsr" { return types.LSR; } "mutable" { return types.MUTABLE; } "method" { return types.METHOD; } "module" { return types.MODULE;} "match" { return types.MATCH; } "mod" { return types.MOD; } "nonrec" { return types.NONREC; } "new" { return types.NEW; } "object" { return types.OBJECT; } "open" { return types.OPEN; } "of" { return types.OF; } "or" { return types.OR; } "private" { return types.PRIVATE; } "rec" { return types.REC; } "struct" { return types.STRUCT; } "sig" { return types.SIG; } "then" { return types.THEN; } "true" { return types.BOOL_VALUE; } "type" { return types.TYPE; } "try" { return types.TRY; } "to" { return types.TO; } "virtual" { return types.VIRTUAL; } "val" { return types.VAL; } "when" { return types.WHEN; } // ?? "pub" { return types.PUB; } "pri" { return types.PRI; } "while" { return types.WHILE; } "with" { return types.WITH; } "raw" { return types.RAW; } "unit" { return types.UNIT; } "ref" { return types.REF; } "raise" { return types.RAISE; } "option" { return types.OPTION; } "None" { return types.NONE; } "Some" { return types.SOME; } "_" { return types.UNDERSCORE; } "'" ( {ESCAPE_CHAR} | . ) "'" { return types.CHAR_VALUE; } {LOWERCASE}{IDENTCHAR}* { return types.LIDENT; } {UPPERCASE}{IDENTCHAR}* { return types.UIDENT; } {INT_LITERAL}{LITERAL_MODIFIER}? { return types.INT_VALUE; } ({FLOAT_LITERAL} | {HEXA_FLOAT_LITERAL}){LITERAL_MODIFIER}? { return types.FLOAT_VALUE; } "'"{LOWERCASE}{IDENTCHAR}* { return types.TYPE_ARGUMENT; } "`"{UPPERCASE}{IDENTCHAR}* { return types.POLY_VARIANT; } "`"{LOWERCASE}{IDENTCHAR}* { return types.POLY_VARIANT; } "\"" { yybegin(IN_STRING); tokenStart(); } "(*" { yybegin(IN_OCAML_ML_COMMENT); inCommentString = false; commentDepth = 1; tokenStart(); } "##" { return types.SHARPSHARP; } "@@" { return types.ARROBASE_2; } "@@@" { return types.ARROBASE_3; } "::" { return types.SHORTCUT; } "=>" { return types.ARROW; } "->" { return types.RIGHT_ARROW; } "<-" { return types.LEFT_ARROW; } "|>" { return types.PIPE_FORWARD; } "|." { return types.PIPE_FIRST; } "" { return types.TAG_AUTO_CLOSE; } "[|" { return types.LARRAY; } "|]" { return types.RARRAY; } "===" { return types.EQEQEQ; } "==" { return types.EQEQ; } "=" { return types.EQ; } "!==" { return types.NOT_EQEQ; } "!=" { return types.NOT_EQ; } ":=" { return types.COLON_EQ; } ":>" { return types.COLON_GT; } "<=" { return types.LT_OR_EQUAL; } ">=" { return types.GT_OR_EQUAL; } ";;" { return types.SEMISEMI; } "||" { return types.L_OR; } "&&" { return types.L_AND; } "<>" { return types.OP_STRUCT_DIFF; } "," { return types.COMMA; } ":" { return types.COLON; } ";" { return types.SEMI; } "'" { return types.SINGLE_QUOTE; } "..." { return types.DOTDOTDOT; } ".." { return types.DOTDOT; } "." { return types.DOT; } "|" { return types.PIPE; } "(" { return types.LPAREN; } ")" { return types.RPAREN; } "{" { return types.LBRACE; } "}" { return types.RBRACE; } "[" { return types.LBRACKET; } "]" { return types.RBRACKET; } "@" { return types.ARROBASE; } "?" { return types.QUESTION_MARK; } "!" { return types.EXCLAMATION_MARK; } "$" { return types.DOLLAR; } "`" { return types.BACKTICK; } "~" { return types.TILDE; } "&" { return types.AMPERSAND; } "#" { if (zzCurrentPos > 0) { char prevChar = yycharat(-1); if (prevChar == '\n' || prevChar == '\r' || prevChar == ' ' || prevChar == '\t') { yybegin(IN_DIRECTIVE); yypushback(1); } else { return types.SHARP; } } else { yybegin(IN_DIRECTIVE); yypushback(1); } } "<" { return types.LT; } ">" { return types.GT; } "\^" { return types.CARRET; } "+." { return types.PLUSDOT; } "-." { return types.MINUSDOT; } "/." { return types.SLASHDOT; } "*." { return types.STARDOT; } "++" { return types.STRING_CONCAT; } "+" { return types.PLUS; } "-" { return types.MINUS; } "/" { return types.SLASH; } "*" { return types.STAR; } "%" { return types.PERCENT; } } { "\"" { yybegin(INITIAL); tokenEnd(); return types.STRING_VALUE; } "\\" { NEWLINE } ([ \t] *) { } "\\" [\\\'\"ntbr ] { } "\\" [0-9] [0-9] [0-9] { } "\\" "o" [0-3] [0-7] [0-7] { } "\\" "x" [0-9a-fA-F] [0-9a-fA-F] { } "\\" . { } { NEWLINE } { } . { } <> { yybegin(INITIAL); tokenEnd(); return types.STRING_VALUE; } } { "'" . "'" { /* a char */ } "(*" { if (!inCommentString) { commentDepth += 1; } } "*)" { if (!inCommentString) { commentDepth -= 1; if(commentDepth == 0) { yybegin(INITIAL); tokenEnd(); return types.MULTI_COMMENT; } } } "\"" { inCommentString = !inCommentString; } . | {NEWLINE} { } <> { yybegin(INITIAL); tokenEnd(); return types.MULTI_COMMENT; } } // Not really OCaml... but anyway { "#if" { yybegin(INITIAL); tokenEnd(); return types.DIRECTIVE_IF; } "#else" { yybegin(INITIAL); tokenEnd(); return types.DIRECTIVE_ELSE; } "#elif" { yybegin(INITIAL); tokenEnd(); return types.DIRECTIVE_ELIF; } "#end" { yybegin(INITIAL); tokenEnd(); return types.DIRECTIVE_END; } "#endif" { yybegin(INITIAL); tokenEnd(); return types.DIRECTIVE_ENDIF; } . | {NEWLINE} { yybegin(INITIAL); tokenEnd(); return types.SHARP; } <> { yybegin(INITIAL); tokenEnd(); return types.SHARP; } } [^] { return BAD_CHARACTER; } ================================================ FILE: src/main/java/com/reason/lang/ocaml/OCamlLexer.java ================================================ // Generated by JFlex 1.9.1 http://jflex.de/ (tweaked for IntelliJ platform) // source: OCaml.flex package com.reason.lang.ocaml; import com.intellij.psi.tree.*; import com.reason.lang.core.type.*; import static com.intellij.psi.TokenType.*; @SuppressWarnings("ALL") public class OCamlLexer implements com.intellij.lexer.FlexLexer { /** 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 INITIAL = 2; public static final int IN_STRING = 4; public static final int IN_OCAML_ML_COMMENT = 6; public static final int IN_DIRECTIVE = 8; /** * 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 }; /** * 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\37\u0100\1\u0200\267\u0100\10\u0300\u1020\u0100"; 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\3\1\4\1\5\22\0\1\1"+ "\1\6\1\7\1\10\1\11\1\12\1\13\1\14\1\15"+ "\1\16\1\17\1\20\1\21\1\22\1\23\1\24\1\25"+ "\1\26\2\27\4\30\2\31\1\32\1\33\1\34\1\35"+ "\1\36\1\37\1\40\1\41\1\42\2\41\1\43\1\41"+ "\7\44\1\45\1\46\1\47\2\44\1\50\4\44\1\51"+ "\2\44\1\52\1\53\1\54\1\55\1\56\1\57\1\60"+ "\1\61\1\62\1\63\1\64\1\65\1\66\1\67\1\70"+ "\1\71\1\72\1\73\1\74\1\75\1\76\1\77\1\72"+ "\1\100\1\101\1\102\1\103\1\104\1\105\1\106\1\107"+ "\1\110\1\111\1\112\1\113\1\114\6\0\1\3\u01a2\0"+ "\2\3\326\0\u0100\3"; private static int [] zzUnpackcmap_blocks() { int [] result = new int[1024]; 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 = "\5\0\1\1\1\2\1\3\1\4\1\5\1\6\1\7"+ "\1\10\1\11\1\12\1\13\1\14\1\15\1\16\1\17"+ "\1\20\1\21\1\22\2\23\1\24\1\25\1\26\1\27"+ "\1\30\1\31\1\32\3\33\1\34\1\35\1\36\1\37"+ "\1\40\23\41\1\42\1\43\1\44\1\45\1\46\1\2"+ "\1\47\1\46\1\50\3\46\1\51\1\2\1\51\1\52"+ "\1\53\1\54\2\0\1\55\1\56\1\57\1\60\1\61"+ "\1\62\1\63\1\64\1\65\1\66\1\67\2\0\3\23"+ "\1\70\1\71\1\72\1\73\1\74\1\75\1\76\1\77"+ "\1\100\1\101\1\102\1\103\2\33\1\104\1\105\1\41"+ "\1\106\3\41\1\107\6\41\1\110\1\111\14\41\1\112"+ "\1\41\1\113\7\41\1\114\7\41\1\115\1\116\1\117"+ "\1\120\1\0\1\46\1\0\3\46\1\0\1\121\1\122"+ "\3\0\1\123\2\124\3\0\1\124\1\55\1\125\1\67"+ "\1\23\1\0\1\67\2\23\1\126\1\127\2\33\1\130"+ "\1\131\7\41\1\132\3\41\1\133\1\134\5\41\1\135"+ "\1\136\1\137\1\140\3\41\1\141\1\41\1\142\4\41"+ "\1\143\1\144\1\41\1\145\1\146\1\147\1\150\3\41"+ "\1\151\2\41\1\152\4\41\3\0\1\153\2\0\1\154"+ "\3\0\1\67\1\23\1\155\1\156\4\41\1\157\1\41"+ "\1\160\6\41\1\161\1\162\1\163\6\41\1\164\4\41"+ "\1\165\1\166\1\167\1\170\1\41\1\171\1\41\1\172"+ "\3\0\1\173\1\0\1\67\1\41\1\174\1\175\10\41"+ "\1\176\7\41\1\177\2\41\1\200\1\201\1\202\1\0"+ "\1\203\1\41\1\204\7\41\1\205\1\206\1\41\1\207"+ "\1\210\1\211\1\41\1\212\1\41\1\213\4\41\1\214"+ "\1\215\1\216\1\41\1\217\1\220\1\221\2\41\1\222"+ "\1\223\2\41\1\224\1\41\1\225\1\41\1\226"; private static int [] zzUnpackAction() { int [] result = new int[365]; 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\115\0\232\0\347\0\u0134\0\u0181\0\u0181\0\u01ce"+ "\0\u021b\0\u0181\0\u0268\0\u0181\0\u0181\0\u02b5\0\u0302\0\u034f"+ "\0\u0181\0\u039c\0\u03e9\0\u0181\0\u0436\0\u0483\0\u04d0\0\u051d"+ "\0\u056a\0\u05b7\0\u0604\0\u0651\0\u069e\0\u06eb\0\u0181\0\u0738"+ "\0\u0785\0\u07d2\0\u081f\0\u086c\0\u0181\0\u0181\0\u08b9\0\u0906"+ "\0\u0953\0\u09a0\0\u09ed\0\u0a3a\0\u0a87\0\u0ad4\0\u08b9\0\u0b21"+ "\0\u0b6e\0\u0bbb\0\u0c08\0\u0c55\0\u0ca2\0\u0cef\0\u0d3c\0\u0d89"+ "\0\u0dd6\0\u0e23\0\u0e70\0\u0181\0\u0ebd\0\u0181\0\u0181\0\u0181"+ "\0\u0f0a\0\u0181\0\u0f57\0\u0181\0\u0fa4\0\u0ff1\0\u103e\0\u0181"+ "\0\u108b\0\u10d8\0\u1125\0\u0181\0\u0181\0\u1172\0\u11bf\0\u120c"+ "\0\u0181\0\u0181\0\u0181\0\u0181\0\u0181\0\u0181\0\u1259\0\u0181"+ "\0\u0181\0\u12a6\0\u12f3\0\u1340\0\u0181\0\u138d\0\u13da\0\u0181"+ "\0\u0181\0\u0181\0\u0181\0\u0181\0\u0181\0\u0181\0\u0181\0\u1427"+ "\0\u0181\0\u0181\0\u1474\0\u14c1\0\u150e\0\u0181\0\u155b\0\u15a8"+ "\0\u15f5\0\u1642\0\u168f\0\u16dc\0\u1729\0\u1776\0\u17c3\0\u1810"+ "\0\u185d\0\u18aa\0\u18f7\0\u08b9\0\u1944\0\u1991\0\u19de\0\u1a2b"+ "\0\u1a78\0\u1ac5\0\u1b12\0\u1b5f\0\u1bac\0\u1bf9\0\u1c46\0\u1c93"+ "\0\u1ce0\0\u08b9\0\u1d2d\0\u08b9\0\u1d7a\0\u1dc7\0\u1e14\0\u1e61"+ "\0\u1eae\0\u1efb\0\u1f48\0\u08b9\0\u1f95\0\u1fe2\0\u202f\0\u207c"+ "\0\u20c9\0\u2116\0\u2163\0\u0181\0\u0181\0\u0181\0\u0181\0\u0f0a"+ "\0\u21b0\0\u21fd\0\u224a\0\u2297\0\u22e4\0\u2331\0\u0181\0\u0181"+ "\0\u108b\0\u237e\0\u23cb\0\u0181\0\u0181\0\u1172\0\u2418\0\u2465"+ "\0\u24b2\0\u24ff\0\u24ff\0\u0181\0\u0181\0\u254c\0\u2599\0\u25e6"+ "\0\u2633\0\u2680\0\u0181\0\u0181\0\u26cd\0\u271a\0\u08b9\0\u08b9"+ "\0\u2767\0\u27b4\0\u2801\0\u284e\0\u289b\0\u28e8\0\u2935\0\u08b9"+ "\0\u2982\0\u29cf\0\u2a1c\0\u08b9\0\u2a69\0\u2ab6\0\u2b03\0\u2b50"+ "\0\u2b9d\0\u2bea\0\u08b9\0\u08b9\0\u08b9\0\u08b9\0\u2c37\0\u2c84"+ "\0\u2cd1\0\u2d1e\0\u2d6b\0\u08b9\0\u2db8\0\u2e05\0\u2e52\0\u2e9f"+ "\0\u2eec\0\u08b9\0\u2f39\0\u08b9\0\u08b9\0\u08b9\0\u08b9\0\u2f86"+ "\0\u2fd3\0\u3020\0\u08b9\0\u306d\0\u30ba\0\u08b9\0\u3107\0\u3154"+ "\0\u31a1\0\u31ee\0\u323b\0\u3288\0\u32d5\0\u0181\0\u3322\0\u336f"+ "\0\u0181\0\u33bc\0\u3409\0\u3456\0\u34a3\0\u1340\0\u0785\0\u0785"+ "\0\u34f0\0\u353d\0\u358a\0\u35d7\0\u08b9\0\u3624\0\u08b9\0\u3671"+ "\0\u36be\0\u370b\0\u3758\0\u37a5\0\u37f2\0\u08b9\0\u08b9\0\u08b9"+ "\0\u383f\0\u388c\0\u38d9\0\u3926\0\u3973\0\u39c0\0\u08b9\0\u3a0d"+ "\0\u3a5a\0\u3aa7\0\u3af4\0\u08b9\0\u08b9\0\u08b9\0\u08b9\0\u3b41"+ "\0\u08b9\0\u3b8e\0\u08b9\0\u3bdb\0\u3c28\0\u3c75\0\u3cc2\0\u3d0f"+ "\0\u1340\0\u3d5c\0\u08b9\0\u08b9\0\u3da9\0\u3df6\0\u3e43\0\u3e90"+ "\0\u3edd\0\u3f2a\0\u3f77\0\u3fc4\0\u08b9\0\u4011\0\u405e\0\u40ab"+ "\0\u40f8\0\u4145\0\u4192\0\u41df\0\u08b9\0\u422c\0\u4279\0\u08b9"+ "\0\u0181\0\u0181\0\u42c6\0\u08b9\0\u4313\0\u08b9\0\u4360\0\u43ad"+ "\0\u43fa\0\u4447\0\u4494\0\u44e1\0\u452e\0\u08b9\0\u08b9\0\u457b"+ "\0\u08b9\0\u08b9\0\u08b9\0\u45c8\0\u08b9\0\u4615\0\u0181\0\u4662"+ "\0\u46af\0\u46fc\0\u4749\0\u08b9\0\u08b9\0\u08b9\0\u4796\0\u08b9"+ "\0\u08b9\0\u08b9\0\u47e3\0\u4830\0\u08b9\0\u08b9\0\u487d\0\u48ca"+ "\0\u08b9\0\u4917\0\u08b9\0\u4964\0\u08b9"; private static int [] zzUnpackRowMap() { int [] result = new int[365]; 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 = "\115\6\1\7\2\10\1\7\2\10\1\11\1\12\1\13"+ "\1\14\1\15\1\16\1\17\1\20\1\21\1\22\1\23"+ "\1\24\1\25\1\26\1\27\1\30\4\31\1\32\1\33"+ "\1\34\1\35\1\36\1\37\1\40\4\41\1\42\2\41"+ "\1\43\1\41\1\44\1\7\1\45\1\46\1\47\1\50"+ "\1\51\1\52\1\53\1\54\1\55\1\56\2\57\1\60"+ "\2\57\1\61\1\62\1\63\1\64\1\65\1\66\1\67"+ "\1\70\1\71\1\72\1\73\3\57\1\74\1\75\1\76"+ "\1\77\3\100\2\7\1\101\1\100\1\102\43\100\1\103"+ "\44\100\2\7\1\101\1\100\1\104\4\100\1\105\1\106"+ "\1\100\1\107\75\100\3\110\2\7\1\111\2\110\1\112"+ "\104\110\116\0\2\10\1\0\2\10\144\0\1\113\67\0"+ "\1\114\117\0\1\115\101\0\2\116\4\0\45\116\1\117"+ "\2\116\1\120\1\116\31\120\4\116\17\0\1\121\120\0"+ "\1\122\111\0\1\123\2\0\1\124\114\0\1\125\12\0"+ "\1\126\101\0\1\127\114\0\1\130\12\0\1\131\101\0"+ "\1\132\1\0\5\31\10\0\1\133\1\134\2\135\1\136"+ "\2\135\1\137\4\0\1\31\2\0\1\133\2\0\1\134"+ "\1\0\10\135\1\136\7\135\1\137\2\135\27\0\1\132"+ "\1\0\5\31\11\0\1\134\6\135\4\0\1\31\5\0"+ "\1\134\1\0\23\135\36\0\1\140\2\0\1\141\1\142"+ "\111\0\1\143\103\0\1\144\1\0\1\145\10\0\1\146"+ "\1\147\113\0\1\150\1\151\113\0\1\152\117\0\1\153"+ "\70\0\1\41\10\0\5\41\7\0\11\41\4\0\1\41"+ "\1\0\31\41\20\0\1\41\10\0\5\41\7\0\11\41"+ "\4\0\1\41\1\0\16\41\1\154\12\41\20\0\1\41"+ "\10\0\5\41\7\0\11\41\4\0\1\41\1\0\16\41"+ "\1\155\12\41\116\0\1\156\16\0\1\57\10\0\5\57"+ "\7\0\11\57\4\0\1\57\1\0\31\57\45\0\11\157"+ "\4\0\1\157\1\0\31\157\20\0\1\57\10\0\5\57"+ "\7\0\11\57\4\0\1\57\1\0\15\57\1\160\3\57"+ "\1\161\7\57\20\0\1\57\10\0\5\57\7\0\11\57"+ "\4\0\1\57\1\0\4\57\1\162\24\57\20\0\1\57"+ "\10\0\5\57\7\0\11\57\4\0\1\57\1\0\13\57"+ "\1\163\2\57\1\164\12\57\20\0\1\57\10\0\5\57"+ "\7\0\11\57\4\0\1\57\1\0\16\57\1\165\12\57"+ "\20\0\1\57\10\0\5\57\7\0\11\57\4\0\1\57"+ "\1\0\13\57\1\166\1\57\1\167\10\57\1\170\2\57"+ "\20\0\1\57\10\0\5\57\7\0\11\57\4\0\1\57"+ "\1\0\1\171\15\57\1\172\4\57\1\173\5\57\20\0"+ "\1\57\10\0\5\57\7\0\11\57\4\0\1\57\1\0"+ "\5\57\1\174\7\57\1\175\13\57\20\0\1\57\10\0"+ "\5\57\7\0\11\57\4\0\1\57\1\0\1\176\3\57"+ "\1\177\11\57\1\200\2\57\1\201\4\57\1\202\2\57"+ "\20\0\1\57\10\0\5\57\7\0\11\57\4\0\1\57"+ "\1\0\1\203\3\57\1\204\11\57\1\205\4\57\1\206"+ "\5\57\20\0\1\57\10\0\5\57\7\0\11\57\4\0"+ "\1\57\1\0\4\57\1\207\11\57\1\210\12\57\20\0"+ "\1\57\10\0\5\57\7\0\11\57\4\0\1\57\1\0"+ "\1\57\1\211\3\57\1\212\11\57\1\213\1\214\10\57"+ "\20\0\1\57\10\0\5\57\7\0\11\57\4\0\1\57"+ "\1\0\20\57\1\215\2\57\1\216\5\57\20\0\1\57"+ "\10\0\5\57\7\0\11\57\4\0\1\57\1\0\1\217"+ "\3\57\1\220\24\57\20\0\1\57\10\0\5\57\7\0"+ "\11\57\4\0\1\57\1\0\10\57\1\221\11\57\1\222"+ "\6\57\20\0\1\57\10\0\5\57\7\0\11\57\4\0"+ "\1\57\1\0\7\57\1\223\6\57\1\224\1\57\1\225"+ "\6\57\1\226\1\57\20\0\1\57\10\0\5\57\7\0"+ "\11\57\4\0\1\57\1\0\15\57\1\227\13\57\20\0"+ "\1\57\10\0\5\57\7\0\11\57\4\0\1\57\1\0"+ "\1\230\7\57\1\231\20\57\20\0\1\57\10\0\5\57"+ "\7\0\11\57\4\0\1\57\1\0\7\57\1\232\1\233"+ "\20\57\27\0\1\234\12\0\1\235\15\0\1\236\35\0"+ "\1\237\4\0\1\100\2\0\1\240\107\0\2\100\1\241"+ "\2\0\1\242\17\100\5\243\44\100\1\244\7\100\1\245"+ "\6\100\2\246\4\0\107\246\17\0\1\247\113\0\1\250"+ "\100\0\1\110\2\0\1\251\173\0\1\252\3\0\1\253"+ "\61\0\1\254\73\0\1\255\107\0\1\116\4\0\1\256"+ "\10\0\5\257\21\0\1\116\5\0\1\116\13\0\1\116"+ "\1\260\1\0\1\116\1\0\1\116\3\0\1\261\22\0"+ "\1\262\10\0\5\263\7\0\11\263\4\0\1\263\1\0"+ "\31\263\27\0\1\264\116\0\5\132\11\0\1\134\6\265"+ "\4\0\1\132\5\0\1\134\1\0\23\265\31\0\2\266"+ "\106\0\1\267\1\0\1\267\2\0\5\270\110\0\4\271"+ "\111\0\5\272\7\0\3\272\14\0\6\272\64\0\1\273"+ "\117\0\1\274\70\0\1\41\10\0\5\41\7\0\11\41"+ "\4\0\1\41\1\0\15\41\1\275\13\41\20\0\1\41"+ "\10\0\5\41\7\0\11\41\4\0\1\41\1\0\14\41"+ "\1\276\14\41\20\0\1\157\10\0\5\157\7\0\11\157"+ "\4\0\1\157\1\0\31\157\20\0\1\57\10\0\5\57"+ "\7\0\11\57\4\0\1\57\1\0\3\57\1\277\25\57"+ "\20\0\1\57\10\0\5\57\7\0\11\57\4\0\1\57"+ "\1\0\20\57\1\300\1\301\7\57\20\0\1\57\10\0"+ "\5\57\7\0\11\57\4\0\1\57\1\0\6\57\1\302"+ "\22\57\20\0\1\57\10\0\5\57\7\0\11\57\4\0"+ "\1\57\1\0\1\303\30\57\20\0\1\57\10\0\5\57"+ "\7\0\11\57\4\0\1\57\1\0\15\57\1\304\13\57"+ "\20\0\1\57\10\0\5\57\7\0\11\57\4\0\1\57"+ "\1\0\15\57\1\305\7\57\1\306\3\57\20\0\1\57"+ "\10\0\5\57\7\0\11\57\4\0\1\57\1\0\21\57"+ "\1\307\7\57\20\0\1\57\10\0\5\57\7\0\11\57"+ "\4\0\1\57\1\0\3\57\1\310\25\57\20\0\1\57"+ "\10\0\5\57\7\0\11\57\4\0\1\57\1\0\2\57"+ "\1\311\17\57\1\312\6\57\20\0\1\57\10\0\5\57"+ "\7\0\11\57\4\0\1\57\1\0\13\57\1\313\15\57"+ "\20\0\1\57\10\0\5\57\7\0\11\57\4\0\1\57"+ "\1\0\20\57\1\314\10\57\20\0\1\57\10\0\5\57"+ "\7\0\11\57\4\0\1\57\1\0\15\57\1\315\13\57"+ "\20\0\1\57\10\0\5\57\7\0\11\57\4\0\1\57"+ "\1\0\2\57\1\316\4\57\1\317\1\320\20\57\20\0"+ "\1\57\10\0\5\57\7\0\11\57\4\0\1\57\1\0"+ "\15\57\1\321\12\57\1\322\20\0\1\57\10\0\5\57"+ "\7\0\11\57\4\0\1\57\1\0\22\57\1\323\6\57"+ "\20\0\1\57\10\0\5\57\7\0\11\57\4\0\1\57"+ "\1\0\20\57\1\324\10\57\20\0\1\57\10\0\5\57"+ "\7\0\11\57\4\0\1\57\1\0\13\57\1\325\4\57"+ "\1\326\10\57\20\0\1\57\10\0\5\57\7\0\11\57"+ "\4\0\1\57\1\0\16\57\1\327\12\57\20\0\1\57"+ "\10\0\5\57\7\0\11\57\4\0\1\57\1\0\22\57"+ "\1\330\6\57\20\0\1\57\10\0\5\57\7\0\11\57"+ "\4\0\1\57\1\0\22\57\1\331\6\57\20\0\1\57"+ "\10\0\5\57\7\0\11\57\4\0\1\57\1\0\3\57"+ "\1\332\25\57\20\0\1\57\10\0\5\57\7\0\11\57"+ "\4\0\1\57\1\0\22\57\1\333\6\57\20\0\1\57"+ "\10\0\5\57\7\0\11\57\4\0\1\57\1\0\25\57"+ "\1\334\3\57\20\0\1\57\10\0\5\57\7\0\11\57"+ "\4\0\1\57\1\0\15\57\1\335\13\57\20\0\1\57"+ "\10\0\5\57\7\0\11\57\4\0\1\57\1\0\11\57"+ "\1\336\17\57\20\0\1\57\10\0\5\57\7\0\11\57"+ "\4\0\1\57\1\0\4\57\1\337\15\57\1\340\6\57"+ "\20\0\1\57\10\0\5\57\7\0\11\57\4\0\1\57"+ "\1\0\10\57\1\341\20\57\20\0\1\57\10\0\5\57"+ "\7\0\11\57\4\0\1\57\1\0\1\57\1\342\27\57"+ "\20\0\1\57\10\0\5\57\7\0\11\57\4\0\1\57"+ "\1\0\10\57\1\343\14\57\1\344\3\57\20\0\1\57"+ "\10\0\5\57\7\0\11\57\4\0\1\57\1\0\2\57"+ "\1\345\2\57\1\346\23\57\20\0\1\57\10\0\5\57"+ "\7\0\11\57\4\0\1\57\1\0\6\57\1\347\22\57"+ "\20\0\1\57\10\0\5\57\7\0\11\57\4\0\1\57"+ "\1\0\20\57\1\350\10\57\20\0\1\57\10\0\5\57"+ "\7\0\11\57\4\0\1\57\1\0\4\57\1\351\24\57"+ "\20\0\1\57\10\0\5\57\7\0\11\57\4\0\1\57"+ "\1\0\23\57\1\352\3\57\1\353\1\57\20\0\1\57"+ "\10\0\5\57\7\0\11\57\4\0\1\57\1\0\17\57"+ "\1\354\11\57\20\0\1\57\10\0\5\57\7\0\11\57"+ "\4\0\1\57\1\0\10\57\1\355\20\57\20\0\1\57"+ "\10\0\5\57\7\0\11\57\4\0\1\57\1\0\13\57"+ "\1\356\15\57\20\0\1\57\10\0\5\57\7\0\11\57"+ "\4\0\1\57\1\0\20\57\1\357\10\57\20\0\1\57"+ "\10\0\5\57\7\0\11\57\4\0\1\57\1\0\4\57"+ "\1\360\3\57\1\361\20\57\20\0\1\57\10\0\5\57"+ "\7\0\11\57\4\0\1\57\1\0\22\57\1\362\6\57"+ "\5\0\1\241\115\0\1\241\2\0\1\242\134\0\5\363"+ "\110\0\3\364\112\0\5\365\7\0\3\365\14\0\6\365"+ "\43\0\1\366\173\0\1\367\1\0\1\370\104\0\1\371"+ "\54\0\5\372\110\0\3\373\112\0\5\374\7\0\3\374"+ "\14\0\6\374\43\0\1\263\10\0\5\263\7\0\11\263"+ "\4\0\1\263\1\0\31\263\31\0\2\266\15\0\6\135"+ "\4\0\1\266\7\0\23\135\31\0\5\270\110\0\5\270"+ "\12\0\6\265\4\0\1\270\7\0\23\265\31\0\4\271"+ "\13\0\6\135\4\0\1\271\7\0\23\135\27\0\1\375"+ "\1\0\5\272\7\0\3\272\3\135\1\376\2\135\4\0"+ "\1\272\1\0\6\272\11\135\1\376\11\135\20\0\1\41"+ "\10\0\5\41\7\0\11\41\4\0\1\41\1\0\4\41"+ "\1\377\24\41\20\0\1\41\10\0\5\41\7\0\11\41"+ "\4\0\1\41\1\0\4\41\1\u0100\24\41\20\0\1\57"+ "\10\0\5\57\7\0\11\57\4\0\1\57\1\0\4\57"+ "\1\u0101\24\57\20\0\1\57\10\0\5\57\7\0\11\57"+ "\4\0\1\57\1\0\10\57\1\u0102\20\57\20\0\1\57"+ "\10\0\5\57\7\0\11\57\4\0\1\57\1\0\21\57"+ "\1\u0103\7\57\20\0\1\57\10\0\5\57\7\0\11\57"+ "\4\0\1\57\1\0\21\57\1\u0104\7\57\20\0\1\57"+ "\10\0\5\57\7\0\11\57\4\0\1\57\1\0\4\57"+ "\1\u0105\24\57\20\0\1\57\10\0\5\57\7\0\11\57"+ "\4\0\1\57\1\0\15\57\1\u0106\13\57\20\0\1\57"+ "\10\0\5\57\7\0\11\57\4\0\1\57\1\0\4\57"+ "\1\u0107\24\57\20\0\1\57\10\0\5\57\7\0\11\57"+ "\4\0\1\57\1\0\4\57\1\u0108\24\57\20\0\1\57"+ "\10\0\5\57\7\0\11\57\4\0\1\57\1\0\4\57"+ "\1\u0109\24\57\20\0\1\57\10\0\5\57\7\0\11\57"+ "\4\0\1\57\1\0\21\57\1\352\7\57\20\0\1\57"+ "\10\0\5\57\7\0\11\57\4\0\1\57\1\0\2\57"+ "\1\u010a\26\57\20\0\1\57\10\0\5\57\7\0\11\57"+ "\4\0\1\57\1\0\13\57\1\u010b\15\57\20\0\1\57"+ "\10\0\5\57\7\0\11\57\4\0\1\57\1\0\4\57"+ "\1\u010c\24\57\20\0\1\57\10\0\5\57\7\0\11\57"+ "\4\0\1\57\1\0\22\57\1\u010d\6\57\20\0\1\57"+ "\10\0\5\57\7\0\11\57\4\0\1\57\1\0\3\57"+ "\1\u010e\25\57\20\0\1\57\10\0\5\57\7\0\11\57"+ "\4\0\1\57\1\0\27\57\1\u010f\1\57\20\0\1\57"+ "\10\0\5\57\7\0\11\57\4\0\1\57\1\0\20\57"+ "\1\u0110\10\57\20\0\1\57\10\0\5\57\7\0\11\57"+ "\4\0\1\57\1\0\2\57\1\u0111\26\57\20\0\1\57"+ "\10\0\5\57\7\0\11\57\4\0\1\57\1\0\7\57"+ "\1\u0112\21\57\20\0\1\57\10\0\5\57\7\0\11\57"+ "\4\0\1\57\1\0\23\57\1\u0113\5\57\20\0\1\57"+ "\10\0\5\57\7\0\11\57\4\0\1\57\1\0\1\u0114"+ "\30\57\20\0\1\57\10\0\5\57\7\0\11\57\4\0"+ "\1\57\1\0\20\57\1\u0115\10\57\20\0\1\57\10\0"+ "\5\57\7\0\11\57\4\0\1\57\1\0\4\57\1\u0116"+ "\24\57\20\0\1\57\10\0\5\57\7\0\11\57\4\0"+ "\1\57\1\0\15\57\1\u0117\13\57\20\0\1\57\10\0"+ "\5\57\7\0\11\57\4\0\1\57\1\0\10\57\1\u0118"+ "\20\57\20\0\1\57\10\0\5\57\7\0\11\57\4\0"+ "\1\57\1\0\24\57\1\u0119\4\57\20\0\1\57\10\0"+ "\5\57\7\0\11\57\4\0\1\57\1\0\21\57\1\u011a"+ "\7\57\20\0\1\57\10\0\5\57\7\0\11\57\4\0"+ "\1\57\1\0\23\57\1\u011b\5\57\20\0\1\57\10\0"+ "\5\57\7\0\11\57\4\0\1\57\1\0\15\57\1\u011c"+ "\13\57\20\0\1\57\10\0\5\57\7\0\11\57\4\0"+ "\1\57\1\0\4\57\1\u011d\24\57\20\0\1\57\10\0"+ "\5\57\7\0\11\57\4\0\1\57\1\0\4\57\1\u011e"+ "\24\57\20\0\1\57\10\0\5\57\7\0\11\57\4\0"+ "\1\57\1\0\22\57\1\u011f\6\57\20\0\1\57\10\0"+ "\5\57\7\0\11\57\4\0\1\57\1\0\22\57\1\u0120"+ "\6\57\20\0\1\57\10\0\5\57\7\0\11\57\4\0"+ "\1\57\1\0\15\57\1\u0121\13\57\20\0\1\57\10\0"+ "\5\57\7\0\11\57\4\0\1\57\1\0\13\57\1\u0122"+ "\15\57\20\0\1\57\10\0\5\57\7\0\11\57\4\0"+ "\1\57\1\0\7\57\1\u0123\21\57\31\0\5\100\110\0"+ "\4\u0124\111\0\5\100\7\0\3\100\14\0\6\100\117\0"+ "\1\u0125\10\0\1\u0126\76\0\1\u0127\56\0\5\116\110\0"+ "\4\u0128\111\0\5\116\7\0\3\116\14\0\6\116\54\0"+ "\5\375\7\0\3\375\3\265\1\u0129\2\265\4\0\1\375"+ "\1\0\6\375\11\265\1\u0129\11\265\20\0\1\57\10\0"+ "\5\57\7\0\11\57\4\0\1\57\1\0\20\57\1\u012a"+ "\10\57\20\0\1\57\10\0\5\57\7\0\11\57\4\0"+ "\1\57\1\0\15\57\1\u012b\13\57\20\0\1\57\10\0"+ "\5\57\7\0\11\57\4\0\1\57\1\0\21\57\1\u012c"+ "\7\57\20\0\1\57\10\0\5\57\7\0\11\57\4\0"+ "\1\57\1\0\22\57\1\u012d\6\57\20\0\1\57\10\0"+ "\5\57\7\0\11\57\4\0\1\57\1\0\22\57\1\u012e"+ "\6\57\20\0\1\57\10\0\5\57\7\0\11\57\4\0"+ "\1\57\1\0\17\57\1\u012f\11\57\20\0\1\57\10\0"+ "\5\57\7\0\11\57\4\0\1\57\1\0\20\57\1\u0130"+ "\10\57\20\0\1\57\10\0\5\57\7\0\11\57\4\0"+ "\1\57\1\0\22\57\1\u0131\6\57\20\0\1\57\10\0"+ "\5\57\7\0\11\57\4\0\1\57\1\0\23\57\1\u0132"+ "\5\57\20\0\1\57\10\0\5\57\7\0\11\57\4\0"+ "\1\57\1\0\20\57\1\u0133\10\57\20\0\1\57\10\0"+ "\5\57\7\0\11\57\4\0\1\57\1\0\10\57\1\u0134"+ "\20\57\20\0\1\57\10\0\5\57\7\0\11\57\4\0"+ "\1\57\1\0\7\57\1\u0135\21\57\20\0\1\57\10\0"+ "\5\57\7\0\11\57\4\0\1\57\1\0\16\57\1\u0136"+ "\12\57\20\0\1\57\10\0\5\57\7\0\11\57\4\0"+ "\1\57\1\0\13\57\1\u0137\15\57\20\0\1\57\10\0"+ "\5\57\7\0\11\57\4\0\1\57\1\0\1\57\1\u0138"+ "\27\57\20\0\1\57\10\0\5\57\7\0\11\57\4\0"+ "\1\57\1\0\4\57\1\u0139\24\57\20\0\1\57\10\0"+ "\5\57\7\0\11\57\4\0\1\57\1\0\2\57\1\u013a"+ "\26\57\20\0\1\57\10\0\5\57\7\0\11\57\4\0"+ "\1\57\1\0\16\57\1\u013b\12\57\20\0\1\57\10\0"+ "\5\57\7\0\11\57\4\0\1\57\1\0\1\u013c\30\57"+ "\20\0\1\57\10\0\5\57\7\0\11\57\4\0\1\57"+ "\1\0\4\57\1\u013d\24\57\20\0\1\57\10\0\5\57"+ "\7\0\11\57\4\0\1\57\1\0\2\57\1\u013e\26\57"+ "\20\0\1\57\10\0\5\57\7\0\11\57\4\0\1\57"+ "\1\0\23\57\1\u013f\5\57\20\0\1\57\10\0\5\57"+ "\7\0\11\57\4\0\1\57\1\0\4\57\1\u0140\24\57"+ "\31\0\4\100\151\0\1\u0141\113\0\1\u0142\120\0\1\u0143"+ "\51\0\4\116\100\0\1\57\10\0\5\57\7\0\11\57"+ "\4\0\1\57\1\0\22\57\1\u0144\6\57\20\0\1\57"+ "\10\0\5\57\7\0\11\57\4\0\1\57\1\0\20\57"+ "\1\u0145\10\57\20\0\1\57\10\0\5\57\7\0\11\57"+ "\4\0\1\57\1\0\16\57\1\u0146\12\57\20\0\1\57"+ "\10\0\5\57\7\0\11\57\4\0\1\57\1\0\22\57"+ "\1\u0147\6\57\20\0\1\57\10\0\5\57\7\0\11\57"+ "\4\0\1\57\1\0\15\57\1\u0148\13\57\20\0\1\57"+ "\10\0\5\57\7\0\11\57\4\0\1\57\1\0\10\57"+ "\1\u0149\5\57\1\u014a\12\57\20\0\1\57\10\0\5\57"+ "\7\0\11\57\4\0\1\57\1\0\3\57\1\u014b\25\57"+ "\20\0\1\57\10\0\5\57\7\0\11\57\4\0\1\57"+ "\1\0\10\57\1\u014c\20\57\20\0\1\57\10\0\5\57"+ "\7\0\11\57\4\0\1\57\1\0\1\u014d\30\57\20\0"+ "\1\57\10\0\5\57\7\0\11\57\4\0\1\57\1\0"+ "\3\57\1\u014e\25\57\20\0\1\57\10\0\5\57\7\0"+ "\11\57\4\0\1\57\1\0\4\57\1\u014f\24\57\20\0"+ "\1\57\10\0\5\57\7\0\11\57\4\0\1\57\1\0"+ "\13\57\1\u0150\15\57\20\0\1\57\10\0\5\57\7\0"+ "\11\57\4\0\1\57\1\0\2\57\1\u0151\26\57\20\0"+ "\1\57\10\0\5\57\7\0\11\57\4\0\1\57\1\0"+ "\22\57\1\u0152\6\57\20\0\1\57\10\0\5\57\7\0"+ "\11\57\4\0\1\57\1\0\15\57\1\u0153\13\57\20\0"+ "\1\57\10\0\5\57\7\0\11\57\4\0\1\57\1\0"+ "\22\57\1\u0154\6\57\20\0\1\57\10\0\5\57\7\0"+ "\11\57\4\0\1\57\1\0\22\57\1\u0155\6\57\20\0"+ "\1\57\10\0\5\57\7\0\11\57\4\0\1\57\1\0"+ "\1\u0156\30\57\71\0\1\u0157\43\0\1\57\10\0\5\57"+ "\7\0\11\57\4\0\1\57\1\0\1\u0158\30\57\20\0"+ "\1\57\10\0\5\57\7\0\11\57\4\0\1\57\1\0"+ "\10\57\1\u0159\20\57\20\0\1\57\10\0\5\57\7\0"+ "\11\57\4\0\1\57\1\0\1\u015a\30\57\20\0\1\57"+ "\10\0\5\57\7\0\11\57\4\0\1\57\1\0\16\57"+ "\1\u015b\12\57\20\0\1\57\10\0\5\57\7\0\11\57"+ "\4\0\1\57\1\0\20\57\1\u015c\10\57\20\0\1\57"+ "\10\0\5\57\7\0\11\57\4\0\1\57\1\0\4\57"+ "\1\u015d\24\57\20\0\1\57\10\0\5\57\7\0\11\57"+ "\4\0\1\57\1\0\22\57\1\u015e\6\57\20\0\1\57"+ "\10\0\5\57\7\0\11\57\4\0\1\57\1\0\13\57"+ "\1\u015f\15\57\20\0\1\57\10\0\5\57\7\0\11\57"+ "\4\0\1\57\1\0\4\57\1\u0160\24\57\20\0\1\57"+ "\10\0\5\57\7\0\11\57\4\0\1\57\1\0\4\57"+ "\1\u0161\24\57\20\0\1\57\10\0\5\57\7\0\11\57"+ "\4\0\1\57\1\0\13\57\1\u0162\15\57\20\0\1\57"+ "\10\0\5\57\7\0\11\57\4\0\1\57\1\0\10\57"+ "\1\u0163\20\57\20\0\1\57\10\0\5\57\7\0\11\57"+ "\4\0\1\57\1\0\16\57\1\u0164\12\57\20\0\1\57"+ "\10\0\5\57\7\0\11\57\4\0\1\57\1\0\13\57"+ "\1\u0165\15\57\20\0\1\57\10\0\5\57\7\0\11\57"+ "\4\0\1\57\1\0\15\57\1\u0166\13\57\20\0\1\57"+ "\10\0\5\57\7\0\11\57\4\0\1\57\1\0\10\57"+ "\1\u0167\20\57\20\0\1\57\10\0\5\57\7\0\11\57"+ "\4\0\1\57\1\0\15\57\1\u0168\13\57\20\0\1\57"+ "\10\0\5\57\7\0\11\57\4\0\1\57\1\0\15\57"+ "\1\u0169\13\57\20\0\1\57\10\0\5\57\7\0\11\57"+ "\4\0\1\57\1\0\30\57\1\u016a\20\0\1\57\10\0"+ "\5\57\7\0\11\57\4\0\1\57\1\0\22\57\1\u016b"+ "\6\57\20\0\1\57\10\0\5\57\7\0\11\57\4\0"+ "\1\57\1\0\4\57\1\u016c\24\57\20\0\1\57\10\0"+ "\5\57\7\0\11\57\4\0\1\57\1\0\20\57\1\u016d"+ "\10\57\4\0"; private static int [] zzUnpacktrans() { int [] result = new int[18865]; 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 = "\5\0\2\11\2\1\1\11\1\1\2\11\3\1\1\11"+ "\2\1\1\11\12\1\1\11\5\1\2\11\25\1\1\11"+ "\1\1\3\11\1\1\1\11\1\1\1\11\3\1\1\11"+ "\3\1\2\11\2\0\1\1\6\11\1\1\2\11\1\1"+ "\2\0\1\11\2\1\10\11\1\1\2\11\3\1\1\11"+ "\55\1\4\11\1\0\1\1\1\0\3\1\1\0\2\11"+ "\3\0\2\11\1\1\3\0\2\1\2\11\1\1\1\0"+ "\3\1\2\11\66\1\3\0\1\11\2\0\1\11\3\0"+ "\47\1\3\0\1\1\1\0\30\1\2\11\1\0\23\1"+ "\1\11\26\1"; private static int [] zzUnpackAttribute() { int [] result = new int[365]; 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: */ public OCamlLexer() { this.types = OclTypes.INSTANCE; } private ORLangTypes types; private int tokenStartIndex; private CharSequence quotedStringId; private int commentDepth; private boolean inCommentString = false; // Store the start index of a token private void tokenStart() { tokenStartIndex = zzStartRead; } // Set the start index of the token to the stored index private void tokenEnd() { zzStartRead = tokenStartIndex; } /** * Creates a new scanner * * @param in the java.io.Reader to read input from. */ public OCamlLexer(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; switch (zzLexicalState) { case IN_STRING: { yybegin(INITIAL); tokenEnd(); return types.STRING_VALUE; } // fall though case 366: break; case IN_OCAML_ML_COMMENT: { yybegin(INITIAL); tokenEnd(); return types.MULTI_COMMENT; } // fall though case 367: break; case IN_DIRECTIVE: { yybegin(INITIAL); tokenEnd(); return types.SHARP; } // fall though case 368: break; default: return null; } } else { switch (zzAction < 0 ? zzAction : ZZ_ACTION[zzAction]) { case 1: { yybegin(INITIAL); yypushback(1); } // fall through case 151: break; case 2: { return BAD_CHARACTER; } // fall through case 152: break; case 3: { return WHITE_SPACE; } // fall through case 153: break; case 4: { return types.EXCLAMATION_MARK; } // fall through case 154: break; case 5: { yybegin(IN_STRING); tokenStart(); } // fall through case 155: break; case 6: { if (zzCurrentPos > 0) { char prevChar = yycharat(-1); if (prevChar == '\n' || prevChar == '\r' || prevChar == ' ' || prevChar == '\t') { yybegin(IN_DIRECTIVE); yypushback(1); } else { return types.SHARP; } } else { yybegin(IN_DIRECTIVE); yypushback(1); } } // fall through case 156: break; case 7: { return types.DOLLAR; } // fall through case 157: break; case 8: { return types.PERCENT; } // fall through case 158: break; case 9: { return types.AMPERSAND; } // fall through case 159: break; case 10: { return types.SINGLE_QUOTE; } // fall through case 160: break; case 11: { return types.LPAREN; } // fall through case 161: break; case 12: { return types.RPAREN; } // fall through case 162: break; case 13: { return types.STAR; } // fall through case 163: break; case 14: { return types.PLUS; } // fall through case 164: break; case 15: { return types.COMMA; } // fall through case 165: break; case 16: { return types.MINUS; } // fall through case 166: break; case 17: { return types.DOT; } // fall through case 167: break; case 18: { return types.SLASH; } // fall through case 168: break; case 19: { return types.INT_VALUE; } // fall through case 169: break; case 20: { return types.COLON; } // fall through case 170: break; case 21: { return types.SEMI; } // fall through case 171: break; case 22: { return types.LT; } // fall through case 172: break; case 23: { return types.EQ; } // fall through case 173: break; case 24: { return types.GT; } // fall through case 174: break; case 25: { return types.QUESTION_MARK; } // fall through case 175: break; case 26: { return types.ARROBASE; } // fall through case 176: break; case 27: { return types.UIDENT; } // fall through case 177: break; case 28: { return types.LBRACKET; } // fall through case 178: break; case 29: { return types.RBRACKET; } // fall through case 179: break; case 30: { return types.CARRET; } // fall through case 180: break; case 31: { return types.UNDERSCORE; } // fall through case 181: break; case 32: { return types.BACKTICK; } // fall through case 182: break; case 33: { return types.LIDENT; } // fall through case 183: break; case 34: { return types.LBRACE; } // fall through case 184: break; case 35: { return types.PIPE; } // fall through case 185: break; case 36: { return types.RBRACE; } // fall through case 186: break; case 37: { return types.TILDE; } // fall through case 187: break; case 38: { } // fall through case 188: break; case 39: { yybegin(INITIAL); tokenEnd(); return types.STRING_VALUE; } // fall through case 189: break; case 40: { inCommentString = !inCommentString; } // fall through case 190: break; case 41: { yybegin(INITIAL); tokenEnd(); return types.SHARP; } // fall through case 191: break; case 42: { return types.NOT_EQ; } // fall through case 192: break; case 43: { return types.SHARPSHARP; } // fall through case 193: break; case 44: { return types.L_AND; } // fall through case 194: break; case 45: { return types.TYPE_ARGUMENT; } // fall through case 195: break; case 46: { yybegin(IN_OCAML_ML_COMMENT); inCommentString = false; commentDepth = 1; tokenStart(); } // fall through case 196: break; case 47: { return types.STARDOT; } // fall through case 197: break; case 48: { return types.STRING_CONCAT; } // fall through case 198: break; case 49: { return types.PLUSDOT; } // fall through case 199: break; case 50: { return types.MINUSDOT; } // fall through case 200: break; case 51: { return types.RIGHT_ARROW; } // fall through case 201: break; case 52: { return types.DOTDOT; } // fall through case 202: break; case 53: { return types.SLASHDOT; } // fall through case 203: break; case 54: { return types.TAG_AUTO_CLOSE; } // fall through case 204: break; case 55: { return types.FLOAT_VALUE; } // fall through case 205: break; case 56: { return types.SHORTCUT; } // fall through case 206: break; case 57: { return types.COLON_EQ; } // fall through case 207: break; case 58: { return types.COLON_GT; } // fall through case 208: break; case 59: { return types.SEMISEMI; } // fall through case 209: break; case 60: { return types.LEFT_ARROW; } // fall through case 210: break; case 61: { return types.TAG_LT_SLASH; } // fall through case 211: break; case 62: { return types.LT_OR_EQUAL; } // fall through case 212: break; case 63: { return types.OP_STRUCT_DIFF; } // fall through case 213: break; case 64: { return types.EQEQ; } // fall through case 214: break; case 65: { return types.ARROW; } // fall through case 215: break; case 66: { return types.GT_OR_EQUAL; } // fall through case 216: break; case 67: { return types.ARROBASE_2; } // fall through case 217: break; case 68: { return types.LARRAY; } // fall through case 218: break; case 69: { return types.POLY_VARIANT; } // fall through case 219: break; case 70: { return types.AS; } // fall through case 220: break; case 71: { return types.DO; } // fall through case 221: break; case 72: { return types.IF; } // fall through case 222: break; case 73: { return types.IN; } // fall through case 223: break; case 74: { return types.OF; } // fall through case 224: break; case 75: { return types.OR; } // fall through case 225: break; case 76: { return types.TO; } // fall through case 226: break; case 77: { return types.PIPE_FIRST; } // fall through case 227: break; case 78: { return types.PIPE_FORWARD; } // fall through case 228: break; case 79: { return types.RARRAY; } // fall through case 229: break; case 80: { return types.L_OR; } // fall through case 230: break; case 81: { if (!inCommentString) { commentDepth += 1; } } // fall through case 231: break; case 82: { if (!inCommentString) { commentDepth -= 1; if(commentDepth == 0) { yybegin(INITIAL); tokenEnd(); return types.MULTI_COMMENT; } } } // fall through case 232: break; case 83: { return types.NOT_EQEQ; } // fall through case 233: break; case 84: { return types.CHAR_VALUE; } // fall through case 234: break; case 85: { return types.DOTDOTDOT; } // fall through case 235: break; case 86: { return types.EQEQEQ; } // fall through case 236: break; case 87: { return types.ARROBASE_3; } // fall through case 237: break; case 88: { return types.AND; } // fall through case 238: break; case 89: { return types.ASR; } // fall through case 239: break; case 90: { return types.END; } // fall through case 240: break; case 91: { return types.FOR; } // fall through case 241: break; case 92: { return types.FUN; } // fall through case 242: break; case 93: { return types.LET; } // fall through case 243: break; case 94: { return types.LOR; } // fall through case 244: break; case 95: { return types.LSL; } // fall through case 245: break; case 96: { return types.LSR; } // fall through case 246: break; case 97: { return types.MOD; } // fall through case 247: break; case 98: { return types.NEW; } // fall through case 248: break; case 99: { return types.PRI; } // fall through case 249: break; case 100: { return types.PUB; } // fall through case 250: break; case 101: { return types.RAW; } // fall through case 251: break; case 102: { return types.REC; } // fall through case 252: break; case 103: { return types.REF; } // fall through case 253: break; case 104: { return types.SIG; } // fall through case 254: break; case 105: { return types.TRY; } // fall through case 255: break; case 106: { return types.VAL; } // fall through case 256: break; case 107: { /* a char */ } // fall through case 257: break; case 108: { yybegin(INITIAL); tokenEnd(); return types.DIRECTIVE_IF; } // fall through case 258: break; case 109: { return types.NONE; } // fall through case 259: break; case 110: { return types.SOME; } // fall through case 260: break; case 111: { return types.DONE; } // fall through case 261: break; case 112: { return types.ELSE; } // fall through case 262: break; case 113: { return types.LAND; } // fall through case 263: break; case 114: { return types.LAZY; } // fall through case 264: break; case 115: { return types.LXOR; } // fall through case 265: break; case 116: { return types.OPEN; } // fall through case 266: break; case 117: { return types.THEN; } // fall through case 267: break; case 118: { return types.BOOL_VALUE; } // fall through case 268: break; case 119: { return types.TYPE; } // fall through case 269: break; case 120: { return types.UNIT; } // fall through case 270: break; case 121: { return types.WHEN; } // fall through case 271: break; case 122: { return types.WITH; } // fall through case 272: break; case 123: { yybegin(INITIAL); tokenEnd(); return types.DIRECTIVE_END; } // fall through case 273: break; case 124: { return types.BEGIN; } // fall through case 274: break; case 125: { return types.CLASS; } // fall through case 275: break; case 126: { return types.MATCH; } // fall through case 276: break; case 127: { return types.RAISE; } // fall through case 277: break; case 128: { return types.WHILE; } // fall through case 278: break; case 129: { yybegin(INITIAL); tokenEnd(); return types.DIRECTIVE_ELIF; } // fall through case 279: break; case 130: { yybegin(INITIAL); tokenEnd(); return types.DIRECTIVE_ELSE; } // fall through case 280: break; case 131: { return types.ASSERT; } // fall through case 281: break; case 132: { return types.DOWNTO; } // fall through case 282: break; case 133: { return types.METHOD; } // fall through case 283: break; case 134: { return types.MODULE; } // fall through case 284: break; case 135: { return types.NONREC; } // fall through case 285: break; case 136: { return types.OBJECT; } // fall through case 286: break; case 137: { return types.OPTION; } // fall through case 287: break; case 138: { return types.STRUCT; } // fall through case 288: break; case 139: { yybegin(INITIAL); tokenEnd(); return types.DIRECTIVE_ENDIF; } // fall through case 289: break; case 140: { return types.FUNCTOR; } // fall through case 290: break; case 141: { return types.INCLUDE; } // fall through case 291: break; case 142: { return types.INHERIT; } // fall through case 292: break; case 143: { return types.MUTABLE; } // fall through case 293: break; case 144: { return types.PRIVATE; } // fall through case 294: break; case 145: { return types.VIRTUAL; } // fall through case 295: break; case 146: { return types.EXTERNAL; } // fall through case 296: break; case 147: { return types.FUNCTION; } // fall through case 297: break; case 148: { return types.EXCEPTION; } // fall through case 298: break; case 149: { return types.CONSTRAINT; } // fall through case 299: break; case 150: { return types.INITIALIZER; } // fall through case 300: break; default: zzScanError(ZZ_NO_MATCH); } } } } } ================================================ FILE: src/main/java/com/reason/lang/ocaml/OclASTFactory.java ================================================ package com.reason.lang.ocaml; import com.reason.lang.core.psi.impl.*; public class OclASTFactory extends ORASTFactory { public OclASTFactory() { super(OclTypes.INSTANCE); } } ================================================ FILE: src/main/java/com/reason/lang/ocaml/OclLanguage.java ================================================ package com.reason.lang.ocaml; import com.intellij.lang.Language; import com.reason.lang.*; import org.jetbrains.annotations.*; public class OclLanguage extends Language implements ORLanguageProperties { public static final OclLanguage INSTANCE = new OclLanguage(); private OclLanguage() { super("OCaml"); } @Override public @NotNull String getParameterSeparator() { return " -> "; } @Override public @NotNull String getFunctionSeparator() { return " -> "; } @Override public @NotNull String getTemplateStart() { return ""; } @Override public @NotNull String getTemplateEnd() { return ""; } } ================================================ FILE: src/main/java/com/reason/lang/ocaml/OclLexer.java ================================================ package com.reason.lang.ocaml; import com.intellij.lexer.*; public class OclLexer extends FlexAdapter { public OclLexer() { super(new OCamlLexer()); } } ================================================ FILE: src/main/java/com/reason/lang/ocaml/OclParser.java ================================================ package com.reason.lang.ocaml; import com.intellij.lang.*; import com.intellij.psi.tree.*; import com.reason.lang.*; import com.reason.lang.core.type.*; import org.jetbrains.annotations.*; import static com.intellij.codeInsight.completion.CompletionUtilCore.*; public class OclParser extends CommonPsiParser { public OclParser(boolean isSafe) { super(isSafe); } @Override protected ORParser getORParser(@NotNull PsiBuilder builder) { return new OclParserState(builder, myIsSafe); } static class OclParserState extends ORLanguageParser { protected OclParserState(@NotNull PsiBuilder builder, boolean isSafe) { super(OclTypes.INSTANCE, builder, isSafe); } @Override public void parse() { IElementType tokenType; long parseStart = System.currentTimeMillis(); int parseCount = 0; while (!myBuilder.eof()) { parseCount++; if (parseCount > 100) { parseCount = 0; long parseTime = System.currentTimeMillis(); if (PARSE_MAX_TIME < parseTime - parseStart) { if (myIsSafe) { // Don't do that in tests error("Parsing cancelled, you should create a github issue with the source code"); break; } } } tokenType = myBuilder.getTokenType(); if (tokenType == myTypes.SEMI) { parseSemi(); } else if (tokenType == myTypes.IN) { parseIn(); } else if (tokenType == myTypes.RIGHT_ARROW) { parseRightArrow(); } else if (tokenType == myTypes.PIPE) { parsePipe(); } else if (tokenType == myTypes.EQ) { parseEq(); } else if (tokenType == myTypes.OF) { parseOf(); } else if (tokenType == myTypes.STAR) { parseStar(); } else if (tokenType == myTypes.COLON) { parseColon(); } else if (tokenType == myTypes.QUESTION_MARK) { parseQuestionMark(); } else if (tokenType == myTypes.INT_VALUE) { parseNumber(); } else if (tokenType == myTypes.FLOAT_VALUE) { parseNumber(); } else if (tokenType == myTypes.STRING_VALUE) { parseStringValue(); } else if (tokenType == myTypes.STRING_CONCAT) { parseStringConcat(); } else if (tokenType == myTypes.TILDE) { parseTilde(); } else if (tokenType == myTypes.LIDENT) { parseLIdent(); } else if (tokenType == myTypes.UIDENT) { parseUIdent(); } else if (tokenType == myTypes.SIG) { parseSig(); } else if (tokenType == myTypes.OBJECT) { parseObject(); } else if (tokenType == myTypes.INITIALIZER) { parseInitializer(); } else if (tokenType == myTypes.IF) { parseIf(); } else if (tokenType == myTypes.THEN) { parseThen(); } else if (tokenType == myTypes.ELSE) { parseElse(); } else if (tokenType == myTypes.MATCH) { parseMatch(); } else if (tokenType == myTypes.TRY) { parseTry(); } else if (tokenType == myTypes.WITH) { parseWith(); } else if (tokenType == myTypes.AND) { parseAnd(); } else if (tokenType == myTypes.DOT) { parseDot(); } else if (tokenType == myTypes.DOTDOT) { parseDotDot(); } else if (tokenType == myTypes.SHARP) { parseSharp(); } else if (tokenType == myTypes.FUNCTION) { // function is a shortcut for a pattern match parseFunction(); } else if (tokenType == myTypes.FUN) { parseFun(); } else if (tokenType == myTypes.UNDERSCORE) { parseUnderscore(); } else if (tokenType == myTypes.ASSERT) { parseAssert(); } else if (tokenType == myTypes.RAISE) { parseRaise(); } else if (tokenType == myTypes.COMMA) { parseComma(); } else if (tokenType == myTypes.ARROBASE) { parseArrobase(); } else if (tokenType == myTypes.ARROBASE_2) { parseArrobase2(); } else if (tokenType == myTypes.ARROBASE_3) { parseArrobase3(); } else if (tokenType == myTypes.OPTION) { parseOption(); } else if (tokenType == myTypes.FUNCTOR) { parseFunctor(); } else if (tokenType == myTypes.TYPE_ARGUMENT) { parseTypeArgument(); } else if (tokenType == myTypes.POLY_VARIANT) { parsePolyVariant(); } // while ... do ... done else if (tokenType == myTypes.WHILE) { parseWhile(); } // for ... to ... do ... done else if (tokenType == myTypes.FOR) { parseFor(); } // do ... done else if (tokenType == myTypes.DO) { parseDo(); } else if (tokenType == myTypes.DONE) { parseDone(); } // begin/struct ... end else if (tokenType == myTypes.BEGIN) { parseBegin(); } else if (tokenType == myTypes.STRUCT) { parseStruct(); } else if (tokenType == myTypes.END) { parseEnd(); } // ( ... ) else if (tokenType == myTypes.LPAREN) { parseLParen(); } else if (tokenType == myTypes.RPAREN) { parseRParen(); } // { ... } else if (tokenType == myTypes.LBRACE) { parseLBrace(); } else if (tokenType == myTypes.RBRACE) { parseRBrace(); } // [ ... ] else if (tokenType == myTypes.LBRACKET) { parseLBracket(); } else if (tokenType == myTypes.RBRACKET) { parseRBracket(); } // [| ... |] else if (tokenType == myTypes.LARRAY) { parseLArray(); } else if (tokenType == myTypes.RARRAY) { parseRArray(); } // < ... > else if (tokenType == myTypes.LT) { parseLt(); } else if (tokenType == myTypes.GT) { parseGt(); } // Starts expression else if (tokenType == myTypes.OPEN) { parseOpen(); } else if (tokenType == myTypes.INCLUDE) { parseInclude(); } else if (tokenType == myTypes.EXTERNAL) { parseExternal(); } else if (tokenType == myTypes.TYPE) { parseType(); } else if (tokenType == myTypes.MODULE) { parseModule(); } else if (tokenType == myTypes.CLASS) { parseClass(); } else if (tokenType == myTypes.INHERIT) { parseInherit(); } else if (tokenType == myTypes.LET) { parseLet(); } else if (tokenType == myTypes.VAL) { parseVal(); } else if (tokenType == myTypes.REF) { parseRef(); } else if (tokenType == myTypes.METHOD) { parseMethod(); } else if (tokenType == myTypes.EXCEPTION) { parseException(); } else if (tokenType == myTypes.DIRECTIVE_IF) { parseDirectiveIf(); } else if (tokenType == myTypes.DIRECTIVE_ELSE) { parseDirectiveElse(); } else if (tokenType == myTypes.DIRECTIVE_ELIF) { parseDirectiveElif(); } else if (tokenType == myTypes.DIRECTIVE_END || tokenType == myTypes.DIRECTIVE_ENDIF) { parseDirectiveEnd(); } if (dontMove) { dontMove = false; } else { myBuilder.advanceLexer(); } } } private void parsePolyVariant() { if (is(myTypes.C_SCOPED_EXPR) && isRawParent(myTypes.C_TYPE_BINDING)) { wrapWith(myTypes.C_VARIANT_DECLARATION); } } private void parseTypeArgument() { if (!isCurrent(myTypes.C_PARAM_DECLARATION) && in(myTypes.C_PARAMETERS)) { wrapWith(myTypes.C_PARAM_DECLARATION); } } private void parseTilde() { if (inAny(myTypes.C_PARAMETERS, myTypes.C_PARAM_DECLARATION)) { if (isFound(myTypes.C_PARAM_DECLARATION)) { popEndUntilFoundIndex() .popEnd() .mark(myTypes.C_PARAM_DECLARATION) .markHolder(myTypes.H_NAMED_PARAM_DECLARATION); } else if (isPrevious(myTypes.C_FUNCTION_CALL, getIndex())) { popEndUntilFoundIndex() .mark(myTypes.C_NAMED_PARAM); } else { mark(myTypes.C_PARAM_DECLARATION) .markHolder(myTypes.H_NAMED_PARAM_DECLARATION); } } else if (strictlyIn(myTypes.C_DEFAULT_VALUE)) { popEndUntil(myTypes.C_PARAMETERS); boolean isCall = in(myTypes.C_FUNCTION_CALL); if (isCall) { mark(myTypes.C_NAMED_PARAM); } else { mark(myTypes.C_PARAM_DECLARATION) .markHolder(myTypes.H_NAMED_PARAM_DECLARATION); } } } private void parseStringConcat() { if (inScopeOrAny(myTypes.C_FUNCTION_CALL) && !isFoundScope(myTypes.LPAREN)) { popEndUntilFoundIndex().popEnd(); } } private void parseOption() { if (strictlyInAny(myTypes.C_TYPE_BINDING, myTypes.C_SIG_ITEM)) { // in type : type t = xxx |>option<| // or signature : ... -> xxx |>option<| ... int pos = getIndex(); if (pos > 0) { markBefore(pos - 1, myTypes.C_OPTION) .advance().popEndUntilIndex(pos).popEnd(); } else if (pos == 0) { duplicateAtIndex(pos). updateLatestComposite(myTypes.C_OPTION).advance().popEnd(); } } else if (isHold()) { Marker latestMarker = getLatestMarker(); ORCompositeType holdType = latestMarker == null ? myTypes.H_PLACE_HOLDER : latestMarker.getCompositeType(); updateLatestComposite(myTypes.C_OPTION); markHolderBefore(0, holdType).advance().popEnd(); } } private void parseFunctor() { if (strictlyIn(myTypes.C_MODULE_SIGNATURE)) { if (strictlyIn(myTypes.C_MODULE_DECLARATION)) { updateCompositeAt(getIndex(), myTypes.C_FUNCTOR_DECLARATION); } } } private void parseRaise() { if (is(myTypes.C_EXTERNAL_DECLARATION)) { // external |>raise<| ... remapCurrentToken(myTypes.LIDENT).wrapAtom(myTypes.CA_LOWER_SYMBOL); } } private void parseComma() { if (in(myTypes.C_TUPLE)) { // a tuple popEndUntilFoundIndex(); } else if (inAny( // all same priority myTypes.C_LET_DECLARATION, myTypes.C_SIG_ITEM, myTypes.C_MATCH_EXPR, myTypes.C_PARAM_DECLARATION, myTypes.C_SCOPED_EXPR )) { if (isFound(myTypes.C_LET_DECLARATION)) { if (in(myTypes.C_DECONSTRUCTION)) { popEndUntilFoundIndex(); } else if (isDone(myTypes.C_DECONSTRUCTION)) { // nested deconstructions // let (a, b) |>,<| ... = ... markBefore(0, myTypes.C_DECONSTRUCTION); } } //else if (isFound(myTypes.C_PARAM_DECLARATION)) { // popEndUntilFoundIndex().popEnd().advance(); // mark(myTypes.C_PARAM_DECLARATION); //} else if (isFound(myTypes.C_SCOPED_EXPR)) { Marker blockScope = find(getIndex()); Marker parentScope = find(getIndex() + 1); if (blockScope != null && parentScope != null) { if (parentScope.isCompositeType(myTypes.C_LET_DECLARATION)) { // let (x |>,<| ... ) // We need to do it again because lower symbols must be wrapped with identifiers rollbackToIndexAndDrop(getIndex()); mark(myTypes.C_DECONSTRUCTION); if (getTokenType() == myTypes.LPAREN) { updateScopeToken(myTypes.LPAREN).advance(); } } else if (parentScope.isCompositeType(myTypes.C_PARAM_DECLARATION)) { // a tuple :: let fn (x |>,<| ... ) ... blockScope.updateCompositeType(myTypes.C_TUPLE); popEndUntil(myTypes.C_TUPLE); } } } } } private void parseArrobase() { if (is(myTypes.C_ANNOTATION)) { mark(myTypes.C_MACRO_NAME); } } private void parseArrobase2() { if (is(myTypes.C_ANNOTATION)) { mark(myTypes.C_MACRO_NAME); } } private void parseArrobase3() { if (is(myTypes.C_ANNOTATION)) { mark(myTypes.C_MACRO_NAME); } } private void parseLt() { if (isCurrent(myTypes.C_SIG_ITEM) || in(myTypes.C_TYPE_BINDING) || isCurrent(myTypes.C_OBJECT_FIELD)) { // |> < <| .. > .. markScope(myTypes.C_OBJECT, myTypes.LT).advance() .mark(myTypes.C_OBJECT_FIELD); } } private void parseGt() { if (strictlyInAny(myTypes.C_OBJECT, myTypes.C_BINARY_CONDITION)) { if (isFound(myTypes.C_OBJECT)) { popEndUntil(myTypes.C_OBJECT); advance().end(); popEnd(); } } } private void parseWhile() { mark(myTypes.C_WHILE).advance() .mark(myTypes.C_BINARY_CONDITION); } private void parseFor() { mark(myTypes.C_FOR_LOOP); } private void parseDo() { if (in(myTypes.C_BINARY_CONDITION)) { popEndUntil(myTypes.C_BINARY_CONDITION).popEnd(); } if (strictlyInAny(myTypes.C_WHILE, myTypes.C_FOR_LOOP)) { popEndUntilFoundIndex() .markScope(myTypes.C_SCOPED_EXPR, myTypes.DO); } else { markScope(myTypes.C_DO_LOOP, myTypes.DO); } } private void parseDone() { Marker scope = popEndUntilScopeToken(myTypes.DO); if (scope != null) { advance().popEnd(); } } private void parseRightArrow() { if (is(myTypes.C_SIG_EXPR)) { advance(); markParenthesisScope(true).mark(myTypes.C_SIG_ITEM); } else if (isCurrent(myTypes.C_MODULE_SIGNATURE) && isRawGrandParent(myTypes.C_FUNCTOR_DECLARATION)) { // signature of a functor in an interface file // module M : function (...) |>-><| ... advance(); markParenthesisScope(true).mark(myTypes.C_FUNCTOR_RESULT); } // same priority else if (strictlyInAny( myTypes.C_PATTERN_MATCH_EXPR, myTypes.C_FUNCTION_EXPR, myTypes.C_TRY_HANDLER, myTypes.C_SIG_ITEM )) { if (isFound(myTypes.C_PATTERN_MATCH_EXPR)) { // | ... |>-><| popEndUntil(myTypes.C_PATTERN_MATCH_EXPR).advance() .mark(myTypes.C_PATTERN_MATCH_BODY); } else if (isFound(myTypes.C_FUNCTION_EXPR)) { // fun ... |>-><| ... popEndUntil(myTypes.C_FUNCTION_EXPR).advance() .mark(myTypes.C_FUNCTION_BODY); } else if (isFound(myTypes.C_TRY_HANDLER)) { // try .. with .. |>-><| popEndUntilFoundIndex().advance() .mark(myTypes.C_TRY_HANDLER_BODY); } else if (isFound(myTypes.C_SIG_ITEM)) { popEndUntilFoundIndex().popEnd(); if (in(myTypes.H_NAMED_PARAM_DECLARATION) && !isFoundScope(myTypes.LPAREN)) { // can't have an arrow in a named param signature // let fn x:int |>-><| y:int popEnd().popEndUntil(myTypes.C_SIG_EXPR); } advance(); markParenthesisScope(true).mark(myTypes.C_SIG_ITEM); } } } private void parseAssert() { mark(myTypes.C_ASSERT_STMT); } private void parseAnd() { if (in(myTypes.C_TYPE_CONSTRAINT)) { popEndUntil(myTypes.C_TYPE_CONSTRAINT).popEnd() .advance().mark(myTypes.C_TYPE_CONSTRAINT); } else if (inAny(myTypes.C_MODULE_DECLARATION, myTypes.C_LET_DECLARATION, myTypes.C_TYPE_DECLARATION)) { // pop scopes until a chainable expression is found popEndUntilIndex(getIndex()); Marker marker = getLatestMarker(); popEnd().advance(); if (marker != null) { mark(marker.getCompositeType()); } } } private void parseDot() { if (in(myTypes.C_TYPE_VARIABLE)) { popEndUntil(myTypes.C_TYPE_VARIABLE).popEnd().advance() .mark(myTypes.C_SIG_EXPR) .mark(myTypes.C_SIG_ITEM); } } private void parseDotDot() { if (is(myTypes.C_OBJECT_FIELD)) { advance().popEnd(); } } private void parseSharp() { if (previousElementType(1) == myTypes.LIDENT) { markBefore(0, myTypes.C_METHOD_CALL); } } private void parsePipe() { if (is(myTypes.C_SCOPED_EXPR) && isRawParent(myTypes.C_LET_DECLARATION)) { // let ( |>|<| ... return; } if (inAny(myTypes.C_TYPE_BINDING, myTypes.C_VARIANT_DECLARATION, myTypes.C_FUNCTION_EXPR, myTypes.C_MATCH_EXPR, myTypes.C_PATTERN_MATCH_EXPR, myTypes.C_PATTERN_MATCH_BODY, myTypes.C_TRY_HANDLERS, myTypes.C_TRY_HANDLER, myTypes.C_SCOPED_EXPR)) { if (isFound(myTypes.C_TYPE_BINDING)) { // remap an upper symbol to a variant if first element is missing pipe // type t = (|) V1 |>|<| ... popEndUntil(myTypes.C_TYPE_BINDING).advance() .mark(myTypes.C_VARIANT_DECLARATION); } else if (isFound(myTypes.C_VARIANT_DECLARATION)) { // type t = | X |>|<| Y ... popEndUntilFoundIndex().popEnd().advance() .mark(myTypes.C_VARIANT_DECLARATION).inAny(); } else if (isFound(myTypes.C_FUNCTION_EXPR)) { if (previousElementType(getIndex() + 1) == myTypes.FUN) { // fun |>|<| ... advance().mark(myTypes.C_PATTERN_MATCH_EXPR); } } else if (isFound(myTypes.C_MATCH_EXPR)) { advance().mark(myTypes.C_PATTERN_MATCH_EXPR); } else if (isFound(myTypes.C_PATTERN_MATCH_EXPR)) { // pattern group // | X |>|<| Y ... popEndUntil(myTypes.C_PATTERN_MATCH_EXPR).popEnd(); advance().mark(myTypes.C_PATTERN_MATCH_EXPR); } else if (isFound(myTypes.C_PATTERN_MATCH_BODY)) { popEndUntil(myTypes.C_PATTERN_MATCH_EXPR).popEnd().advance() .mark(myTypes.C_PATTERN_MATCH_EXPR); } else if (isFound(myTypes.C_TRY_HANDLERS)) { mark(myTypes.C_TRY_HANDLER); } else if (isFound(myTypes.C_TRY_HANDLER)) { popEndUntil(myTypes.C_TRY_HANDLERS).mark(myTypes.C_TRY_HANDLER); } } } private void parseMatch() { mark(myTypes.C_MATCH_EXPR).advance() .mark(myTypes.C_BINARY_CONDITION); } private void parseTry() { mark(myTypes.C_TRY_EXPR).advance() .mark(myTypes.C_TRY_BODY); } private void parseWith() { if (in(myTypes.C_FUNCTOR_RESULT)) { // A functor with constraints // module Make (M : Input) : S |>with<| ... popEndUntil(myTypes.C_FUNCTOR_RESULT).popEnd().advance() .mark(myTypes.C_CONSTRAINTS) .mark(myTypes.C_TYPE_CONSTRAINT); } else if (in(myTypes.C_MODULE_SIGNATURE)) { // A module with a signature and constraints // module G : sig ... end |>with<| ... // module G : X |>with<| ... popEndUntil(myTypes.C_MODULE_SIGNATURE).popEnd().advance() .mark(myTypes.C_CONSTRAINTS) .mark(myTypes.C_TYPE_CONSTRAINT); } else if (in(myTypes.C_INCLUDE)) { // include with constraints // include M |>with<| ... mark(myTypes.C_CONSTRAINTS).advance() .mark(myTypes.C_TYPE_CONSTRAINT); } else if (isRawParent(myTypes.C_MIXIN_FIELD)) { // ... = { mixinField |>with<| ... popEndUntil(myTypes.C_RECORD_EXPR).advance(); if (getTokenType() != myTypes.RBRACE) { mark(myTypes.C_RECORD_FIELD); } } else if (inAny(myTypes.C_TRY_BODY, myTypes.C_BINARY_CONDITION)) { if (isFound(myTypes.C_TRY_BODY)) { // try ... |>with<| ... popEndUntil(myTypes.C_TRY_EXPR).advance() .mark(myTypes.C_TRY_HANDLERS); if (getTokenType() != myTypes.PIPE) { mark(myTypes.C_TRY_HANDLER); } } else if (isFound(myTypes.C_BINARY_CONDITION)) { if (isPrevious(myTypes.C_MATCH_EXPR, getIndex())) { // match ... |>with<| ... popEndUntil(myTypes.C_MATCH_EXPR).advance(); if (getTokenType() != myTypes.PIPE) { // a match/with can skip pipe for first pattern !!! // match ... with |>_?_<| mark(myTypes.C_PATTERN_MATCH_EXPR); } } } } } private void parseIf() { // |>if<| ... mark(myTypes.C_IF).advance() .mark(myTypes.C_BINARY_CONDITION); } private void parseThen() { if (!in(myTypes.C_DIRECTIVE)) { // if ... |>then<| ... popEndUntil(myTypes.C_IF).advance() .mark(myTypes.C_IF_THEN_ELSE); } } private void parseElse() { // if ... then ... |>else<| ... popEndUntil(myTypes.C_IF).advance() .mark(myTypes.C_IF_THEN_ELSE); } private void parseStruct() { if (is(myTypes.C_FUNCTOR_DECLARATION)) { // module X (...) = |>struct<| ... markScope(myTypes.C_FUNCTOR_BINDING, myTypes.STRUCT); } else if (is(myTypes.C_MODULE_BINDING)) { // module X = |>struct<| ... updateScopeToken(myTypes.STRUCT); } else { markScope(myTypes.C_STRUCT_EXPR, myTypes.STRUCT); } } private void parseSig() { if (is(myTypes.C_MODULE_BINDING)) { // This is the body of a module type // module type X = |>sig<| ... updateScopeToken(myTypes.SIG); } else if (is(myTypes.C_MODULE_SIGNATURE)) { // module X : |>sig<| ... markDummyScope(myTypes.C_SCOPED_EXPR, myTypes.SIG); } else { markScope(myTypes.C_SIG_EXPR, myTypes.SIG); } } private void parseSemi() { if (inScopeOrAny( myTypes.C_FUNCTION_BODY, myTypes.C_LET_BINDING, myTypes.C_DIRECTIVE, myTypes.C_OBJECT, myTypes.C_RECORD_FIELD, myTypes.C_OBJECT_FIELD, myTypes.C_TRY_BODY, myTypes.C_TRY_HANDLER, myTypes.C_PATTERN_MATCH_BODY )) { if (isFound(myTypes.C_FUNCTION_BODY) || isFound(myTypes.C_LET_BINDING) || isFound(myTypes.C_DIRECTIVE) || isFound(myTypes.C_TRY_BODY) || isFound(myTypes.C_TRY_HANDLER) || isFound(myTypes.C_PATTERN_MATCH_BODY) || isFound(myTypes.C_SCOPED_EXPR)) { // A SEMI operator ends the previous expression popEndUntilFoundIndex(); } else if (isRawParent(myTypes.C_OBJECT)) { // SEMI ends the field, and starts a new one popEnd().advance().mark(myTypes.C_OBJECT_FIELD); } else if (strictlyIn(myTypes.C_RECORD_FIELD)) { // SEMI ends the field, and starts a new one popEndUntil(myTypes.C_RECORD_FIELD).popEnd().advance(); if (getTokenType() != myTypes.RBRACE) { mark(myTypes.C_RECORD_FIELD); } } else if (strictlyIn(myTypes.C_OBJECT_FIELD)) { // SEMI ends the field, and starts a new one popEndUntil(myTypes.C_OBJECT_FIELD).popEnd().advance(); if (getTokenType() != myTypes.RBRACE) { mark(myTypes.C_OBJECT_FIELD); } } else if (rawHasScope()) { popEndUntilScope(); } } } private void parseIn() { if (in(myTypes.C_TRY_HANDLER)) { if (strictlyInAny(myTypes.C_LET_DECLARATION)) { popEndUntil(myTypes.C_TRY_HANDLER); } else { popEndUntil(myTypes.C_TRY_EXPR); } } else if (strictlyInAny(myTypes.C_LET_DECLARATION, myTypes.C_MODULE_DECLARATION, myTypes.C_OPEN/*local open*/)) { boolean isStart = isFound(myTypes.C_LET_DECLARATION) || isFound(myTypes.C_MODULE_DECLARATION) || isFound(myTypes.C_OPEN); popEndUntilFoundIndex(); if (isStart) { popEnd(); } } else { popEnd(); } } private void parseObject() { markScope(myTypes.C_OBJECT, myTypes.OBJECT); } private void parseInitializer() { popEndUntil(myTypes.C_OBJECT) .markScope(myTypes.C_CLASS_INITIALIZER, myTypes.INITIALIZER); } private void parseBegin() { markScope(myTypes.C_SCOPED_EXPR, myTypes.BEGIN); } private void parseEnd() { Marker scope = popEndUntilOneOfElementType(myTypes.BEGIN, myTypes.SIG, myTypes.STRUCT, myTypes.OBJECT); advance().popEnd(); if (scope != null) { if (is(myTypes.C_MODULE_DECLARATION)) { // module M = struct .. |>end<| popEnd(); IElementType nextToken = getTokenType(); if (nextToken == myTypes.AND) { // module M = struct .. end |>and<| advance().mark(myTypes.C_MODULE_DECLARATION); } } else if (is(myTypes.C_MODULE_SIGNATURE) && getTokenType() != myTypes.WITH) { // module M : sig .. |>end<| popEnd(); } } } private void parseColon() { if (is(myTypes.C_FUNCTOR_DECLARATION)) { // module M (...) |> :<| ... advance(); markParenthesisScope(true).mark(myTypes.C_FUNCTOR_RESULT); } else if (isRawParent(myTypes.H_NAMED_PARAM_DECLARATION)) { advance(); if (getTokenType() == myTypes.LPAREN) { // ?x : |>(<| ... Marker namedScope = getPrevious(); updateScopeToken(namedScope, myTypes.LPAREN).advance(); } if (strictlyIn(myTypes.C_SIG_ITEM)) { // A named param in signature // let x : c|> :<| .. mark(myTypes.C_SIG_EXPR) .mark(myTypes.C_SIG_ITEM); } } else if (isRawParent(myTypes.C_NAMED_PARAM)) { advance().mark(myTypes.C_DEFAULT_VALUE); } else if (inAny( myTypes.C_EXTERNAL_DECLARATION, myTypes.C_CLASS_METHOD, myTypes.C_VAL_DECLARATION, myTypes.C_LET_DECLARATION, myTypes.C_TERNARY, myTypes.C_PARAM_DECLARATION, myTypes.C_FIRST_CLASS )) { if (isFound(myTypes.C_TERNARY)) { // x ? y |> :<| ... popEndUntilFoundIndex() .advance().mark(myTypes.C_IF_THEN_ELSE).markHolder(myTypes.H_PLACE_HOLDER); } else if (isFound(myTypes.C_FIRST_CLASS)) { advance().mark(myTypes.C_MODULE_SIGNATURE); } else { advance(); if (getTokenType() == myTypes.LPAREN && lookAhead(1) == myTypes.MODULE) { // first class signature markScope(myTypes.C_MODULE_SIGNATURE, myTypes.LPAREN).advance().advance(); } else if (getTokenType() == myTypes.TYPE) { // Local type mark(myTypes.C_TYPE_VARIABLE); } else { // external x |> : <| ... OR val x |> : <| ... OR let x |> : <| ... parseSignatureExpression(); } } } else if (in(myTypes.C_MODULE_DECLARATION)) { // module M |> : <| ... advance(); markParenthesisScope(true) .mark(myTypes.C_MODULE_SIGNATURE); } else if (in(myTypes.C_RECORD_FIELD)) { advance(); parseSignatureExpression(); } } private void parseSignatureExpression() { mark(myTypes.C_SIG_EXPR); markParenthesisScope(true). mark(myTypes.C_SIG_ITEM).markHolder(myTypes.H_PLACE_HOLDER); } private void parseQuestionMark() { if (is(myTypes.C_PARAMETERS) && isRawParent(myTypes.C_FUNCTION_EXPR)) { // First param // let f |>?<| ( x ... mark(myTypes.C_PARAM_DECLARATION) .markHolder(myTypes.H_NAMED_PARAM_DECLARATION); } else if (strictlyIn(myTypes.C_PARAM_DECLARATION)) { // let f ~x |>~<| y popEndUntilFoundIndex() .popEnd() .mark(myTypes.C_PARAM_DECLARATION) .markHolder(myTypes.H_NAMED_PARAM_DECLARATION); } else if (!strictlyInAny(myTypes.C_TERNARY)) { if (inScopeOrAny(myTypes.C_LET_BINDING)) { // a new ternary int foundPos = getIndex(); int nextPos = foundPos - 1; if (isAtIndex(nextPos, myTypes.H_PLACE_HOLDER)) { markBefore(nextPos, myTypes.C_TERNARY) .updateCompositeAt(nextPos, myTypes.C_BINARY_CONDITION) .popEndUntilIndex(nextPos).end() .advance().mark(myTypes.C_IF_THEN_ELSE); markHolder(myTypes.H_PLACE_HOLDER); } } } } private void parseFunction() { if (inAny(myTypes.C_LET_BINDING, myTypes.C_FUNCTION_EXPR)) { if (isFound(myTypes.C_LET_BINDING)) { mark(myTypes.C_FUNCTION_EXPR).advance() .mark(myTypes.C_FUNCTION_BODY); } mark(myTypes.C_MATCH_EXPR).advance(); if (getTokenType() != myTypes.PIPE) { mark(myTypes.C_PATTERN_MATCH_EXPR); } } } private void parseFun() { mark(myTypes.C_FUNCTION_EXPR).advance(); IElementType tokenType = getTokenType(); if (tokenType != myTypes.PIPE) { mark(myTypes.C_PARAMETERS); } } private void parseUnderscore() { if (is(myTypes.C_PARAMETERS) && isRawParent(myTypes.C_FUNCTION_EXPR)) { mark(myTypes.C_PARAM_DECLARATION); } } @SuppressWarnings("StatementWithEmptyBody") private void parseEq() { if (in(myTypes.H_NAMED_PARAM_DECLARATION) && isFoundScope(myTypes.LPAREN)) { // let fn ?(x |> = <| ... advance().mark(myTypes.C_DEFAULT_VALUE); } else if (in(myTypes.C_RECORD_FIELD)) { // { x |> = <| ... } advance(); mark(myTypes.C_FIELD_VALUE) .markHolder(myTypes.H_PLACE_HOLDER); } else if (in(myTypes.C_FOR_LOOP)) { // for x |> = <| ... // nope } else if (strictlyIn(myTypes.C_BINARY_CONDITION)) { // nope } else if (in(myTypes.C_TYPE_DECLARATION, /*not*/myTypes.C_TYPE_BINDING)) { // type t |> =<| ... popEndUntil(myTypes.C_TYPE_DECLARATION).advance() .mark(myTypes.C_TYPE_BINDING); } else if (strictlyIn(myTypes.C_EXTERNAL_DECLARATION)) { // external e : sig |> = <| ... popEndUntil(myTypes.C_SIG_EXPR).popEnd().advance(); } else if (strictlyInAny(myTypes.C_LET_DECLARATION, myTypes.C_MODULE_DECLARATION)) { // if inside a let binding, do nothing if (isFound(myTypes.C_LET_DECLARATION)) { int letPos = getIndex(); if (isCurrent(myTypes.C_LET_BINDING) && is(myTypes.H_PLACE_HOLDER)) { // inside a let binding, it might be a binary condition updateLatestComposite(myTypes.C_BINARY_CONDITION); markHolderBefore(0, myTypes.H_PLACE_HOLDER); } else if (in(myTypes.C_LET_BINDING, null, letPos, false)) { int letBinding = getIndex(); if (in(myTypes.C_FUNCTION_EXPR, null, letBinding, false)) { // in a function :: let (x) y z |> = <| ... popEndUntil(myTypes.C_FUNCTION_EXPR).advance() .mark(myTypes.C_FUNCTION_BODY); } else { // inside a let binding, but not a function expression. it might be a binary condition markBefore(letBinding - 1, myTypes.C_BINARY_CONDITION). popEndUntil(myTypes.C_BINARY_CONDITION); } } else { // let x |> = <| ... popEndUntilIndex(letPos).advance(). mark(myTypes.C_LET_BINDING). markHolder(myTypes.H_PLACE_HOLDER); } } else if (isFound(myTypes.C_MODULE_DECLARATION)) { // module M |> = <| ... popEndUntil(myTypes.C_MODULE_DECLARATION).advance() .mark(myTypes.C_MODULE_BINDING); } } else if (in(myTypes.C_FUNCTOR_RESULT)) { popEndUntil(myTypes.C_FUNCTOR_RESULT).popEnd(); } else if (in(myTypes.C_CONSTRAINTS)) { popEndUntil(myTypes.C_CONSTRAINTS).popEnd(); } } private void parseOf() { if (isRawParent(myTypes.C_VARIANT_DECLARATION)) { // Variant params :: type t = | Variant |>of<| .. advance().mark(myTypes.C_VARIANT_CONSTRUCTOR).mark(myTypes.C_PARAM_DECLARATION); } } private void parseStar() { if (strictlyIn(myTypes.C_TUPLE)) { popEndUntilFoundIndex(); } else if (inScopeOrAny(myTypes.C_PARAM_DECLARATION, myTypes.C_VARIANT_CONSTRUCTOR, myTypes.C_SIG_ITEM)) { if (isFoundScope(myTypes.LPAREN)) { // ( ... |>*<| updateCompositeAt(getIndex(), myTypes.C_TUPLE); } else if (isFound(myTypes.C_PARAM_DECLARATION) || isFound(myTypes.C_VARIANT_CONSTRUCTOR)) { // type t = | Variant of x |>*<| y .. popEndUntil(myTypes.C_PARAM_DECLARATION).popEnd().advance() .mark(myTypes.C_PARAM_DECLARATION); } } } private void parseLParen() { if (is(myTypes.C_EXTERNAL_DECLARATION)) { // Overloading an operator // external |>(<| ... markScope(myTypes.C_SCOPED_EXPR, myTypes.LPAREN); } else if (is(myTypes.C_TYPE_DECLARATION)) { markScope(myTypes.C_PARAMETERS, myTypes.LPAREN); } else if (isRawParent(myTypes.C_MODULE_DECLARATION) && previousElementType(1) == myTypes.A_MODULE_NAME) { // module M |>(<| ... ) updateCompositeAt(1, myTypes.C_FUNCTOR_DECLARATION).popEnd() .mark(myTypes.C_PARAMETERS) .markScope(myTypes.C_PARAM_DECLARATION, myTypes.LPAREN).advance(); } else if (is(myTypes.C_MODULE_BINDING)) { if (lookAhead(1) == myTypes.VAL) { // module M = »(« val ... ) updateComposite(myTypes.C_UNPACK) .updateScopeToken(myTypes.LPAREN).advance() .advance(); // skip 'val' in a first class module decoding } else { markScope(myTypes.C_SCOPED_EXPR, myTypes.LPAREN).advance(); } } else if (previousElementType(2) == myTypes.A_MODULE_NAME && previousElementType(1) == myTypes.DOT) { // Detecting a local open // M1.M2. |>(<| ... ) popEnd(). markScope(myTypes.C_LOCAL_OPEN, myTypes.LPAREN); } else if (is(myTypes.C_PARAMETERS) && isRawParent(myTypes.C_FUNCTION_EXPR)) { // Start of the first parameter // let x |>(<| ... markScope(myTypes.C_PARAM_DECLARATION, myTypes.LPAREN); } else if (is(myTypes.C_PARAMETERS) && isRawParent(myTypes.C_FUNCTION_CALL)) { // Start of the first parameter // fn |>(<| ... markScope(myTypes.C_PARAM, myTypes.LPAREN); } else if (is(myTypes.H_NAMED_PARAM_DECLARATION)) { // A named param with default value // let fn ?|>(<| x ... ) updateScopeToken(myTypes.LPAREN); } else if (is(myTypes.CA_UPPER_SYMBOL) && isRawParent(myTypes.C_PATTERN_MATCH_EXPR)) { // match x with | V |>(<| : a variant constructor markScope(myTypes.C_PARAM, myTypes.LPAREN); } else if (inAny(// myTypes.C_PARAM_DECLARATION, myTypes.C_PARAMETERS, myTypes.C_PARAM, myTypes.C_SIG_ITEM, myTypes.C_FUNCTION_BODY, myTypes.C_LET_BINDING, myTypes.C_CLASS_DECLARATION, myTypes.C_DEFAULT_VALUE, myTypes.C_TUPLE, myTypes.C_FIELD_VALUE, myTypes.H_NAMED_PARAM_DECLARATION )) { boolean isDeclaration = isFound(myTypes.C_PARAM_DECLARATION); boolean isParam = isFound(myTypes.C_PARAM); boolean isFoundScopeLParen = isFoundScope(myTypes.LPAREN); int foundIndex = getIndex(); IElementType aheadType = lookAhead(1); if (isFound(myTypes.C_LET_BINDING) && aheadType == myTypes.MODULE) { // let x = »(« module ... ) popIfHold() .updateComposite(myTypes.C_FIRST_CLASS).updateScopeToken(myTypes.LPAREN).advance() .advance(); // skip 'module' in a first class module } else if (aheadType == myTypes.MATCH) { // fn (|>(<|match markScope(myTypes.C_SCOPED_EXPR, myTypes.LPAREN); } else if (isFound(myTypes.C_FIELD_VALUE)) { // Tuple in field value (destructuring) markScope(myTypes.C_TUPLE, myTypes.LPAREN); } else if (isFound(myTypes.C_PARAMETERS) || isFound(myTypes.C_DEFAULT_VALUE)) { markScope(myTypes.C_SCOPED_EXPR, myTypes.LPAREN); } else if (isFound(myTypes.H_NAMED_PARAM_DECLARATION)) { popEnd(); markParenthesisScope(false); } else if (isFound(myTypes.C_CLASS_DECLARATION)) { // class x |>(<| ... markScope(myTypes.C_CLASS_CONSTR, myTypes.LPAREN); } else if (isDeclaration && isRawParent(myTypes.C_VARIANT_CONSTRUCTOR)) { // Tuple in variant declaration markScope(myTypes.C_TUPLE, myTypes.LPAREN); } else { if (!isFoundScopeLParen && (isDeclaration || isParam)) { // Start of a new parameter popEndUntilIndex(foundIndex).popEnd() .mark(isDeclaration ? myTypes.C_PARAM_DECLARATION : myTypes.C_PARAM); if (aheadType == myTypes.LIDENT) { // let f x |>(<| fn ... markParenthesisScope(true) .mark(myTypes.C_FUNCTION_CALL) .wrapAtom(myTypes.CA_LOWER_SYMBOL).popEnd() .mark(myTypes.C_PARAMETERS); } else { // let f x |>(<| ...tuple? markScope(myTypes.C_SCOPED_EXPR, myTypes.LPAREN); } } else if (is(myTypes.C_SIG_ITEM) && !rawHasScope()) { updateScopeToken(myTypes.LPAREN); } else { markScope(myTypes.C_SCOPED_EXPR, myTypes.LPAREN); } } } else { inAny(myTypes.C_OPEN, myTypes.C_INCLUDE); int openPos = getIndex(); if (openPos >= 0) { // a functor call inside open/include :: open/include M |>(<| ... markBefore(0, myTypes.C_FUNCTOR_CALL) .markScope(myTypes.C_PARAMETERS, myTypes.LPAREN).advance() .mark(myTypes.C_PARAM); } else { markScope(myTypes.C_SCOPED_EXPR, myTypes.LPAREN); } } } private void parseRParen() { // special case when an option is used in a signature item IElementType nextElementType = lookAhead(1); if (nextElementType == myTypes.OPTION) { // normally, a signature item is defined inside the (..), but when using an option, it must be the opposite if (strictlyIn(myTypes.C_SIG_ITEM) && isScopeAtIndex(getIndex() + 2, myTypes.LPAREN)) { // need a rollback to swap elements : lparen -> sigItem to sigItem -> lparen rollbackToIndex(getIndex() + 2); markBefore(0, myTypes.C_OPTION); markBefore(1, myTypes.C_SIG_ITEM); markHolder(myTypes.H_COLLECTION_ITEM); return; } } Marker lParen = popEndUntilScopeToken(myTypes.LPAREN); if (lParen == null) { return; } advance(); int scopeLength = lParen.getLength(); if (isRawParent(myTypes.C_LET_DECLARATION)) { if (scopeLength <= 3) { // unit :: let () lParen.updateCompositeType(myTypes.C_UNIT); } } if (is(myTypes.C_DECONSTRUCTION)) { end(); } else { popEnd(); if (lParen.isCompositeType(myTypes.H_NAMED_PARAM_DECLARATION) && nextElementType != myTypes.EQ) { popEnd(); if (is(myTypes.C_PARAM_DECLARATION)) { popEnd(); } } else if (is(myTypes.C_PARAMETERS) && isParent(myTypes.C_FUNCTOR_DECLARATION)) { // module M (...|>)<| if (nextElementType == myTypes.LPAREN) { markScope(myTypes.C_PARAM_DECLARATION, myTypes.LPAREN).advance(); } else { popEnd(); } } else if (lParen.isCompositeType(myTypes.C_SCOPED_EXPR) && is(myTypes.C_LET_DECLARATION) && nextElementType != myTypes.EQ) { // This is a custom infix operator mark(myTypes.C_PARAMETERS); } else if (is(myTypes.C_OPTION)) { advance().popEnd(); } else if (isCurrent(myTypes.C_FUNCTOR_CALL)) { popEndUntil(myTypes.C_FUNCTOR_CALL).popEnd(); } else if (nextElementType == myTypes.RIGHT_ARROW && lParen.isCompositeType(myTypes.C_SIG_ITEM)) { advance().mark(myTypes.C_SIG_ITEM); } else if (is(myTypes.C_PARAM_DECLARATION) && isRawParent(myTypes.C_VARIANT_CONSTRUCTOR) && !lParen.isCompositeType(myTypes.C_TUPLE)) { popEnd(); } else if (is(myTypes.C_PARAM) && !currentHasScope()) { popEnd(); } else if (nextElementType == myTypes.AND) { // close intermediate elements if (in(myTypes.C_LET_DECLARATION)) { popEndUntilFoundIndex(); while (in(myTypes.C_LET_BINDING)) { popEnd(); popEndUntil(myTypes.C_LET_DECLARATION); } } else { popEndUntilScope(); } } else if (strictlyIn(myTypes.C_FUNCTION_CALL) && nextElementType == myTypes.EQ) { // a boolean op ? let _ = ignore () = () popEndUntil(myTypes.C_FUNCTION_CALL).popEnd(); } else if (lParen.isCompositeType(myTypes.C_FIRST_CLASS)) { popEnd(); } } } private void parseLBrace() { if (is(myTypes.C_PARAMETERS) && isRawParent(myTypes.C_FUNCTION_EXPR)) { // let fn |>{<| ... } = ... mark(myTypes.C_PARAM_DECLARATION); } if (is(myTypes.C_LET_DECLARATION)) { // let |>{<| ... , markScope(myTypes.C_DECONSTRUCTION, myTypes.LBRACE); } else { // ... = |>{<| ... markScope(myTypes.C_RECORD_EXPR, myTypes.LBRACE).advance(); if (lookAhead(1) == myTypes.WITH) { // a mixin mark(myTypes.C_MIXIN_FIELD); } else { mark(myTypes.C_RECORD_FIELD); } } } private void parseRBrace() { Marker scope = popEndUntilScopeToken(myTypes.LBRACE); advance(); if (scope != null) { popEnd(); } } private void parseLBracket() { IElementType nextType = rawLookup(1); if (nextType == myTypes.ARROBASE || nextType == myTypes.ARROBASE_2 || nextType == myTypes.ARROBASE_3) { // https://ocaml.org/manual/attributes.html // |> [ <| @?? ... if (nextType == myTypes.ARROBASE) { markScope(myTypes.C_ANNOTATION, myTypes.LBRACKET); } else if (nextType == myTypes.ARROBASE_2) { // attribute attached to a 'block' expression if (inAny(myTypes.C_LET_BINDING, myTypes.C_SIG_EXPR)) { if (isFound(myTypes.C_SIG_EXPR)) { // block attribute inside a signature popEnd(); } popEndUntilIndex(getIndex()); } markScope(myTypes.C_ANNOTATION, myTypes.LBRACKET); } else { // floating attribute endLikeSemi(); markScope(myTypes.C_ANNOTATION, myTypes.LBRACKET); } } else if (nextType == myTypes.GT) { // |> [ <| > ... ] markScope(myTypes.C_OPEN_VARIANT, myTypes.LBRACKET).advance().advance(); IElementType tokenType = getTokenType(); if (tokenType == myTypes.POLY_VARIANT || tokenType == myTypes.A_VARIANT_NAME) { mark(myTypes.C_VARIANT_DECLARATION); } } else if (nextType == myTypes.LT) { // |> [ <| < ... ] markScope(myTypes.C_CLOSED_VARIANT, myTypes.LBRACKET).advance().advance(); IElementType tokenType = getTokenType(); if (tokenType == myTypes.POLY_VARIANT || tokenType == myTypes.A_VARIANT_NAME || tokenType == myTypes.UIDENT) { mark(myTypes.C_VARIANT_DECLARATION); if (tokenType != myTypes.UIDENT) { advance(); } } } else { markScope(myTypes.C_SCOPED_EXPR, myTypes.LBRACKET); } } private void parseRBracket() { popEndUntilScopeToken(myTypes.LBRACKET); advance().popEnd(); } private void parseLArray() { if (strictlyIn(myTypes.C_PARAMETERS)) { // ... ( xxx |>yyy<| ) .. popEndUntil(myTypes.C_PARAMETERS); boolean isCall = strictlyIn(myTypes.C_FUNCTION_CALL); mark(isCall ? myTypes.C_PARAM : myTypes.C_PARAM_DECLARATION); } markScope(myTypes.C_ARRAY, myTypes.LARRAY); } private void parseRArray() { Marker scope = popEndUntilScopeToken(myTypes.LARRAY); advance(); if (scope != null) { popEnd(); if (isCurrent(myTypes.C_PARAM) && !isCurrentScope(myTypes.LPAREN)) { popEnd(); } } } private void parseNumber() { if (is(myTypes.C_PARAM_DECLARATION)) { // Start of a new parameter // ... fn x |>1<| .. popEnd().mark(myTypes.C_PARAM_DECLARATION).advance().popEnd(); } else if (is(myTypes.C_PARAM) && !currentHasScope()) { // Start of new parameter reference // ... fn x |>1<| .. popEnd().mark(myTypes.C_PARAM).advance().popEnd(); } else if (is(myTypes.C_PARAMETERS) && strictlyIn(myTypes.C_FUNCTION_CALL)) { mark(myTypes.C_PARAM); } } private void parseStringValue() { if (is(myTypes.C_PARAM) && !currentHasScope()) { popEnd().mark(myTypes.C_PARAM).advance().popEnd(); } else if (is(myTypes.C_PARAMETERS)) { boolean isCall = strictlyIn(myTypes.C_FUNCTION_CALL); mark(isCall ? myTypes.C_PARAM : myTypes.C_PARAM_DECLARATION).advance().popEnd(); } else if (is(myTypes.C_PARAM_DECLARATION)) { popEnd().mark(myTypes.C_PARAM_DECLARATION).advance().popEnd(); } } private void parseLIdent() { if (is(myTypes.C_LET_DECLARATION)) { // let |>x<| ... IElementType nextToken = lookAhead(1); if (nextToken == myTypes.COMMA) { // A deconstruction without parenthesis mark(myTypes.C_DECONSTRUCTION); wrapWith(myTypes.C_LOWER_NAME); } else { wrapAtom(myTypes.CA_LOWER_SYMBOL); } if (nextToken != myTypes.COMMA && nextToken != myTypes.EQ && nextToken != myTypes.COLON) { // This is a function, we need to create the let binding now, to be in sync with reason // let |>x<| y z = ... vs let x = y z => ... mark(myTypes.C_LET_BINDING). mark(myTypes.C_FUNCTION_EXPR). mark(myTypes.C_PARAMETERS); } } else if (is(myTypes.C_EXTERNAL_DECLARATION)) { // external |>x<| ... wrapAtom(myTypes.CA_LOWER_SYMBOL); } else if (is(myTypes.C_TYPE_DECLARATION)) { // type |>x<| ... wrapAtom(myTypes.CA_LOWER_SYMBOL); } else if (is(myTypes.C_CLASS_DECLARATION)) { // class |>x<| ... wrapAtom(myTypes.CA_LOWER_SYMBOL); } else if (is(myTypes.C_CLASS_METHOD)) { // ... object method |>x<| ... wrapAtom(myTypes.CA_LOWER_SYMBOL); } else if (is(myTypes.C_VAL_DECLARATION)) { // val |>x<| ... wrapAtom(myTypes.CA_LOWER_SYMBOL); } else if (is(myTypes.C_RECORD_FIELD)) { // { |>x<| : ... } wrapAtom(myTypes.CA_LOWER_SYMBOL); } else if (is(myTypes.C_MACRO_NAME)) { // [@ |>x.y<| ... ] advance(); while (getTokenType() == myTypes.DOT) { advance(); if (getTokenType() == myTypes.LIDENT) { advance(); } } popEnd(); } else if (is(myTypes.C_DECONSTRUCTION)) { wrapWith(myTypes.C_LOWER_NAME); } else if (is(myTypes.C_PARAMETERS) && !rawHasScope()) { // ... ( xxx |>yyy<| ) .. IElementType nextElementType = lookAhead(1); popEndUntil(myTypes.C_PARAMETERS); boolean isCall = strictlyIn(myTypes.C_FUNCTION_CALL) || nextElementType == myTypes.SHARP; mark(isCall ? myTypes.C_PARAM : myTypes.C_PARAM_DECLARATION).wrapAtom(myTypes.CA_LOWER_SYMBOL); if (nextElementType != myTypes.SHARP) { popEndUntil(myTypes.C_PARAMETERS); } } else { IElementType nextTokenType = lookAhead(1); if (nextTokenType == myTypes.COLON && isCurrent(myTypes.C_SIG_ITEM)) { // let fn: |>x<| : ... mark(myTypes.C_PARAM_DECLARATION).markHolder(myTypes.H_NAMED_PARAM_DECLARATION); } else if (nextTokenType == myTypes.LIDENT/*or !EOL ? */ && isRawParent(myTypes.C_INHERIT)) { // inherit [M.]classType |>x<| wrapAtom(myTypes.CA_LOWER_SYMBOL).mark(myTypes.C_PARAMETERS); return; } else if (is(myTypes.H_NAMED_PARAM_DECLARATION) && (nextTokenType == myTypes.LIDENT || nextTokenType == myTypes.LPAREN)) { wrapAtom(myTypes.CA_LOWER_SYMBOL).popEndUntil(myTypes.C_PARAMETERS); return; } else if (!in(myTypes.C_SIG_ITEM) && !is(myTypes.C_TYPE_VARIABLE) && !is(myTypes.C_TYPE_CONSTRAINT) && !is(myTypes.C_BINARY_CONDITION) && !is(myTypes.C_CLASS_FIELD) && !in(myTypes.C_TYPE_BINDING) && !is(myTypes.C_PARAMETERS) && !strictlyInAny(myTypes.C_PARAM_DECLARATION, myTypes.C_DEFAULT_VALUE)) { if (nextTokenType == myTypes.LIDENT || nextTokenType == myTypes.INT_VALUE || nextTokenType == myTypes.FLOAT_VALUE || nextTokenType == myTypes.STRING_VALUE || nextTokenType == myTypes.TILDE || nextTokenType == myTypes.LPAREN) { if (isCurrent(myTypes.C_SCOPED_EXPR) || !in(myTypes.C_FUNCTION_CALL, /*not*/myTypes.C_PARAMETERS)) { // a function call // |>fn<| ... mark(myTypes.C_FUNCTION_CALL) .wrapAtom(myTypes.CA_LOWER_SYMBOL).popEnd() .mark(myTypes.C_PARAMETERS); return; } } } if (strictlyIn(myTypes.C_DECONSTRUCTION)) { wrapWith(myTypes.C_LOWER_NAME); } else { wrapAtom(myTypes.CA_LOWER_SYMBOL); } } } private void parseUIdent() { if (DUMMY_IDENTIFIER_TRIMMED.equals(getTokenText())) { return; } if (is(myTypes.C_MODULE_DECLARATION) && previousElementType(1) != myTypes.OF) { // module |>M<| ... remapCurrentToken(myTypes.A_MODULE_NAME).wrapAtom(myTypes.CA_UPPER_SYMBOL); } else if (isCurrent(myTypes.C_MODULE_BINDING)) { IElementType nextElement = lookAhead(1); if (nextElement == myTypes.LPAREN) { parseFunctorCall(); } else { // module M = |>X<| // module M = X.|>Y<| remapCurrentToken(myTypes.A_MODULE_NAME).wrapAtom(myTypes.CA_UPPER_SYMBOL); } } else if (is(myTypes.C_OPEN) || is(myTypes.C_INCLUDE)) { // It is a module name/path, or might be a functor call // open/include |>M<| ... remapCurrentToken(myTypes.A_MODULE_NAME).wrapAtom(myTypes.CA_UPPER_SYMBOL); IElementType nextToken = getTokenType(); if (nextToken != myTypes.DOT && nextToken != myTypes.LPAREN && nextToken != myTypes.WITH) { // Not a path, nor a functor, must close that open popEndUntilOneOf(myTypes.C_OPEN, myTypes.C_INCLUDE); popEnd(); } if (nextToken == myTypes.IN) { // let _ = let open M |>in<| .. advance(); } } else if (is(myTypes.C_TYPE_BINDING)) { IElementType nextToken = lookAhead(1); if (nextToken == myTypes.DOT) { // a path remapCurrentToken(myTypes.A_MODULE_NAME).wrapAtom(myTypes.CA_UPPER_SYMBOL); } else { // Variant declaration without a pipe // type t = |>X<| | ... mark(myTypes.C_VARIANT_DECLARATION) .remapCurrentToken(myTypes.A_VARIANT_NAME).wrapAtom(myTypes.CA_UPPER_SYMBOL); } } else if (is(myTypes.C_VARIANT_DECLARATION)) { // Declaring a variant // type t = | |>X<| ... IElementType nextToken = lookAhead(1); remapCurrentToken(nextToken == myTypes.DOT ? myTypes.A_MODULE_NAME : myTypes.A_VARIANT_NAME) .wrapAtom(myTypes.CA_UPPER_SYMBOL); } else if (is(myTypes.C_EXCEPTION_DECLARATION)) { // Declaring an exception // exception |>X<| ... remapCurrentToken(myTypes.A_EXCEPTION_NAME).wrapAtom(myTypes.CA_UPPER_SYMBOL); } else if (isCurrent(myTypes.C_TRY_HANDLER)) { // try .. with |>X<| .. remapCurrentToken(myTypes.A_EXCEPTION_NAME).wrapAtom(myTypes.CA_UPPER_SYMBOL); } else { IElementType nextToken = lookAhead(1); if (((in(myTypes.C_PATTERN_MATCH_EXPR, /*not*/myTypes.C_PATTERN_MATCH_BODY) || isCurrent(myTypes.C_LET_BINDING))) && nextToken != myTypes.DOT) { // Pattern matching a variant or using it // match c with | |>X<| ... / let x = |>X<| ... remapCurrentToken(myTypes.A_VARIANT_NAME).wrapAtom(myTypes.CA_UPPER_SYMBOL); } else if (nextToken == myTypes.LPAREN && isCurrent(myTypes.C_SIG_ITEM)) { parseFunctorCall(); } else if (nextToken == myTypes.LPAREN && !strictlyInAny(myTypes.C_OPEN, myTypes.C_INCLUDE)) { // |>X<| ( remapCurrentToken(myTypes.A_VARIANT_NAME).wrapAtom(myTypes.CA_UPPER_SYMBOL) .advance().markScope(myTypes.C_VARIANT_CONSTRUCTOR, myTypes.LPAREN); } else { remapCurrentToken(myTypes.A_MODULE_NAME).wrapAtom(myTypes.CA_UPPER_SYMBOL); } } } private void parseFunctorCall() { // functor call :: |>X<| ( ... // functor call with path :: A.B.|>X<| ( ... mark(myTypes.C_FUNCTOR_CALL) .remapCurrentToken(myTypes.A_MODULE_NAME).wrapAtom(myTypes.CA_UPPER_SYMBOL) .markScope(myTypes.C_PARAMETERS, myTypes.LPAREN).advance() .mark(myTypes.C_PARAM); } private void parseOpen() { if (is(myTypes.C_LET_DECLARATION)) { // let open X (coq/indtypes.ml) updateComposite(myTypes.C_OPEN); } else { popEndUntilScope(); mark(myTypes.C_OPEN); } } private void parseInclude() { popEndUntilScope(); mark(myTypes.C_INCLUDE); } private void parseExternal() { popEndUntilScope(); mark(myTypes.C_EXTERNAL_DECLARATION); } @SuppressWarnings("StatementWithEmptyBody") private void parseType() { if (is(myTypes.C_MODULE_DECLARATION) || is(myTypes.C_MODULE_SIGNATURE)) { // module |>type<| M = ... } else if (is(myTypes.C_TYPE_VARIABLE)) { // let x : |>type<| ... } else if (is(myTypes.C_CLASS_DECLARATION)) { // class |>type<| ... } else { if (previousElementType(1) == myTypes.AND && in(myTypes.C_TYPE_CONSTRAINT)) { popEndUntil(myTypes.C_TYPE_CONSTRAINT); } else if (!is(myTypes.C_TYPE_CONSTRAINT)) { popEndUntilScope(); } mark(myTypes.C_TYPE_DECLARATION); } } private void parseException() { if (previousElementType(1) != myTypes.PIPE) { popEndUntilScope(); mark(myTypes.C_EXCEPTION_DECLARATION); } } private void parseDirectiveIf() { endLikeSemi(); mark(myTypes.C_DIRECTIVE); } private void parseDirectiveElse() { popEndUntil(myTypes.C_DIRECTIVE); } private void parseDirectiveElif() { popEndUntil(myTypes.C_DIRECTIVE); } private void parseDirectiveEnd() { popEndUntil(myTypes.C_DIRECTIVE); if (is(myTypes.C_DIRECTIVE)) { advance().popEnd(); } } private void parseVal() { boolean insideClass = in(myTypes.C_OBJECT); if (insideClass) { popEndUntil(myTypes.C_OBJECT); } else { popEndUntilScope(); } mark(insideClass ? myTypes.C_CLASS_FIELD : myTypes.C_VAL_DECLARATION); } private void parseRef() { if (is(myTypes.C_RECORD_FIELD) || is(myTypes.C_TYPE_DECLARATION) || is(myTypes.C_EXTERNAL_DECLARATION)) { remapCurrentToken(myTypes.LIDENT).wrapAtom(myTypes.CA_LOWER_SYMBOL); } } private void parseMethod() { if (is(myTypes.C_RECORD_FIELD)) { remapCurrentToken(myTypes.LIDENT).wrapAtom(myTypes.CA_LOWER_SYMBOL); } else { popEndUntil(myTypes.C_OBJECT); mark(myTypes.C_CLASS_METHOD); } } private void parseLet() { if (!is(myTypes.C_TRY_BODY) && previousElementType(1) != myTypes.RIGHT_ARROW) { endLikeSemi(); } mark(myTypes.C_LET_DECLARATION); advance(); if (getTokenType() == myTypes.LPAREN) { // an operator overload or a deconstruction // let |>(<| ... markScope(myTypes.C_SCOPED_EXPR, myTypes.LPAREN).advance(); } } private void parseModule() { if (isScope(myTypes.LPAREN) && isRawParent(myTypes.C_DEFAULT_VALUE)) { updateComposite(myTypes.C_FIRST_CLASS); } else if (is(myTypes.C_LET_DECLARATION)) { updateComposite(myTypes.C_MODULE_DECLARATION); } else if (is(myTypes.C_INCLUDE)) { mark(myTypes.C_MODULE_DECLARATION); } else if (!is(myTypes.C_MACRO_NAME) && !is(myTypes.C_MODULE_SIGNATURE)) { popEndUntilScope(); mark(myTypes.C_MODULE_DECLARATION); } } private void parseClass() { endLikeSemi(); mark(myTypes.C_CLASS_DECLARATION); } private void parseInherit() { endLikeSemi(); mark(myTypes.C_INHERIT); } private void endLikeSemi() { int previousStep = 1; IElementType previousElementType = previousElementType(previousStep); while (previousElementType == myTypes.MULTI_COMMENT) { previousStep++; previousElementType = previousElementType(previousStep); } if (previousElementType != myTypes.EQ && previousElementType != myTypes.RIGHT_ARROW && previousElementType != myTypes.TRY && previousElementType != myTypes.SEMI && previousElementType != myTypes.THEN && previousElementType != myTypes.ELSE && previousElementType != myTypes.IN && previousElementType != myTypes.LPAREN && previousElementType != myTypes.DO && previousElementType != myTypes.STRUCT && previousElementType != myTypes.SIG && previousElementType != myTypes.COLON) { popEndUntilScope(); } } } } ================================================ FILE: src/main/java/com/reason/lang/ocaml/OclParserDefinition.java ================================================ package com.reason.lang.ocaml; import com.intellij.lang.*; import com.intellij.lexer.*; import com.intellij.openapi.project.*; import com.intellij.psi.*; import com.intellij.psi.tree.*; import com.reason.ide.files.*; import com.reason.lang.core.stub.type.*; import org.jetbrains.annotations.*; public class OclParserDefinition implements ParserDefinition { private static final TokenSet WHITE_SPACES = TokenSet.create(TokenType.WHITE_SPACE); private static final TokenSet COMMENTS = TokenSet.create(OclTypes.INSTANCE.MULTI_COMMENT); private static final TokenSet STRINGS = TokenSet.create(OclTypes.INSTANCE.STRING_VALUE); @Override public @NotNull Lexer createLexer(Project project) { return new OclLexer(); } @Override public @NotNull TokenSet getWhitespaceTokens() { return WHITE_SPACES; } @Override public @NotNull TokenSet getCommentTokens() { return COMMENTS; } @Override public @NotNull TokenSet getStringLiteralElements() { return STRINGS; } @Override public @NotNull PsiParser createParser(final Project project) { return new OclParser(false); } @Override public @NotNull IFileElementType getFileNodeType() { return OclFileStubElementType.INSTANCE; } @Override public @NotNull PsiFile createFile(@NotNull FileViewProvider viewProvider) { return viewProvider.getFileType() instanceof OclInterfaceFileType ? new OclInterfaceFile(viewProvider) : new OclFile(viewProvider); } @Override public @NotNull SpaceRequirements spaceExistenceTypeBetweenTokens(ASTNode left, ASTNode right) { return SpaceRequirements.MAY; } @Override public @NotNull PsiElement createElement(@NotNull ASTNode node) { IElementType type = node.getElementType(); if (type instanceof ORStubElementType) { //noinspection rawtypes return ((ORStubElementType) type).createPsi(node); } throw new IllegalArgumentException("Not an OCaml stub node: " + node + " (" + type + ", " + type.getLanguage() + "): " + node.getText()); } } ================================================ FILE: src/main/java/com/reason/lang/ocaml/OclSafeParserDefinition.java ================================================ package com.reason.lang.ocaml; import com.intellij.lang.*; import com.intellij.openapi.project.*; import org.jetbrains.annotations.*; public class OclSafeParserDefinition extends OclParserDefinition { @Override public @NotNull PsiParser createParser(Project project) { return new OclParser(true); } } ================================================ FILE: src/main/java/com/reason/lang/ocaml/OclTypes.java ================================================ package com.reason.lang.ocaml; import com.reason.lang.core.stub.*; import com.reason.lang.core.type.*; public class OclTypes extends ORLangTypes { public static final OclTypes INSTANCE = new OclTypes(); private OclTypes() { // Stub element types C_CLASS_DECLARATION = (ORCompositeType) OclStubBasedElementTypes.C_CLASS_DECLARATION; C_CLASS_METHOD = (ORCompositeType) OclStubBasedElementTypes.C_CLASS_METHOD; C_EXCEPTION_DECLARATION = (ORCompositeType) OclStubBasedElementTypes.C_EXCEPTION_DECLARATION; C_EXTERNAL_DECLARATION = (ORCompositeType) OclStubBasedElementTypes.C_EXTERNAL_DECLARATION; C_FUNCTOR_DECLARATION = (ORCompositeType) OclStubBasedElementTypes.C_FUNCTOR_DECLARATION; C_INCLUDE = (ORCompositeType) OclStubBasedElementTypes.C_INCLUDE; C_LET_DECLARATION = (ORCompositeType) OclStubBasedElementTypes.C_LET_DECLARATION; C_MODULE_DECLARATION = (ORCompositeType) OclStubBasedElementTypes.C_MODULE_DECLARATION; C_OBJECT_FIELD = (ORCompositeType) OclStubBasedElementTypes.C_OBJECT_FIELD; C_OPEN = (ORCompositeType) OclStubBasedElementTypes.C_OPEN; C_PARAM_DECLARATION = (ORCompositeType) OclStubBasedElementTypes.C_PARAM_DECLARATION; C_RECORD_FIELD = (ORCompositeType) OclStubBasedElementTypes.C_RECORD_FIELD; C_TYPE_DECLARATION = (ORCompositeType) OclStubBasedElementTypes.C_TYPE_DECLARATION; C_VARIANT_DECLARATION = (ORCompositeType) OclStubBasedElementTypes.C_VARIANT_DECLARATION; C_VAL_DECLARATION = (ORCompositeType) OclStubBasedElementTypes.C_VAL_DECLARATION; // Composite element types C_ANNOTATION = new ORCompositeElementType("C_ANNOTATION", OclLanguage.INSTANCE); C_ARRAY = new ORCompositeElementType("C_ARRAY", OclLanguage.INSTANCE); C_ASSERT_STMT = new ORCompositeElementType("C_ASSERT_STMT", OclLanguage.INSTANCE); C_BINARY_CONDITION = new ORCompositeElementType("C_BINARY_CONDITION", OclLanguage.INSTANCE); C_CLASS_CONSTR = new ORCompositeElementType("C_CLASS_CONSTR", OclLanguage.INSTANCE); C_CLASS_FIELD = new ORCompositeElementType("C_CLASS_FIELD", OclLanguage.INSTANCE); C_CLASS_INITIALIZER = new ORCompositeElementType("C_CLASS_INITIALIZER", OclLanguage.INSTANCE); C_CLOSED_VARIANT = new ORCompositeElementType("C_CLOSED_VARIANT", OclLanguage.INSTANCE); C_CONSTRAINTS = new ORCompositeElementType("C_CONSTRAINTS", OclLanguage.INSTANCE); C_TYPE_CONSTRAINT = new ORCompositeElementType("C_TYPE_CONSTRAINT", OclLanguage.INSTANCE); C_CUSTOM_OPERATOR = new ORCompositeElementType("C_CUSTOM_OPERATOR", OclLanguage.INSTANCE); C_DECONSTRUCTION = new ORCompositeElementType("C_DECONSTRUCTION", OclLanguage.INSTANCE); C_DEFAULT_VALUE = new ORCompositeElementType("C_DEFAULT_VALUE", OclLanguage.INSTANCE); C_DIRECTIVE = new ORCompositeElementType("C_DIRECTIVE", OclLanguage.INSTANCE); C_DO_LOOP = new ORCompositeElementType("C_DO_LOOP", OclLanguage.INSTANCE); C_FIRST_CLASS = new ORCompositeElementType("C_FIRST_CLASS", OclLanguage.INSTANCE); C_FOR_LOOP = new ORCompositeElementType("C_FOR_LOOP", OclLanguage.INSTANCE); C_FIELD_VALUE = new ORCompositeElementType("C_FIELD_VALUE", OclLanguage.INSTANCE); C_FUN_EXPR = new ORCompositeElementType("C_FUN_EXPR", OclLanguage.INSTANCE); C_FUNCTION_BODY = new ORCompositeElementType("C_FUNCTION_BODY", OclLanguage.INSTANCE); C_FUNCTION_CALL = new ORCompositeElementType("C_FUNCTION_CALL", OclLanguage.INSTANCE); C_FUNCTION_EXPR = new ORCompositeElementType("C_FUNCTION_EXPR", OclLanguage.INSTANCE); C_FUNCTOR_BINDING = new ORCompositeElementType("C_FUNCTOR_BINDING", OclLanguage.INSTANCE); C_FUNCTOR_CALL = new ORCompositeElementType("C_FUNCTOR_CALL", OclLanguage.INSTANCE); C_FUNCTOR_RESULT = new ORCompositeElementType("C_FUNCTOR_RESULT", OclLanguage.INSTANCE); C_GUARD = new ORCompositeElementType("C_GUARD", OclLanguage.INSTANCE); C_IF = new ORCompositeElementType("C_IF", OclLanguage.INSTANCE); C_IF_THEN_ELSE = new ORCompositeElementType("C_IF_THEN_ELSE", OclLanguage.INSTANCE); C_INHERIT = new ORCompositeElementType("C_INHERIT", OclLanguage.INSTANCE); C_INTERPOLATION_EXPR = new ORCompositeElementType("C_INTERPOLATION_EXPR", OclLanguage.INSTANCE); C_INTERPOLATION_PART = new ORCompositeElementType("C_INTERPOLATION_PART", OclLanguage.INSTANCE); C_INTERPOLATION_REF = new ORCompositeElementType("C_INTERPOLATION_REF", OclLanguage.INSTANCE); C_JS_OBJECT = new ORCompositeElementType("C_JS_OBJECT", OclLanguage.INSTANCE); C_LET_ATTR = new ORCompositeElementType("C_LET_ATTR", OclLanguage.INSTANCE); C_LET_BINDING = new ORCompositeElementType("C_LET_BINDING", OclLanguage.INSTANCE); C_LOCAL_OPEN = new ORCompositeElementType("C_LOCAL_OPEN", OclLanguage.INSTANCE); C_TYPE_VARIABLE = new ORCompositeElementType("C_TYPE_VARIABLE", OclLanguage.INSTANCE); C_MACRO_EXPR = new ORCompositeElementType("C_MACRO_EXPR", OclLanguage.INSTANCE); C_MACRO_NAME = new ORCompositeElementType("C_MACRO_NAME", OclLanguage.INSTANCE); C_MACRO_BODY = new ORCompositeElementType("C_MACRO_RAW_BODY", OclLanguage.INSTANCE); C_METHOD_CALL = new ORCompositeElementType("C_METHOD_CALL", OclLanguage.INSTANCE); C_MODULE_BINDING = new ORCompositeElementType("C_MODULE_BINDING", OclLanguage.INSTANCE); C_MODULE_SIGNATURE = new ORCompositeElementType("C_MODULE_SIGNATURE", OclLanguage.INSTANCE); C_ML_INTERPOLATOR = new ORCompositeElementType("C_ML_INTERPOLATOR", OclLanguage.INSTANCE); C_NAMED_PARAM = new ORCompositeElementType("C_NAMED_PARAM", OclLanguage.INSTANCE); C_NONE = new ORCompositeElementType("C_NONE", OclLanguage.INSTANCE); C_MATCH_EXPR = new ORCompositeElementType("C_MATCH_EXPR", OclLanguage.INSTANCE); C_PARAM = new ORCompositeElementType("C_PARAM", OclLanguage.INSTANCE); C_OBJECT = new ORCompositeElementType("C_OBJECT", OclLanguage.INSTANCE); C_OPEN_VARIANT = new ORCompositeElementType("C_OPEN_VARIANT", OclLanguage.INSTANCE); C_OPTION = new ORCompositeElementType("C_OPTION", OclLanguage.INSTANCE); C_LOWER_NAME = new ORCompositeElementType("C_LOWER_NAME", OclLanguage.INSTANCE); C_PARAMETERS = new ORCompositeElementType("C_PARAMETERS", OclLanguage.INSTANCE); C_PATTERN_MATCH_BODY = new ORCompositeElementType("C_PATTERN_MATCH_BODY", OclLanguage.INSTANCE); C_PATTERN_MATCH_EXPR = new ORCompositeElementType("C_PATTERN_MATCH_EXPR", OclLanguage.INSTANCE); C_RECORD_EXPR = new ORCompositeElementType("C_RECORD_EXPR", OclLanguage.INSTANCE); C_SIG_EXPR = new ORCompositeElementType("C_SIG_EXPR", OclLanguage.INSTANCE); C_SIG_ITEM = new ORCompositeElementType("C_SIG_ITEM", OclLanguage.INSTANCE); C_SOME = new ORCompositeElementType("C_SOME", OclLanguage.INSTANCE); C_TAG = new ORCompositeElementType("C_TAG", OclLanguage.INSTANCE); C_TAG_BODY = new ORCompositeElementType("C_TAG_BODY", OclLanguage.INSTANCE); C_TAG_CLOSE = new ORCompositeElementType("C_TAG_CLOSE", OclLanguage.INSTANCE); C_TAG_PROP_VALUE = new ORCompositeElementType("C_TAG_PROP_VALUE", OclLanguage.INSTANCE); C_TAG_PROPERTY = new ORCompositeElementType("C_TAG_PROPERTY", OclLanguage.INSTANCE); C_TAG_START = new ORCompositeElementType("C_TAG_START", OclLanguage.INSTANCE); C_TERNARY = new ORCompositeElementType("C_TERNARY", OclLanguage.INSTANCE); C_TRY_EXPR = new ORCompositeElementType("C_TRY_EXPR", OclLanguage.INSTANCE); C_TRY_BODY = new ORCompositeElementType("C_TRY_BODY", OclLanguage.INSTANCE); C_TRY_HANDLER = new ORCompositeElementType("C_TRY_HANDLER", OclLanguage.INSTANCE); C_TRY_HANDLER_BODY = new ORCompositeElementType("C_TRY_HANDLER_BODY", OclLanguage.INSTANCE); C_TRY_HANDLERS = new ORCompositeElementType("C_TRY_HANDLERS", OclLanguage.INSTANCE); C_TUPLE = new ORCompositeElementType("C_TUPLE", OclLanguage.INSTANCE); C_TYPE_BINDING = new ORCompositeElementType("C_TYPE_BINDING", OclLanguage.INSTANCE); C_UNIT = new ORCompositeElementType("C_UNIT", OclLanguage.INSTANCE); C_UNPACK = new ORCompositeElementType("C_UNPACK", OclLanguage.INSTANCE); C_VARIANT_CONSTRUCTOR = new ORCompositeElementType("C_VARIANT_CONSTRUCTOR", OclLanguage.INSTANCE); C_SCOPED_EXPR = new ORCompositeElementType("C_SCOPED_EXPR", OclLanguage.INSTANCE); C_STRUCT_EXPR = new ORCompositeElementType("C_STRUCT_EXPR", OclLanguage.INSTANCE); C_MIXIN_FIELD = new ORCompositeElementType("C_MIXIN_FIELD", OclLanguage.INSTANCE); C_SWITCH_EXPR = new ORCompositeElementType("C_SWITCH_EXPR", OclLanguage.INSTANCE); C_SWITCH_BODY = new ORCompositeElementType("C_SWITCH_BODY", OclLanguage.INSTANCE); C_WHILE = new ORCompositeElementType("C_WHILE", OclLanguage.INSTANCE); // Atom types CA_LOWER_SYMBOL = new ORCompositeElementType("CA_LOWER_SYMBOL", OclLanguage.INSTANCE); CA_UPPER_SYMBOL = new ORCompositeElementType("CA_UPPER_SYMBOL", OclLanguage.INSTANCE); A_LOWER_TAG_NAME = new ORTokenElementType("A_LOWER_TAG_NAME", OclLanguage.INSTANCE); A_UPPER_TAG_NAME = new ORTokenElementType("A_UPPER_TAG_NAME", OclLanguage.INSTANCE); A_VARIANT_NAME = new ORTokenElementType("A_VARIANT_NAME", OclLanguage.INSTANCE); A_MODULE_NAME = new ORTokenElementType("A_MODULE_NAME", OclLanguage.INSTANCE); A_EXCEPTION_NAME = new ORTokenElementType("A_EXCEPTION_NAME", OclLanguage.INSTANCE); // Dummy types H_ATOM = new ORCompositeElementType("H_ATOM", OclLanguage.INSTANCE); H_PLACE_HOLDER = new ORCompositeElementType("H_PLACE_HOLDER", OclLanguage.INSTANCE); H_COLLECTION_ITEM = new ORCompositeElementType("H_COLLECTION_ITEM", OclLanguage.INSTANCE); H_NAMED_PARAM_DECLARATION = new ORCompositeElementType("H_NAMED_PARAM_DECLARATION", OclLanguage.INSTANCE); // Token element types ASYNC = new ORTokenElementType("ASYNC", OclLanguage.INSTANCE); AWAIT = new ORTokenElementType("AWAIT", OclLanguage.INSTANCE); BOOL_VALUE = new ORTokenElementType("BOOL_VALUE", OclLanguage.INSTANCE); STRING_VALUE = new ORTokenElementType("STRING_VALUE", OclLanguage.INSTANCE); FLOAT_VALUE = new ORTokenElementType("FLOAT_VALUE", OclLanguage.INSTANCE); CATCH = new ORTokenElementType("CATCH", OclLanguage.INSTANCE); CHAR_VALUE = new ORTokenElementType("CHAR_VALUE", OclLanguage.INSTANCE); INT_VALUE = new ORTokenElementType("INT_VALUE", OclLanguage.INSTANCE); SWITCH = new ORTokenElementType("SWITCH", OclLanguage.INSTANCE); FUNCTION = new ORTokenElementType("FUNCTION", OclLanguage.INSTANCE); FUN = new ORTokenElementType("FUN", OclLanguage.INSTANCE); FUNCTOR = new ORTokenElementType("FUNCTOR", OclLanguage.INSTANCE); IF = new ORTokenElementType("IF", OclLanguage.INSTANCE); PROPERTY_NAME = new ORTokenElementType("PROPERTY_NAME", OclLanguage.INSTANCE); AND = new ORTokenElementType("AND", OclLanguage.INSTANCE); L_AND = new ORTokenElementType("L_AND", OclLanguage.INSTANCE); L_OR = new ORTokenElementType("L_OR", OclLanguage.INSTANCE); ARROBASE = new ORTokenElementType("ARROBASE", OclLanguage.INSTANCE); ARROBASE_2 = new ORTokenElementType("ARROBASE_2", OclLanguage.INSTANCE); ARROBASE_3 = new ORTokenElementType("ARROBASE_3", OclLanguage.INSTANCE); ARROW = new ORTokenElementType("ARROW", OclLanguage.INSTANCE); ASSERT = new ORTokenElementType("ASSERT", OclLanguage.INSTANCE); AS = new ORTokenElementType("AS", OclLanguage.INSTANCE); BACKTICK = new ORTokenElementType("BACKTICK", OclLanguage.INSTANCE); BEGIN = new ORTokenElementType("BEGIN", OclLanguage.INSTANCE); CARRET = new ORTokenElementType("CARRET", OclLanguage.INSTANCE); COLON = new ORTokenElementType("COLON", OclLanguage.INSTANCE); COMMA = new ORTokenElementType("COMMA", OclLanguage.INSTANCE); SINGLE_COMMENT = new ORTokenElementType("SINGLE_COMMENT", OclLanguage.INSTANCE); MULTI_COMMENT = new ORTokenElementType("MULTI_COMMENT", OclLanguage.INSTANCE); DIFF = new ORTokenElementType("DIFF", OclLanguage.INSTANCE); DIRECTIVE_IF = new ORTokenElementType("DIRECTIVE_IF", OclLanguage.INSTANCE); DIRECTIVE_ELSE = new ORTokenElementType("DIRECTIVE_ELSE", OclLanguage.INSTANCE); DIRECTIVE_ELIF = new ORTokenElementType("DIRECTIVE_ELIF", OclLanguage.INSTANCE); DIRECTIVE_END = new ORTokenElementType("DIRECTIVE_END", OclLanguage.INSTANCE); DIRECTIVE_ENDIF = new ORTokenElementType("DIRECTIVE_ENDIF", OclLanguage.INSTANCE); LT_OR_EQUAL = new ORTokenElementType("LT_OR_EQUAL", OclLanguage.INSTANCE); GT_OR_EQUAL = new ORTokenElementType("GT_OR_EQUAL", OclLanguage.INSTANCE); DOLLAR = new ORTokenElementType("DOLLAR", OclLanguage.INSTANCE); DOT = new ORTokenElementType("DOT", OclLanguage.INSTANCE); DOTDOTDOT = new ORTokenElementType("DOTDOTDOT", OclLanguage.INSTANCE); DO = new ORTokenElementType("DO", OclLanguage.INSTANCE); DONE = new ORTokenElementType("DONE", OclLanguage.INSTANCE); ELSE = new ORTokenElementType("ELSE", OclLanguage.INSTANCE); END = new ORTokenElementType("END", OclLanguage.INSTANCE); NOT_EQ = new ORTokenElementType("EQ", OclLanguage.INSTANCE); NOT_EQEQ = new ORTokenElementType("EQEQ", OclLanguage.INSTANCE); EQ = new ORTokenElementType("EQ", OclLanguage.INSTANCE); EQEQ = new ORTokenElementType("EQEQ", OclLanguage.INSTANCE); EQEQEQ = new ORTokenElementType("EQEQEQ", OclLanguage.INSTANCE); EXCEPTION = new ORTokenElementType("EXCEPTION", OclLanguage.INSTANCE); EXCLAMATION_MARK = new ORTokenElementType("EXCLAMATION_MARK", OclLanguage.INSTANCE); EXTERNAL = new ORTokenElementType("EXTERNAL", OclLanguage.INSTANCE); PIPE_FIRST = new ORTokenElementType("PIPE_FIRST", OclLanguage.INSTANCE); FOR = new ORTokenElementType("FOR", OclLanguage.INSTANCE); TYPE_ARGUMENT = new ORTokenElementType("TYPE_ARGUMENT", OclLanguage.INSTANCE); GT = new ORTokenElementType("GT", OclLanguage.INSTANCE); IN = new ORTokenElementType("IN", OclLanguage.INSTANCE); LAZY = new ORTokenElementType("LAZY", OclLanguage.INSTANCE); INCLUDE = new ORTokenElementType("INCLUDE", OclLanguage.INSTANCE); LARRAY = new ORTokenElementType("LARRAY", OclLanguage.INSTANCE); LBRACE = new ORTokenElementType("LBRACE", OclLanguage.INSTANCE); LBRACKET = new ORTokenElementType("LBRACKET", OclLanguage.INSTANCE); LET = new ORTokenElementType("LET", OclLanguage.INSTANCE); LIST = new ORTokenElementType("LIST", OclLanguage.INSTANCE); LIDENT = new ORTokenElementType("LIDENT", OclLanguage.INSTANCE); LPAREN = new ORTokenElementType("LPAREN", OclLanguage.INSTANCE); LT = new ORTokenElementType("LT", OclLanguage.INSTANCE); MATCH = new ORTokenElementType("MATCH", OclLanguage.INSTANCE); MINUS = new ORTokenElementType("MINUS", OclLanguage.INSTANCE); MINUSDOT = new ORTokenElementType("MINUSDOT", OclLanguage.INSTANCE); MODULE = new ORTokenElementType("MODULE", OclLanguage.INSTANCE); MUTABLE = new ORTokenElementType("MUTABLE", OclLanguage.INSTANCE); NONE = new ORTokenElementType("NONE", OclLanguage.INSTANCE); OF = new ORTokenElementType("OF", OclLanguage.INSTANCE); OPEN = new ORTokenElementType("OPEN", OclLanguage.INSTANCE); OPTION = new ORTokenElementType("OPTION", OclLanguage.INSTANCE); POLY_VARIANT = new ORTokenElementType("POLY_VARIANT", OclLanguage.INSTANCE); PIPE = new ORTokenElementType("PIPE", OclLanguage.INSTANCE); PIPE_FORWARD = new ORTokenElementType("PIPE_FORWARD", OclLanguage.INSTANCE); PLUS = new ORTokenElementType("PLUS", OclLanguage.INSTANCE); PERCENT = new ORTokenElementType("PERCENT", OclLanguage.INSTANCE); PLUSDOT = new ORTokenElementType("PLUSDOT", OclLanguage.INSTANCE); QUESTION_MARK = new ORTokenElementType("QUESTION_MARK", OclLanguage.INSTANCE); SINGLE_QUOTE = new ORTokenElementType("SINGLE_QUOTE", OclLanguage.INSTANCE); DOUBLE_QUOTE = new ORTokenElementType("DOUBLE_QUOTE", OclLanguage.INSTANCE); RAISE = new ORTokenElementType("RAISE", OclLanguage.INSTANCE); RARRAY = new ORTokenElementType("RARRAY", OclLanguage.INSTANCE); RBRACE = new ORTokenElementType("RBRACE", OclLanguage.INSTANCE); RBRACKET = new ORTokenElementType("RBRACKET", OclLanguage.INSTANCE); REC = new ORTokenElementType("REC", OclLanguage.INSTANCE); REF = new ORTokenElementType("REF", OclLanguage.INSTANCE); RPAREN = new ORTokenElementType("RPAREN", OclLanguage.INSTANCE); SEMI = new ORTokenElementType("SEMI", OclLanguage.INSTANCE); SIG = new ORTokenElementType("SIG", OclLanguage.INSTANCE); SHARP = new ORTokenElementType("SHARP", OclLanguage.INSTANCE); SHARPSHARP = new ORTokenElementType("SHARPSHARP", OclLanguage.INSTANCE); SHORTCUT = new ORTokenElementType("SHORTCUT", OclLanguage.INSTANCE); SLASH = new ORTokenElementType("SLASH", OclLanguage.INSTANCE); SLASH_2 = new ORTokenElementType("SLASH_2", OclLanguage.INSTANCE); SLASHDOT = new ORTokenElementType("SLASHDOT", OclLanguage.INSTANCE); SOME = new ORTokenElementType("SOME", OclLanguage.INSTANCE); STAR = new ORTokenElementType("STAR", OclLanguage.INSTANCE); STARDOT = new ORTokenElementType("STARDOT", OclLanguage.INSTANCE); STRING_CONCAT = new ORTokenElementType("STRING_CONCAT", OclLanguage.INSTANCE); STRUCT = new ORTokenElementType("STRUCT", OclLanguage.INSTANCE); OP_STRUCT_DIFF = new ORTokenElementType("OP_STRUCT_DIFF", OclLanguage.INSTANCE); TAG_AUTO_CLOSE = new ORTokenElementType("TAG_AUTO_CLOSE", OclLanguage.INSTANCE); TAG_LT_SLASH = new ORTokenElementType("TAG_LT_SLASH", OclLanguage.INSTANCE); TILDE = new ORTokenElementType("TILDE", OclLanguage.INSTANCE); TO = new ORTokenElementType("TO", OclLanguage.INSTANCE); THEN = new ORTokenElementType("THEN", OclLanguage.INSTANCE); TRY = new ORTokenElementType("TRY", OclLanguage.INSTANCE); TYPE = new ORTokenElementType("TYPE", OclLanguage.INSTANCE); UNPACK = new ORTokenElementType("UNPACK", OclLanguage.INSTANCE); // rescript UIDENT = new ORTokenElementType("UIDENT", OclLanguage.INSTANCE); UNIT = new ORTokenElementType("UNIT", OclLanguage.INSTANCE); VAL = new ORTokenElementType("VAL", OclLanguage.INSTANCE); PUB = new ORTokenElementType("PUB", OclLanguage.INSTANCE); PRI = new ORTokenElementType("PRI", OclLanguage.INSTANCE); WHEN = new ORTokenElementType("WHEN", OclLanguage.INSTANCE); WHILE = new ORTokenElementType("WHILE", OclLanguage.INSTANCE); WITH = new ORTokenElementType("WITH", OclLanguage.INSTANCE); RAW = new ORTokenElementType("RAW", OclLanguage.INSTANCE); ASR = new ORTokenElementType("ASR", OclLanguage.INSTANCE); CLASS = new ORTokenElementType("CLASS", OclLanguage.INSTANCE); CONSTRAINT = new ORTokenElementType("CONSTRAINT", OclLanguage.INSTANCE); DOWNTO = new ORTokenElementType("DOWNTO", OclLanguage.INSTANCE); INHERIT = new ORTokenElementType("INHERIT", OclLanguage.INSTANCE); INITIALIZER = new ORTokenElementType("INITIALIZER", OclLanguage.INSTANCE); LAND = new ORTokenElementType("LAND", OclLanguage.INSTANCE); LOR = new ORTokenElementType("LOR", OclLanguage.INSTANCE); LSL = new ORTokenElementType("LSL", OclLanguage.INSTANCE); LSR = new ORTokenElementType("LSR", OclLanguage.INSTANCE); LXOR = new ORTokenElementType("LXOR", OclLanguage.INSTANCE); METHOD = new ORTokenElementType("METHOD", OclLanguage.INSTANCE); MOD = new ORTokenElementType("MOD", OclLanguage.INSTANCE); NEW = new ORTokenElementType("NEW", OclLanguage.INSTANCE); NONREC = new ORTokenElementType("NONREC", OclLanguage.INSTANCE); OR = new ORTokenElementType("OR", OclLanguage.INSTANCE); PRIVATE = new ORTokenElementType("PRIVATE", OclLanguage.INSTANCE); VIRTUAL = new ORTokenElementType("VIRTUAL", OclLanguage.INSTANCE); COLON_EQ = new ORTokenElementType("COLON_EQ", OclLanguage.INSTANCE); COLON_GT = new ORTokenElementType("COLON_GT", OclLanguage.INSTANCE); DOTDOT = new ORTokenElementType("DOTDOT", OclLanguage.INSTANCE); SEMISEMI = new ORTokenElementType("SEMISEMI", OclLanguage.INSTANCE); GT_BRACKET = new ORTokenElementType("GT_BRACKET", OclLanguage.INSTANCE); GT_BRACE = new ORTokenElementType("GT_BRACE", OclLanguage.INSTANCE); LEFT_ARROW = new ORTokenElementType("LEFT_ARROW", OclLanguage.INSTANCE); RIGHT_ARROW = new ORTokenElementType("RIGHT_ARROW", OclLanguage.INSTANCE); OBJECT = new ORTokenElementType("OBJECT", OclLanguage.INSTANCE); RECORD = new ORTokenElementType("RECORD", OclLanguage.INSTANCE); AMPERSAND = new ORTokenElementType("AMPERSAND", OclLanguage.INSTANCE); BRACKET_GT = new ORTokenElementType("BRACKET_GT", OclLanguage.INSTANCE); BRACKET_LT = new ORTokenElementType("BRACKET_LT", OclLanguage.INSTANCE); BRACE_LT = new ORTokenElementType("BRACE_LT", OclLanguage.INSTANCE); ML_STRING_VALUE = new ORTokenElementType("ML_STRING_VALUE", OclLanguage.INSTANCE); ML_STRING_OPEN = new ORTokenElementType("ML_STRING_OPEN", OclLanguage.INSTANCE); ML_STRING_CLOSE = new ORTokenElementType("ML_STRING_CLOSE", OclLanguage.INSTANCE); JS_STRING_OPEN = new ORTokenElementType("JS_STRING_OPEN", OclLanguage.INSTANCE); JS_STRING_CLOSE = new ORTokenElementType("JS_STRING_CLOSE", OclLanguage.INSTANCE); UNDERSCORE = new ORTokenElementType("UNDERSCORE", OclLanguage.INSTANCE); } } ================================================ FILE: src/main/java/com/reason/lang/ocamlgrammar/OclGrammarAstFactory.java ================================================ package com.reason.lang.ocamlgrammar; import com.intellij.extapi.psi.*; import com.intellij.lang.*; import com.intellij.psi.*; import com.intellij.psi.tree.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.ocamlgrammar.*; import org.jetbrains.annotations.*; class OclGrammarAstFactory extends ASTFactory { private OclGrammarAstFactory() { } public static @NotNull PsiElement createElement(@NotNull ASTNode node) { IElementType type = node.getElementType(); if (type == OclGrammarTypes.INSTANCE.C_VERNAC) { return new RPsiGrammarVernac(node); } if (type == OclGrammarTypes.INSTANCE.C_TACTIC) { return new RPsiGrammarTactic(node); } if (type == OclGrammarTypes.INSTANCE.C_ARGUMENT) { return new RPsiGrammarArgument(node); } if (type == OclGrammarTypes.INSTANCE.C_GRAMMAR) { return new RPsiGrammarGrammar(node); } if (type == OclGrammarTypes.INSTANCE.C_INJECTION) { return new RPsiOCamlInjection(node); } return new ASTWrapperPsiElement(node); } } ================================================ FILE: src/main/java/com/reason/lang/ocamlgrammar/OclGrammarElementType.java ================================================ package com.reason.lang.ocamlgrammar; import com.intellij.psi.tree.*; import org.jetbrains.annotations.*; public class OclGrammarElementType extends IElementType { OclGrammarElementType(@NotNull String debugName) { super(debugName, OclGrammarLanguage.INSTANCE); } } ================================================ FILE: src/main/java/com/reason/lang/ocamlgrammar/OclGrammarLanguage.java ================================================ package com.reason.lang.ocamlgrammar; import com.intellij.lang.*; public class OclGrammarLanguage extends Language { public static final OclGrammarLanguage INSTANCE = new OclGrammarLanguage(); private OclGrammarLanguage() { super("Mlg"); } } ================================================ FILE: src/main/java/com/reason/lang/ocamlgrammar/OclGrammarLexer.java ================================================ /* The following code was generated by JFlex 1.7.0 tweaked for IntelliJ platform */ package com.reason.lang.ocamlgrammar; import com.intellij.psi.tree.IElementType; import com.intellij.lexer.FlexLexer; import static com.intellij.psi.TokenType.*; @SuppressWarnings("ALL") /** * This class is a scanner generated by * JFlex 1.7.0 * from the specification file mlg.flex */ public class OclGrammarLexer implements FlexLexer { /** 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 INITIAL = 2; public static final int IN_TEMPLATE = 4; public static final int IN_STRING = 6; public static final int IN_ML_COMMENT = 8; public static final int IN_SL_COMMENT = 10; /** * 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 }; /** * Translates characters to character classes * Chosen bits are [9, 6, 6] * Total runtime size is 1568 bytes */ public static int ZZ_CMAP(int ch) { return ZZ_CMAP_A[(ZZ_CMAP_Y[ZZ_CMAP_Z[ch>>12]|((ch>>6)&0x3f)]<<6)|(ch&0x3f)]; } /* The ZZ_CMAP_Z table has 272 entries */ static final char ZZ_CMAP_Z[] = zzUnpackCMap( "\1\0\1\100\1\200\u010d\100"); /* The ZZ_CMAP_Y table has 192 entries */ static final char ZZ_CMAP_Y[] = zzUnpackCMap( "\1\0\1\1\1\2\175\3\1\4\77\3"); /* The ZZ_CMAP_A table has 320 entries */ static final char ZZ_CMAP_A[] = zzUnpackCMap( "\11\0\1\3\1\1\1\41\1\42\1\2\22\0\1\3\1\0\1\40\5\0\1\32\1\33\1\37\2\0\1\27"+ "\1\0\1\36\12\4\4\0\1\30\2\0\1\11\1\4\1\7\1\5\1\6\1\4\1\15\1\4\1\16\2\4\1\10"+ "\1\20\1\17\1\24\1\13\1\4\1\12\1\4\1\22\1\14\1\21\1\4\1\23\2\4\1\34\1\0\1\35"+ "\1\0\1\4\1\0\32\4\1\25\1\31\1\26\7\0\1\41\242\0\2\41\26\0"); /** * Translates DFA states to action switch labels. */ private static final int [] ZZ_ACTION = zzUnpackAction(); private static final String ZZ_ACTION_PACKED_0 = "\6\0\1\1\1\2\1\3\11\4\1\5\1\6\1\2"+ "\1\7\1\10\1\11\1\12\1\13\1\2\1\14\1\15"+ "\1\16\1\17\1\20\1\16\1\21\1\15\1\22\1\16"+ "\11\4\1\23\1\24\1\25\1\0\1\26\1\0\1\4"+ "\1\27\30\4\1\30\2\4\1\31\1\4\1\32\1\33"+ "\1\34\1\35\1\4\1\36\1\37"; private static int [] zzUnpackAction() { int [] result = new int[90]; 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\43\0\106\0\151\0\214\0\257\0\322\0\322"+ "\0\365\0\u0118\0\u013b\0\u015e\0\u0181\0\u01a4\0\u01c7\0\u01ea"+ "\0\u020d\0\u0230\0\322\0\322\0\u0253\0\322\0\u0276\0\322"+ "\0\322\0\322\0\u0299\0\322\0\322\0\u02bc\0\322\0\322"+ "\0\322\0\322\0\u02df\0\322\0\u0302\0\u0325\0\u0348\0\u036b"+ "\0\u038e\0\u03b1\0\u03d4\0\u03f7\0\u041a\0\u043d\0\322\0\322"+ "\0\322\0\u02bc\0\322\0\u0302\0\u0460\0\u0118\0\u0483\0\u04a6"+ "\0\u04c9\0\u04ec\0\u050f\0\u0532\0\u0555\0\u0578\0\u059b\0\u05be"+ "\0\u05e1\0\u0604\0\u0627\0\u064a\0\u066d\0\u0690\0\u06b3\0\u06d6"+ "\0\u06f9\0\u071c\0\u073f\0\u0762\0\u0785\0\u07a8\0\u0118\0\u07cb"+ "\0\u07ee\0\u0118\0\u0811\0\u0118\0\u0118\0\u0118\0\u0118\0\u0834"+ "\0\u0118\0\u0118"; private static int [] zzUnpackRowMap() { int [] result = new int[90]; 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(); 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 = "\43\7\1\10\3\11\1\12\1\13\1\14\1\15\1\12"+ "\1\16\1\12\1\17\1\12\1\20\3\12\1\21\1\22"+ "\2\12\1\23\1\24\1\25\1\10\1\26\1\27\1\30"+ "\1\31\1\32\1\33\1\10\1\34\1\10\1\11\2\35"+ "\1\36\22\35\1\37\1\40\12\35\2\41\2\35\1\36"+ "\35\35\1\42\2\41\2\35\1\36\34\35\1\43\1\35"+ "\2\41\1\35\1\44\1\45\36\35\2\41\44\0\3\11"+ "\36\0\1\11\4\0\21\12\22\0\2\12\1\46\16\12"+ "\22\0\13\12\1\47\3\12\1\50\1\12\22\0\20\12"+ "\1\51\22\0\6\12\1\52\12\12\22\0\4\12\1\53"+ "\14\12\22\0\6\12\1\54\12\12\22\0\2\12\1\55"+ "\16\12\22\0\5\12\1\56\13\12\46\0\1\57\51\0"+ "\1\60\41\0\1\61\1\60\4\0\1\35\1\62\73\0"+ "\1\63\2\0\1\63\5\0\1\44\1\64\44\0\3\12"+ "\1\65\15\12\22\0\1\12\1\66\17\12\22\0\16\12"+ "\1\67\2\12\22\0\14\12\1\70\4\12\22\0\11\12"+ "\1\71\7\12\22\0\10\12\1\72\10\12\22\0\5\12"+ "\1\73\13\12\22\0\6\12\1\74\12\12\22\0\3\12"+ "\1\75\15\12\22\0\4\12\1\76\14\12\22\0\2\12"+ "\1\77\16\12\22\0\14\12\1\100\4\12\22\0\10\12"+ "\1\101\10\12\22\0\11\12\1\102\7\12\22\0\14\12"+ "\1\103\4\12\22\0\13\12\1\104\5\12\22\0\16\12"+ "\1\105\2\12\22\0\5\12\1\106\13\12\22\0\13\12"+ "\1\107\5\12\22\0\5\12\1\110\13\12\22\0\14\12"+ "\1\111\4\12\22\0\12\12\1\112\6\12\22\0\14\12"+ "\1\113\4\12\22\0\5\12\1\114\13\12\22\0\12\12"+ "\1\115\6\12\22\0\6\12\1\116\12\12\22\0\1\12"+ "\1\117\17\12\22\0\13\12\1\120\5\12\22\0\2\12"+ "\1\121\16\12\22\0\13\12\1\122\5\12\22\0\5\12"+ "\1\123\13\12\22\0\3\12\1\124\15\12\22\0\3\12"+ "\1\125\15\12\22\0\2\12\1\126\16\12\22\0\1\12"+ "\1\127\17\12\22\0\13\12\1\130\5\12\22\0\6\12"+ "\1\131\12\12\22\0\16\12\1\132\2\12\16\0"; private static int [] zzUnpackTrans() { int [] result = new int[2135]; 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 aState */ private static final int [] ZZ_ATTRIBUTE = zzUnpackAttribute(); private static final String ZZ_ATTRIBUTE_PACKED_0 = "\6\0\2\11\12\1\2\11\1\1\1\11\1\1\3\11"+ "\1\1\2\11\1\1\4\11\1\1\1\11\12\1\3\11"+ "\1\0\1\11\1\0\46\1"; private static int [] zzUnpackAttribute() { int [] result = new int[90]; 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; /* user code: */ private OclGrammarTypes types; private int tokenStartIndex; private CharSequence quotedStringId; private int braceDepth; private boolean rulesDone = false; private boolean zzEOFDone = false; private boolean zzAtBOL = false; public OclGrammarLexer() { this((java.io.Reader)null); this.types = OclGrammarTypes.INSTANCE; } // Store the start index of a token private void tokenStart() { tokenStartIndex = zzStartRead; } // Set the start index of the token to the stored index private void tokenEnd() { zzStartRead = tokenStartIndex; } /** * Creates a new scanner * * @param in the java.io.Reader to read input from. */ public OclGrammarLexer(java.io.Reader in) { this.zzReader = in; } /** * Unpacks the compressed character translation table. * * @param packed the packed character translation table * @return the unpacked character translation table */ private static char [] zzUnpackCMap(String packed) { int size = 0; for (int i = 0, length = packed.length(); i < length; i += 2) { size += packed.charAt(i); } char[] map = new char[size]; int i = 0; /* index in packed string */ int j = 0; /* index in unpacked array */ while (i < packed.length()) { int count = packed.charAt(i++); char value = packed.charAt(i++); do map[j++] = value; while (--count > 0); } return map; } 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/*, zzEndReadL*/); 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/*, zzEndReadL*/); zzCurrentPosL += Character.charCount(zzInput); } } int zzNext = zzTransL[ zzRowMapL[zzState] + ZZ_CMAP(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; switch (zzLexicalState) { case IN_TEMPLATE: { yybegin(INITIAL); tokenEnd(); return types.TEMPLATE_OCAML_TEXT; } // fall though case 91: break; case IN_STRING: { yybegin(INITIAL); tokenEnd(); return types.STRING_VALUE; } // fall though case 92: break; case IN_ML_COMMENT: { yybegin(INITIAL); tokenEnd(); return types.MULTI_COMMENT; } // fall though case 93: break; case IN_SL_COMMENT: { yybegin(INITIAL); tokenEnd(); return types.SINGLE_COMMENT; } // fall though case 94: break; default: return null; } } else { switch (zzAction < 0 ? zzAction : ZZ_ACTION[zzAction]) { case 1: { yybegin(INITIAL); yypushback(1); } // fall through case 32: break; case 2: { return types.ATOM; } // fall through case 33: break; case 3: { return WHITE_SPACE; } // fall through case 34: break; case 4: { return types.IDENT; } // fall through case 35: break; case 5: { yybegin(IN_TEMPLATE); braceDepth = 1; tokenStart(); return types.LBRACE; } // fall through case 36: break; case 6: { return types.RBRACE; } // fall through case 37: break; case 7: { return types.PIPE; } // fall through case 38: break; case 8: { return types.LPAREN; } // fall through case 39: break; case 9: { return types.RPAREN; } // fall through case 40: break; case 10: { return types.LBRACKET; } // fall through case 41: break; case 11: { return types.RBRACKET; } // fall through case 42: break; case 12: { yybegin(IN_STRING); tokenStart(); } // fall through case 43: break; case 13: { } // fall through case 44: break; case 14: { return BAD_CHARACTER; } // fall through case 45: break; case 15: { braceDepth += 1; } // fall through case 46: break; case 16: { braceDepth -= 1; if(braceDepth == 0) { yypushback(1); tokenEnd(); yybegin(INITIAL); return types.TEMPLATE_OCAML_TEXT; } } // fall through case 47: break; case 17: { yybegin(INITIAL); tokenEnd(); return types.STRING_VALUE; } // fall through case 48: break; case 18: { yybegin(INITIAL); tokenEnd(); return types.SINGLE_COMMENT; } // fall through case 49: break; case 19: { return types.ARROW; } // fall through case 50: break; case 20: { yybegin(IN_ML_COMMENT); tokenStart(); } // fall through case 51: break; case 21: { yybegin(IN_SL_COMMENT); tokenStart(); } // fall through case 52: break; case 22: { yybegin(INITIAL); tokenEnd(); return types.MULTI_COMMENT; } // fall through case 53: break; case 23: { return types.END; } // fall through case 54: break; case 24: { return types.EXTEND; } // fall through case 55: break; case 25: { return types.PLUGIN; } // fall through case 56: break; case 26: { return types.VERNAC; } // fall through case 57: break; case 27: { return types.TACTIC; } // fall through case 58: break; case 28: { return types.DECLARE; } // fall through case 59: break; case 29: { return types.COMMAND; } // fall through case 60: break; case 30: { return types.GRAMMAR; } // fall through case 61: break; case 31: { return types.ARGUMENT; } // fall through case 62: break; default: zzScanError(ZZ_NO_MATCH); } } } } } ================================================ FILE: src/main/java/com/reason/lang/ocamlgrammar/OclGrammarParser.java ================================================ package com.reason.lang.ocamlgrammar; import com.intellij.lang.*; import com.intellij.psi.tree.*; import com.reason.lang.*; import org.jetbrains.annotations.*; public class OclGrammarParser extends CommonPsiParser { protected OclGrammarParser(boolean isSafe) { super(isSafe); } @Override protected ORParser getORParser(@NotNull PsiBuilder builder) { return new OclYaccParserState(builder, !myIsSafe); } static class OclYaccParserState extends ORParser { protected OclYaccParserState(@NotNull PsiBuilder builder, boolean verbose) { super(OclGrammarTypes.INSTANCE, builder, verbose); } @Override public void parse() { while (!myBuilder.eof()) { IElementType tokenType = myBuilder.getTokenType(); if (tokenType == myTypes.VERNAC) { popEndUntilScope(); mark(myTypes.C_VERNAC); } else if (tokenType == myTypes.TACTIC) { popEndUntilScope(); mark(myTypes.C_TACTIC); } else if (tokenType == myTypes.ARGUMENT) { if (!isCurrent(myTypes.C_VERNAC)) { popEndUntilScope(); mark(myTypes.C_ARGUMENT); } } else if (tokenType == myTypes.GRAMMAR) { popEndUntilScope(); mark(myTypes.C_GRAMMAR); } else if (tokenType == myTypes.END) { advance().popEndUntilScope(); } else if (tokenType == myTypes.LBRACE) { markScope(myTypes.C_INJECTION, myTypes.LBRACE); } else if (tokenType == myTypes.RBRACE) { popEndUntilScopeToken(myTypes.LBRACE); advance().popEnd(); } if (dontMove) { dontMove = false; } else { myBuilder.advanceLexer(); } } } } } ================================================ FILE: src/main/java/com/reason/lang/ocamlgrammar/OclGrammarParserDefinition.java ================================================ package com.reason.lang.ocamlgrammar; import com.intellij.lang.*; import com.intellij.lexer.*; import com.intellij.openapi.project.*; import com.intellij.psi.*; import com.intellij.psi.tree.*; import com.reason.ide.files.*; import org.jetbrains.annotations.*; public class OclGrammarParserDefinition implements ParserDefinition { private static final TokenSet WHITE_SPACES = TokenSet.create(TokenType.WHITE_SPACE); private static final TokenSet COMMENTS = TokenSet.create(OclGrammarTypes.INSTANCE.MULTI_COMMENT, OclGrammarTypes.INSTANCE.SINGLE_COMMENT); private static final IFileElementType FILE = new IFileElementType(Language.findInstance(OclGrammarLanguage.class)); @Override public @NotNull Lexer createLexer(Project project) { return new FlexAdapter(new OclGrammarLexer()); } public @NotNull TokenSet getWhitespaceTokens() { return WHITE_SPACES; } public @NotNull TokenSet getCommentTokens() { return COMMENTS; } public @NotNull TokenSet getStringLiteralElements() { return TokenSet.EMPTY; } public @NotNull PsiParser createParser(Project project) { return new OclGrammarParser(true); } @Override public @NotNull IFileElementType getFileNodeType() { return FILE; } public @NotNull PsiFile createFile(@NotNull FileViewProvider viewProvider) { return new MlgFile(viewProvider); } public @NotNull SpaceRequirements spaceExistenceTypeBetweenTokens(ASTNode left, ASTNode right) { return SpaceRequirements.MAY; } @NotNull public PsiElement createElement(@NotNull ASTNode node) { return OclGrammarAstFactory.createElement(node); } } ================================================ FILE: src/main/java/com/reason/lang/ocamlgrammar/OclGrammarTypes.java ================================================ // This is a generated file. Not intended for manual editing. package com.reason.lang.ocamlgrammar; import com.reason.lang.core.type.*; public class OclGrammarTypes extends ORTypes { public static final OclGrammarTypes INSTANCE = new OclGrammarTypes(); public ORCompositeElementType C_ARGUMENT = new ORCompositeElementType("C_ARGUMENT", OclGrammarLanguage.INSTANCE); public ORCompositeElementType C_GRAMMAR = new ORCompositeElementType("C_GRAMMAR", OclGrammarLanguage.INSTANCE); public ORCompositeElementType C_INJECTION = new ORCompositeElementType("C_INJECTION", OclGrammarLanguage.INSTANCE); public ORCompositeElementType C_VERNAC = new ORCompositeElementType("C_VERNAC", OclGrammarLanguage.INSTANCE); public ORCompositeElementType C_TACTIC = new ORCompositeElementType("C_TACTIC", OclGrammarLanguage.INSTANCE); public OclGrammarElementType ARGUMENT = new OclGrammarElementType("ARGUMENT"); public OclGrammarElementType ARROW = new OclGrammarElementType("ARROW"); public OclGrammarElementType ATOM = new OclGrammarElementType("ATOM"); public OclGrammarElementType COMMAND = new OclGrammarElementType("COMMAND"); public OclGrammarElementType DECLARE = new OclGrammarElementType("DECLARE"); public OclGrammarElementType END = new OclGrammarElementType("END"); public OclGrammarElementType EXTEND = new OclGrammarElementType("EXTEND"); public OclGrammarElementType GRAMMAR = new OclGrammarElementType("GRAMMAR"); public OclGrammarElementType IDENT = new OclGrammarElementType("IDENT"); public OclGrammarElementType LBRACE = new OclGrammarElementType("LBRACE"); public OclGrammarElementType LBRACKET = new OclGrammarElementType("LBRACKET"); public OclGrammarElementType LPAREN = new OclGrammarElementType("LPAREN"); public OclGrammarElementType PIPE = new OclGrammarElementType("PIPE"); public OclGrammarElementType PLUGIN = new OclGrammarElementType("PLUGIN"); public OclGrammarElementType MULTI_COMMENT = new OclGrammarElementType("MULTI_COMMENT"); public OclGrammarElementType RBRACE = new OclGrammarElementType("RBRACE"); public OclGrammarElementType RBRACKET = new OclGrammarElementType("RBRACKET"); public OclGrammarElementType RPAREN = new OclGrammarElementType("RPAREN"); public OclGrammarElementType SINGLE_COMMENT = new OclGrammarElementType("SINGLE_COMMENT"); public OclGrammarElementType STRING_VALUE = new OclGrammarElementType("STRING_VALUE"); public OclGrammarElementType TACTIC = new OclGrammarElementType("TACTIC"); public OclGrammarElementType TEMPLATE_OCAML_TEXT = new OclGrammarElementType("TEMPLATE_OCAML_TEXT"); public OclGrammarElementType VERNAC = new OclGrammarElementType("VERNAC"); } ================================================ FILE: src/main/java/com/reason/lang/ocamlgrammar/mlg.flex ================================================ package com.reason.lang.ocamlgrammar; import com.intellij.psi.tree.IElementType; import com.intellij.lexer.FlexLexer; import static com.intellij.psi.TokenType.*; @SuppressWarnings("ALL") %% %public %unicode %class OclGrammarLexer %implements FlexLexer %function advance %type IElementType %{ private OclGrammarTypes types; private int tokenStartIndex; private CharSequence quotedStringId; private int braceDepth; private boolean rulesDone = false; private boolean zzEOFDone = false; private boolean zzAtBOL = false; public OclGrammarLexer() { this((java.io.Reader)null); this.types = OclGrammarTypes.INSTANCE; } // Store the start index of a token private void tokenStart() { tokenStartIndex = zzStartRead; } // Set the start index of the token to the stored index private void tokenEnd() { zzStartRead = tokenStartIndex; } %} %state INITIAL %state IN_TEMPLATE %state IN_STRING %state IN_ML_COMMENT %state IN_SL_COMMENT EOL=\n|\r|\r\n WHITE_SPACE_CHAR=[\ \t\f]|{EOL} WHITE_SPACE={WHITE_SPACE_CHAR}+ NEWLINE=("\r"* "\n") IDENT=[A-Za-z_0-9] %% { [^] { yybegin(INITIAL); yypushback(1); } } { {WHITE_SPACE} { return WHITE_SPACE; } "DECLARE" { return types.DECLARE; } "PLUGIN" { return types.PLUGIN; } "GRAMMAR" { return types.GRAMMAR; } "VERNAC" { return types.VERNAC; } "ARGUMENT" { return types.ARGUMENT; } "EXTEND" { return types.EXTEND; } "COMMAND" { return types.COMMAND; } "TACTIC" { return types.TACTIC; } "END" { return types.END; } "{" { yybegin(IN_TEMPLATE); braceDepth = 1; tokenStart(); return types.LBRACE; } "}" { return types.RBRACE; } "->" { return types.ARROW; } "|" { return types.PIPE; } "(" { return types.LPAREN; } ")" { return types.RPAREN; } "[" { return types.LBRACKET; } "]" { return types.RBRACKET; } "/*" { yybegin(IN_ML_COMMENT); tokenStart(); } "(*" { yybegin(IN_ML_COMMENT); tokenStart(); } "//" { yybegin(IN_SL_COMMENT); tokenStart(); } "\"" { yybegin(IN_STRING); tokenStart(); } {IDENT}+ { return types.IDENT; } [^\ \t\f] { return types.ATOM; } } { "{" { braceDepth += 1; } "}" { braceDepth -= 1; if(braceDepth == 0) { yypushback(1); tokenEnd(); yybegin(INITIAL); return types.TEMPLATE_OCAML_TEXT; } } . | {NEWLINE} { } <> { yybegin(INITIAL); tokenEnd(); return types.TEMPLATE_OCAML_TEXT; } } { "\"" { yybegin(INITIAL); tokenEnd(); return types.STRING_VALUE; } . | {NEWLINE} { } <> { yybegin(INITIAL); tokenEnd(); return types.STRING_VALUE; } } { "*/" { yybegin(INITIAL); tokenEnd(); return types.MULTI_COMMENT; } "*)" { yybegin(INITIAL); tokenEnd(); return types.MULTI_COMMENT; } . | {NEWLINE} { } <> { yybegin(INITIAL); tokenEnd(); return types.MULTI_COMMENT; } } { . { } {NEWLINE} { yybegin(INITIAL); tokenEnd(); return types.SINGLE_COMMENT; } <> { yybegin(INITIAL); tokenEnd(); return types.SINGLE_COMMENT; } } [^] { return BAD_CHARACTER; } ================================================ FILE: src/main/java/com/reason/lang/ocamllex/OclLexAstFactory.java ================================================ package com.reason.lang.ocamllex; import com.intellij.extapi.psi.*; import com.intellij.lang.*; import com.intellij.psi.*; import com.intellij.psi.tree.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.ocamllex.*; import org.jetbrains.annotations.*; class OclLexAstFactory extends ASTFactory { private OclLexAstFactory() { } public static @NotNull PsiElement createElement(@NotNull ASTNode node) { IElementType type = node.getElementType(); if (type == OclLexTypes.INSTANCE.C_LET) { return new RPsiLexLet(node); } if (type == OclLexTypes.INSTANCE.C_RULE) { return new RPsiLexRule(node); } if (type == OclLexTypes.INSTANCE.C_PATTERN) { return new RPsiLexPattern(node); } if (type == OclLexTypes.INSTANCE.C_INJECTION) { return new RPsiOCamlInjection(node); } return new ASTWrapperPsiElement(node); } } ================================================ FILE: src/main/java/com/reason/lang/ocamllex/OclLexElementType.java ================================================ package com.reason.lang.ocamllex; import com.intellij.psi.tree.*; import org.jetbrains.annotations.*; public class OclLexElementType extends IElementType { public OclLexElementType(@NotNull String debugName) { super(debugName, OclLexLanguage.INSTANCE); } } ================================================ FILE: src/main/java/com/reason/lang/ocamllex/OclLexLanguage.java ================================================ package com.reason.lang.ocamllex; import com.intellij.lang.*; public class OclLexLanguage extends Language { public static final OclLexLanguage INSTANCE = new OclLexLanguage(); private OclLexLanguage() { super("Mll"); } } ================================================ FILE: src/main/java/com/reason/lang/ocamllex/OclLexLexer.java ================================================ /* The following code was generated by JFlex 1.7.0 tweaked for IntelliJ platform */ package com.reason.lang.ocamllex; import com.intellij.psi.tree.IElementType; import com.intellij.lexer.FlexLexer; import static com.intellij.psi.TokenType.*; @SuppressWarnings("ALL") /** * This class is a scanner generated by * JFlex 1.7.0 * from the specification file mll.flex */ public class OclLexLexer implements FlexLexer { /** 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 INITIAL = 2; public static final int IN_COMMENT = 4; public static final int IN_OCAML_SCOPE = 6; public static final int IN_STRING = 8; public static final int IN_CHAR = 10; /** * 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 }; /** * Translates characters to character classes * Chosen bits are [9, 6, 6] * Total runtime size is 1568 bytes */ public static int ZZ_CMAP(int ch) { return ZZ_CMAP_A[(ZZ_CMAP_Y[ZZ_CMAP_Z[ch>>12]|((ch>>6)&0x3f)]<<6)|(ch&0x3f)]; } /* The ZZ_CMAP_Z table has 272 entries */ static final char ZZ_CMAP_Z[] = zzUnpackCMap( "\1\0\1\100\1\200\u010d\100"); /* The ZZ_CMAP_Y table has 192 entries */ static final char ZZ_CMAP_Y[] = zzUnpackCMap( "\1\0\1\1\1\2\175\3\1\4\77\3"); /* The ZZ_CMAP_A table has 320 entries */ static final char ZZ_CMAP_A[] = zzUnpackCMap( "\11\0\1\3\1\1\1\32\1\33\1\2\22\0\1\35\1\0\1\7\4\0\1\10\1\5\1\0\1\6\2\0\1\17"+ "\2\0\12\4\3\0\1\13\3\0\32\4\1\15\1\34\1\16\1\0\1\4\1\0\1\26\1\36\1\4\1\31"+ "\1\21\6\4\1\20\1\4\1\30\1\4\1\25\1\4\1\23\1\27\1\22\1\24\5\4\1\11\1\14\1\12"+ "\7\0\1\32\242\0\2\32\26\0"); /** * Translates DFA states to action switch labels. */ private static final int [] ZZ_ACTION = zzUnpackAction(); private static final String ZZ_ACTION_PACKED_0 = "\6\0\1\1\1\2\1\3\1\4\1\2\1\5\1\6"+ "\1\7\1\10\1\11\1\12\1\13\1\14\1\15\4\4"+ "\1\16\1\17\3\20\1\21\1\22\1\23\1\16\1\24"+ "\4\4\2\0\1\25\2\4\1\26\1\27\1\4\1\30"; private static int [] zzUnpackAction() { int [] result = new int[47]; 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\37\0\76\0\135\0\174\0\233\0\272\0\272"+ "\0\331\0\370\0\u0117\0\272\0\272\0\272\0\272\0\272"+ "\0\272\0\272\0\272\0\272\0\u0136\0\u0155\0\u0174\0\u0193"+ "\0\272\0\272\0\u01b2\0\272\0\u01d1\0\272\0\272\0\272"+ "\0\u01f0\0\272\0\u020f\0\u022e\0\u024d\0\u026c\0\u01b2\0\u01d1"+ "\0\370\0\u028b\0\u02aa\0\370\0\370\0\u02c9\0\370"; private static int [] zzUnpackRowMap() { int [] result = new int[47]; 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(); 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 = "\37\7\1\10\3\11\1\12\1\13\1\10\1\14\1\15"+ "\1\16\1\17\1\20\1\21\1\22\1\23\1\24\1\25"+ "\2\12\1\26\1\12\1\27\1\30\3\12\1\10\1\11"+ "\1\10\1\11\1\12\1\31\1\32\1\33\27\31\2\34"+ "\5\31\1\35\6\31\1\36\1\37\17\31\2\34\5\31"+ "\1\35\4\31\1\40\22\31\2\34\1\41\4\31\1\35"+ "\5\31\1\40\21\31\2\34\1\41\2\31\40\0\3\11"+ "\27\0\1\11\1\0\1\11\5\0\1\12\13\0\12\12"+ "\4\0\1\12\6\0\1\42\34\0\1\12\13\0\1\12"+ "\1\43\10\12\4\0\1\12\4\0\1\12\13\0\4\12"+ "\1\44\5\12\4\0\1\12\4\0\1\12\13\0\6\12"+ "\1\45\3\12\4\0\1\12\4\0\1\12\13\0\10\12"+ "\1\46\1\12\4\0\1\12\1\0\1\32\1\47\35\0"+ "\1\31\1\50\43\0\2\31\11\0\2\31\4\0\1\31"+ "\3\0\3\31\4\0\1\12\13\0\2\12\1\51\7\12"+ "\4\0\1\12\4\0\1\12\13\0\1\52\11\12\4\0"+ "\1\12\4\0\1\12\13\0\3\12\1\53\6\12\4\0"+ "\1\12\4\0\1\12\13\0\11\12\1\54\4\0\1\12"+ "\4\0\1\12\13\0\1\12\1\55\10\12\4\0\1\12"+ "\4\0\1\12\13\0\7\12\1\56\2\12\4\0\1\12"+ "\4\0\1\12\13\0\1\12\1\57\10\12\4\0\1\12"; private static int [] zzUnpackTrans() { int [] result = new int[744]; 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 aState */ private static final int [] ZZ_ATTRIBUTE = zzUnpackAttribute(); private static final String ZZ_ATTRIBUTE_PACKED_0 = "\6\0\2\11\3\1\11\11\4\1\2\11\1\1\1\11"+ "\1\1\3\11\1\1\1\11\4\1\2\0\7\1"; private static int [] zzUnpackAttribute() { int [] result = new int[47]; 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; /* user code: */ private int tokenStartIndex; private CharSequence quotedStringId; private int braceDepth; private boolean rulesDone = false; private boolean zzEOFDone = false; private boolean zzAtBOL = false; public OclLexLexer() { this((java.io.Reader)null); } // Store the start index of a token private void tokenStart() { tokenStartIndex = zzStartRead; } // Set the start index of the token to the stored index private void tokenEnd() { zzStartRead = tokenStartIndex; } /** * Creates a new scanner * * @param in the java.io.Reader to read input from. */ public OclLexLexer(java.io.Reader in) { this.zzReader = in; } /** * Unpacks the compressed character translation table. * * @param packed the packed character translation table * @return the unpacked character translation table */ private static char [] zzUnpackCMap(String packed) { int size = 0; for (int i = 0, length = packed.length(); i < length; i += 2) { size += packed.charAt(i); } char[] map = new char[size]; int i = 0; /* index in packed string */ int j = 0; /* index in unpacked array */ while (i < packed.length()) { int count = packed.charAt(i++); char value = packed.charAt(i++); do map[j++] = value; while (--count > 0); } return map; } 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/*, zzEndReadL*/); 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/*, zzEndReadL*/); zzCurrentPosL += Character.charCount(zzInput); } } int zzNext = zzTransL[ zzRowMapL[zzState] + ZZ_CMAP(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; switch (zzLexicalState) { case IN_COMMENT: { yybegin(INITIAL); tokenEnd(); return OclLexTypes.INSTANCE.SINGLE_COMMENT; } // fall though case 48: break; case IN_OCAML_SCOPE: { yybegin(INITIAL); tokenEnd(); return OclLexTypes.INSTANCE.TEMPLATE_OCAML_TEXT; } // fall though case 49: break; case IN_STRING: { yybegin(INITIAL); tokenEnd(); return OclLexTypes.INSTANCE.STRING_VALUE; } // fall though case 50: break; case IN_CHAR: { yybegin(INITIAL); tokenEnd(); return OclLexTypes.INSTANCE.STRING_VALUE; } // fall though case 51: break; default: return null; } } else { switch (zzAction < 0 ? zzAction : ZZ_ACTION[zzAction]) { case 1: { yybegin(INITIAL); yypushback(1); } // fall through case 25: break; case 2: { return OclLexTypes.INSTANCE.ATOM; } // fall through case 26: break; case 3: { return WHITE_SPACE; } // fall through case 27: break; case 4: { return OclLexTypes.INSTANCE.IDENT; } // fall through case 28: break; case 5: { yybegin(IN_STRING); tokenStart(); } // fall through case 29: break; case 6: { yybegin(IN_CHAR); tokenStart(); } // fall through case 30: break; case 7: { yybegin(IN_OCAML_SCOPE); braceDepth = 1; tokenStart(); return OclLexTypes.INSTANCE.LBRACE; } // fall through case 31: break; case 8: { return OclLexTypes.INSTANCE.RBRACE; } // fall through case 32: break; case 9: { return OclLexTypes.INSTANCE.EQ; } // fall through case 33: break; case 10: { return OclLexTypes.INSTANCE.PIPE; } // fall through case 34: break; case 11: { return OclLexTypes.INSTANCE.LBRACKET; } // fall through case 35: break; case 12: { return OclLexTypes.INSTANCE.RBRACKET; } // fall through case 36: break; case 13: { return OclLexTypes.INSTANCE.DASH; } // fall through case 37: break; case 14: { } // fall through case 38: break; case 15: { yybegin(INITIAL); tokenEnd(); return OclLexTypes.INSTANCE.SINGLE_COMMENT; } // fall through case 39: break; case 16: { return BAD_CHARACTER; } // fall through case 40: break; case 17: { braceDepth += 1; } // fall through case 41: break; case 18: { braceDepth -= 1; if(braceDepth == 0) { yypushback(1); tokenEnd(); yybegin(INITIAL); return OclLexTypes.INSTANCE.TEMPLATE_OCAML_TEXT; } } // fall through case 42: break; case 19: { yybegin(INITIAL); tokenEnd(); return OclLexTypes.INSTANCE.STRING_VALUE; } // fall through case 43: break; case 20: { yybegin(IN_COMMENT); tokenStart(); } // fall through case 44: break; case 21: { return OclLexTypes.INSTANCE.LET; } // fall through case 45: break; case 22: { return OclLexTypes.INSTANCE.AND; } // fall through case 46: break; case 23: { return OclLexTypes.INSTANCE.RULE; } // fall through case 47: break; case 24: { return OclLexTypes.INSTANCE.PARSE; } // fall through case 48: break; default: zzScanError(ZZ_NO_MATCH); } } } } } ================================================ FILE: src/main/java/com/reason/lang/ocamllex/OclLexParser.java ================================================ package com.reason.lang.ocamllex; import com.intellij.lang.*; import com.intellij.psi.tree.*; import com.reason.lang.*; import org.jetbrains.annotations.*; public class OclLexParser extends CommonPsiParser { protected OclLexParser(boolean isSafe) { super(isSafe); } @Override protected ORParser getORParser(@NotNull PsiBuilder builder) { return new OclLexParserState(builder, !myIsSafe); } static class OclLexParserState extends ORParser { protected OclLexParserState(@NotNull PsiBuilder builder, boolean verbose) { super(OclLexTypes.INSTANCE, builder, verbose); } @Override public void parse() { while (!myBuilder.eof()) { IElementType tokenType = myBuilder.getTokenType(); if (tokenType == myTypes.LET) { popEndUntilScope(); mark(myTypes.C_LET); } else if (tokenType == myTypes.RULE) { popEndUntilScope(); mark(myTypes.C_RULE); } else if (tokenType == myTypes.PIPE) { if (strictlyIn(myTypes.C_RULE)) { popEndUntilFoundIndex(); mark(myTypes.C_PATTERN); } } else if (tokenType == myTypes.LBRACE) { if (!isCurrent(myTypes.C_PATTERN)) { popEndUntilScope(); } markScope(myTypes.C_INJECTION, myTypes.LBRACE); } else if (tokenType == myTypes.RBRACE) { popEndUntilScope(); advance().popEnd(); if (isCurrent(myTypes.C_PATTERN)) { popEnd(); } } else if (tokenType == myTypes.AND) { if (strictlyIn(myTypes.C_RULE)) { popEndUntilScope(); advance().mark(myTypes.C_RULE); } } if (dontMove) { dontMove = false; } else { myBuilder.advanceLexer(); } } } } } ================================================ FILE: src/main/java/com/reason/lang/ocamllex/OclLexParserDefinition.java ================================================ package com.reason.lang.ocamllex; import com.intellij.lang.*; import com.intellij.lexer.*; import com.intellij.openapi.project.*; import com.intellij.psi.*; import com.intellij.psi.tree.*; import com.reason.ide.files.*; import org.jetbrains.annotations.*; public class OclLexParserDefinition implements ParserDefinition { private static final TokenSet WHITE_SPACES = TokenSet.create(TokenType.WHITE_SPACE); private static final TokenSet COMMENTS = TokenSet.create(OclLexTypes.INSTANCE.SINGLE_COMMENT); private static final IFileElementType FILE = new IFileElementType(Language.findInstance(OclLexLanguage.class)); @Override public @NotNull Lexer createLexer(Project project) { return new FlexAdapter(new OclLexLexer()); } public @NotNull TokenSet getWhitespaceTokens() { return WHITE_SPACES; } public @NotNull TokenSet getCommentTokens() { return COMMENTS; } public @NotNull TokenSet getStringLiteralElements() { return TokenSet.EMPTY; } public @NotNull PsiParser createParser(Project project) { return new OclLexParser(true); } @Override public @NotNull IFileElementType getFileNodeType() { return FILE; } public @NotNull PsiFile createFile(@NotNull FileViewProvider viewProvider) { return new MllFile(viewProvider); } public @NotNull SpaceRequirements spaceExistenceTypeBetweenTokens(ASTNode left, ASTNode right) { return SpaceRequirements.MAY; } public @NotNull PsiElement createElement(@NotNull ASTNode node) { return OclLexAstFactory.createElement(node); } } ================================================ FILE: src/main/java/com/reason/lang/ocamllex/OclLexTypes.java ================================================ // This is a generated file. Not intended for manual editing. package com.reason.lang.ocamllex; import com.reason.lang.core.type.*; public class OclLexTypes extends ORTypes { public static final OclLexTypes INSTANCE = new OclLexTypes(); ORCompositeElementType C_LET = new ORCompositeElementType("C_LET", OclLexLanguage.INSTANCE); ORCompositeElementType C_RULE = new ORCompositeElementType("C_RULE", OclLexLanguage.INSTANCE); ORCompositeElementType C_PATTERN = new ORCompositeElementType("C_PATTERN", OclLexLanguage.INSTANCE); ORCompositeElementType C_INJECTION = new ORCompositeElementType("C_INJECTION", OclLexLanguage.INSTANCE); public OclLexElementType AND = new OclLexElementType("AND"); public OclLexElementType ATOM = new OclLexElementType("ATOM"); public OclLexElementType DASH = new OclLexElementType("DASH"); public OclLexElementType EQ = new OclLexElementType("EQ"); public OclLexElementType IDENT = new OclLexElementType("IDENT"); public OclLexElementType LBRACE = new OclLexElementType("LBRACE"); public OclLexElementType LBRACKET = new OclLexElementType("LBRACKET"); public OclLexElementType LET = new OclLexElementType("LET"); public OclLexElementType PARSE = new OclLexElementType("PARSE"); public OclLexElementType PIPE = new OclLexElementType("PIPE"); public OclLexElementType RBRACE = new OclLexElementType("RBRACE"); public OclLexElementType RBRACKET = new OclLexElementType("RBRACKET"); public OclLexElementType RULE = new OclLexElementType("RULE"); public OclLexElementType SINGLE_COMMENT = new OclLexElementType("SINGLE_COMMENT"); public OclLexElementType STRING_VALUE = new OclLexElementType("STRING_VALUE"); public OclLexElementType TEMPLATE_OCAML_TEXT = new OclLexElementType("TEMPLATE_OCAML_TEXT"); } ================================================ FILE: src/main/java/com/reason/lang/ocamllex/mll.flex ================================================ package com.reason.lang.ocamllex; import com.intellij.psi.tree.IElementType; import com.intellij.lexer.FlexLexer; import static com.intellij.psi.TokenType.*; @SuppressWarnings("ALL") %% %unicode %public %class OclLexLexer %implements FlexLexer %function advance %type IElementType %{ private int tokenStartIndex; private CharSequence quotedStringId; private int braceDepth; private boolean rulesDone = false; private boolean zzEOFDone = false; private boolean zzAtBOL = false; public OclLexLexer() { this((java.io.Reader)null); } // Store the start index of a token private void tokenStart() { tokenStartIndex = zzStartRead; } // Set the start index of the token to the stored index private void tokenEnd() { zzStartRead = tokenStartIndex; } %} %state INITIAL %state IN_COMMENT %state IN_OCAML_SCOPE %state IN_STRING %state IN_CHAR EOL=\n|\r|\r\n WHITE_SPACE_CHAR=[\ \t\f]|{EOL} WHITE_SPACE={WHITE_SPACE_CHAR}+ NEWLINE=("\r"* "\n") IDENT=[A-Za-z_0-9] %% { [^] { yybegin(INITIAL); yypushback(1); } } { {WHITE_SPACE} { return WHITE_SPACE; } "(*" { yybegin(IN_COMMENT); tokenStart(); } "\"" { yybegin(IN_STRING); tokenStart(); } "'" { yybegin(IN_CHAR); tokenStart(); } "{" { yybegin(IN_OCAML_SCOPE); braceDepth = 1; tokenStart(); return OclLexTypes.INSTANCE.LBRACE; } "}" { return OclLexTypes.INSTANCE.RBRACE; } "=" { return OclLexTypes.INSTANCE.EQ; } "|" { return OclLexTypes.INSTANCE.PIPE; } "[" { return OclLexTypes.INSTANCE.LBRACKET; } "]" { return OclLexTypes.INSTANCE.RBRACKET; } "-" { return OclLexTypes.INSTANCE.DASH; } "let" { return OclLexTypes.INSTANCE.LET; } "rule" { return OclLexTypes.INSTANCE.RULE; } "parse" { return OclLexTypes.INSTANCE.PARSE; } "and" { return OclLexTypes.INSTANCE.AND; } {IDENT}+ { return OclLexTypes.INSTANCE.IDENT; } [^\ \t\f] { return OclLexTypes.INSTANCE.ATOM; } } { . { } {NEWLINE} { yybegin(INITIAL); tokenEnd(); return OclLexTypes.INSTANCE.SINGLE_COMMENT; } <> { yybegin(INITIAL); tokenEnd(); return OclLexTypes.INSTANCE.SINGLE_COMMENT; } } { "{" { braceDepth += 1; } "}" { braceDepth -= 1; if(braceDepth == 0) { yypushback(1); tokenEnd(); yybegin(INITIAL); return OclLexTypes.INSTANCE.TEMPLATE_OCAML_TEXT; } } . { } {NEWLINE} { } <> { yybegin(INITIAL); tokenEnd(); return OclLexTypes.INSTANCE.TEMPLATE_OCAML_TEXT; } } { "\"" { yybegin(INITIAL); tokenEnd(); return OclLexTypes.INSTANCE.STRING_VALUE; } "\\" [\\\'\"ntbr ] { } { NEWLINE } { } . { } <> { yybegin(INITIAL); tokenEnd(); return OclLexTypes.INSTANCE.STRING_VALUE; } } { "'" { yybegin(INITIAL); tokenEnd(); return OclLexTypes.INSTANCE.STRING_VALUE; } "\\" [\\\'\"ntbr ] { } { NEWLINE } { } . { } <> { yybegin(INITIAL); tokenEnd(); return OclLexTypes.INSTANCE.STRING_VALUE; } } [^] { return BAD_CHARACTER; } ================================================ FILE: src/main/java/com/reason/lang/ocamlyacc/OclYaccAstFactory.java ================================================ package com.reason.lang.ocamlyacc; import com.intellij.extapi.psi.*; import com.intellij.lang.*; import com.intellij.psi.*; import com.intellij.psi.tree.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.ocamlyacc.*; import org.jetbrains.annotations.*; class OclYaccAstFactory extends ASTFactory { private OclYaccAstFactory() { } public static @NotNull PsiElement createElement(@NotNull ASTNode node) { IElementType type = node.getElementType(); if (type == OclYaccTypes.INSTANCE.C_HEADER) { return new RPsiYaccHeader(node); } if (type == OclYaccTypes.INSTANCE.C_DECLARATION) { return new RPsiYaccDeclaration(node); } if (type == OclYaccTypes.INSTANCE.C_RULE) { return new RPsiYaccRule(node); } if (type == OclYaccTypes.INSTANCE.C_RULE_BODY) { return new RPsiYaccRuleBody(node); } if (type == OclYaccTypes.INSTANCE.C_INJECTION) { return new RPsiOCamlInjection(node); } return new ASTWrapperPsiElement(node); } } ================================================ FILE: src/main/java/com/reason/lang/ocamlyacc/OclYaccElementType.java ================================================ package com.reason.lang.ocamlyacc; import com.intellij.psi.tree.IElementType; import org.jetbrains.annotations.NotNull; public class OclYaccElementType extends IElementType { OclYaccElementType(@NotNull String debugName) { super(debugName, OclYaccLanguage.INSTANCE); } } ================================================ FILE: src/main/java/com/reason/lang/ocamlyacc/OclYaccLanguage.java ================================================ package com.reason.lang.ocamlyacc; import com.intellij.lang.Language; public class OclYaccLanguage extends Language { public static final OclYaccLanguage INSTANCE = new OclYaccLanguage(); private OclYaccLanguage() { super("Mly"); } } ================================================ FILE: src/main/java/com/reason/lang/ocamlyacc/OclYaccLexer.java ================================================ /* The following code was generated by JFlex 1.7.0 tweaked for IntelliJ platform */ package com.reason.lang.ocamlyacc; import com.intellij.psi.tree.IElementType; import com.intellij.lexer.FlexLexer; import static com.intellij.psi.TokenType.*; @SuppressWarnings("ALL") /** * This class is a scanner generated by * JFlex 1.7.0 * from the specification file mly.flex */ public class OclYaccLexer implements FlexLexer { /** 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 INITIAL = 2; public static final int IN_HEADER = 4; public static final int IN_SEMANTIC_ACTION = 6; public static final int IN_ML_COMMENT = 8; public static final int IN_SL_COMMENT = 10; public static final int IN_TRAILER = 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 }; /** * Translates characters to character classes * Chosen bits are [9, 6, 6] * Total runtime size is 1568 bytes */ public static int ZZ_CMAP(int ch) { return ZZ_CMAP_A[(ZZ_CMAP_Y[ZZ_CMAP_Z[ch>>12]|((ch>>6)&0x3f)]<<6)|(ch&0x3f)]; } /* The ZZ_CMAP_Z table has 272 entries */ static final char ZZ_CMAP_Z[] = zzUnpackCMap( "\1\0\1\100\1\200\u010d\100"); /* The ZZ_CMAP_Y table has 192 entries */ static final char ZZ_CMAP_Y[] = zzUnpackCMap( "\1\0\1\1\1\2\175\3\1\4\77\3"); /* The ZZ_CMAP_A table has 320 entries */ static final char ZZ_CMAP_A[] = zzUnpackCMap( "\11\0\1\3\1\1\1\41\1\42\1\2\22\0\1\3\4\0\1\5\2\0\1\40\1\43\1\37\3\0\1\30\1"+ "\36\12\4\1\31\1\32\1\34\1\0\1\35\2\0\32\4\4\0\1\4\1\0\1\16\1\4\1\27\1\4\1"+ "\13\1\23\1\25\1\26\1\24\1\4\1\12\1\22\1\4\1\14\1\11\1\21\1\4\1\17\1\15\1\10"+ "\4\4\1\20\1\4\1\6\1\33\1\7\7\0\1\41\242\0\2\41\26\0"); /** * 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\1\3\1\4\1\2\1\5\1\6"+ "\1\7\1\10\1\11\1\12\1\13\1\14\2\2\1\15"+ "\1\16\1\15\1\16\1\17\1\20\1\15\1\21\1\16"+ "\1\22\1\23\1\24\6\0\1\25\1\26\1\0\1\27"+ "\1\30\20\0\1\31\3\0\1\32\1\0\1\33\1\0"+ "\1\34\1\35\2\0\1\36\1\0\1\37"; private static int [] zzUnpackAction() { int [] result = new int[76]; 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\44\0\110\0\154\0\220\0\264\0\330\0\374"+ "\0\374\0\u0120\0\u0144\0\u0168\0\374\0\374\0\374\0\374"+ "\0\374\0\374\0\374\0\374\0\u018c\0\u01b0\0\374\0\u01d4"+ "\0\u01f8\0\374\0\374\0\374\0\u021c\0\374\0\u0240\0\374"+ "\0\374\0\374\0\u0264\0\u0288\0\u02ac\0\u02d0\0\u02f4\0\u0318"+ "\0\374\0\374\0\u01d4\0\374\0\374\0\u0240\0\u033c\0\u0360"+ "\0\u0384\0\u03a8\0\u03cc\0\u03f0\0\u0414\0\u0438\0\u045c\0\u0480"+ "\0\u04a4\0\u04c8\0\u04ec\0\u0510\0\u0534\0\374\0\u0558\0\u057c"+ "\0\u05a0\0\374\0\u05c4\0\374\0\u05e8\0\374\0\374\0\u060c"+ "\0\u0630\0\374\0\u0654\0\374"; private static int [] zzUnpackRowMap() { int [] result = new int[76]; 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(); 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 = "\44\10\1\11\3\12\1\13\1\14\1\15\1\16\20\13"+ "\1\17\1\20\1\21\1\22\1\23\1\24\1\25\1\11"+ "\1\26\1\11\1\12\1\11\2\27\1\30\2\27\1\31"+ "\33\27\2\32\3\27\1\30\3\27\1\33\1\34\31\27"+ "\2\32\3\27\1\30\34\27\1\35\1\27\2\32\2\27"+ "\1\36\1\37\36\27\2\32\3\27\1\30\36\27\2\32"+ "\1\27\45\0\3\12\36\0\1\12\5\0\1\13\3\0"+ "\20\13\21\0\1\40\1\41\1\42\1\43\3\0\1\44"+ "\1\45\1\0\1\46\2\0\1\47\1\0\1\50\55\0"+ "\1\51\1\52\43\0\1\52\5\0\1\27\1\53\50\0"+ "\1\54\72\0\1\55\4\0\1\55\1\0\1\36\1\56"+ "\52\0\1\57\6\0\1\60\34\0\1\61\42\0\1\62"+ "\57\0\1\63\32\0\1\64\44\0\1\65\41\0\1\66"+ "\52\0\1\67\36\0\1\70\45\0\1\71\52\0\1\72"+ "\41\0\1\73\42\0\1\74\34\0\1\75\43\0\1\76"+ "\46\0\1\77\44\0\1\100\52\0\1\101\25\0\1\102"+ "\57\0\1\103\33\0\1\104\44\0\1\105\36\0\1\106"+ "\43\0\1\107\47\0\1\110\44\0\1\111\41\0\1\112"+ "\41\0\1\113\61\0\1\114\14\0"; private static int [] zzUnpackTrans() { int [] result = new int[1656]; 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 aState */ private static final int [] ZZ_ATTRIBUTE = zzUnpackAttribute(); private static final String ZZ_ATTRIBUTE_PACKED_0 = "\7\0\2\11\3\1\10\11\2\1\1\11\2\1\3\11"+ "\1\1\1\11\1\1\3\11\6\0\2\11\1\0\2\11"+ "\20\0\1\11\3\0\1\11\1\0\1\11\1\0\2\11"+ "\2\0\1\11\1\0\1\11"; private static int [] zzUnpackAttribute() { int [] result = new int[76]; 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; /* user code: */ private OclYaccTypes types; private int tokenStartIndex; private CharSequence quotedStringId; private int braceDepth; private boolean rulesDone = false; private boolean zzEOFDone = false; private boolean zzAtBOL = false; public OclYaccLexer() { this((java.io.Reader)null); this.types = OclYaccTypes.INSTANCE; } // Store the start index of a token private void tokenStart() { tokenStartIndex = zzStartRead; } // Set the start index of the token to the stored index private void tokenEnd() { zzStartRead = tokenStartIndex; } /** * Creates a new scanner * * @param in the java.io.Reader to read input from. */ public OclYaccLexer(java.io.Reader in) { this.zzReader = in; } /** * Unpacks the compressed character translation table. * * @param packed the packed character translation table * @return the unpacked character translation table */ private static char [] zzUnpackCMap(String packed) { int size = 0; for (int i = 0, length = packed.length(); i < length; i += 2) { size += packed.charAt(i); } char[] map = new char[size]; int i = 0; /* index in packed string */ int j = 0; /* index in unpacked array */ while (i < packed.length()) { int count = packed.charAt(i++); char value = packed.charAt(i++); do map[j++] = value; while (--count > 0); } return map; } 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/*, zzEndReadL*/); 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/*, zzEndReadL*/); zzCurrentPosL += Character.charCount(zzInput); } } int zzNext = zzTransL[ zzRowMapL[zzState] + ZZ_CMAP(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; switch (zzLexicalState) { case IN_HEADER: { yybegin(INITIAL); tokenEnd(); return types.TEMPLATE_OCAML_TEXT; } // fall though case 77: break; case IN_SEMANTIC_ACTION: { yybegin(INITIAL); tokenEnd(); return types.TEMPLATE_OCAML_TEXT; } // fall though case 78: break; case IN_ML_COMMENT: { yybegin(INITIAL); tokenEnd(); return types.MULTI_COMMENT; } // fall though case 79: break; case IN_SL_COMMENT: { yybegin(INITIAL); tokenEnd(); return types.SINGLE_COMMENT; } // fall though case 80: break; case IN_TRAILER: { yybegin(INITIAL); tokenEnd(); return types.TEMPLATE_OCAML_TEXT; } // fall though case 81: break; default: return null; } } else { switch (zzAction < 0 ? zzAction : ZZ_ACTION[zzAction]) { case 1: { yybegin(INITIAL); yypushback(1); } // fall through case 32: break; case 2: { return types.ATOM; } // fall through case 33: break; case 3: { return WHITE_SPACE; } // fall through case 34: break; case 4: { return types.IDENT; } // fall through case 35: break; case 5: { yybegin(IN_SEMANTIC_ACTION); braceDepth = 1; tokenStart(); return types.LBRACE; } // fall through case 36: break; case 6: { return types.RBRACE; } // fall through case 37: break; case 7: { return types.DOT; } // fall through case 38: break; case 8: { return types.COLON; } // fall through case 39: break; case 9: { return types.SEMI; } // fall through case 40: break; case 10: { return types.PIPE; } // fall through case 41: break; case 11: { return types.LT; } // fall through case 42: break; case 12: { return types.GT; } // fall through case 43: break; case 13: { } // fall through case 44: break; case 14: { return BAD_CHARACTER; } // fall through case 45: break; case 15: { braceDepth += 1; } // fall through case 46: break; case 16: { braceDepth -= 1; if(braceDepth == 0) { yypushback(1); tokenEnd(); yybegin(INITIAL); return types.TEMPLATE_OCAML_TEXT; } } // fall through case 47: break; case 17: { yybegin(INITIAL); tokenEnd(); return types.SINGLE_COMMENT; } // fall through case 48: break; case 18: { if (rulesDone) { yybegin(IN_TRAILER); } rulesDone = true; return types.SECTION_SEPARATOR; } // fall through case 49: break; case 19: { yybegin(IN_HEADER); tokenStart(); return types.HEADER_START; } // fall through case 50: break; case 20: { return types.HEADER_STOP; } // fall through case 51: break; case 21: { yybegin(IN_SL_COMMENT); tokenStart(); } // fall through case 52: break; case 22: { yybegin(IN_ML_COMMENT); tokenStart(); } // fall through case 53: break; case 23: { yypushback(2); tokenEnd(); yybegin(INITIAL); return types.TEMPLATE_OCAML_TEXT; } // fall through case 54: break; case 24: { yybegin(INITIAL); tokenEnd(); return types.MULTI_COMMENT; } // fall through case 55: break; case 25: { return types.TYPE; } // fall through case 56: break; case 26: { return types.LEFT; } // fall through case 57: break; case 27: { return types.TOKEN; } // fall through case 58: break; case 28: { return types.START; } // fall through case 59: break; case 29: { return types.RIGHT; } // fall through case 60: break; case 30: { return types.INLINE; } // fall through case 61: break; case 31: { return types.NON_ASSOC; } // fall through case 62: break; default: zzScanError(ZZ_NO_MATCH); } } } } } ================================================ FILE: src/main/java/com/reason/lang/ocamlyacc/OclYaccParser.java ================================================ package com.reason.lang.ocamlyacc; import com.intellij.lang.*; import com.intellij.psi.tree.*; import com.reason.lang.*; import org.jetbrains.annotations.*; public class OclYaccParser extends CommonPsiParser { protected OclYaccParser(boolean isSafe) { super(isSafe); } @Override protected ORParser getORParser(@NotNull PsiBuilder builder) { return new OclYaccParserState(builder, !myIsSafe); } static class OclYaccParserState extends ORParser { protected OclYaccParserState(@NotNull PsiBuilder builder, boolean verbose) { super(OclYaccTypes.INSTANCE, builder, verbose); } @Override public void parse() { boolean inRules = false; while (!myBuilder.eof()) { IElementType tokenType = myBuilder.getTokenType(); if (tokenType == myTypes.HEADER_START) { markScope(myTypes.C_HEADER, myTypes.HEADER_START); } else if (tokenType == myTypes.HEADER_STOP) { advance().popEnd(); } else if (tokenType == myTypes.TOKEN || tokenType == myTypes.START || tokenType == myTypes.TYPE || tokenType == myTypes.LEFT || tokenType == myTypes.RIGHT || tokenType == myTypes.NON_ASSOC) { popEndUntilScope(); mark(myTypes.C_DECLARATION); } else if (tokenType == myTypes.SECTION_SEPARATOR) { popEndUntilScope(); if (!inRules) { inRules = true; } } else if (inRules) { if (tokenType == myTypes.IDENT) { if (rawLookup(1) == myTypes.COLON) { popEndUntilScope(); mark(myTypes.C_RULE).advance() .mark(myTypes.C_RULE_BODY); } } else if (tokenType == myTypes.SEMI) { if (strictlyIn(myTypes.C_RULE)) { advance(); popEndUntilFoundIndex(); popEnd(); } } else if (tokenType == myTypes.LBRACE) { markScope(myTypes.C_INJECTION, myTypes.LBRACE); } else if (tokenType == myTypes.RBRACE) { popEndUntilScopeToken(myTypes.LBRACE); advance().popEnd(); } else if (tokenType == myTypes.INLINE) { popEndUntilScope(); } } if (dontMove) { dontMove = false; } else { myBuilder.advanceLexer(); } } } } } ================================================ FILE: src/main/java/com/reason/lang/ocamlyacc/OclYaccParserDefinition.java ================================================ package com.reason.lang.ocamlyacc; import com.intellij.lang.*; import com.intellij.lexer.*; import com.intellij.openapi.project.*; import com.intellij.psi.*; import com.intellij.psi.tree.*; import com.reason.ide.files.*; import org.jetbrains.annotations.*; public class OclYaccParserDefinition implements ParserDefinition { private static final TokenSet WHITE_SPACES = TokenSet.create(TokenType.WHITE_SPACE); private static final TokenSet COMMENTS = TokenSet.create(OclYaccTypes.INSTANCE.MULTI_COMMENT, OclYaccTypes.INSTANCE.SINGLE_COMMENT); private static final IFileElementType FILE = new IFileElementType(Language.findInstance(OclYaccLanguage.class)); @Override public @NotNull Lexer createLexer(Project project) { return new FlexAdapter(new OclYaccLexer()); } public @NotNull TokenSet getWhitespaceTokens() { return WHITE_SPACES; } public @NotNull TokenSet getCommentTokens() { return COMMENTS; } public @NotNull TokenSet getStringLiteralElements() { return TokenSet.EMPTY; } public @NotNull PsiParser createParser(Project project) { return new OclYaccParser(true); } @Override public @NotNull IFileElementType getFileNodeType() { return FILE; } public @NotNull PsiFile createFile(@NotNull FileViewProvider viewProvider) { return new MlyFile(viewProvider); } public @NotNull SpaceRequirements spaceExistenceTypeBetweenTokens(ASTNode left, ASTNode right) { return SpaceRequirements.MAY; } @NotNull public PsiElement createElement(@NotNull ASTNode node) { return OclYaccAstFactory.createElement(node); } } ================================================ FILE: src/main/java/com/reason/lang/ocamlyacc/OclYaccTypes.java ================================================ // This is a generated file. Not intended for manual editing. package com.reason.lang.ocamlyacc; import com.reason.lang.core.type.*; public class OclYaccTypes extends ORTypes { public static final OclYaccTypes INSTANCE = new OclYaccTypes(); ORCompositeElementType C_DECLARATION = new ORCompositeElementType("C_DECLARATION", OclYaccLanguage.INSTANCE); ORCompositeElementType C_HEADER = new ORCompositeElementType("C_HEADER", OclYaccLanguage.INSTANCE); ORCompositeElementType C_RULE = new ORCompositeElementType("C_RULE", OclYaccLanguage.INSTANCE); ORCompositeElementType C_RULE_BODY = new ORCompositeElementType("C_RULE_BODY", OclYaccLanguage.INSTANCE); ORCompositeElementType C_INJECTION = new ORCompositeElementType("C_INJECTION", OclYaccLanguage.INSTANCE); OclYaccElementType ATOM = new OclYaccElementType("ATOM"); public OclYaccElementType COLON = new OclYaccElementType("COLON"); public OclYaccElementType SINGLE_COMMENT = new OclYaccElementType("SINGLE_COMMENT"); public OclYaccElementType MULTI_COMMENT = new OclYaccElementType("MULTI_COMMENT"); OclYaccElementType DOT = new OclYaccElementType("DOT"); OclYaccElementType GT = new OclYaccElementType("GT"); public OclYaccElementType HEADER_START = new OclYaccElementType("HEADER_START"); public OclYaccElementType HEADER_STOP = new OclYaccElementType("HEADER_STOP"); public OclYaccElementType IDENT = new OclYaccElementType("IDENT"); public OclYaccElementType LBRACE = new OclYaccElementType("LBRACE"); public OclYaccElementType LEFT = new OclYaccElementType("LEFT"); OclYaccElementType LT = new OclYaccElementType("LT"); public OclYaccElementType NON_ASSOC = new OclYaccElementType("NON_ASSOC"); public OclYaccElementType INLINE = new OclYaccElementType("INLINE"); public OclYaccElementType PIPE = new OclYaccElementType("PIPE"); public OclYaccElementType RBRACE = new OclYaccElementType("RBRACE"); public OclYaccElementType RIGHT = new OclYaccElementType("RIGHT"); public OclYaccElementType SECTION_SEPARATOR = new OclYaccElementType("SECTION_SEPARATOR"); public OclYaccElementType SEMI = new OclYaccElementType("SEMI"); public OclYaccElementType START = new OclYaccElementType("START"); public OclYaccElementType TOKEN = new OclYaccElementType("TOKEN"); public OclYaccElementType TYPE = new OclYaccElementType("TYPE"); public OclYaccElementType TEMPLATE_OCAML_TEXT = new OclYaccElementType("TEMPLATE_OCAML_TEXT"); } ================================================ FILE: src/main/java/com/reason/lang/ocamlyacc/mly.flex ================================================ package com.reason.lang.ocamlyacc; import com.intellij.psi.tree.IElementType; import com.intellij.lexer.FlexLexer; import static com.intellij.psi.TokenType.*; @SuppressWarnings("ALL") %% %public %unicode %class OclYaccLexer %implements FlexLexer %function advance %type IElementType %{ private OclYaccTypes types; private int tokenStartIndex; private CharSequence quotedStringId; private int braceDepth; private boolean rulesDone = false; private boolean zzEOFDone = false; private boolean zzAtBOL = false; public OclYaccLexer() { this((java.io.Reader)null); this.types = OclYaccTypes.INSTANCE; } // Store the start index of a token private void tokenStart() { tokenStartIndex = zzStartRead; } // Set the start index of the token to the stored index private void tokenEnd() { zzStartRead = tokenStartIndex; } %} %state INITIAL %state IN_HEADER %state IN_SEMANTIC_ACTION %state IN_ML_COMMENT %state IN_SL_COMMENT %state IN_TRAILER EOL=\n|\r|\r\n WHITE_SPACE_CHAR=[\ \t\f]|{EOL} WHITE_SPACE={WHITE_SPACE_CHAR}+ NEWLINE=("\r"* "\n") IDENT=[A-Za-z_0-9] %% { [^] { yybegin(INITIAL); yypushback(1); } } { {WHITE_SPACE} { return WHITE_SPACE; } "%{" { yybegin(IN_HEADER); tokenStart(); return types.HEADER_START; } "%}" { return types.HEADER_STOP; } "{" { yybegin(IN_SEMANTIC_ACTION); braceDepth = 1; tokenStart(); return types.LBRACE; } "}" { return types.RBRACE; } "%%" { if (rulesDone) { yybegin(IN_TRAILER); } rulesDone = true; return types.SECTION_SEPARATOR; } "%token" { return types.TOKEN; } "%start" { return types.START; } "%type" { return types.TYPE; } "%left" { return types.LEFT; } "%right" { return types.RIGHT; } "%nonassoc" { return types.NON_ASSOC; } "%inline" { return types.INLINE; } "." { return types.DOT; } ":" { return types.COLON; } ";" { return types.SEMI; } "|" { return types.PIPE; } "<" { return types.LT; } ">" { return types.GT; } "/*" { yybegin(IN_ML_COMMENT); tokenStart(); } "(*" { yybegin(IN_ML_COMMENT); tokenStart(); } "//" { yybegin(IN_SL_COMMENT); tokenStart(); } {IDENT}+ { return types.IDENT; } [^\ \t\f] { return types.ATOM; } } { "%}" { yypushback(2); tokenEnd(); yybegin(INITIAL); return types.TEMPLATE_OCAML_TEXT; } . { } {NEWLINE} { } <> { yybegin(INITIAL); tokenEnd(); return types.TEMPLATE_OCAML_TEXT; } } { "{" { braceDepth += 1; } "}" { braceDepth -= 1; if(braceDepth == 0) { yypushback(1); tokenEnd(); yybegin(INITIAL); return types.TEMPLATE_OCAML_TEXT; } } . | {NEWLINE} { } <> { yybegin(INITIAL); tokenEnd(); return types.TEMPLATE_OCAML_TEXT; } } { "*/" { yybegin(INITIAL); tokenEnd(); return types.MULTI_COMMENT; } "*)" { yybegin(INITIAL); tokenEnd(); return types.MULTI_COMMENT; } . | {NEWLINE} { } <> { yybegin(INITIAL); tokenEnd(); return types.MULTI_COMMENT; } } { . { } {NEWLINE} { yybegin(INITIAL); tokenEnd(); return types.SINGLE_COMMENT; } <> { yybegin(INITIAL); tokenEnd(); return types.SINGLE_COMMENT; } } { . | {NEWLINE} { } <> { yybegin(INITIAL); tokenEnd(); return types.TEMPLATE_OCAML_TEXT; } } [^] { return BAD_CHARACTER; } ================================================ FILE: src/main/java/com/reason/lang/reason/ReasonML.flex ================================================ package com.reason.lang.reason; import com.intellij.psi.tree.IElementType; import com.reason.lang.core.type.ORLangTypes; import com.intellij.lexer.FlexLexer; import static com.intellij.psi.TokenType.*; @SuppressWarnings("ALL") %% %{ public ReasonMLLexer(ORLangTypes types) { this.types = types; } private ORLangTypes types; private int tokenStartIndex; private CharSequence quotedStringId; private int commentDepth; private boolean inCommentString = false; //Store the start index of a token private void tokenStart() { tokenStartIndex = zzStartRead; } //Set the start index of the token to the stored index private void tokenEnd() { zzStartRead = tokenStartIndex; } %} %public %class ReasonMLLexer %implements FlexLexer %unicode %function advance %type IElementType EOL=\n|\r|\r\n WHITE_SPACE_CHAR=[\ \t\f]|{EOL} WHITE_SPACE={WHITE_SPACE_CHAR}+ LOWERCASE=[a-z_] UPPERCASE=[A-Z] IDENTCHAR=[A-Za-z_0-9'] DECIMAL=[0-9] DECIMAL_SEP=[0-9_] HEXA=[0-9A-Fa-f] HEXA_SEP=[0-9A-Fa-f_] OCTAL=[0-7] OCTAL_SEP=[0-7_] DECIMAL_LITERAL={DECIMAL} {DECIMAL_SEP}* HEXA_LITERAL="0" [xX] {HEXA} {HEXA_SEP}* OCT_LITERAL="0" [oO] {OCTAL} {OCTAL_SEP}* BIN_LITERAL="0" [bB] [0-1] [0-1_]* INT_LITERAL= {DECIMAL_LITERAL} | {HEXA_LITERAL} | {OCT_LITERAL} | {BIN_LITERAL} FLOAT_LITERAL={DECIMAL} {DECIMAL_SEP}* ("." {DECIMAL_SEP}* )? ([eE] [+-]? {DECIMAL} {DECIMAL_SEP}* )? HEXA_FLOAT_LITERAL="0" [xX] {HEXA} {HEXA_SEP}* ("." {HEXA_SEP}* )? ([pP] [+-]? {DECIMAL} {DECIMAL_SEP}* )? LITERAL_MODIFIER=[G-Zg-z] ESCAPE_BACKSLASH="\\\\" ESCAPE_SINGLE_QUOTE="\\'" ESCAPE_LF="\\n" ESCAPE_TAB="\\t" ESCAPE_BACKSPACE="\\b" ESCAPE_CR="\\r" ESCAPE_QUOTE="\\\"" ESCAPE_DECIMAL="\\" {DECIMAL} {DECIMAL} {DECIMAL} ESCAPE_HEXA="\\x" {HEXA} {HEXA} ESCAPE_OCTAL="\\o" [0-3] {OCTAL} {OCTAL} ESCAPE_CHAR= {ESCAPE_BACKSLASH} | {ESCAPE_SINGLE_QUOTE} | {ESCAPE_LF} | {ESCAPE_TAB} | {ESCAPE_BACKSPACE } | { ESCAPE_CR } | { ESCAPE_QUOTE } | {ESCAPE_DECIMAL} | {ESCAPE_HEXA} | {ESCAPE_OCTAL} %state INITIAL %state IN_STRING %state IN_REASON_ML_COMMENT %state IN_REASON_SL_COMMENT %% { [^] { yybegin(INITIAL); yypushback(1); } } { {WHITE_SPACE} { return WHITE_SPACE; } "and" { return types.AND; } "as" { return types.AS; } "assert" { return types.ASSERT; } "begin" { return types.BEGIN; } "class" { return types.CLASS; } "constraint" { return types.CONSTRAINT; } "do" { return types.DO; } "done" { return types.DONE; } "downto" { return types.DOWNTO; } "else" { return types.ELSE; } "end" { return types.END; } "exception" { return types.EXCEPTION; } "external" { return types.EXTERNAL; } "for" { return types.FOR; } "fun" { return types.FUN; } "function" { return types.FUNCTION; } "functor" { return types.FUNCTOR; } "if" { return types.IF; } "in" { return types.IN; } "include" { return types.INCLUDE; } "inherit" { return types.INHERIT; } "initializer" { return types.INITIALIZER; } "lazy" { return types.LAZY; } "let" { return types.LET; } "module" { return types.MODULE;} "mutable" { return types.MUTABLE; } "new" { return types.NEW; } "nonrec" { return types.NONREC; } "object" { return types.OBJECT; } "of" { return types.OF; } "open" { return types.OPEN; } "or" { return types.OR; } "pub" { return types.PUB; } "pri" { return types.PRI; } "rec" { return types.REC; } "sig" { return types.SIG; } "struct" { return types.STRUCT; } "switch" { return types.SWITCH; } "then" { return types.THEN; } "to" { return types.TO; } "try" { return types.TRY; } "type" { return types.TYPE; } "val" { return types.VAL; } "virtual" { return types.VIRTUAL; } "when" { return types.WHEN; } "while" { return types.WHILE; } "with" { return types.WITH; } "raw" { return types.RAW; } "mod" { return types.MOD; } "land" { return types.LAND; } "lor" { return types.LOR; } "lxor" { return types.LXOR; } "lsl" { return types.LSL; } "lsr" { return types.LSR; } "asr" { return types.ASR; } "unit" { return types.UNIT; } "ref" { return types.REF; } "raise" { return types.RAISE; } "method" { return types.METHOD; } "private" { return types.PRIVATE; } "match" { return types.MATCH; } "option" { return types.OPTION; } "None" { return types.NONE; } "Some" { return types.SOME; } "false" { return types.BOOL_VALUE; } "true" { return types.BOOL_VALUE; } "_" { return types.UNDERSCORE; } "'" ( {ESCAPE_CHAR} | . ) "'" { return types.CHAR_VALUE; } {LOWERCASE}{IDENTCHAR}* { return types.LIDENT; } {UPPERCASE}{IDENTCHAR}* { return types.UIDENT; } {INT_LITERAL}{LITERAL_MODIFIER}? { return types.INT_VALUE; } ({FLOAT_LITERAL} | {HEXA_FLOAT_LITERAL}){LITERAL_MODIFIER}? { return types.FLOAT_VALUE; } "'"{LOWERCASE}{IDENTCHAR}* { return types.TYPE_ARGUMENT; } "`"{UPPERCASE}{IDENTCHAR}* { return types.POLY_VARIANT; } "`"{LOWERCASE}{IDENTCHAR}* { return types.POLY_VARIANT; } "\"" { yybegin(IN_STRING); tokenStart(); } "/*" { yybegin(IN_REASON_ML_COMMENT); commentDepth = 1; tokenStart(); } "//" { yybegin(IN_REASON_SL_COMMENT); tokenStart(); } "#if" { return types.DIRECTIVE_IF; } "#else" { return types.DIRECTIVE_ELSE; } "#elif" { return types.DIRECTIVE_ELIF; } "#endif" { return types.DIRECTIVE_ENDIF; } "#end" { return types.DIRECTIVE_END; } "##" { return types.SHARPSHARP; } "@@" { return types.ARROBASE_2; } "@@@" { return types.ARROBASE_3; } "::" { return types.SHORTCUT; } "=>" { return types.ARROW; } "->" { return types.RIGHT_ARROW; } "<-" { return types.LEFT_ARROW; } "|>" { return types.PIPE_FORWARD; } "" { return types.TAG_AUTO_CLOSE; } "[|" { return types.LARRAY; } "|]" { return types.RARRAY; } //">]" { return types.GT_BRACKET; } //">}" { return types.GT_BRACE; } //"{<" { return types.BRACE_LT; } //"[<" { return types.BRACKET_LT; } //"[>" { return types.BRACKET_GT; } "{|" { return types.ML_STRING_OPEN; /*bs MultiLine*/ } "|}" { return types.ML_STRING_CLOSE; /*bs MultiLine*/ } "{j|" { return types.JS_STRING_OPEN; /*js interpolation*/ } "|j}" { return types.JS_STRING_CLOSE; /*js interpolation*/ } "===" { return types.EQEQEQ; } "==" { return types.EQEQ; } "=" { return types.EQ; } "!==" { return types.NOT_EQEQ; } "!=" { return types.NOT_EQ; } ":=" { return types.COLON_EQ; } ":>" { return types.COLON_GT; } "<=" { return types.LT_OR_EQUAL; } ">=" { return types.GT_OR_EQUAL; } ";;" { return types.SEMISEMI; } "||" { return types.L_OR; } "&&" { return types.L_AND; } "," { return types.COMMA; } ":" { return types.COLON; } ";" { return types.SEMI; } "'" { return types.SINGLE_QUOTE; } //"\"" { return types.DOUBLE_QUOTE; } can't be reached ? "..." { return types.DOTDOTDOT; } ".." { return types.DOTDOT; } "." { return types.DOT; } "|" { return types.PIPE; } "(" { return types.LPAREN; } ")" { return types.RPAREN; } "{" { return types.LBRACE; } "}" { return types.RBRACE; } "[" { return types.LBRACKET; } "]" { return types.RBRACKET; } "@" { return types.ARROBASE; } "#" { return types.SHARP; } "?" { return types.QUESTION_MARK; } "!" { return types.EXCLAMATION_MARK; } "$" { return types.DOLLAR; } "`" { return types.BACKTICK; } "~" { return types.TILDE; } "&" { return types.AMPERSAND; } "<" { return types.LT; } ">" { return types.GT; } "\^" { return types.CARRET; } "+." { return types.PLUSDOT; } "-." { return types.MINUSDOT; } "/." { return types.SLASHDOT; } "*." { return types.STARDOT; } "+" { return types.PLUS; } "-" { return types.MINUS; } "/" { return types.SLASH; } "*" { return types.STAR; } "%" { return types.PERCENT; } } { "\"" { yybegin(INITIAL); tokenEnd(); return types.STRING_VALUE; } "\\" {EOL} ([ \t] *) { } "\\" [\\\'\"ntbr ] { } "\\" [0-9] [0-9] [0-9] { } "\\" "o" [0-3] [0-7] [0-7] { } "\\" "x" [0-9a-fA-F] [0-9a-fA-F] { } "\\" . { } {EOL} { } . { } <> { yybegin(INITIAL); tokenEnd(); return types.STRING_VALUE; } } { "/*" { if (!inCommentString) commentDepth += 1; } "*/" { if (!inCommentString) { commentDepth -= 1; if(commentDepth == 0) { yybegin(INITIAL); tokenEnd(); return types.MULTI_COMMENT; } } } "\"" { inCommentString = !inCommentString; } . | {EOL} { } <> { yybegin(INITIAL); tokenEnd(); return types.MULTI_COMMENT; } } { . { } {EOL} { yybegin(INITIAL); tokenEnd(); return types.SINGLE_COMMENT; } <> { yybegin(INITIAL); tokenEnd(); return types.SINGLE_COMMENT; } } [^] { return BAD_CHARACTER; } ================================================ FILE: src/main/java/com/reason/lang/reason/ReasonMLLexer.java ================================================ /* The following code was generated by JFlex 1.7.0 tweaked for IntelliJ platform */ package com.reason.lang.reason; import com.intellij.psi.tree.IElementType; import com.reason.lang.core.type.ORLangTypes; import com.intellij.lexer.FlexLexer; import static com.intellij.psi.TokenType.*; @SuppressWarnings("ALL") /** * This class is a scanner generated by * JFlex 1.7.0 * from the specification file ReasonML.flex */ public class ReasonMLLexer implements FlexLexer { /** 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 INITIAL = 2; public static final int IN_STRING = 4; public static final int IN_REASON_ML_COMMENT = 6; public static final int IN_REASON_SL_COMMENT = 8; /** * 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 }; /** * Translates characters to character classes * Chosen bits are [9, 6, 6] * Total runtime size is 1568 bytes */ public static int ZZ_CMAP(int ch) { return ZZ_CMAP_A[(ZZ_CMAP_Y[ZZ_CMAP_Z[ch>>12]|((ch>>6)&0x3f)]<<6)|(ch&0x3f)]; } /* The ZZ_CMAP_Z table has 272 entries */ static final char ZZ_CMAP_Z[] = zzUnpackCMap( "\1\0\1\100\1\200\u010d\100"); /* The ZZ_CMAP_Y table has 192 entries */ static final char ZZ_CMAP_Y[] = zzUnpackCMap( "\1\0\1\1\1\2\175\3\1\4\77\3"); /* The ZZ_CMAP_A table has 320 entries */ static final char ZZ_CMAP_A[] = zzUnpackCMap( "\11\0\1\3\1\1\1\61\1\62\1\2\22\0\1\3\1\102\1\36\1\66\1\111\1\114\1\104\1\6"+ "\1\106\1\107\1\65\1\27\1\105\1\73\1\24\1\64\1\14\1\23\2\37\4\13\2\7\1\70\1"+ "\103\1\74\1\71\1\72\1\110\1\67\1\12\1\22\2\12\1\26\1\12\7\5\1\57\1\20\1\31"+ "\2\5\1\60\4\5\1\16\2\5\1\76\1\32\1\77\1\113\1\10\1\63\1\40\1\21\1\45\1\41"+ "\1\25\1\11\1\43\1\51\1\44\1\55\1\4\1\46\1\54\1\33\1\17\1\30\1\4\1\35\1\42"+ "\1\34\1\50\1\56\1\47\1\15\1\53\1\52\1\100\1\75\1\101\1\112\6\0\1\61\242\0"+ "\2\61\26\0"); /** * Translates DFA states to action switch labels. */ private static final int [] ZZ_ACTION = zzUnpackAction(); private static final String ZZ_ACTION_PACKED_0 = "\5\0\1\1\1\2\1\3\1\4\1\5\1\6\1\7"+ "\1\10\1\4\1\7\2\4\1\11\1\4\1\12\4\4"+ "\1\13\12\4\2\5\1\14\1\15\1\16\1\17\1\20"+ "\1\21\1\22\1\23\1\24\1\25\1\26\1\27\1\30"+ "\1\31\1\32\1\33\1\34\1\35\1\36\1\37\1\40"+ "\1\41\1\42\1\43\1\44\1\45\3\46\1\47\1\50"+ "\2\46\2\51\1\0\1\52\1\0\1\7\1\53\1\0"+ "\3\4\2\7\1\0\1\54\2\4\1\55\1\4\1\56"+ "\3\4\1\57\4\4\1\60\6\4\1\61\1\62\3\4"+ "\1\63\1\64\20\4\2\5\1\65\1\66\1\67\1\70"+ "\1\71\1\72\2\0\1\73\1\74\1\75\1\76\1\77"+ "\1\100\1\101\1\102\1\103\1\104\1\105\1\106\1\107"+ "\1\0\1\110\1\111\1\112\1\113\1\114\1\0\1\115"+ "\1\116\1\117\1\120\5\46\1\121\1\122\1\123\1\52"+ "\2\123\3\0\2\53\1\0\1\124\1\4\1\125\3\7"+ "\4\4\1\126\2\4\1\127\1\4\1\130\1\131\1\4"+ "\1\132\1\4\1\133\2\4\1\134\1\135\1\4\1\136"+ "\1\137\1\140\4\4\1\141\7\4\1\142\1\143\2\4"+ "\1\144\1\145\4\4\1\146\3\4\1\147\1\4\2\5"+ "\2\0\1\150\1\151\1\152\1\153\1\154\1\155\6\0"+ "\1\4\1\53\1\7\1\4\1\156\4\4\1\157\2\4"+ "\1\160\1\161\1\162\2\4\1\163\10\4\1\164\1\165"+ "\1\166\1\167\1\170\1\4\1\171\5\4\1\172\1\173"+ "\1\174\4\0\1\4\1\53\2\4\1\175\4\4\1\176"+ "\10\4\1\177\1\200\2\4\1\201\2\4\1\0\1\202"+ "\1\203\2\4\1\204\1\205\3\4\1\206\1\207\1\210"+ "\1\211\1\212\4\4\1\213\1\214\2\4\1\215\1\216"+ "\3\4\1\217\1\4\1\220\1\221\1\4\1\222\1\223"+ "\1\224\1\225\3\4\1\226\3\4\1\227\1\230"; private static int [] zzUnpackAction() { int [] result = new int[368]; 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\115\0\232\0\347\0\u0134\0\u0181\0\u0181\0\u01ce"+ "\0\u021b\0\u0268\0\u02b5\0\u0302\0\u021b\0\u034f\0\u039c\0\u03e9"+ "\0\u0436\0\u0483\0\u04d0\0\u051d\0\u056a\0\u05b7\0\u0604\0\u0651"+ "\0\u0181\0\u069e\0\u06eb\0\u0738\0\u0785\0\u07d2\0\u081f\0\u086c"+ "\0\u08b9\0\u0906\0\u0953\0\u09a0\0\u09ed\0\u0a3a\0\u0a87\0\u0ad4"+ "\0\u0b21\0\u0b6e\0\u0bbb\0\u0c08\0\u0c55\0\u0ca2\0\u0cef\0\u0d3c"+ "\0\u0d89\0\u0181\0\u0dd6\0\u0181\0\u0e23\0\u0e70\0\u0ebd\0\u0181"+ "\0\u0181\0\u0181\0\u0181\0\u0181\0\u0181\0\u0181\0\u0181\0\u0181"+ "\0\u0f0a\0\u0f57\0\u0181\0\u0181\0\u0fa4\0\u0ff1\0\u0181\0\u103e"+ "\0\u108b\0\u10d8\0\u1125\0\u0181\0\u1172\0\u11bf\0\u120c\0\u1259"+ "\0\u12a6\0\u12f3\0\u1340\0\u138d\0\u021b\0\u13da\0\u1427\0\u021b"+ "\0\u1474\0\u14c1\0\u150e\0\u155b\0\u15a8\0\u0181\0\u15f5\0\u1642"+ "\0\u168f\0\u16dc\0\u021b\0\u1729\0\u1776\0\u17c3\0\u1810\0\u185d"+ "\0\u18aa\0\u18f7\0\u1944\0\u1991\0\u19de\0\u1a2b\0\u021b\0\u1a78"+ "\0\u1ac5\0\u1b12\0\u1b5f\0\u1bac\0\u1bf9\0\u1c46\0\u1c93\0\u1ce0"+ "\0\u1d2d\0\u1d7a\0\u1dc7\0\u1e14\0\u1e61\0\u1eae\0\u1efb\0\u1f48"+ "\0\u1f95\0\u1fe2\0\u202f\0\u0181\0\u0181\0\u0181\0\u0181\0\u0181"+ "\0\u207c\0\u20c9\0\u0181\0\u2116\0\u0181\0\u0181\0\u0181\0\u2163"+ "\0\u0181\0\u0181\0\u0181\0\u0181\0\u0181\0\u0181\0\u0181\0\u21b0"+ "\0\u0181\0\u0181\0\u0181\0\u0181\0\u0181\0\u21fd\0\u0181\0\u224a"+ "\0\u0181\0\u0181\0\u2297\0\u22e4\0\u2331\0\u237e\0\u23cb\0\u0181"+ "\0\u0181\0\u0181\0\u2418\0\u2418\0\u108b\0\u2465\0\u24b2\0\u24ff"+ "\0\u0181\0\u254c\0\u2599\0\u021b\0\u25e6\0\u2633\0\u2680\0\u26cd"+ "\0\u271a\0\u2767\0\u27b4\0\u2801\0\u284e\0\u0181\0\u289b\0\u28e8"+ "\0\u021b\0\u2935\0\u2982\0\u021b\0\u29cf\0\u021b\0\u2a1c\0\u021b"+ "\0\u2a69\0\u2ab6\0\u021b\0\u021b\0\u2b03\0\u021b\0\u021b\0\u021b"+ "\0\u2b50\0\u2b9d\0\u2bea\0\u2c37\0\u021b\0\u2c84\0\u2cd1\0\u2d1e"+ "\0\u2d6b\0\u2db8\0\u2e05\0\u2e52\0\u021b\0\u021b\0\u2e9f\0\u2eec"+ "\0\u021b\0\u021b\0\u2f39\0\u2f86\0\u2fd3\0\u3020\0\u306d\0\u30ba"+ "\0\u3107\0\u3154\0\u021b\0\u31a1\0\u31ee\0\u323b\0\u3288\0\u32d5"+ "\0\u0181\0\u0181\0\u0181\0\u0181\0\u0181\0\u0181\0\u3322\0\u336f"+ "\0\u33bc\0\u3409\0\u3456\0\u34a3\0\u34f0\0\u353d\0\u11bf\0\u358a"+ "\0\u021b\0\u35d7\0\u3624\0\u3671\0\u36be\0\u021b\0\u370b\0\u3758"+ "\0\u021b\0\u021b\0\u021b\0\u37a5\0\u37f2\0\u021b\0\u383f\0\u388c"+ "\0\u38d9\0\u3926\0\u3973\0\u39c0\0\u3a0d\0\u3a5a\0\u021b\0\u021b"+ "\0\u021b\0\u021b\0\u021b\0\u3aa7\0\u021b\0\u3af4\0\u3b41\0\u3b8e"+ "\0\u3bdb\0\u3c28\0\u0268\0\u0268\0\u3c75\0\u3cc2\0\u3d0f\0\u3d5c"+ "\0\u3da9\0\u3df6\0\u11bf\0\u3e43\0\u3e90\0\u021b\0\u3edd\0\u3f2a"+ "\0\u3f77\0\u3fc4\0\u021b\0\u4011\0\u405e\0\u40ab\0\u40f8\0\u4145"+ "\0\u4192\0\u41df\0\u422c\0\u021b\0\u021b\0\u4279\0\u42c6\0\u021b"+ "\0\u4313\0\u4360\0\u43ad\0\u0181\0\u0181\0\u43fa\0\u4447\0\u021b"+ "\0\u021b\0\u4494\0\u44e1\0\u452e\0\u021b\0\u021b\0\u021b\0\u021b"+ "\0\u021b\0\u457b\0\u45c8\0\u4615\0\u4662\0\u021b\0\u021b\0\u46af"+ "\0\u46fc\0\u0181\0\u021b\0\u4749\0\u4796\0\u47e3\0\u021b\0\u4830"+ "\0\u021b\0\u021b\0\u487d\0\u021b\0\u021b\0\u021b\0\u021b\0\u48ca"+ "\0\u4917\0\u4964\0\u021b\0\u49b1\0\u49fe\0\u4a4b\0\u021b\0\u021b"; private static int [] zzUnpackRowMap() { int [] result = new int[368]; 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(); 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 = "\115\6\1\7\3\10\1\11\1\12\1\13\1\14\1\15"+ "\1\16\1\12\1\14\1\17\1\11\1\12\1\20\1\12"+ "\1\21\1\12\1\14\1\22\1\23\1\12\1\24\1\25"+ "\1\12\1\7\1\26\1\27\1\30\1\31\1\14\1\32"+ "\1\33\1\34\1\11\1\35\1\36\1\37\1\40\1\41"+ "\3\11\1\42\1\11\1\43\1\44\1\45\1\7\1\10"+ "\1\46\1\47\1\50\1\51\1\52\1\53\1\54\1\55"+ "\1\56\1\57\1\60\1\61\1\62\1\63\1\64\1\65"+ "\1\66\1\67\1\70\1\71\1\72\1\73\1\74\1\75"+ "\1\76\1\77\2\100\1\101\27\100\1\102\3\100\1\103"+ "\22\100\2\7\34\100\1\101\33\100\1\104\22\100\2\7"+ "\1\100\1\105\1\106\30\100\1\107\1\110\56\100\2\7"+ "\32\100\116\0\3\10\56\0\1\10\36\0\20\11\1\0"+ "\2\11\1\0\2\11\1\0\3\11\1\0\22\11\40\0"+ "\20\12\1\0\2\12\1\0\2\12\1\0\3\12\1\0"+ "\22\12\34\0\1\111\2\0\1\111\1\112\3\111\2\112"+ "\3\111\1\112\1\111\1\112\1\111\1\112\3\111\1\112"+ "\2\111\1\112\1\111\1\113\3\112\2\111\17\112\2\111"+ "\2\0\32\111\4\0\2\114\1\0\2\14\2\0\2\14"+ "\4\114\2\0\1\14\1\115\2\116\1\0\2\114\1\0"+ "\3\114\1\0\1\14\2\0\3\114\1\0\13\114\40\0"+ "\13\11\1\117\4\11\1\0\2\11\1\0\2\11\1\0"+ "\3\11\1\0\1\11\1\120\7\11\1\121\10\11\40\0"+ "\2\114\1\0\2\14\2\0\2\14\2\122\2\123\2\124"+ "\1\14\1\115\2\116\1\0\2\114\1\0\3\114\1\0"+ "\1\14\2\0\3\114\1\0\13\114\40\0\5\11\1\125"+ "\7\11\1\126\2\11\1\0\2\11\1\0\1\127\1\11"+ "\1\0\2\11\1\130\1\0\22\11\40\0\20\11\1\0"+ "\1\131\1\11\1\0\2\11\1\0\3\11\1\0\22\11"+ "\60\0\1\132\74\0\11\11\1\133\6\11\1\0\2\11"+ "\1\0\2\11\1\0\1\134\2\11\1\0\7\11\1\135"+ "\12\11\60\0\1\136\74\0\20\11\1\0\2\11\1\0"+ "\2\11\1\0\2\11\1\137\1\0\11\11\1\140\10\11"+ "\40\0\13\11\1\141\4\11\1\0\1\142\1\11\1\0"+ "\2\11\1\0\3\11\1\0\22\11\40\0\13\11\1\143"+ "\4\11\1\0\2\11\1\0\2\11\1\0\2\11\1\144"+ "\1\0\12\11\1\145\1\11\1\146\5\11\40\0\20\11"+ "\1\0\1\147\1\11\1\0\2\11\1\0\3\11\1\0"+ "\1\11\1\150\20\11\40\0\20\11\1\0\2\11\1\0"+ "\2\11\1\0\1\151\2\11\1\0\3\11\1\152\16\11"+ "\40\0\13\11\1\153\4\11\1\0\2\11\1\0\2\11"+ "\1\0\3\11\1\0\22\11\40\0\20\11\1\0\2\11"+ "\1\0\2\11\1\0\1\11\1\154\1\11\1\0\5\11"+ "\1\155\2\11\1\156\11\11\40\0\5\11\1\157\12\11"+ "\1\0\2\11\1\0\2\11\1\0\1\160\2\11\1\0"+ "\22\11\40\0\13\11\1\161\4\11\1\0\2\11\1\0"+ "\2\11\1\0\3\11\1\0\7\11\1\162\12\11\40\0"+ "\11\11\1\163\1\11\1\164\4\11\1\0\1\165\1\11"+ "\1\0\2\11\1\0\3\11\1\0\1\11\1\166\1\11"+ "\1\167\16\11\40\0\20\11\1\0\2\11\1\0\2\11"+ "\1\0\3\11\1\0\5\11\1\170\4\11\1\171\7\11"+ "\40\0\20\11\1\0\2\11\1\0\2\11\1\0\1\172"+ "\2\11\1\0\22\11\40\0\13\11\1\173\4\11\1\0"+ "\1\174\1\11\1\0\2\11\1\0\3\11\1\0\1\11"+ "\1\175\7\11\1\176\10\11\40\0\20\11\1\0\2\11"+ "\1\0\2\11\1\0\3\11\1\0\1\11\1\177\3\11"+ "\1\200\14\11\40\0\13\12\1\201\4\12\1\0\2\12"+ "\1\0\2\12\1\0\3\12\1\0\22\12\40\0\13\12"+ "\1\202\4\12\1\0\2\12\1\0\2\12\1\0\3\12"+ "\1\0\22\12\40\0\2\203\2\0\3\203\2\0\6\203"+ "\2\0\2\203\1\0\2\203\1\0\3\203\2\0\21\203"+ "\60\0\1\204\37\0\1\205\1\206\4\0\1\207\46\0"+ "\1\210\115\0\1\211\16\0\1\212\21\0\1\213\115\0"+ "\1\214\115\0\1\215\1\216\1\217\113\0\1\220\1\221"+ "\113\0\1\222\47\0\1\223\45\0\1\224\106\0\1\225"+ "\4\0\1\226\1\0\1\227\76\0\1\230\14\0\1\231"+ "\2\0\1\232\1\0\1\233\1\0\1\234\110\0\1\235"+ "\74\0\1\236\17\0\1\237\110\0\1\240\126\0\1\241"+ "\115\0\1\242\11\0\1\100\113\0\1\100\1\243\1\244"+ "\4\100\1\245\3\100\2\245\1\246\1\100\1\247\3\100"+ "\1\245\13\100\1\245\21\100\2\0\32\100\65\0\1\250"+ "\113\0\1\251\31\0\1\107\121\0\1\252\112\0\2\253"+ "\1\254\15\253\1\0\2\253\1\0\2\253\1\0\3\253"+ "\1\0\22\253\42\0\1\255\1\256\3\0\2\256\1\257"+ "\1\0\1\260\1\0\1\111\1\0\1\256\6\0\5\111"+ "\1\256\61\0\2\261\1\0\2\115\2\0\2\115\4\261"+ "\2\0\1\115\1\0\2\116\1\0\2\261\1\0\3\261"+ "\1\0\1\115\2\0\3\261\1\0\13\261\43\0\1\262"+ "\3\0\2\262\6\0\1\262\3\0\1\263\7\0\1\262"+ "\33\0\1\263\25\0\20\11\1\0\2\11\1\0\2\11"+ "\1\0\2\11\1\264\1\0\22\11\40\0\20\11\1\0"+ "\2\11\1\0\2\11\1\0\3\11\1\0\7\11\1\265"+ "\12\11\40\0\20\11\1\0\2\11\1\0\2\11\1\0"+ "\1\266\2\11\1\0\22\11\43\0\1\267\1\0\4\267"+ "\4\0\3\267\1\0\2\267\10\0\3\267\3\0\1\267"+ "\62\0\2\270\6\0\1\270\13\0\1\270\71\0\1\271"+ "\6\0\1\271\75\0\20\11\1\0\2\11\1\0\2\11"+ "\1\0\3\11\1\0\16\11\1\272\3\11\40\0\20\11"+ "\1\0\1\273\1\11\1\0\2\11\1\0\1\11\1\274"+ "\1\11\1\0\22\11\40\0\20\11\1\0\2\11\1\0"+ "\2\11\1\0\3\11\1\0\4\11\1\275\15\11\60\0"+ "\1\276\74\0\20\11\1\0\2\11\1\0\2\11\1\0"+ "\1\11\1\277\1\11\1\0\6\11\1\300\13\11\40\0"+ "\20\11\1\0\2\11\1\0\2\11\1\0\3\11\1\0"+ "\2\11\1\301\17\11\40\0\20\11\1\0\2\11\1\0"+ "\2\11\1\0\3\11\1\0\3\11\1\302\16\11\40\0"+ "\20\11\1\0\2\11\1\0\2\11\1\0\3\11\1\0"+ "\5\11\1\303\14\11\40\0\15\11\1\304\2\11\1\0"+ "\2\11\1\0\2\11\1\0\3\11\1\0\22\11\40\0"+ "\20\11\1\0\2\11\1\0\2\11\1\0\1\305\2\11"+ "\1\0\22\11\40\0\20\11\1\0\2\11\1\0\2\11"+ "\1\0\3\11\1\0\10\11\1\306\11\11\40\0\20\11"+ "\1\0\2\11\1\0\2\11\1\0\3\11\1\0\11\11"+ "\1\307\2\11\1\310\5\11\40\0\20\11\1\0\1\311"+ "\1\11\1\0\2\11\1\0\3\11\1\0\22\11\40\0"+ "\20\11\1\0\2\11\1\0\1\312\1\11\1\0\3\11"+ "\1\0\22\11\40\0\5\11\1\313\12\11\1\0\2\11"+ "\1\0\2\11\1\0\3\11\1\0\6\11\1\314\13\11"+ "\40\0\20\11\1\0\2\11\1\0\2\11\1\0\3\11"+ "\1\0\5\11\1\315\2\11\1\316\11\11\40\0\20\11"+ "\1\0\2\11\1\0\2\11\1\0\3\11\1\0\2\11"+ "\1\317\17\11\40\0\20\11\1\0\2\11\1\0\2\11"+ "\1\0\2\11\1\320\1\0\3\11\1\321\16\11\40\0"+ "\20\11\1\0\2\11\1\0\2\11\1\0\1\322\2\11"+ "\1\0\10\11\1\323\11\11\40\0\20\11\1\0\2\11"+ "\1\0\2\11\1\0\2\11\1\324\1\0\22\11\40\0"+ "\20\11\1\0\2\11\1\0\2\11\1\0\3\11\1\0"+ "\4\11\1\325\15\11\40\0\20\11\1\0\2\11\1\0"+ "\2\11\1\0\3\11\1\0\5\11\1\326\14\11\40\0"+ "\20\11\1\0\2\11\1\0\2\11\1\0\3\11\1\0"+ "\5\11\1\327\1\330\3\11\1\331\7\11\40\0\20\11"+ "\1\0\2\11\1\0\2\11\1\0\1\332\2\11\1\0"+ "\22\11\40\0\20\11\1\0\2\11\1\0\2\11\1\0"+ "\3\11\1\0\1\11\1\333\20\11\40\0\13\11\1\334"+ "\4\11\1\0\2\11\1\0\2\11\1\0\3\11\1\0"+ "\22\11\40\0\20\11\1\0\2\11\1\0\2\11\1\0"+ "\2\11\1\335\1\0\22\11\40\0\20\11\1\0\2\11"+ "\1\0\2\11\1\0\1\11\1\336\1\11\1\0\22\11"+ "\40\0\20\11\1\0\2\11\1\0\2\11\1\0\1\337"+ "\2\11\1\0\13\11\1\340\6\11\40\0\20\11\1\0"+ "\2\11\1\0\2\11\1\0\2\11\1\341\1\0\7\11"+ "\1\342\12\11\40\0\20\11\1\0\2\11\1\0\2\11"+ "\1\0\1\11\1\343\1\11\1\0\22\11\40\0\20\11"+ "\1\0\1\344\1\11\1\0\2\11\1\0\3\11\1\0"+ "\5\11\1\345\14\11\40\0\20\11\1\0\2\11\1\0"+ "\2\11\1\0\3\11\1\0\5\11\1\346\14\11\40\0"+ "\20\11\1\0\2\11\1\0\2\11\1\0\3\11\1\0"+ "\2\11\1\347\17\11\40\0\20\11\1\0\2\11\1\0"+ "\2\11\1\0\1\11\1\350\1\11\1\0\22\11\40\0"+ "\20\11\1\0\2\11\1\0\2\11\1\0\1\11\1\351"+ "\1\11\1\0\22\11\40\0\20\11\1\0\2\11\1\0"+ "\2\11\1\0\1\11\1\352\1\11\1\0\22\11\40\0"+ "\20\11\1\0\2\11\1\0\2\11\1\0\3\11\1\0"+ "\7\11\1\353\12\11\40\0\20\11\1\0\2\11\1\0"+ "\2\11\1\0\2\11\1\354\1\0\22\11\40\0\20\12"+ "\1\0\2\12\1\0\2\12\1\0\1\355\2\12\1\0"+ "\22\12\40\0\20\12\1\0\2\12\1\0\2\12\1\0"+ "\3\12\1\0\15\12\1\356\4\12\40\0\20\203\1\0"+ "\2\203\1\0\2\203\1\0\3\203\1\0\22\203\67\0"+ "\1\357\12\0\1\360\57\0\1\361\172\0\1\362\116\0"+ "\1\363\124\0\1\364\110\0\1\365\110\0\1\366\26\0"+ "\1\243\112\0\1\243\1\0\1\243\120\0\1\367\3\0"+ "\2\367\6\0\1\367\13\0\1\367\64\0\1\370\1\0"+ "\4\370\4\0\3\370\1\0\2\370\10\0\3\370\3\0"+ "\1\370\63\0\1\371\6\0\1\371\13\0\1\371\61\0"+ "\20\253\1\0\2\253\1\0\2\253\1\0\3\253\1\0"+ "\22\253\43\0\1\372\3\0\2\372\6\0\1\372\13\0"+ "\1\372\64\0\1\373\1\0\4\373\4\0\3\373\1\0"+ "\2\373\10\0\3\373\3\0\1\373\63\0\1\374\6\0"+ "\1\374\13\0\1\374\61\0\2\261\1\0\2\262\2\0"+ "\2\262\4\261\2\0\1\262\4\0\2\261\1\0\3\261"+ "\1\0\1\262\2\0\3\261\1\0\13\261\43\0\1\262"+ "\3\0\2\262\6\0\1\262\13\0\1\262\61\0\20\11"+ "\1\0\2\11\1\0\2\11\1\0\3\11\1\0\3\11"+ "\1\307\16\11\40\0\20\11\1\0\2\11\1\0\2\11"+ "\1\0\3\11\1\0\6\11\1\375\13\11\40\0\2\114"+ "\1\0\6\267\4\114\3\267\1\376\2\267\1\0\2\377"+ "\1\0\3\114\1\0\3\267\3\114\1\267\13\114\40\0"+ "\2\114\2\0\1\270\2\0\2\270\4\114\2\0\1\270"+ "\4\0\2\114\1\0\3\114\1\0\1\270\2\0\3\114"+ "\1\0\13\114\40\0\2\114\2\0\1\271\3\0\1\271"+ "\4\114\2\0\1\271\4\0\2\114\1\0\3\114\4\0"+ "\3\114\1\0\13\114\40\0\20\11\1\0\1\u0100\1\11"+ "\1\0\2\11\1\0\3\11\1\0\22\11\40\0\20\11"+ "\1\0\2\11\1\0\2\11\1\0\1\u0101\2\11\1\0"+ "\22\11\40\0\20\11\1\0\2\11\1\0\2\11\1\0"+ "\3\11\1\0\5\11\1\u0102\14\11\40\0\20\11\1\0"+ "\2\11\1\0\2\11\1\0\3\11\1\0\5\11\1\u0103"+ "\14\11\40\0\20\11\1\0\1\u0104\1\11\1\0\2\11"+ "\1\0\3\11\1\0\22\11\40\0\20\11\1\0\1\u0105"+ "\1\11\1\0\2\11\1\0\3\11\1\0\22\11\40\0"+ "\20\11\1\0\1\u0106\1\11\1\0\2\11\1\0\3\11"+ "\1\0\22\11\40\0\20\11\1\0\2\11\1\0\2\11"+ "\1\0\3\11\1\0\17\11\1\u0107\2\11\40\0\20\11"+ "\1\0\2\11\1\0\2\11\1\0\2\11\1\u0108\1\0"+ "\22\11\40\0\20\11\1\0\1\u0109\1\11\1\0\2\11"+ "\1\0\3\11\1\0\22\11\40\0\20\11\1\0\2\11"+ "\1\0\2\11\1\0\1\u010a\2\11\1\0\22\11\40\0"+ "\20\11\1\0\1\u010b\1\11\1\0\2\11\1\0\3\11"+ "\1\0\22\11\40\0\20\11\1\0\2\11\1\0\2\11"+ "\1\0\3\11\1\0\3\11\1\u010c\16\11\40\0\20\11"+ "\1\0\1\u010d\1\11\1\0\2\11\1\0\3\11\1\0"+ "\22\11\40\0\20\11\1\0\1\u010e\1\11\1\0\2\11"+ "\1\0\3\11\1\0\22\11\40\0\20\11\1\0\2\11"+ "\1\0\2\11\1\0\1\u010f\2\11\1\0\22\11\40\0"+ "\20\11\1\0\2\11\1\0\2\11\1\0\3\11\1\0"+ "\11\11\1\u0110\10\11\40\0\20\11\1\0\2\11\1\0"+ "\2\11\1\0\1\11\1\u0111\1\11\1\0\22\11\40\0"+ "\20\11\1\0\2\11\1\0\2\11\1\0\1\11\1\u0112"+ "\1\11\1\0\22\11\40\0\20\11\1\0\2\11\1\0"+ "\2\11\1\0\3\11\1\0\7\11\1\u0113\12\11\40\0"+ "\20\11\1\0\1\u0114\1\11\1\0\2\11\1\0\3\11"+ "\1\0\22\11\40\0\20\11\1\0\2\11\1\0\2\11"+ "\1\0\3\11\1\0\3\11\1\u0115\16\11\40\0\20\11"+ "\1\0\2\11\1\0\2\11\1\0\3\11\1\0\3\11"+ "\1\u0116\16\11\40\0\20\11\1\0\2\11\1\0\2\11"+ "\1\0\2\11\1\u0117\1\0\22\11\40\0\20\11\1\0"+ "\2\11\1\0\2\11\1\0\3\11\1\0\2\11\1\u0118"+ "\17\11\40\0\20\11\1\0\2\11\1\0\2\11\1\0"+ "\3\11\1\0\14\11\1\u0119\5\11\40\0\20\11\1\0"+ "\2\11\1\0\2\11\1\0\3\11\1\0\12\11\1\u011a"+ "\7\11\40\0\20\11\1\0\2\11\1\0\2\11\1\0"+ "\1\u011b\2\11\1\0\22\11\40\0\20\11\1\0\2\11"+ "\1\0\2\11\1\0\3\11\1\0\7\11\1\u011c\12\11"+ "\40\0\20\11\1\0\2\11\1\0\2\11\1\0\1\11"+ "\1\u011d\1\11\1\0\22\11\40\0\20\11\1\0\2\11"+ "\1\0\2\11\1\0\3\11\1\0\11\11\1\u011e\10\11"+ "\40\0\20\11\1\0\2\11\1\0\2\11\1\0\3\11"+ "\1\0\12\11\1\u011f\7\11\40\0\20\11\1\0\2\11"+ "\1\0\2\11\1\0\3\11\1\0\6\11\1\u0120\13\11"+ "\40\0\20\11\1\0\2\11\1\0\2\11\1\0\3\11"+ "\1\0\1\11\1\u0121\20\11\40\0\20\11\1\0\2\11"+ "\1\0\2\11\1\0\1\11\1\u0122\1\11\1\0\22\11"+ "\40\0\20\12\1\0\1\u0123\1\12\1\0\2\12\1\0"+ "\3\12\1\0\22\12\40\0\20\12\1\0\1\u0124\1\12"+ "\1\0\2\12\1\0\3\12\1\0\22\12\75\0\1\u0125"+ "\115\0\1\u0126\1\0\1\u0127\57\0\1\100\3\0\2\100"+ "\6\0\1\100\13\0\1\100\64\0\1\100\1\0\4\100"+ "\4\0\3\100\1\0\2\100\10\0\3\100\3\0\1\100"+ "\62\0\2\u0128\6\0\1\u0128\13\0\1\u0128\64\0\1\111"+ "\3\0\2\111\6\0\1\111\13\0\1\111\64\0\1\111"+ "\1\0\4\111\4\0\3\111\1\0\2\111\10\0\3\111"+ "\3\0\1\111\62\0\2\u0129\6\0\1\u0129\13\0\1\u0129"+ "\61\0\20\11\1\0\2\11\1\0\2\11\1\0\1\11"+ "\1\u012a\1\11\1\0\22\11\40\0\2\261\1\0\6\376"+ "\4\261\3\376\1\0\2\376\1\0\2\u012b\1\0\3\261"+ "\1\0\3\376\3\261\1\376\13\261\40\0\20\11\1\0"+ "\2\11\1\0\2\11\1\0\3\11\1\0\6\11\1\u012c"+ "\13\11\40\0\13\11\1\u012d\4\11\1\0\2\11\1\0"+ "\2\11\1\0\3\11\1\0\22\11\40\0\20\11\1\0"+ "\2\11\1\0\2\11\1\0\1\u012e\2\11\1\0\22\11"+ "\40\0\20\11\1\0\2\11\1\0\2\11\1\0\2\11"+ "\1\u012f\1\0\22\11\40\0\20\11\1\0\2\11\1\0"+ "\1\u0130\1\11\1\0\3\11\1\0\22\11\40\0\20\11"+ "\1\0\2\11\1\0\2\11\1\0\3\11\1\0\1\11"+ "\1\u0131\20\11\40\0\20\11\1\0\1\u0132\1\11\1\0"+ "\2\11\1\0\3\11\1\0\22\11\40\0\20\11\1\0"+ "\1\u0133\1\11\1\0\2\11\1\0\3\11\1\0\22\11"+ "\40\0\20\11\1\0\2\11\1\0\2\11\1\0\2\11"+ "\1\u0134\1\0\22\11\40\0\20\11\1\0\2\11\1\0"+ "\2\11\1\0\1\11\1\u0135\1\11\1\0\22\11\40\0"+ "\20\11\1\0\2\11\1\0\2\11\1\0\3\11\1\0"+ "\6\11\1\u0136\13\11\40\0\20\11\1\0\2\11\1\0"+ "\2\11\1\0\3\11\1\0\6\11\1\u0137\13\11\40\0"+ "\20\11\1\0\2\11\1\0\2\11\1\0\3\11\1\0"+ "\5\11\1\u0138\14\11\40\0\20\11\1\0\2\11\1\0"+ "\2\11\1\0\3\11\1\0\11\11\1\u0139\10\11\40\0"+ "\20\11\1\0\2\11\1\0\2\11\1\0\2\11\1\u013a"+ "\1\0\22\11\40\0\20\11\1\0\2\11\1\0\2\11"+ "\1\0\1\11\1\u013b\1\11\1\0\22\11\40\0\20\11"+ "\1\0\2\11\1\0\2\11\1\0\3\11\1\0\3\11"+ "\1\u013c\16\11\40\0\20\11\1\0\1\u013d\1\11\1\0"+ "\2\11\1\0\3\11\1\0\22\11\40\0\20\11\1\0"+ "\2\11\1\0\2\11\1\0\3\11\1\0\7\11\1\u013e"+ "\12\11\40\0\13\11\1\u013f\4\11\1\0\2\11\1\0"+ "\2\11\1\0\3\11\1\0\22\11\40\0\20\11\1\0"+ "\2\11\1\0\2\11\1\0\3\11\1\0\12\11\1\u0140"+ "\7\11\40\0\15\11\1\u0141\2\11\1\0\2\11\1\0"+ "\2\11\1\0\3\11\1\0\22\11\40\0\20\11\1\0"+ "\2\11\1\0\2\11\1\0\3\11\1\0\11\11\1\u0142"+ "\10\11\100\0\1\u0143\75\0\1\u0144\100\0\1\u0145\116\0"+ "\2\100\6\0\1\100\13\0\1\100\70\0\2\111\6\0"+ "\1\111\13\0\1\111\61\0\13\11\1\u0146\4\11\1\0"+ "\2\11\1\0\2\11\1\0\3\11\1\0\5\11\1\u0147"+ "\14\11\40\0\20\11\1\0\2\11\1\0\2\11\1\0"+ "\1\11\1\u0148\1\11\1\0\22\11\40\0\20\11\1\0"+ "\2\11\1\0\2\11\1\0\1\u0149\2\11\1\0\22\11"+ "\40\0\20\11\1\0\2\11\1\0\2\11\1\0\1\u014a"+ "\2\11\1\0\22\11\40\0\20\11\1\0\2\11\1\0"+ "\2\11\1\0\1\11\1\u014b\1\11\1\0\22\11\40\0"+ "\20\11\1\0\2\11\1\0\2\11\1\0\1\11\1\u014c"+ "\1\11\1\0\22\11\40\0\20\11\1\0\2\11\1\0"+ "\2\11\1\0\3\11\1\0\6\11\1\u014d\13\11\40\0"+ "\20\11\1\0\2\11\1\0\2\11\1\0\1\11\1\u014e"+ "\1\11\1\0\22\11\40\0\13\11\1\u014f\4\11\1\0"+ "\2\11\1\0\2\11\1\0\3\11\1\0\22\11\40\0"+ "\20\11\1\0\2\11\1\0\2\11\1\0\1\11\1\u0150"+ "\1\11\1\0\22\11\40\0\20\11\1\0\2\11\1\0"+ "\2\11\1\0\3\11\1\0\12\11\1\u0151\7\11\40\0"+ "\20\11\1\0\2\11\1\0\2\11\1\0\3\11\1\0"+ "\1\11\1\u0152\20\11\40\0\20\11\1\0\2\11\1\0"+ "\2\11\1\0\3\11\1\0\2\11\1\u0153\17\11\40\0"+ "\20\11\1\0\2\11\1\0\2\11\1\0\3\11\1\0"+ "\5\11\1\u0154\14\11\40\0\20\11\1\0\2\11\1\0"+ "\2\11\1\0\2\11\1\u0155\1\0\22\11\40\0\20\11"+ "\1\0\1\u0156\1\11\1\0\2\11\1\0\3\11\1\0"+ "\22\11\40\0\20\11\1\0\2\11\1\0\2\11\1\0"+ "\3\11\1\0\2\11\1\u0157\17\11\40\0\20\11\1\0"+ "\2\11\1\0\2\11\1\0\3\11\1\0\7\11\1\u0158"+ "\12\11\40\0\20\11\1\0\2\11\1\0\2\11\1\0"+ "\3\11\1\0\1\11\1\u0159\20\11\45\0\1\u015a\107\0"+ "\20\11\1\0\2\11\1\0\2\11\1\0\2\11\1\u015b"+ "\1\0\22\11\40\0\13\11\1\u015c\4\11\1\0\2\11"+ "\1\0\2\11\1\0\3\11\1\0\22\11\40\0\20\11"+ "\1\0\2\11\1\0\2\11\1\0\3\11\1\0\1\11"+ "\1\u015d\20\11\40\0\20\11\1\0\2\11\1\0\2\11"+ "\1\0\3\11\1\0\5\11\1\u015e\14\11\40\0\20\11"+ "\1\0\1\u015f\1\11\1\0\2\11\1\0\3\11\1\0"+ "\22\11\40\0\20\11\1\0\2\11\1\0\2\11\1\0"+ "\3\11\1\0\7\11\1\u0160\12\11\40\0\20\11\1\0"+ "\1\u0161\1\11\1\0\2\11\1\0\3\11\1\0\22\11"+ "\40\0\20\11\1\0\2\11\1\0\2\11\1\0\1\11"+ "\1\u0162\1\11\1\0\22\11\40\0\20\11\1\0\2\11"+ "\1\0\2\11\1\0\3\11\1\0\1\11\1\u0163\20\11"+ "\40\0\20\11\1\0\1\u0164\1\11\1\0\2\11\1\0"+ "\3\11\1\0\22\11\40\0\20\11\1\0\2\11\1\0"+ "\2\11\1\0\3\11\1\0\7\11\1\u0165\12\11\40\0"+ "\20\11\1\0\2\11\1\0\2\11\1\0\1\u0166\2\11"+ "\1\0\22\11\40\0\20\11\1\0\2\11\1\0\2\11"+ "\1\0\3\11\1\0\7\11\1\u0167\12\11\40\0\13\11"+ "\1\u0168\4\11\1\0\2\11\1\0\2\11\1\0\3\11"+ "\1\0\22\11\40\0\20\11\1\0\2\11\1\0\2\11"+ "\1\0\3\11\1\0\5\11\1\u0169\14\11\40\0\20\11"+ "\1\0\2\11\1\0\2\11\1\0\3\11\1\0\5\11"+ "\1\u016a\14\11\40\0\20\11\1\0\2\11\1\0\2\11"+ "\1\0\1\u016b\2\11\1\0\22\11\40\0\20\11\1\0"+ "\2\11\1\0\2\11\1\0\3\11\1\0\13\11\1\u016c"+ "\6\11\40\0\20\11\1\0\2\11\1\0\2\11\1\0"+ "\1\u016d\2\11\1\0\22\11\40\0\20\11\1\0\1\u016e"+ "\1\11\1\0\2\11\1\0\3\11\1\0\22\11\40\0"+ "\20\11\1\0\2\11\1\0\2\11\1\0\1\11\1\u016f"+ "\1\11\1\0\22\11\40\0\20\11\1\0\2\11\1\0"+ "\2\11\1\0\2\11\1\u0170\1\0\22\11\34\0"; private static int [] zzUnpackTrans() { int [] result = new int[19096]; 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 aState */ private static final int [] ZZ_ATTRIBUTE = zzUnpackAttribute(); private static final String ZZ_ATTRIBUTE_PACKED_0 = "\5\0\2\11\21\1\1\11\30\1\1\11\1\1\1\11"+ "\3\1\11\11\2\1\2\11\2\1\1\11\1\1\1\0"+ "\1\1\1\0\1\11\1\1\1\0\5\1\1\0\11\1"+ "\1\11\45\1\5\11\2\0\1\11\1\1\3\11\1\1"+ "\7\11\1\0\5\11\1\0\1\11\1\1\2\11\5\1"+ "\3\11\3\1\3\0\1\11\1\1\1\0\12\1\1\11"+ "\60\1\2\0\6\11\6\0\51\1\4\0\31\1\1\0"+ "\2\11\24\1\1\11\26\1"; private static int [] zzUnpackAttribute() { int [] result = new int[368]; 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; /* user code: */ public ReasonMLLexer(ORLangTypes types) { this.types = types; } private ORLangTypes types; private int tokenStartIndex; private CharSequence quotedStringId; private int commentDepth; private boolean inCommentString = false; //Store the start index of a token private void tokenStart() { tokenStartIndex = zzStartRead; } //Set the start index of the token to the stored index private void tokenEnd() { zzStartRead = tokenStartIndex; } /** * Creates a new scanner * * @param in the java.io.Reader to read input from. */ public ReasonMLLexer(java.io.Reader in) { this.zzReader = in; } /** * Unpacks the compressed character translation table. * * @param packed the packed character translation table * @return the unpacked character translation table */ private static char [] zzUnpackCMap(String packed) { int size = 0; for (int i = 0, length = packed.length(); i < length; i += 2) { size += packed.charAt(i); } char[] map = new char[size]; int i = 0; /* index in packed string */ int j = 0; /* index in unpacked array */ while (i < packed.length()) { int count = packed.charAt(i++); char value = packed.charAt(i++); do map[j++] = value; while (--count > 0); } return map; } 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; 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/*, zzEndReadL*/); 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/*, zzEndReadL*/); zzCurrentPosL += Character.charCount(zzInput); } } int zzNext = zzTransL[ zzRowMapL[zzState] + ZZ_CMAP(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; switch (zzLexicalState) { case IN_STRING: { yybegin(INITIAL); tokenEnd(); return types.STRING_VALUE; } // fall though case 369: break; case IN_REASON_ML_COMMENT: { yybegin(INITIAL); tokenEnd(); return types.MULTI_COMMENT; } // fall though case 370: break; case IN_REASON_SL_COMMENT: { yybegin(INITIAL); tokenEnd(); return types.SINGLE_COMMENT; } // fall though case 371: break; default: return null; } } else { switch (zzAction < 0 ? zzAction : ZZ_ACTION[zzAction]) { case 1: { yybegin(INITIAL); yypushback(1); } // fall through case 153: break; case 2: { return BAD_CHARACTER; } // fall through case 154: break; case 3: { return WHITE_SPACE; } // fall through case 155: break; case 4: { return types.LIDENT; } // fall through case 156: break; case 5: { return types.UIDENT; } // fall through case 157: break; case 6: { return types.SINGLE_QUOTE; } // fall through case 158: break; case 7: { return types.INT_VALUE; } // fall through case 159: break; case 8: { return types.UNDERSCORE; } // fall through case 160: break; case 9: { return types.DOT; } // fall through case 161: break; case 10: { return types.PLUS; } // fall through case 162: break; case 11: { yybegin(IN_STRING); tokenStart(); } // fall through case 163: break; case 12: { return types.BACKTICK; } // fall through case 164: break; case 13: { return types.SLASH; } // fall through case 165: break; case 14: { return types.STAR; } // fall through case 166: break; case 15: { return types.SHARP; } // fall through case 167: break; case 16: { return types.ARROBASE; } // fall through case 168: break; case 17: { return types.COLON; } // fall through case 169: break; case 18: { return types.EQ; } // fall through case 170: break; case 19: { return types.GT; } // fall through case 171: break; case 20: { return types.MINUS; } // fall through case 172: break; case 21: { return types.LT; } // fall through case 173: break; case 22: { return types.PIPE; } // fall through case 174: break; case 23: { return types.LBRACKET; } // fall through case 175: break; case 24: { return types.RBRACKET; } // fall through case 176: break; case 25: { return types.LBRACE; } // fall through case 177: break; case 26: { return types.RBRACE; } // fall through case 178: break; case 27: { return types.EXCLAMATION_MARK; } // fall through case 179: break; case 28: { return types.SEMI; } // fall through case 180: break; case 29: { return types.AMPERSAND; } // fall through case 181: break; case 30: { return types.COMMA; } // fall through case 182: break; case 31: { return types.LPAREN; } // fall through case 183: break; case 32: { return types.RPAREN; } // fall through case 184: break; case 33: { return types.QUESTION_MARK; } // fall through case 185: break; case 34: { return types.DOLLAR; } // fall through case 186: break; case 35: { return types.TILDE; } // fall through case 187: break; case 36: { return types.CARRET; } // fall through case 188: break; case 37: { return types.PERCENT; } // fall through case 189: break; case 38: { } // fall through case 190: break; case 39: { yybegin(INITIAL); tokenEnd(); return types.STRING_VALUE; } // fall through case 191: break; case 40: { inCommentString = !inCommentString; } // fall through case 192: break; case 41: { yybegin(INITIAL); tokenEnd(); return types.SINGLE_COMMENT; } // fall through case 193: break; case 42: { return types.TYPE_ARGUMENT; } // fall through case 194: break; case 43: { return types.FLOAT_VALUE; } // fall through case 195: break; case 44: { return types.OF; } // fall through case 196: break; case 45: { return types.OR; } // fall through case 197: break; case 46: { return types.DOTDOT; } // fall through case 198: break; case 47: { return types.PLUSDOT; } // fall through case 199: break; case 48: { return types.TO; } // fall through case 200: break; case 49: { return types.AS; } // fall through case 201: break; case 50: { return types.DO; } // fall through case 202: break; case 51: { return types.IF; } // fall through case 203: break; case 52: { return types.IN; } // fall through case 204: break; case 53: { return types.POLY_VARIANT; } // fall through case 205: break; case 54: { return types.SLASHDOT; } // fall through case 206: break; case 55: { yybegin(IN_REASON_SL_COMMENT); tokenStart(); } // fall through case 207: break; case 56: { yybegin(IN_REASON_ML_COMMENT); commentDepth = 1; tokenStart(); } // fall through case 208: break; case 57: { return types.TAG_AUTO_CLOSE; } // fall through case 209: break; case 58: { return types.STARDOT; } // fall through case 210: break; case 59: { return types.SHARPSHARP; } // fall through case 211: break; case 60: { return types.ARROBASE_2; } // fall through case 212: break; case 61: { return types.SHORTCUT; } // fall through case 213: break; case 62: { return types.COLON_EQ; } // fall through case 214: break; case 63: { return types.COLON_GT; } // fall through case 215: break; case 64: { return types.EQEQ; } // fall through case 216: break; case 65: { return types.ARROW; } // fall through case 217: break; case 66: { return types.GT_OR_EQUAL; } // fall through case 218: break; case 67: { return types.MINUSDOT; } // fall through case 219: break; case 68: { return types.RIGHT_ARROW; } // fall through case 220: break; case 69: { return types.TAG_LT_SLASH; } // fall through case 221: break; case 70: { return types.LT_OR_EQUAL; } // fall through case 222: break; case 71: { return types.LEFT_ARROW; } // fall through case 223: break; case 72: { return types.PIPE_FORWARD; } // fall through case 224: break; case 73: { return types.L_OR; } // fall through case 225: break; case 74: { return types.RARRAY; } // fall through case 226: break; case 75: { return types.ML_STRING_CLOSE; /*bs MultiLine*/ } // fall through case 227: break; case 76: { return types.LARRAY; } // fall through case 228: break; case 77: { return types.ML_STRING_OPEN; /*bs MultiLine*/ } // fall through case 229: break; case 78: { return types.NOT_EQ; } // fall through case 230: break; case 79: { return types.SEMISEMI; } // fall through case 231: break; case 80: { return types.L_AND; } // fall through case 232: break; case 81: { if (!inCommentString) commentDepth += 1; } // fall through case 233: break; case 82: { if (!inCommentString) { commentDepth -= 1; if(commentDepth == 0) { yybegin(INITIAL); tokenEnd(); return types.MULTI_COMMENT; } } } // fall through case 234: break; case 83: { return types.CHAR_VALUE; } // fall through case 235: break; case 84: { return types.FOR; } // fall through case 236: break; case 85: { return types.FUN; } // fall through case 237: break; case 86: { return types.DOTDOTDOT; } // fall through case 238: break; case 87: { return types.END; } // fall through case 239: break; case 88: { return types.PRI; } // fall through case 240: break; case 89: { return types.PUB; } // fall through case 241: break; case 90: { return types.NEW; } // fall through case 242: break; case 91: { return types.TRY; } // fall through case 243: break; case 92: { return types.REF; } // fall through case 244: break; case 93: { return types.REC; } // fall through case 245: break; case 94: { return types.RAW; } // fall through case 246: break; case 95: { return types.AND; } // fall through case 247: break; case 96: { return types.ASR; } // fall through case 248: break; case 97: { return types.SIG; } // fall through case 249: break; case 98: { return types.LOR; } // fall through case 250: break; case 99: { return types.LET; } // fall through case 251: break; case 100: { return types.LSR; } // fall through case 252: break; case 101: { return types.LSL; } // fall through case 253: break; case 102: { return types.MOD; } // fall through case 254: break; case 103: { return types.VAL; } // fall through case 255: break; case 104: { return types.DIRECTIVE_IF; } // fall through case 256: break; case 105: { return types.ARROBASE_3; } // fall through case 257: break; case 106: { return types.EQEQEQ; } // fall through case 258: break; case 107: { return types.JS_STRING_CLOSE; /*js interpolation*/ } // fall through case 259: break; case 108: { return types.JS_STRING_OPEN; /*js interpolation*/ } // fall through case 260: break; case 109: { return types.NOT_EQEQ; } // fall through case 261: break; case 110: { return types.OPEN; } // fall through case 262: break; case 111: { return types.ELSE; } // fall through case 263: break; case 112: { return types.BOOL_VALUE; } // fall through case 264: break; case 113: { return types.THEN; } // fall through case 265: break; case 114: { return types.TYPE; } // fall through case 266: break; case 115: { return types.DONE; } // fall through case 267: break; case 116: { return types.LXOR; } // fall through case 268: break; case 117: { return types.LAND; } // fall through case 269: break; case 118: { return types.LAZY; } // fall through case 270: break; case 119: { return types.WITH; } // fall through case 271: break; case 120: { return types.WHEN; } // fall through case 272: break; case 121: { return types.UNIT; } // fall through case 273: break; case 122: { return types.NONE; } // fall through case 274: break; case 123: { return types.SOME; } // fall through case 275: break; case 124: { return types.DIRECTIVE_END; } // fall through case 276: break; case 125: { return types.BEGIN; } // fall through case 277: break; case 126: { return types.RAISE; } // fall through case 278: break; case 127: { return types.CLASS; } // fall through case 279: break; case 128: { return types.WHILE; } // fall through case 280: break; case 129: { return types.MATCH; } // fall through case 281: break; case 130: { return types.DIRECTIVE_ELSE; } // fall through case 282: break; case 131: { return types.DIRECTIVE_ELIF; } // fall through case 283: break; case 132: { return types.OBJECT; } // fall through case 284: break; case 133: { return types.OPTION; } // fall through case 285: break; case 134: { return types.NONREC; } // fall through case 286: break; case 135: { return types.ASSERT; } // fall through case 287: break; case 136: { return types.DOWNTO; } // fall through case 288: break; case 137: { return types.STRUCT; } // fall through case 289: break; case 138: { return types.SWITCH; } // fall through case 290: break; case 139: { return types.MODULE; } // fall through case 291: break; case 140: { return types.METHOD; } // fall through case 292: break; case 141: { return types.DIRECTIVE_ENDIF; } // fall through case 293: break; case 142: { return types.FUNCTOR; } // fall through case 294: break; case 143: { return types.PRIVATE; } // fall through case 295: break; case 144: { return types.INCLUDE; } // fall through case 296: break; case 145: { return types.INHERIT; } // fall through case 297: break; case 146: { return types.MUTABLE; } // fall through case 298: break; case 147: { return types.VIRTUAL; } // fall through case 299: break; case 148: { return types.FUNCTION; } // fall through case 300: break; case 149: { return types.EXTERNAL; } // fall through case 301: break; case 150: { return types.EXCEPTION; } // fall through case 302: break; case 151: { return types.CONSTRAINT; } // fall through case 303: break; case 152: { return types.INITIALIZER; } // fall through case 304: break; default: zzScanError(ZZ_NO_MATCH); } } } } } ================================================ FILE: src/main/java/com/reason/lang/reason/RmlASTFactory.java ================================================ package com.reason.lang.reason; import com.reason.lang.core.psi.impl.ORASTFactory; public class RmlASTFactory extends ORASTFactory { public RmlASTFactory() { super(RmlTypes.INSTANCE); } } ================================================ FILE: src/main/java/com/reason/lang/reason/RmlLanguage.java ================================================ package com.reason.lang.reason; import com.intellij.lang.Language; import com.reason.lang.*; import org.jetbrains.annotations.*; public class RmlLanguage extends Language implements ORLanguageProperties { public static final RmlLanguage INSTANCE = new RmlLanguage(); private RmlLanguage() { super("Reason"); } @Override public @NotNull String getParameterSeparator() { return ", "; } @Override public @NotNull String getFunctionSeparator() { return " => "; } @Override public @NotNull String getTemplateStart() { return "("; } @Override public @NotNull String getTemplateEnd() { return ")"; } } ================================================ FILE: src/main/java/com/reason/lang/reason/RmlLexer.java ================================================ package com.reason.lang.reason; import com.intellij.lexer.*; public class RmlLexer extends FlexAdapter { public RmlLexer() { super(new ReasonMLLexer(RmlTypes.INSTANCE)); } } ================================================ FILE: src/main/java/com/reason/lang/reason/RmlParser.java ================================================ package com.reason.lang.reason; import com.intellij.lang.*; import com.intellij.psi.tree.*; import com.reason.lang.*; import org.jetbrains.annotations.*; import static com.intellij.codeInsight.completion.CompletionUtilCore.*; public class RmlParser extends CommonPsiParser { RmlParser(boolean isSafe) { super(isSafe); } @Override protected ORParser getORParser(@NotNull PsiBuilder builder) { return new RmlParserState(builder, myIsSafe); } static class RmlParserState extends ORLanguageParser { public RmlParserState(@NotNull PsiBuilder builder, boolean isSafe) { super(RmlTypes.INSTANCE, builder, isSafe); } @Override public void parse() { IElementType tokenType; long parseStart = System.currentTimeMillis(); int parseCount = 0; while (!myBuilder.eof()) { parseCount++; if (parseCount > 100) { parseCount = 0; long parseTime = System.currentTimeMillis(); if (PARSE_MAX_TIME < parseTime - parseStart) { if (myIsSafe) { // Don't do that in tests error("Parsing cancelled, you should create a github issue with the source code"); break; } } } tokenType = myBuilder.getTokenType(); if (in(myTypes.C_INTERPOLATION_EXPR)) { // special analysis when inside an interpolation string if (tokenType == myTypes.JS_STRING_CLOSE) { parseJsStringClose(); } else if (tokenType == myTypes.DOLLAR) { if (is(myTypes.C_INTERPOLATION_PART)) { popEnd(); advance().mark(myTypes.C_INTERPOLATION_REF); } else if (is(myTypes.C_INTERPOLATION_EXPR)) { // first element advance().mark(myTypes.C_INTERPOLATION_REF); } } else if (is(myTypes.C_INTERPOLATION_REF)) { advance().popEnd(); } else if (!is(myTypes.C_INTERPOLATION_PART)) { mark(myTypes.C_INTERPOLATION_PART); } } else { if (tokenType == myTypes.SEMI) { parseSemi(); } else if (tokenType == myTypes.EQ) { parseEq(); } else if (tokenType == myTypes.COLON_EQ) { parseColonEq(); } else if (tokenType == myTypes.ARROW) { parseArrow(); } else if (tokenType == myTypes.REF) { parseRef(); } else if (tokenType == myTypes.METHOD) { parseMethod(); } else if (tokenType == myTypes.INITIALIZER) { parseInitializer(); } else if (tokenType == myTypes.OPTION) { parseOption(); } else if (tokenType == myTypes.MATCH) { parseMatch(); } else if (tokenType == myTypes.TRY) { parseTry(); } else if (tokenType == myTypes.SWITCH) { parseSwitch(); } else if (tokenType == myTypes.LIDENT) { parseLIdent(); } else if (tokenType == myTypes.UIDENT) { parseUIdent(); } else if (tokenType == myTypes.POLY_VARIANT) { parsePolyVariant(); } else if (tokenType == myTypes.ARROBASE) { parseArrobase(); } else if (tokenType == myTypes.PERCENT) { parsePercent(); } else if (tokenType == myTypes.COLON) { parseColon(); } else if (tokenType == myTypes.RAW) { parseRaw(); } else if (tokenType == myTypes.STRING_VALUE) { parseStringValue(); } else if (tokenType == myTypes.PIPE) { parsePipe(); } else if (tokenType == myTypes.COMMA) { parseComma(); } else if (tokenType == myTypes.AND) { parseAnd(); } else if (tokenType == myTypes.FUN) { parseFun(); } else if (tokenType == myTypes.ASSERT) { parseAssert(); } else if (tokenType == myTypes.IF) { parseIf(); } else if (tokenType == myTypes.ELSE) { parseElse(); } else if (tokenType == myTypes.THEN) { parseThen(); } else if (tokenType == myTypes.DOT) { parseDot(); } else if (tokenType == myTypes.DOTDOTDOT) { parseDotDotDot(); } else if (tokenType == myTypes.WITH) { parseWith(); } else if (tokenType == myTypes.TILDE) { parseTilde(); } else if (tokenType == myTypes.QUESTION_MARK) { parseQuestionMark(); } else if (tokenType == myTypes.UNDERSCORE) { parseUnderscore(); } else if (tokenType == myTypes.SOME) { parseSome(); } else if (tokenType == myTypes.TYPE_ARGUMENT) { parseTypeArgument(); } // ( ... ) else if (tokenType == myTypes.LPAREN) { parseLParen(); } else if (tokenType == myTypes.RPAREN) { parseRParen(); } // { ... } else if (tokenType == myTypes.LBRACE) { parseLBrace(); } else if (tokenType == myTypes.RBRACE) { parseRBrace(); } // [| ... |] else if (tokenType == myTypes.LARRAY) { parseLArray(); } else if (tokenType == myTypes.RARRAY) { parseRArray(); } // [ ... ] // [> ... ] else if (tokenType == myTypes.LBRACKET) { parseLBracket(); } else if (tokenType == myTypes.BRACKET_GT) { parseBracketGt(); } else if (tokenType == myTypes.RBRACKET) { parseRBracket(); } // < ... > else if (tokenType == myTypes.LT) { parseLt(); } else if (tokenType == myTypes.TAG_LT_SLASH) { parseLtSlash(); } else if (tokenType == myTypes.GT) { parseGt(); } else if (tokenType == myTypes.TAG_AUTO_CLOSE) { parseGtAutoClose(); } // {| ... |} else if (tokenType == myTypes.ML_STRING_OPEN) { parseMlStringOpen(); } else if (tokenType == myTypes.ML_STRING_CLOSE) { parseMlStringClose(); } // {j| ... |j} else if (tokenType == myTypes.JS_STRING_OPEN) { parseJsStringOpen(); } // Starts an expression else if (tokenType == myTypes.OPEN) { parseOpen(); } else if (tokenType == myTypes.INCLUDE) { parseInclude(); } else if (tokenType == myTypes.EXTERNAL) { parseExternal(); } else if (tokenType == myTypes.TYPE) { parseType(); } else if (tokenType == myTypes.MODULE) { parseModule(); } else if (tokenType == myTypes.CLASS) { parseClass(); } else if (tokenType == myTypes.LET) { parseLet(); } else if (tokenType == myTypes.VAL) { parseVal(); } else if (tokenType == myTypes.PUB) { parsePub(); } else if (tokenType == myTypes.EXCEPTION) { parseException(); } } if (dontMove) { dontMove = false; } else { myBuilder.advanceLexer(); } } } private void parseTypeArgument() { if (strictlyIn(myTypes.C_PARAMETERS) && isParent(myTypes.C_TYPE_DECLARATION) && !isCurrent(myTypes.C_PARAM_DECLARATION)) { mark(myTypes.C_PARAM_DECLARATION); } } private void parseSome() { mark(myTypes.C_SOME); advance(); if (getTokenType() != myTypes.LPAREN) { error("Missing parenthesis"); } else { markScope(myTypes.C_PARAMETERS, myTypes.LPAREN).advance() .markHolder(myTypes.H_COLLECTION_ITEM); } } private void parseMethod() { if (isCurrent(myTypes.C_RECORD_EXPR)) { // { |>method<| : ... remapCurrentToken(myTypes.LIDENT).mark(myTypes.C_RECORD_FIELD); } } private void parseInitializer() { mark(myTypes.C_CLASS_INITIALIZER); advance(); if (getTokenType() == myTypes.LBRACE) { updateScopeToken(myTypes.LBRACE).advance(); } } private void parsePolyVariant() { if (isRawParent(myTypes.C_TYPE_BINDING)) { // type t = [ |>`xxx<| ... if (!is(myTypes.C_VARIANT_DECLARATION)) { wrapWith(myTypes.C_VARIANT_DECLARATION); } } else { wrapAtom(myTypes.CA_UPPER_SYMBOL); } markParenthesisScope(false); } private void parseUnderscore() { if (is(myTypes.C_PARAMETERS) && isRawParent(myTypes.C_FUNCTION_EXPR)) { // ( |>_<| ... mark(myTypes.C_PARAM_DECLARATION); } else { IElementType nextElementType = lookAhead(1); if (nextElementType == myTypes.ARROW && strictlyInAny(myTypes.C_LET_BINDING, myTypes.C_DEFAULT_VALUE, myTypes.C_PARAM, myTypes.C_FIELD_VALUE, myTypes.C_PARAMETERS)) { // A paren-less function definition :: |>_<| => mark(myTypes.C_FUNCTION_EXPR) .mark(myTypes.C_PARAMETERS) .mark(myTypes.C_PARAM_DECLARATION) .wrapAtom(myTypes.CA_LOWER_SYMBOL); } } } private void parseTilde() { IElementType nextType = rawLookup(1); if (nextType == myTypes.LIDENT) { if (isCurrent(myTypes.C_PARAM_DECLARATION)) { markBefore(0, myTypes.C_PARAM_DECLARATION) .updateComposite(myTypes.H_NAMED_PARAM_DECLARATION).updateToHolder() .advance().wrapAtom(myTypes.CA_LOWER_SYMBOL); } else if (isCurrent(myTypes.C_SIG_ITEM)) { mark(myTypes.C_PARAM_DECLARATION) .markHolder(myTypes.H_NAMED_PARAM_DECLARATION) .advance().wrapAtom(myTypes.CA_LOWER_SYMBOL); } else { if (isCurrent(myTypes.C_PARAMETERS) && in(myTypes.C_FUNCTION_EXPR)) { mark(myTypes.C_PARAM_DECLARATION).markHolder(myTypes.H_NAMED_PARAM_DECLARATION); } else if (isCurrent(myTypes.C_PARAM)) { updateComposite(myTypes.C_NAMED_PARAM).popIfHold(); } else { mark(myTypes.C_NAMED_PARAM).popIfHold(); } advance().wrapAtom(myTypes.CA_LOWER_SYMBOL); } } } private void parseQuestionMark() { if (is(myTypes.C_TAG_START)) { // ?<|prop ... mark(myTypes.C_TAG_PROPERTY) .setWhitespaceSkippedCallback(endJsxPropertyIfWhitespace()) .advance() .remapCurrentToken(myTypes.PROPERTY_NAME); } else if (strictlyIn(myTypes.C_IF_THEN_ELSE)) { // if_then_scope can be inside a ternary parseTernary(); } else if (!strictlyInAny(myTypes.C_TERNARY) && previousElementType(1) != myTypes.EQ /*default optional value*/) { if (inScopeOrAny(myTypes.C_LET_BINDING, myTypes.C_FIELD_VALUE, myTypes.C_PARAM_DECLARATION, myTypes.C_PARAM, myTypes.C_PATTERN_MATCH_BODY, myTypes.C_IF_THEN_ELSE, myTypes.C_FUNCTION_BODY, myTypes.H_COLLECTION_ITEM, myTypes.C_DEFAULT_VALUE)) { // a new ternary parseTernary(); } } } private void parseTernary() { // «placeHolder» ... |>?<| ... int foundPos = getIndex(); int nextPos = foundPos - 1; if (isAtIndex(nextPos, myTypes.H_PLACE_HOLDER)) { markBefore(nextPos, myTypes.C_TERNARY) .updateCompositeAt(nextPos, myTypes.C_BINARY_CONDITION) .popEndUntilIndex(nextPos).end() .advance().mark(myTypes.C_IF_THEN_ELSE); markHolder(myTypes.H_PLACE_HOLDER); } else if (isAtIndex(foundPos, myTypes.H_COLLECTION_ITEM)) { markHolderBefore(foundPos, myTypes.H_COLLECTION_ITEM) .markBefore(foundPos, myTypes.C_TERNARY) .updateCompositeAt(foundPos, myTypes.C_BINARY_CONDITION) .popEndUntilIndex(foundPos).end() .advance().mark(myTypes.C_IF_THEN_ELSE); } } private void parseRef() { if (isCurrent(myTypes.C_RECORD_EXPR)) { remapCurrentToken(myTypes.LIDENT).mark(myTypes.C_RECORD_FIELD); } else if (strictlyIn(myTypes.C_TAG_START)) { remapCurrentToken(myTypes.PROPERTY_NAME) .mark(myTypes.C_TAG_PROPERTY); } } private void generateJsxPropertyName() { remapCurrentToken(myTypes.PROPERTY_NAME) .mark(myTypes.C_TAG_PROPERTY) .setWhitespaceSkippedCallback(endJsxPropertyIfWhitespace()); } private void parseOption() { if (strictlyIn(myTypes.C_TAG_START)) { generateJsxPropertyName(); } else { mark(myTypes.C_OPTION); } } private void parseMatch() { if (strictlyIn(myTypes.C_TAG_START)) { generateJsxPropertyName(); } } private void parseRaw() { if (is(myTypes.C_MACRO_NAME)) { // % |>raw<| ... advance().popEnd(); } } private void parseIf() { mark(myTypes.C_IF); } private void parseThen() { // if ... |>then<| ... popEndUntil(myTypes.C_IF).advance() .mark(myTypes.C_IF_THEN_ELSE); } private void parseElse() { // if ... then ... |>else<| ... popEndUntil(myTypes.C_IF).advance() .mark(myTypes.C_IF_THEN_ELSE); } private void parseDot() { if (previousElementType(1) == myTypes.LBRACE && (is(myTypes.C_JS_OBJECT))) { // Js object definition // ... { |>.<| ... } advance().mark(myTypes.C_OBJECT_FIELD); } } private void parseDotDotDot() { if (previousElementType(1) == myTypes.LBRACE) { // Mixin, always first element in a record // { |>...<| x ... if (isCurrent(myTypes.C_FUNCTION_BODY)) { markBefore(0, myTypes.C_FUNCTION_BODY) .updateLatestComposite(myTypes.C_RECORD_EXPR) .mark(myTypes.C_MIXIN_FIELD); } else { popIfHold() .updateComposite(myTypes.C_RECORD_EXPR) .mark(myTypes.C_MIXIN_FIELD); } } } private void parseWith() { if (strictlyInAny(myTypes.C_FUNCTOR_RESULT, myTypes.C_MODULE_SIGNATURE)) { // module M (X) : ( S |>with<| ... ) = ... popEndUntilFoundIndex().popEnd() .advance().mark(myTypes.C_CONSTRAINTS); } } private void parseAssert() { mark(myTypes.C_ASSERT_STMT).advance(); } private void parseFun() { if (isCurrent(myTypes.C_LET_BINDING) && isHold()) { // fun keyword is equivalent to a switch body // let x = |>fun<| | ... updateLatestComposite(myTypes.C_FUN_EXPR); } } private void parseAnd() { if (in(myTypes.C_TYPE_CONSTRAINT)) { // module M = (X) : ( S with ... |>and<| ... ) = ... popEndUntilFoundIndex().popEnd(); } else { Marker scope = popEndUntilScope(); if (scope != null) { if (scope.isCompositeType(myTypes.C_TYPE_DECLARATION)) { advance().mark(myTypes.C_TYPE_DECLARATION); } else if (scope.isCompositeType(myTypes.C_LET_DECLARATION)) { advance().mark(myTypes.C_LET_DECLARATION); } else if (scope.isCompositeType(myTypes.C_MODULE_DECLARATION) || scope.isCompositeType(myTypes.C_MODULE_SIGNATURE)) { advance().mark(myTypes.C_MODULE_DECLARATION); } } } } private void parseComma() { if (inScopeOrAny( myTypes.C_RECORD_FIELD, myTypes.C_OBJECT_FIELD, myTypes.C_MIXIN_FIELD, myTypes.C_VARIANT_CONSTRUCTOR, myTypes.C_SIG_EXPR, myTypes.C_SIG_ITEM, myTypes.H_COLLECTION_ITEM, myTypes.C_PARAMETERS, myTypes.C_PARAM_DECLARATION, myTypes.C_NAMED_PARAM, myTypes.C_PARAM )) { if (isFound(myTypes.H_COLLECTION_ITEM)) { popEndUntilFoundIndex().popEnd().advance(); markHolder(myTypes.H_COLLECTION_ITEM); return; } if (isFound(myTypes.C_SCOPED_EXPR) && isAtIndex(getIndex() + 1, myTypes.C_LET_DECLARATION)) { // It must be a deconstruction // let ( a |>,<| b ) = ... // We need to do it again because lower symbols must be wrapped with identifiers rollbackToFoundIndex() .updateComposite(myTypes.C_DECONSTRUCTION); return; } if (isFound(myTypes.C_SCOPED_EXPR) && isFoundScope(myTypes.LBRACE) && isAtIndex(getIndex() + 1, myTypes.C_PARAM_DECLARATION)) { // It must be a deconstruction in parameters // { a |>,<| b } => ... // We need to do it again because lower symbols must be wrapped with identifiers rollbackToFoundIndex() .updateComposite(myTypes.C_DECONSTRUCTION); return; } if (isFound(myTypes.C_SIG_ITEM) || (isFound(myTypes.C_PARAM_DECLARATION) && isAtIndex(getIndex() + 1, myTypes.C_SIG_ITEM))) { popEndUntilFoundIndex(); if (isRawParent(myTypes.H_COLLECTION_ITEM)) { // ( ... |>,<| popEnd().popEnd().advance() .markHolder(myTypes.H_COLLECTION_ITEM) .mark(myTypes.C_SIG_ITEM); return; } if (strictlyInAny(myTypes.C_RECORD_FIELD, myTypes.C_OBJECT_FIELD)) { // { x:t , ... popEndUntilFoundIndex().popEnd(); } else if (in(myTypes.C_PARAM_DECLARATION, /*not*/myTypes.C_SCOPED_EXPR)) { // double sig ? ~x:int, popEndUntilFoundIndex().popEnd(); popEnd(); } else { popEnd(); } } if (isScope(myTypes.LPAREN) && isRawParent(myTypes.C_SIG_EXPR)) { // type t = ( ... |>,<| advance().mark(myTypes.C_SIG_ITEM); } else if (strictlyInAny(myTypes.C_RECORD_FIELD, myTypes.C_OBJECT_FIELD)) { boolean isRecord = isFound(myTypes.C_RECORD_FIELD); popEndUntilFoundIndex().popEnd().advance(); IElementType tokenType = getTokenType(); if (tokenType != myTypes.RBRACE && tokenType != myTypes.LBRACKET) { mark(isRecord ? myTypes.C_RECORD_FIELD : myTypes.C_OBJECT_FIELD); } } else if (strictlyIn(myTypes.C_MIXIN_FIELD)) { popEndUntilFoundIndex().popEnd().advance(); } else if (strictlyIn(myTypes.C_DECONSTRUCTION)) { popEndUntilScope(); } else if (strictlyInAny(myTypes.C_PARAM_DECLARATION)) { popEndUntilFoundIndex().popEnd().advance(); if (getTokenType() != myTypes.RPAREN) { // not at the end of a list: ie not => (p1, p2<,> ) mark(myTypes.C_PARAM_DECLARATION); markHolder(myTypes.H_PLACE_HOLDER); } } else if (strictlyInAny(myTypes.C_NAMED_PARAM, myTypes.C_PARAM)) { popEndUntilFoundIndex().popEnd().advance(); if (getTokenType() != myTypes.RPAREN) { // not at the end of a list: ie not => (p1, p2<,> ) mark(myTypes.C_PARAM); markHolder(myTypes.H_PLACE_HOLDER); } } else if (strictlyInAny(myTypes.C_VARIANT_CONSTRUCTOR, myTypes.C_PARAMETERS)) { popEndUntilFoundIndex().advance(); if (getTokenType() != myTypes.RPAREN) { // not at the end of a list: ie not => (p1, p2<,> ) markHolder(myTypes.H_COLLECTION_ITEM); mark(myTypes.C_PARAM_DECLARATION); markHolder(myTypes.H_PLACE_HOLDER); } } else if (strictlyIn(myTypes.C_SCOPED_EXPR)) { popEndUntilFoundIndex(); } } } private void parsePipe() { if (is(myTypes.C_TRY_HANDLERS)) { // try (...) { |>|<| ... advance().mark(myTypes.C_TRY_HANDLER); } else if (is(myTypes.C_TYPE_BINDING)) { // type t = |>|<| ... popEndUntil(myTypes.C_TYPE_BINDING).advance() .mark(myTypes.C_VARIANT_DECLARATION); } else if (in(myTypes.C_VARIANT_DECLARATION)) { // type t = | X |>|<| Y ... popEndUntilFoundIndex().popEnd().advance() .mark(myTypes.C_VARIANT_DECLARATION); } else if (in(myTypes.C_PATTERN_MATCH_BODY)) { // can be a switchBody or a 'fun' if (!is(myTypes.C_SWITCH_BODY)) { popEndUntil(myTypes.C_PATTERN_MATCH_EXPR).popEnd().advance(); } mark(myTypes.C_PATTERN_MATCH_EXPR); } else { if (!is(myTypes.C_SWITCH_BODY) && in(myTypes.C_PATTERN_MATCH_EXPR, /*not*/myTypes.C_PATTERN_MATCH_BODY)) { // pattern grouping // | X |>|<| Y => ... popEndUntilIndex(getIndex()).popEnd(); } if (isScope(myTypes.LBRACKET) && isRawParent(myTypes.C_TYPE_BINDING)) { // type t = [ |>|<| ... advance().mark(myTypes.C_VARIANT_DECLARATION); } else { // By default, a pattern match advance().mark(myTypes.C_PATTERN_MATCH_EXPR); } } } private void parseStringValue() { if (is(myTypes.C_MACRO_EXPR)) { // [%raw |>"x"<| ... wrapWith(myTypes.C_MACRO_BODY); } else if (in(myTypes.C_MACRO_NAME)) { // [@bs |>"x<| ... popEndUntilScope(); } else if (is(myTypes.C_JS_OBJECT)) { mark(myTypes.C_OBJECT_FIELD).wrapAtom(myTypes.CA_LOWER_SYMBOL); } else if (is(myTypes.C_OBJECT_FIELD)) { wrapAtom(myTypes.CA_LOWER_SYMBOL); } } private void parseMlStringOpen() { if (strictlyIn(myTypes.C_MACRO_EXPR)) { popEndUntilFoundIndex().mark(myTypes.C_MACRO_BODY); } markScope(myTypes.C_ML_INTERPOLATOR, myTypes.ML_STRING_OPEN); } private void parseMlStringClose() { Marker scope = popEndUntilScopeToken(myTypes.ML_STRING_OPEN); advance(); if (scope != null) { popEnd(); } } private void parseJsStringOpen() { markScope(myTypes.C_INTERPOLATION_EXPR, myTypes.JS_STRING_OPEN); } private void parseJsStringClose() { Marker scope = popEndUntilScopeToken(myTypes.JS_STRING_OPEN); advance(); if (scope != null) { popEnd(); } } private void parseLet() { mark(myTypes.C_LET_DECLARATION); } private void parseVal() { if (!in(myTypes.C_MACRO_NAME)) { popEndUntilScope(); if (is(myTypes.C_OBJECT)) { mark(myTypes.C_CLASS_FIELD); } } } private void parsePub() { if (in(myTypes.C_OBJECT)) { popEndUntil(myTypes.C_OBJECT). mark(myTypes.C_CLASS_METHOD); } } private void parseModule() { if (in(myTypes.C_DEFAULT_VALUE)) { // let fn = (~x:(module X)= ( »module« ... ) ... Marker foundMarker = popEndUntilScopeToken(myTypes.LPAREN); if (foundMarker != null) { foundMarker.updateCompositeType(myTypes.C_FIRST_CLASS); } } else if (in(myTypes.C_FIRST_CLASS)) { // } else if (!in(myTypes.C_MACRO_NAME)) { popEndUntilScope(); mark(myTypes.C_MODULE_DECLARATION); } } private void parseException() { popEndUntilScope(); mark(myTypes.C_EXCEPTION_DECLARATION); } private void parseClass() { popEndUntilScope(); mark(myTypes.C_CLASS_DECLARATION); } private void parseType() { if (is(myTypes.C_CONSTRAINTS)) { // module M = (X) : ( S with |>type<| ... ) = ... mark(myTypes.C_TYPE_CONSTRAINT); } else if (!is(myTypes.C_MODULE_DECLARATION) && !is(myTypes.C_CLASS_DECLARATION)) { popEndUntilScope(); mark(myTypes.C_TYPE_DECLARATION); } } private void parseExternal() { popEndUntilScope(); mark(myTypes.C_EXTERNAL_DECLARATION); } private void parseOpen() { popEndUntilScope(); mark(myTypes.C_OPEN); } private void parseInclude() { popEndUntilScope(); mark(myTypes.C_INCLUDE); } private void parsePercent() { if (is(myTypes.C_MACRO_EXPR)) { mark(myTypes.C_MACRO_NAME); } else if (in(myTypes.C_LET_DECLARATION)) { // let name|>%<|private = ... mark(myTypes.C_LET_ATTR); } else { IElementType nextTokenType = rawLookup(1); if (nextTokenType == myTypes.RAW) { // |>%<| raw ... mark(myTypes.C_MACRO_EXPR). mark(myTypes.C_MACRO_NAME); } } } private void parseColon() { if (isCurrent(myTypes.C_MODULE_DECLARATION)) { // module M |> :<| ... advance(); boolean isParen = getTokenType() == myTypes.LPAREN; if (isParen) { // module M : |>(<| ... advance(); } mark(myTypes.C_MODULE_SIGNATURE).updateScopeToken(isParen ? myTypes.LPAREN : null); } else if (is(myTypes.C_EXTERNAL_DECLARATION/*operator overriding*/) || isRawParent(myTypes.C_EXTERNAL_DECLARATION)) { advance(); parseLetSignature(); } else if (isRawParent(myTypes.C_LET_DECLARATION)) { advance(); if (getTokenType() == myTypes.MODULE) { // First class module :: let x : >module< I advance().mark(myTypes.C_MODULE_SIGNATURE); } else { parseLetSignature(); } } else if (inScopeOrAny( myTypes.H_NAMED_PARAM_DECLARATION, myTypes.C_PARAM_DECLARATION, myTypes.C_NAMED_PARAM, myTypes.C_RECORD_FIELD, myTypes.C_OBJECT_FIELD, myTypes.C_TERNARY, myTypes.C_UNPACK, myTypes.C_CLASS_METHOD )) { if (isFound(myTypes.H_NAMED_PARAM_DECLARATION) || isFound(myTypes.C_PARAM_DECLARATION) || isFound(myTypes.C_NAMED_PARAM)) { advance(); if (getTokenType() == myTypes.LPAREN && lookAhead(1) == myTypes.MODULE) { // let _ = (~x »:« (module ... markScope(myTypes.C_MODULE_SIGNATURE, myTypes.LPAREN).advance().advance(); } else { // let _ = (~x »:« ... mark(myTypes.C_SIG_EXPR); markParenthesisScope(true).mark(myTypes.C_SIG_ITEM); } } else if (isFound(myTypes.C_RECORD_FIELD) || isFound(myTypes.C_OBJECT_FIELD)) { advance(); if (in(myTypes.C_TYPE_BINDING)) { mark(myTypes.C_SIG_EXPR) .mark(myTypes.C_SIG_ITEM); } else { mark(myTypes.C_FIELD_VALUE); } } else if (isFound(myTypes.C_TERNARY)) { // x ? y »:« ... popEndUntilFoundIndex() .advance().mark(myTypes.C_IF_THEN_ELSE); markHolder(myTypes.H_PLACE_HOLDER); } else if (isFound(myTypes.C_UNPACK)) { // module M = (val »:« ... ) advance(); markParenthesisScope(true) .mark(myTypes.C_MODULE_SIGNATURE); } else { advance(); parseSignatureExpression(); } } } private void parseSignatureExpression() { mark(myTypes.C_SIG_EXPR); markParenthesisScope(true). mark(myTypes.C_SIG_ITEM);//.markHolder(myTypes.H_PLACE_HOLDER); } private void parseLetSignature() { // external/let e |> :<| ... mark(myTypes.C_SIG_EXPR); if (getTokenType() == myTypes.LPAREN) { // external/let e : |>(<| ... markDummyScope(myTypes.C_SCOPED_EXPR, myTypes.LPAREN).advance(); if (getTokenType() == myTypes.DOT) { // external/let e : ( |>.<| ... advance(); } } mark(myTypes.C_SIG_ITEM); } private void parseArrobase() { if (is(myTypes.C_ANNOTATION)) { mark(myTypes.C_MACRO_NAME); } } private void parseLt() { // Can be a symbol or a JSX tag IElementType nextTokenType = rawLookup(1); // Note that option is a ReasonML keyword but also a JSX keyword ! if (nextTokenType == myTypes.LIDENT || nextTokenType == myTypes.UIDENT || nextTokenType == myTypes.OPTION) { // Surely a tag mark(myTypes.C_TAG) .markScope(myTypes.C_TAG_START, myTypes.LT) .advance() .remapCurrentToken(nextTokenType == myTypes.UIDENT ? myTypes.A_UPPER_TAG_NAME : myTypes.A_LOWER_TAG_NAME) .wrapAtom(nextTokenType == myTypes.UIDENT ? myTypes.CA_UPPER_SYMBOL : myTypes.CA_LOWER_SYMBOL) .popEnd(); } else if (nextTokenType == myTypes.GT) { // a React fragment start mark(myTypes.C_TAG) .mark(myTypes.C_TAG_START) .advance().advance().popEnd() .mark(myTypes.C_TAG_BODY); } } private void parseLtSlash() { IElementType nextTokenType = rawLookup(1); // Note that option is a ReasonML keyword but also a JSX keyword ! if (nextTokenType == myTypes.LIDENT || nextTokenType == myTypes.UIDENT || nextTokenType == myTypes.OPTION) { // A closing tag if (in(myTypes.C_TAG_BODY)) { popEndUntil(myTypes.C_TAG); } mark(myTypes.C_TAG_CLOSE) .advance() .remapCurrentToken(nextTokenType == myTypes.UIDENT ? myTypes.A_UPPER_TAG_NAME : myTypes.A_LOWER_TAG_NAME) .wrapAtom(nextTokenType == myTypes.UIDENT ? myTypes.CA_UPPER_SYMBOL : myTypes.CA_LOWER_SYMBOL); } else if (nextTokenType == myTypes.GT) { // a React fragment end mark(myTypes.C_TAG_CLOSE) .advance().advance().popEnd(); } } private void parseGt() { if (isCurrent(myTypes.C_TAG_PROP_VALUE)) { // ?prop=value |> > <| ... popEndUntil(myTypes.C_TAG_PROP_VALUE).popEnd().popEnd(); } else if (is(myTypes.C_TAG_PROPERTY)) { // ?prop |> > <| ... popEnd(); } //if (inAny(myTypes.C_TAG, myTypes.C_PATTERN_MATCH_BODY)) { // if (isFound()) if (inScopeOrAny(myTypes.C_TAG_PROP_VALUE, myTypes.C_TAG_START, myTypes.C_TAG_CLOSE, myTypes.C_PATTERN_MATCH_BODY)) { if (isFound(myTypes.C_PATTERN_MATCH_BODY)) { // nope } else { advance().popEndUntilFoundIndex(); if (is(myTypes.C_TAG_START)) { popEnd().mark(myTypes.C_TAG_BODY); } else if (is(myTypes.C_TAG_CLOSE)) { // end the tag popEndUntil(myTypes.C_TAG).popEnd(); } } } //} } private void parseGtAutoClose() { popEndUntilScope(); advance().popEnd(/*tag_start*/).popEnd(/*tag*/); } private void parseLIdent() { if (is(myTypes.C_LET_DECLARATION)) { // let |>x<| ... wrapAtom(myTypes.CA_LOWER_SYMBOL); } else if (is(myTypes.C_TYPE_DECLARATION)) { // type |>x<| ... wrapAtom(myTypes.CA_LOWER_SYMBOL); } else if (is(myTypes.C_EXTERNAL_DECLARATION)) { // external |>x<| ... wrapAtom(myTypes.CA_LOWER_SYMBOL); } else if (is(myTypes.C_CLASS_DECLARATION)) { // class |>x<| ... wrapAtom(myTypes.CA_LOWER_SYMBOL); } else if (is(myTypes.C_RECORD_EXPR)) { // let x = { |>y<| ... mark(myTypes.C_RECORD_FIELD).wrapAtom(myTypes.CA_LOWER_SYMBOL); } else if (is(myTypes.C_RECORD_FIELD)) { // let x = { y, |>z<| ... wrapAtom(myTypes.CA_LOWER_SYMBOL); } else if ((isCurrent(myTypes.C_PARAMETERS)) || isCurrent(myTypes.C_VARIANT_CONSTRUCTOR)) { // ( x , |>y<| ... mark(myTypes.C_PARAM_DECLARATION); markHolder(myTypes.H_PLACE_HOLDER); wrapAtom(myTypes.CA_LOWER_SYMBOL); } else if (strictlyInAny(myTypes.C_TAG_START, myTypes.C_TAG_PROP_VALUE)) { if (previousElementType(1) != myTypes.LT && isFound(myTypes.C_TAG_START)) { // This is a property popEndUntilScope(); generateJsxPropertyName(); } } else { IElementType nextElementType = lookAhead(1); if (isCurrent(myTypes.C_SCOPED_EXPR) && isCurrentScope(myTypes.LBRACE) && nextElementType == myTypes.COLON) { // this is a record usage :: { |>x<| : ... popIfHold() .updateComposite(myTypes.C_RECORD_EXPR) .mark(myTypes.C_RECORD_FIELD) .wrapAtom(myTypes.CA_LOWER_SYMBOL); } else if (nextElementType == myTypes.LPAREN && !inAny(myTypes.C_TYPE_BINDING, myTypes.C_TYPE_CONSTRAINT, myTypes.C_SIG_ITEM)) { mark(myTypes.C_FUNCTION_CALL) .wrapAtom(myTypes.CA_LOWER_SYMBOL); } else if (is(myTypes.C_DECONSTRUCTION)) { wrapWith(myTypes.C_LOWER_NAME); } else if (nextElementType == myTypes.ARROW && strictlyInAny( myTypes.C_LET_BINDING, myTypes.C_DEFAULT_VALUE, myTypes.C_PARAM, myTypes.C_FIELD_VALUE, myTypes.C_SCOPED_EXPR )) { // A paren-less function definition :: |>x<| => mark(myTypes.C_FUNCTION_EXPR) .mark(myTypes.C_PARAMETERS) .mark(myTypes.C_PARAM_DECLARATION) .wrapAtom(myTypes.CA_LOWER_SYMBOL); } else { wrapAtom(myTypes.CA_LOWER_SYMBOL); } } } private void parseLArray() { markScope(myTypes.C_SCOPED_EXPR, myTypes.LARRAY).advance(); markHolder(myTypes.H_COLLECTION_ITEM); } private void parseRArray() { Marker scope = popEndUntilScopeToken(myTypes.LARRAY); advance(); if (scope != null) { popEnd(); } } private void parseLBracket() { IElementType nextType = rawLookup(1); if (nextType == myTypes.ARROBASE) { // |>[ <| @ ... markScope(myTypes.C_ANNOTATION, myTypes.LBRACKET); } else if (nextType == myTypes.PERCENT) { // |>[ <| % ... markScope(myTypes.C_MACRO_EXPR, myTypes.LBRACKET); } else if (previousElementType(2) == myTypes.A_MODULE_NAME && previousElementType(1) == myTypes.DOT) { // Local open // M.|>(<| ... markScope(myTypes.C_LOCAL_OPEN, myTypes.LBRACKET); } else if (nextType == myTypes.GT) { // |> [ <| > ... ] markScope(myTypes.C_OPEN_VARIANT, myTypes.LBRACKET).advance().advance(); if (getTokenType() != myTypes.RBRACKET) { mark(myTypes.C_VARIANT_DECLARATION);//.advance(); } } else if (nextType == myTypes.LT) { // |> [ <| < ... ] markScope(myTypes.C_CLOSED_VARIANT, myTypes.LBRACKET).advance().advance(); if (getTokenType() != myTypes.RBRACKET) { mark(myTypes.C_VARIANT_DECLARATION);//.advance(); } } else { markScope(myTypes.C_SCOPED_EXPR, myTypes.LBRACKET).advance(); if (getTokenType() != myTypes.PIPE) { markHolder(myTypes.H_COLLECTION_ITEM); } } } private void parseRBracket() { Marker scope = popEndUntilScopeToken(myTypes.LBRACKET); advance(); if (scope != null) { popEnd(); } } private void parseBracketGt() { markScope(myTypes.C_SCOPED_EXPR, myTypes.LBRACKET); } private void parseLBrace() { if (previousElementType(2) == myTypes.A_MODULE_NAME && previousElementType(1) == myTypes.DOT) { // Local open a js object or a record // Xxx.|>{<| ... } mark(myTypes.C_LOCAL_OPEN); IElementType nextElementType = lookAhead(1); if (nextElementType == myTypes.LIDENT) { markScope(myTypes.C_RECORD_EXPR, myTypes.LBRACE); } else { markScope(myTypes.C_JS_OBJECT, myTypes.LBRACE); } } else if (is(myTypes.C_TYPE_BINDING)) { boolean isJsObject = lookAhead(1) == myTypes.DOT; markScope(isJsObject ? myTypes.C_JS_OBJECT : myTypes.C_RECORD_EXPR, myTypes.LBRACE); } else if (is(myTypes.C_TRY_EXPR)) { // A try expression // try (..) |>{<| .. } markScope(myTypes.C_TRY_HANDLERS, myTypes.LBRACE); } else if (is(myTypes.C_MODULE_BINDING)) { // module M = |>{<| ... updateScopeToken(myTypes.LBRACE); } else if (is(myTypes.C_FUNCTOR_BINDING)) { // module M = (...) => |>{<| ... updateScopeToken(myTypes.LBRACE); } else if (isCurrent(myTypes.C_IF)) { markScope(myTypes.C_IF_THEN_ELSE, myTypes.LBRACE); } else if (is(myTypes.C_MODULE_SIGNATURE)) { // module M : |>{<| ... updateScopeToken(myTypes.LBRACE); } else if (in(myTypes.C_CLASS_DECLARATION)) { // class x = |>{<| ... } markScope(myTypes.C_OBJECT, myTypes.LBRACE); } else if (is(myTypes.C_SWITCH_EXPR)) { markScope(myTypes.C_SWITCH_BODY, myTypes.LBRACE); } else { // it might be a js object IElementType nextElement = lookAhead(1); if (nextElement == myTypes.STRING_VALUE || nextElement == myTypes.DOT) { // js object detected (in usage) // |>{<| "x" ... } markScope(myTypes.C_JS_OBJECT, myTypes.LBRACE); } else if (is(myTypes.C_FUNCTION_BODY) && !isScope(myTypes.LBRACE) && nextElement != myTypes.LIDENT) { // function body // x => |>{<| ... } updateScopeToken(myTypes.LBRACE); } else { if (isHold()) { popEnd(); } markScope(myTypes.C_SCOPED_EXPR, myTypes.LBRACE) .advance().markHolder(myTypes.H_PLACE_HOLDER); } } } private void parseRBrace() { Marker scope = popEndUntilOneOfElementType(myTypes.LBRACE, myTypes.RECORD, myTypes.SWITCH); advance(); if (scope != null) { popEnd(); if (is(myTypes.C_LOCAL_OPEN) && !rawHasScope()) { // X.{ ... |>}<| popEnd(); } else if (is(myTypes.C_TAG_PROP_VALUE)) { popEndUntil(myTypes.C_TAG_PROPERTY).popEnd(); } } } private void parseLParen() { if (isCurrent(myTypes.C_PARAM_DECLARATION)) { markScope(myTypes.C_SCOPED_EXPR, myTypes.LPAREN).advance(); markHolder(myTypes.H_PLACE_HOLDER); } else if (isCurrent(myTypes.C_PARAMETERS)) { if (!currentHasScope()) { // |>(<| ... ) => ... updateScopeToken(myTypes.LPAREN); markHolder(myTypes.H_COLLECTION_ITEM); } else { // ( |>(<| ... ) , ... ) => ... mark(myTypes.C_PARAM_DECLARATION) .markScope(myTypes.C_SCOPED_EXPR, myTypes.LPAREN).advance() .markHolder(myTypes.H_COLLECTION_ITEM); } } else if (is(myTypes.C_ASSERT_STMT)) { // assert |>(<| ... markScope(myTypes.C_BINARY_CONDITION, myTypes.LPAREN); } else if (is(myTypes.C_TRY_EXPR)) { // try |>(<| ... markScope(myTypes.C_TRY_BODY, myTypes.LPAREN); } else if (is(myTypes.C_IF) || is(myTypes.C_SWITCH_EXPR)) { // if |>(<| ... OR switch |>(<| ... markScope(myTypes.C_BINARY_CONDITION, myTypes.LPAREN); } else if (is(myTypes.C_LET_DECLARATION)) { // Overloading operator OR deconstructing a term // let |>(<| + ) = // let |>(<| a, b ) = markScope(myTypes.C_SCOPED_EXPR, myTypes.LPAREN); } else if (isCurrent(myTypes.C_TYPE_DECLARATION)) { // type t »(« ... markScope(myTypes.C_PARAMETERS, myTypes.LPAREN); } else if (previousElementType(2) == myTypes.A_MODULE_NAME && previousElementType(1) == myTypes.DOT) { // Local open // M.|>(<| ... markScope(myTypes.C_LOCAL_OPEN, myTypes.LPAREN); } else if (is(myTypes.C_MODULE_BINDING) && !in(myTypes.C_FUNCTOR_DECLARATION)) { if (myBuilder.lookAhead(1) == myTypes.VAL) { // module M = »(« val ... ) updateComposite(myTypes.C_UNPACK) .updateScopeToken(myTypes.LPAREN).advance() .advance(); // skip 'val' in a first class module decoding } else if (in(myTypes.C_MODULE_DECLARATION)) { // This is a functor :: module M = |>(<| .. ) updateCompositeAt(getIndex(), myTypes.C_FUNCTOR_DECLARATION) .updateComposite(myTypes.C_PARAMETERS).updateScopeToken(myTypes.LPAREN).advance() .markHolder(myTypes.H_COLLECTION_ITEM) .mark(myTypes.C_PARAM_DECLARATION) .markHolder(myTypes.H_PLACE_HOLDER); } } else if (is(myTypes.C_DECONSTRUCTION) && isRawParent(myTypes.C_LET_DECLARATION)) { // let ((x |>,<| ... markScope(myTypes.C_DECONSTRUCTION, myTypes.LPAREN); } else if (previousElementType(1) == myTypes.SOME) { // Some |>(<| markScope(myTypes.C_SCOPED_EXPR, myTypes.LPAREN) .advance().markHolder(myTypes.H_PLACE_HOLDER); } else if (in(myTypes.C_FUNCTION_CALL) && !(is(myTypes.C_TYPE_DECLARATION) || isCurrent(myTypes.C_PARAM) || inAny(myTypes.C_TYPE_BINDING, myTypes.C_SIG_ITEM))) { // calling a function markScope(myTypes.C_PARAMETERS, myTypes.LPAREN).advance(); IElementType nextTokenType = getTokenType(); if (nextTokenType == myTypes.RPAREN) { markHolder(myTypes.H_COLLECTION_ITEM); } else { mark(myTypes.C_PARAM); markHolder(myTypes.H_PLACE_HOLDER); } } else if (inAny(myTypes.C_FUNCTOR_CALL, myTypes.C_FUNCTOR_RESULT)) { // module M = ( ... ) : |>(<| ... if (isFound(myTypes.C_FUNCTOR_CALL)) { markScope(myTypes.C_PARAMETERS, myTypes.LPAREN).advance() .mark(myTypes.C_PARAM) .markHolder(myTypes.H_PLACE_HOLDER); } } else if (in(myTypes.C_VARIANT_DECLARATION)) { // Variant params // type t = | Variant |>(<| .. ) markScope(myTypes.C_VARIANT_CONSTRUCTOR, myTypes.LPAREN).advance(); markHolder(myTypes.H_COLLECTION_ITEM); mark(myTypes.C_PARAM_DECLARATION); } else if (inAny(myTypes.C_CLASS_DECLARATION, myTypes.C_OBJECT, myTypes.C_SIG_ITEM)) { if (isFound(myTypes.C_CLASS_DECLARATION)) { popEndUntil(myTypes.C_CLASS_DECLARATION). markScope(myTypes.C_CLASS_CONSTR, myTypes.LPAREN); } else if (isFound(myTypes.C_SIG_ITEM)) { markScope(myTypes.C_SCOPED_EXPR, myTypes.LPAREN).advance(); markHolder(myTypes.H_COLLECTION_ITEM); } } else { markScope(myTypes.C_SCOPED_EXPR, myTypes.LPAREN).advance(); markHolder(myTypes.H_COLLECTION_ITEM); } } private void parseRParen() { // Removing intermediate resolutions Marker lParen = popEndUntilScopeToken(myTypes.LPAREN); advance(); if (is(myTypes.C_BINARY_CONDITION) && isRawParent(myTypes.C_IF)) { // if ( x |>)<| ... end(); if (getTokenType() != myTypes.LBRACE) { mark(myTypes.C_IF_THEN_ELSE); } } else if (lParen != null) { IElementType nextTokenType = getTokenType(); if (nextTokenType == myTypes.ARROW && !isParent(myTypes.C_FUNCTION_EXPR) && !inAny(myTypes.C_SIG_EXPR, myTypes.C_PATTERN_MATCH_EXPR, myTypes.C_FUNCTOR_DECLARATION)) { // a missed function expression rollbackToIndex(0) .markBefore(0, myTypes.C_FUNCTION_EXPR) .updateComposite(myTypes.C_PARAMETERS) .markHolder(myTypes.H_COLLECTION_ITEM); } else { popEnd(); if (isRawParent(myTypes.C_FUNCTION_CALL)) { popEnd().popEnd(); } else if (isCurrent(myTypes.C_FUNCTOR_DECLARATION)) { if (nextTokenType == myTypes.COLON) { // module M = (P) |> :<| R ... advance(); markParenthesisScope(true); mark(myTypes.C_FUNCTOR_RESULT); } else if (nextTokenType == myTypes.ARROW) { // module M = (P) |>=><| ... advance().mark(myTypes.C_FUNCTOR_BINDING); } } else if (isCurrent(myTypes.C_TAG_PROP_VALUE)) { popEndUntil(myTypes.C_TAG_PROP_VALUE).popEnd(); } else if (strictlyInAny(myTypes.C_PATTERN_MATCH_EXPR, myTypes.C_PATTERN_MATCH_BODY, myTypes.C_TERNARY)) { if (!isFound(myTypes.C_TERNARY) && !isFound(myTypes.C_PATTERN_MATCH_BODY)) { popEndUntilFoundIndex(); } } } } } private void parseEq() { if (is(myTypes.C_TAG_PROPERTY)) { // =<| ... advance().mark(myTypes.C_TAG_PROP_VALUE); } else if (isCurrent(myTypes.C_MODULE_DECLARATION) || isRawParent(myTypes.C_MODULE_DECLARATION)) { // module M |> =<| ... advance().mark(myTypes.C_MODULE_BINDING); } else if (isParent(myTypes.C_PARAM_DECLARATION) || isRawParent(myTypes.C_NAMED_PARAM) || isRawParent(myTypes.H_NAMED_PARAM_DECLARATION)) { // ( ~x »=« ... popEndUntilFoundIndex() .advance().mark(myTypes.C_DEFAULT_VALUE) .markHolder(myTypes.H_PLACE_HOLDER); } else if (strictlyIn(myTypes.C_TYPE_BINDING) && strictlyIn(myTypes.C_CONSTRAINTS)) { // .. with type .. = .. |> =<| .. popEndUntilFoundIndex().popEnd(); if (strictlyIn(myTypes.C_MODULE_DECLARATION)) { popEndUntilFoundIndex() .advance().mark(myTypes.C_MODULE_BINDING); } } else if (strictlyInAny(myTypes.C_TYPE_DECLARATION, myTypes.C_TYPE_CONSTRAINT)) { // type t |> =<| ... advance().mark(myTypes.C_TYPE_BINDING); } else if (inScopeOrAny(myTypes.C_LET_DECLARATION, myTypes.C_MODULE_SIGNATURE, myTypes.C_SIG_EXPR)) { if (isFound(myTypes.C_SIG_EXPR)) { popEndUntil(myTypes.C_SIG_EXPR).popEnd(); if (inAny(myTypes.H_NAMED_PARAM_DECLARATION, myTypes.C_NAMED_PARAM)) { advance().mark(myTypes.C_DEFAULT_VALUE); } else if (in(myTypes.C_LET_DECLARATION)) { parseLetBinding(); } } else if (isFound(myTypes.C_MODULE_SIGNATURE)) { popEndUntilIndex(getIndex()) .popEnd() .advance(); if (strictlyIn(myTypes.C_LET_DECLARATION)) { // let x : I >=< ... mark(myTypes.C_FIRST_CLASS); } else { // module M : T |> =<| ... mark(myTypes.C_MODULE_BINDING); } } else if (!isFound(myTypes.C_SCOPED_EXPR) && in(myTypes.C_LET_DECLARATION)) { if (!isAtIndex(getIndex() - 1, myTypes.C_LET_BINDING)) { // already parsed parseLetBinding(); } } } } private void parseColonEq() { if (strictlyInAny(myTypes.C_TYPE_CONSTRAINT)) { if (isFound(myTypes.C_TYPE_CONSTRAINT)) { // ... with type t |> :=<| ... advance().mark(myTypes.C_TYPE_BINDING); } } } // let ... |> =<| ... private void parseLetBinding() { popEndUntil(myTypes.C_LET_DECLARATION).advance() .mark(myTypes.C_LET_BINDING); markHolder(myTypes.H_PLACE_HOLDER); } private void parseSemi() { if (in(myTypes.C_PATTERN_MATCH_BODY)) { if (in(myTypes.C_FUN_EXPR)) { // Special case for the `fun` keyword popEndUntilScope(); } } else { popEndUntilScope(); } if (is(myTypes.C_LET_BINDING) || rawHasScope()) { // let x = ... |>;<| // { ... |>;<| advance().markHolder(myTypes.H_PLACE_HOLDER); } } private void parseUIdent() { if (DUMMY_IDENTIFIER_TRIMMED.equals(getTokenText())) { return; } if (is(myTypes.C_MODULE_DECLARATION)) { // module |>M<| ... remapCurrentToken(myTypes.A_MODULE_NAME).wrapAtom(myTypes.CA_UPPER_SYMBOL); } else if (isCurrent(myTypes.C_PARAM_DECLARATION) && in(myTypes.C_FUNCTOR_DECLARATION)) { // module M = ( |>P<| ... remapCurrentToken(myTypes.A_MODULE_NAME).wrapAtom(myTypes.CA_UPPER_SYMBOL); } else if (isCurrent(myTypes.C_FUNCTOR_RESULT)) { // module M = ( .. ) : |>R<| ... remapCurrentToken(myTypes.A_MODULE_NAME).wrapAtom(myTypes.CA_UPPER_SYMBOL); } else if (isCurrent(myTypes.C_SIG_ITEM) && in(myTypes.C_FUNCTOR_DECLARATION, /*not*/myTypes.C_FUNCTOR_BINDING)) { // module M = (P: |>S<| ... remapCurrentToken(myTypes.A_MODULE_NAME).wrapAtom(myTypes.CA_UPPER_SYMBOL); } else if (is(myTypes.C_VARIANT_DECLARATION)) { // type t = | |>X<| .. IElementType nextElementType = rawLookup(1); remapCurrentToken(nextElementType == myTypes.DOT ? myTypes.A_MODULE_NAME : myTypes.A_VARIANT_NAME); wrapAtom(myTypes.CA_UPPER_SYMBOL); } else if (is(myTypes.C_EXCEPTION_DECLARATION)) { // exception |>E<| .. remapCurrentToken(myTypes.A_EXCEPTION_NAME).wrapAtom(myTypes.CA_UPPER_SYMBOL); } else if (isCurrent(myTypes.C_TRY_HANDLER)) { // try .. { | |>X<| .. remapCurrentToken(myTypes.A_EXCEPTION_NAME).wrapAtom(myTypes.CA_UPPER_SYMBOL); } else if ((in(myTypes.C_TAG_START, /*not*/myTypes.C_TAG_PROP_VALUE) || in(myTypes.C_TAG_CLOSE)) && previousElementType(1) == myTypes.DOT) { // a namespaced custom component // Y<| ... remapCurrentToken(myTypes.A_UPPER_TAG_NAME).wrapAtom(myTypes.CA_UPPER_SYMBOL); } else { IElementType nextToken = lookAhead(1); if (is(myTypes.C_TYPE_BINDING) && nextToken != myTypes.DOT) { // We are declaring a variant without a pipe before :: type t = |>X<| | ... mark(myTypes.C_VARIANT_DECLARATION) .remapCurrentToken(myTypes.A_VARIANT_NAME).wrapAtom(myTypes.CA_UPPER_SYMBOL); } else if (nextToken == myTypes.LPAREN && (isCurrent(myTypes.C_MODULE_BINDING) || isCurrent(myTypes.C_OPEN) || isCurrent(myTypes.C_INCLUDE))) { // functor call :: |>X<| ( ... // functor call with path :: A.B.|>X<| ( ... mark(myTypes.C_FUNCTOR_CALL) .remapCurrentToken(myTypes.A_MODULE_NAME).wrapAtom(myTypes.CA_UPPER_SYMBOL); } else if (((isCurrent(myTypes.C_PATTERN_MATCH_EXPR) || isCurrent(myTypes.C_LET_BINDING))) && nextToken != myTypes.DOT) { // Pattern matching a variant or using it // switch (c) { | |>X<| ... / let x = |>X<| ... remapCurrentToken(myTypes.A_VARIANT_NAME).wrapAtom(myTypes.CA_UPPER_SYMBOL); } else { remapCurrentToken(nextToken == myTypes.DOT || isCurrent(myTypes.C_MODULE_BINDING) || isCurrent(myTypes.C_MODULE_SIGNATURE) || isCurrent(myTypes.C_FIRST_CLASS) || isCurrent(myTypes.C_OPEN) || isCurrent(myTypes.C_INCLUDE) ? myTypes.A_MODULE_NAME : myTypes.A_VARIANT_NAME); wrapAtom(myTypes.CA_UPPER_SYMBOL); } } } private void parseSwitch() { mark(myTypes.C_SWITCH_EXPR); } private void parseTry() { mark(myTypes.C_TRY_EXPR); } private void parseArrow() { if (inScopeOrAny( myTypes.C_SIG_EXPR, myTypes.C_SIG_ITEM, myTypes.C_FUNCTOR_RESULT, myTypes.C_PATTERN_MATCH_EXPR, myTypes.C_FUNCTION_EXPR, myTypes.C_PARAMETERS, myTypes.C_PARAM_DECLARATION, myTypes.C_TRY_HANDLER )) { if (isFound(myTypes.C_SIG_EXPR)) { advance().mark(myTypes.C_SIG_ITEM); } else if (isFound(myTypes.C_SIG_ITEM)) { popEndUntil(myTypes.C_SIG_ITEM).popEnd() .advance().mark(myTypes.C_SIG_ITEM); } else if (isFound(myTypes.C_FUNCTOR_RESULT)) { // module Make = (M) : R |>=><| ... popEndUntilFoundIndex().popEnd() .advance().mark(myTypes.C_FUNCTOR_BINDING); } else if (isFound(myTypes.C_PARAM_DECLARATION)) { if (isGrandParent(myTypes.C_SOME) && isParent(myTypes.C_PARAMETERS)) { // Some((...) |>=><| rollbackToIndex(getIndex()/*parent, ie parameters*/) .mark(myTypes.C_FUNCTION_EXPR); } else if (isRawParent(myTypes.H_COLLECTION_ITEM)) { // inside a parenthesis, function not declared yet } else { // x |>=><| ... popEndUntil(myTypes.C_FUNCTION_EXPR).advance() .mark(myTypes.C_FUNCTION_BODY); markHolder(myTypes.H_PLACE_HOLDER); } } else if (isFound(myTypes.C_PARAMETERS) || isFound(myTypes.C_FUNCTION_EXPR)) { popEndUntilOneOf(myTypes.C_PARAMETERS, myTypes.C_FUNCTION_EXPR); if (isRawParent(myTypes.C_FUNCTION_EXPR) || isRawParent(myTypes.C_FUNCTION_CALL)) { popEnd(); } advance().mark(myTypes.C_FUNCTION_BODY); } else if (isFound(myTypes.C_PATTERN_MATCH_EXPR)) { advance().mark(myTypes.C_PATTERN_MATCH_BODY); markHolder(myTypes.H_PLACE_HOLDER); } else if (isFound(myTypes.C_TRY_HANDLER)) { popEndUntilFoundIndex().advance() .mark(myTypes.C_TRY_HANDLER_BODY); } } } } } ================================================ FILE: src/main/java/com/reason/lang/reason/RmlParserDefinition.java ================================================ package com.reason.lang.reason; import com.intellij.lang.*; import com.intellij.lexer.*; import com.intellij.openapi.project.*; import com.intellij.psi.*; import com.intellij.psi.tree.*; import com.reason.ide.files.*; import com.reason.lang.core.stub.type.*; import org.jetbrains.annotations.*; public class RmlParserDefinition implements ParserDefinition { @Override public @NotNull Lexer createLexer(Project project) { return new RmlLexer(); } public @NotNull TokenSet getWhitespaceTokens() { return TokenSet.create(TokenType.WHITE_SPACE); } public @NotNull TokenSet getCommentTokens() { return TokenSet.create(RmlTypes.INSTANCE.MULTI_COMMENT, RmlTypes.INSTANCE.SINGLE_COMMENT); } public @NotNull TokenSet getStringLiteralElements() { return TokenSet.create(RmlTypes.INSTANCE.STRING_VALUE); } public @NotNull PsiParser createParser(Project project) { return new RmlParser(false); } @Override public @NotNull IFileElementType getFileNodeType() { return RmlFileStubElementType.INSTANCE; } public @NotNull PsiFile createFile(@NotNull FileViewProvider viewProvider) { return viewProvider.getFileType() instanceof RmlInterfaceFileType ? new RmlInterfaceFile(viewProvider) : new RmlFile(viewProvider); } public @NotNull SpaceRequirements spaceExistenceTypeBetweenTokens(ASTNode left, ASTNode right) { return SpaceRequirements.MAY; } public @NotNull PsiElement createElement(@NotNull ASTNode node) { IElementType type = node.getElementType(); if (type instanceof ORStubElementType) { //noinspection rawtypes return ((ORStubElementType) node.getElementType()).createPsi(node); } throw new IllegalArgumentException("Not a ReasonML node: " + node + " (" + type + ", " + type.getLanguage() + ")"); } } ================================================ FILE: src/main/java/com/reason/lang/reason/RmlSafeParserDefinition.java ================================================ package com.reason.lang.reason; import com.intellij.lang.*; import com.intellij.openapi.project.*; import org.jetbrains.annotations.*; public class RmlSafeParserDefinition extends RmlParserDefinition { @Override public @NotNull PsiParser createParser(Project project) { return new RmlParser(true); } } ================================================ FILE: src/main/java/com/reason/lang/reason/RmlTypes.java ================================================ package com.reason.lang.reason; import com.reason.lang.core.stub.*; import com.reason.lang.core.type.*; public class RmlTypes extends ORLangTypes { public static final RmlTypes INSTANCE = new RmlTypes(); private RmlTypes() { // Stub element types C_CLASS_DECLARATION = (ORCompositeType) RmlStubBasedElementTypes.C_CLASS_DECLARATION; C_CLASS_METHOD = (ORCompositeType) RmlStubBasedElementTypes.C_CLASS_METHOD; C_EXCEPTION_DECLARATION = (ORCompositeType) RmlStubBasedElementTypes.C_EXCEPTION_DECLARATION; C_EXTERNAL_DECLARATION = (ORCompositeType) RmlStubBasedElementTypes.C_EXTERNAL_DECLARATION; C_FUNCTOR_DECLARATION = (ORCompositeType) RmlStubBasedElementTypes.C_FUNCTOR_DECLARATION; C_INCLUDE = (ORCompositeType) RmlStubBasedElementTypes.C_INCLUDE; C_LET_DECLARATION = (ORCompositeType) RmlStubBasedElementTypes.C_LET_DECLARATION; C_MODULE_DECLARATION = (ORCompositeType) RmlStubBasedElementTypes.C_MODULE_DECLARATION; C_OBJECT_FIELD = (ORCompositeType) RmlStubBasedElementTypes.C_OBJECT_FIELD; C_OPEN = (ORCompositeType) RmlStubBasedElementTypes.C_OPEN; C_PARAM_DECLARATION = (ORCompositeType) RmlStubBasedElementTypes.C_PARAM_DECLARATION; C_RECORD_FIELD = (ORCompositeType) RmlStubBasedElementTypes.C_RECORD_FIELD; C_TYPE_DECLARATION = (ORCompositeType) RmlStubBasedElementTypes.C_TYPE_DECLARATION; C_VAL_DECLARATION = (ORCompositeType) RmlStubBasedElementTypes.C_VAL_DECLARATION; C_VARIANT_DECLARATION = (ORCompositeType) RmlStubBasedElementTypes.C_VARIANT_DECLARATION; // Composite element types C_ANNOTATION = new ORCompositeElementType("C_ANNOTATION", RmlLanguage.INSTANCE); C_ARRAY = new ORCompositeElementType("C_ARRAY", RmlLanguage.INSTANCE); C_ASSERT_STMT = new ORCompositeElementType("C_ASSERT_STMT", RmlLanguage.INSTANCE); C_BINARY_CONDITION = new ORCompositeElementType("C_BINARY_CONDITION", RmlLanguage.INSTANCE); C_CLASS_CONSTR = new ORCompositeElementType("C_CLASS_CONSTR", RmlLanguage.INSTANCE); C_CLASS_FIELD = new ORCompositeElementType("C_CLASS_FIELD", RmlLanguage.INSTANCE); C_CLASS_INITIALIZER = new ORCompositeElementType("C_CLASS_INITIALIZER", RmlLanguage.INSTANCE); C_CLOSED_VARIANT = new ORCompositeElementType("C_CLOSED_VARIANT", RmlLanguage.INSTANCE); C_CONSTRAINTS = new ORCompositeElementType("C_CONSTRAINTS", RmlLanguage.INSTANCE); C_TYPE_CONSTRAINT = new ORCompositeElementType("C_TYPE_CONSTRAINT", RmlLanguage.INSTANCE); C_CUSTOM_OPERATOR = new ORCompositeElementType("C_CUSTOM_OPERATOR", RmlLanguage.INSTANCE); C_DECONSTRUCTION = new ORCompositeElementType("C_DECONSTRUCTION", RmlLanguage.INSTANCE); C_DEFAULT_VALUE = new ORCompositeElementType("C_DEFAULT_VALUE", RmlLanguage.INSTANCE); C_DIRECTIVE = new ORCompositeElementType("C_DIRECTIVE", RmlLanguage.INSTANCE); C_DO_LOOP = new ORCompositeElementType("C_DO_LOOP", RmlLanguage.INSTANCE); C_FIRST_CLASS = new ORCompositeElementType("C_FIRST_CLASS", RmlLanguage.INSTANCE); C_FOR_LOOP = new ORCompositeElementType("C_FOR_LOOP", RmlLanguage.INSTANCE); C_FIELD_VALUE = new ORCompositeElementType("C_FIELD_VALUE", RmlLanguage.INSTANCE); C_FUN_EXPR = new ORCompositeElementType("C_FUN_EXPR", RmlLanguage.INSTANCE); C_FUNCTION_BODY = new ORCompositeElementType("C_FUNCTION_BODY", RmlLanguage.INSTANCE); C_FUNCTION_CALL = new ORCompositeElementType("C_FUNCTION_CALL", RmlLanguage.INSTANCE); C_FUNCTION_EXPR = new ORCompositeElementType("C_FUNCTION_EXPR", RmlLanguage.INSTANCE); C_FUNCTOR_BINDING = new ORCompositeElementType("C_FUNCTOR_BINDING", RmlLanguage.INSTANCE); C_FUNCTOR_CALL = new ORCompositeElementType("C_FUNCTOR_CALL", RmlLanguage.INSTANCE); C_FUNCTOR_RESULT = new ORCompositeElementType("C_FUNCTOR_RESULT", RmlLanguage.INSTANCE); C_GUARD = new ORCompositeElementType("C_GUARD", RmlLanguage.INSTANCE); C_IF = new ORCompositeElementType("C_IF", RmlLanguage.INSTANCE); C_IF_THEN_ELSE = new ORCompositeElementType("C_IF_THEN_ELSE", RmlLanguage.INSTANCE); C_INHERIT = new ORCompositeElementType("C_INHERIT", RmlLanguage.INSTANCE); C_INTERPOLATION_EXPR = new ORCompositeElementType("C_INTERPOLATION_EXPR", RmlLanguage.INSTANCE); C_INTERPOLATION_PART = new ORCompositeElementType("C_INTERPOLATION_PART", RmlLanguage.INSTANCE); C_INTERPOLATION_REF = new ORCompositeElementType("C_INTERPOLATION_REF", RmlLanguage.INSTANCE); C_JS_OBJECT = new ORCompositeElementType("C_JS_OBJECT", RmlLanguage.INSTANCE); C_LET_ATTR = new ORCompositeElementType("C_LET_ATTR", RmlLanguage.INSTANCE); C_LET_BINDING = new ORCompositeElementType("C_LET_BINDING", RmlLanguage.INSTANCE); C_LOCAL_OPEN = new ORCompositeElementType("C_LOCAL_OPEN", RmlLanguage.INSTANCE); C_TYPE_VARIABLE = new ORCompositeElementType("C_TYPE_VARIABLE", RmlLanguage.INSTANCE); C_MACRO_EXPR = new ORCompositeElementType("C_MACRO_EXPR", RmlLanguage.INSTANCE); C_MACRO_NAME = new ORCompositeElementType("C_MACRO_NAME", RmlLanguage.INSTANCE); C_MACRO_BODY = new ORCompositeElementType("C_MACRO_RAW_BODY", RmlLanguage.INSTANCE); C_METHOD_CALL = new ORCompositeElementType("C_METHOD_CALL", RmlLanguage.INSTANCE); C_MIXIN_FIELD = new ORCompositeElementType("C_MIXIN_FIELD", RmlLanguage.INSTANCE); C_MODULE_BINDING = new ORCompositeElementType("C_MODULE_BINDING", RmlLanguage.INSTANCE); C_MODULE_SIGNATURE = new ORCompositeElementType("C_MODULE_SIGNATURE", RmlLanguage.INSTANCE); C_ML_INTERPOLATOR = new ORCompositeElementType("C_ML_INTERPOLATOR", RmlLanguage.INSTANCE); C_NAMED_PARAM = new ORCompositeElementType("C_NAMED_PARAM", RmlLanguage.INSTANCE); C_NONE = new ORCompositeElementType("C_NONE", RmlLanguage.INSTANCE); C_OBJECT = new ORCompositeElementType("C_OBJECT", RmlLanguage.INSTANCE); C_OPEN_VARIANT = new ORCompositeElementType("C_OPEN_VARIANT", RmlLanguage.INSTANCE); C_OPTION = new ORCompositeElementType("C_OPTION", RmlLanguage.INSTANCE); C_LOWER_NAME = new ORCompositeElementType("C_LOWER_NAME", RmlLanguage.INSTANCE); C_PARAM = new ORCompositeElementType("C_PARAM", RmlLanguage.INSTANCE); C_PARAMETERS = new ORCompositeElementType("C_PARAMETERS", RmlLanguage.INSTANCE); C_PATTERN_MATCH_BODY = new ORCompositeElementType("C_PATTERN_MATCH_BODY", RmlLanguage.INSTANCE); C_PATTERN_MATCH_EXPR = new ORCompositeElementType("C_PATTERN_MATCH_EXPR", RmlLanguage.INSTANCE); C_RECORD_EXPR = new ORCompositeElementType("C_RECORD_EXPR", RmlLanguage.INSTANCE); C_SCOPED_EXPR = new ORCompositeElementType("C_SCOPED_EXPR", RmlLanguage.INSTANCE); C_SIG_EXPR = new ORCompositeElementType("C_SIG_EXPR", RmlLanguage.INSTANCE); C_SIG_ITEM = new ORCompositeElementType("C_SIG_ITEM", RmlLanguage.INSTANCE); C_SOME = new ORCompositeElementType("C_SOME", RmlLanguage.INSTANCE); C_STRUCT_EXPR = new ORCompositeElementType("C_STRUCT_EXPR", RmlLanguage.INSTANCE); C_SWITCH_BODY = new ORCompositeElementType("C_SWITCH_BODY", RmlLanguage.INSTANCE); C_SWITCH_EXPR = new ORCompositeElementType("C_SWITCH_EXPR", RmlLanguage.INSTANCE); C_TAG = new ORCompositeElementType("C_TAG", RmlLanguage.INSTANCE); C_TAG_PROP_VALUE = new ORCompositeElementType("C_TAG_PROP_VALUE", RmlLanguage.INSTANCE); C_TAG_BODY = new ORCompositeElementType("C_TAG_BODY", RmlLanguage.INSTANCE); C_TAG_CLOSE = new ORCompositeElementType("C_TAG_CLOSE", RmlLanguage.INSTANCE); C_TAG_PROPERTY = new ORCompositeElementType("C_TAG_PROPERTY", RmlLanguage.INSTANCE); C_TAG_START = new ORCompositeElementType("C_TAG_START", RmlLanguage.INSTANCE); C_TERNARY = new ORCompositeElementType("C_TERNARY", RmlLanguage.INSTANCE); C_TRY_EXPR = new ORCompositeElementType("C_TRY_EXPR", RmlLanguage.INSTANCE); C_TRY_BODY = new ORCompositeElementType("C_TRY_BODY", RmlLanguage.INSTANCE); C_TRY_HANDLER = new ORCompositeElementType("C_TRY_HANDLER", RmlLanguage.INSTANCE); C_TRY_HANDLER_BODY = new ORCompositeElementType("C_TRY_HANDLER_BODY", RmlLanguage.INSTANCE); C_TRY_HANDLERS = new ORCompositeElementType("C_TRY_HANDLERS", RmlLanguage.INSTANCE); C_TUPLE = new ORCompositeElementType("C_TUPLE", RmlLanguage.INSTANCE); C_TYPE_BINDING = new ORCompositeElementType("C_TYPE_BINDING", RmlLanguage.INSTANCE); C_UNIT = new ORCompositeElementType("C_UNIT", RmlLanguage.INSTANCE); C_UNPACK = new ORCompositeElementType("C_UNPACK", RmlLanguage.INSTANCE); C_VARIANT_CONSTRUCTOR = new ORCompositeElementType("C_VARIANT_CONSTRUCTOR", RmlLanguage.INSTANCE); C_WHILE = new ORCompositeElementType("C_WHILE", RmlLanguage.INSTANCE); // Atom types CA_LOWER_SYMBOL = new ORCompositeElementType("CA_LOWER_SYMBOL", RmlLanguage.INSTANCE); CA_UPPER_SYMBOL = new ORCompositeElementType("CA_UPPER_SYMBOL", RmlLanguage.INSTANCE); A_LOWER_TAG_NAME = new ORTokenElementType("A_LOWER_TAG_NAME", RmlLanguage.INSTANCE); A_UPPER_TAG_NAME = new ORTokenElementType("A_UPPER_TAG_NAME", RmlLanguage.INSTANCE); A_VARIANT_NAME = new ORTokenElementType("A_VARIANT_NAME", RmlLanguage.INSTANCE); A_MODULE_NAME = new ORTokenElementType("A_MODULE_NAME", RmlLanguage.INSTANCE); A_EXCEPTION_NAME = new ORTokenElementType("A_EXCEPTION_NAME", RmlLanguage.INSTANCE); // Dummy types H_ATOM = new ORCompositeElementType("H_ATOM", RmlLanguage.INSTANCE); H_PLACE_HOLDER = new ORCompositeElementType("H_PLACE_HOLDER", RmlLanguage.INSTANCE); H_COLLECTION_ITEM = new ORCompositeElementType("H_COLLECTION_ITEM", RmlLanguage.INSTANCE); H_NAMED_PARAM_DECLARATION = new ORCompositeElementType("H_NAMED_PARAM_DECLARATION", RmlLanguage.INSTANCE); // Token element types ASYNC = new ORTokenElementType("ASYNC", RmlLanguage.INSTANCE); AWAIT = new ORTokenElementType("AWAIT", RmlLanguage.INSTANCE); BOOL_VALUE = new ORTokenElementType("BOOL_VALUE", RmlLanguage.INSTANCE); STRING_VALUE = new ORTokenElementType("STRING_VALUE", RmlLanguage.INSTANCE); FLOAT_VALUE = new ORTokenElementType("FLOAT_VALUE", RmlLanguage.INSTANCE); CATCH = new ORTokenElementType("CATCH", RmlLanguage.INSTANCE); CHAR_VALUE = new ORTokenElementType("CHAR_VALUE", RmlLanguage.INSTANCE); INT_VALUE = new ORTokenElementType("INT_VALUE", RmlLanguage.INSTANCE); PROPERTY_NAME = new ORTokenElementType("PROPERTY_NAME", RmlLanguage.INSTANCE); SWITCH = new ORTokenElementType("SWITCH", RmlLanguage.INSTANCE); PIPE_FIRST = new ORTokenElementType("PIPE_FIRST", RmlLanguage.INSTANCE); FUNCTION = new ORTokenElementType("FUNCTION", RmlLanguage.INSTANCE); FUN = new ORTokenElementType("FUN", RmlLanguage.INSTANCE); FUNCTOR = new ORTokenElementType("FUNCTOR", RmlLanguage.INSTANCE); IF = new ORTokenElementType("IF", RmlLanguage.INSTANCE); AND = new ORTokenElementType("AND", RmlLanguage.INSTANCE); L_AND = new ORTokenElementType("L_AND", RmlLanguage.INSTANCE); L_OR = new ORTokenElementType("L_OR", RmlLanguage.INSTANCE); ARROBASE = new ORTokenElementType("ARROBASE", RmlLanguage.INSTANCE); ARROBASE_2 = new ORTokenElementType("ARROBASE_2", RmlLanguage.INSTANCE); ARROBASE_3 = new ORTokenElementType("ARROBASE_3", RmlLanguage.INSTANCE); ARROW = new ORTokenElementType("ARROW", RmlLanguage.INSTANCE); ASSERT = new ORTokenElementType("ASSERT", RmlLanguage.INSTANCE); AS = new ORTokenElementType("AS", RmlLanguage.INSTANCE); BACKTICK = new ORTokenElementType("BACKTICK", RmlLanguage.INSTANCE); BEGIN = new ORTokenElementType("BEGIN", RmlLanguage.INSTANCE); CARRET = new ORTokenElementType("CARRET", RmlLanguage.INSTANCE); COLON = new ORTokenElementType("COLON", RmlLanguage.INSTANCE); COMMA = new ORTokenElementType("COMMA", RmlLanguage.INSTANCE); SINGLE_COMMENT = new ORTokenElementType("SINGLE_COMMENT", RmlLanguage.INSTANCE); MULTI_COMMENT = new ORTokenElementType("MULTI_COMMENT", RmlLanguage.INSTANCE); DIFF = new ORTokenElementType("DIFF", RmlLanguage.INSTANCE); DIRECTIVE_IF = new ORTokenElementType("DIRECTIVE_IF", RmlLanguage.INSTANCE); DIRECTIVE_ELSE = new ORTokenElementType("DIRECTIVE_ELSE", RmlLanguage.INSTANCE); DIRECTIVE_ELIF = new ORTokenElementType("DIRECTIVE_ELIF", RmlLanguage.INSTANCE); DIRECTIVE_END = new ORTokenElementType("DIRECTIVE_END", RmlLanguage.INSTANCE); DIRECTIVE_ENDIF = new ORTokenElementType("DIRECTIVE_ENDIF", RmlLanguage.INSTANCE); LT_OR_EQUAL = new ORTokenElementType("LT_OR_EQUAL", RmlLanguage.INSTANCE); GT_OR_EQUAL = new ORTokenElementType("GT_OR_EQUAL", RmlLanguage.INSTANCE); DOLLAR = new ORTokenElementType("DOLLAR", RmlLanguage.INSTANCE); DOT = new ORTokenElementType("DOT", RmlLanguage.INSTANCE); DOTDOTDOT = new ORTokenElementType("DOTDOTDOT", RmlLanguage.INSTANCE); DO = new ORTokenElementType("DO", RmlLanguage.INSTANCE); DONE = new ORTokenElementType("DONE", RmlLanguage.INSTANCE); ELSE = new ORTokenElementType("ELSE", RmlLanguage.INSTANCE); END = new ORTokenElementType("END", RmlLanguage.INSTANCE); NOT_EQ = new ORTokenElementType("EQ", RmlLanguage.INSTANCE); NOT_EQEQ = new ORTokenElementType("EQEQ", RmlLanguage.INSTANCE); EQ = new ORTokenElementType("EQ", RmlLanguage.INSTANCE); EQEQ = new ORTokenElementType("EQEQ", RmlLanguage.INSTANCE); EQEQEQ = new ORTokenElementType("EQEQEQ", RmlLanguage.INSTANCE); EXCEPTION = new ORTokenElementType("EXCEPTION", RmlLanguage.INSTANCE); EXCLAMATION_MARK = new ORTokenElementType("EXCLAMATION_MARK", RmlLanguage.INSTANCE); EXTERNAL = new ORTokenElementType("EXTERNAL", RmlLanguage.INSTANCE); FOR = new ORTokenElementType("FOR", RmlLanguage.INSTANCE); TYPE_ARGUMENT = new ORTokenElementType("TYPE_ARGUMENT", RmlLanguage.INSTANCE); GT = new ORTokenElementType("GT", RmlLanguage.INSTANCE); IN = new ORTokenElementType("IN", RmlLanguage.INSTANCE); LAZY = new ORTokenElementType("LAZY", RmlLanguage.INSTANCE); INCLUDE = new ORTokenElementType("INCLUDE", RmlLanguage.INSTANCE); LARRAY = new ORTokenElementType("LARRAY", RmlLanguage.INSTANCE); LBRACE = new ORTokenElementType("LBRACE", RmlLanguage.INSTANCE); LBRACKET = new ORTokenElementType("LBRACKET", RmlLanguage.INSTANCE); LET = new ORTokenElementType("LET", RmlLanguage.INSTANCE); LIST = new ORTokenElementType("LIST", RmlLanguage.INSTANCE); LIDENT = new ORTokenElementType("LIDENT", RmlLanguage.INSTANCE); LPAREN = new ORTokenElementType("LPAREN", RmlLanguage.INSTANCE); LT = new ORTokenElementType("LT", RmlLanguage.INSTANCE); MATCH = new ORTokenElementType("MATCH", RmlLanguage.INSTANCE); MINUS = new ORTokenElementType("MINUS", RmlLanguage.INSTANCE); MINUSDOT = new ORTokenElementType("MINUSDOT", RmlLanguage.INSTANCE); MODULE = new ORTokenElementType("MODULE", RmlLanguage.INSTANCE); MUTABLE = new ORTokenElementType("MUTABLE", RmlLanguage.INSTANCE); NONE = new ORTokenElementType("NONE", RmlLanguage.INSTANCE); OF = new ORTokenElementType("OF", RmlLanguage.INSTANCE); OPEN = new ORTokenElementType("OPEN", RmlLanguage.INSTANCE); OPTION = new ORTokenElementType("OPTION", RmlLanguage.INSTANCE); POLY_VARIANT = new ORTokenElementType("POLY_VARIANT", RmlLanguage.INSTANCE); PIPE = new ORTokenElementType("PIPE", RmlLanguage.INSTANCE); PIPE_FORWARD = new ORTokenElementType("PIPE_FORWARD", RmlLanguage.INSTANCE); PLUS = new ORTokenElementType("PLUS", RmlLanguage.INSTANCE); PERCENT = new ORTokenElementType("PERCENT", RmlLanguage.INSTANCE); PLUSDOT = new ORTokenElementType("PLUSDOT", RmlLanguage.INSTANCE); QUESTION_MARK = new ORTokenElementType("QUESTION_MARK", RmlLanguage.INSTANCE); SINGLE_QUOTE = new ORTokenElementType("SINGLE_QUOTE", RmlLanguage.INSTANCE); DOUBLE_QUOTE = new ORTokenElementType("DOUBLE_QUOTE", RmlLanguage.INSTANCE); RAISE = new ORTokenElementType("RAISE", RmlLanguage.INSTANCE); RARRAY = new ORTokenElementType("RARRAY", RmlLanguage.INSTANCE); RBRACE = new ORTokenElementType("RBRACE", RmlLanguage.INSTANCE); RBRACKET = new ORTokenElementType("RBRACKET", RmlLanguage.INSTANCE); REC = new ORTokenElementType("REC", RmlLanguage.INSTANCE); REF = new ORTokenElementType("REF", RmlLanguage.INSTANCE); RPAREN = new ORTokenElementType("RPAREN", RmlLanguage.INSTANCE); SEMI = new ORTokenElementType("SEMI", RmlLanguage.INSTANCE); SIG = new ORTokenElementType("SIG", RmlLanguage.INSTANCE); SHARP = new ORTokenElementType("SHARP", RmlLanguage.INSTANCE); SHARPSHARP = new ORTokenElementType("SHARPSHARP", RmlLanguage.INSTANCE); SHORTCUT = new ORTokenElementType("SHORTCUT", RmlLanguage.INSTANCE); SLASH = new ORTokenElementType("SLASH", RmlLanguage.INSTANCE); SLASH_2 = new ORTokenElementType("SLASH_2", RmlLanguage.INSTANCE); SLASHDOT = new ORTokenElementType("SLASHDOT", RmlLanguage.INSTANCE); SOME = new ORTokenElementType("SOME", RmlLanguage.INSTANCE); STAR = new ORTokenElementType("STAR", RmlLanguage.INSTANCE); STARDOT = new ORTokenElementType("STARDOT", RmlLanguage.INSTANCE); STRING_CONCAT = new ORTokenElementType("STRING_CONCAT", RmlLanguage.INSTANCE); STRUCT = new ORTokenElementType("STRUCT", RmlLanguage.INSTANCE); OP_STRUCT_DIFF = new ORTokenElementType("OP_STRUCT_DIFF", RmlLanguage.INSTANCE); TAG_AUTO_CLOSE = new ORTokenElementType("TAG_AUTO_CLOSE", RmlLanguage.INSTANCE); TAG_LT_SLASH = new ORTokenElementType("TAG_LT_SLASH", RmlLanguage.INSTANCE); TILDE = new ORTokenElementType("TILDE", RmlLanguage.INSTANCE); TO = new ORTokenElementType("TO", RmlLanguage.INSTANCE); THEN = new ORTokenElementType("THEN", RmlLanguage.INSTANCE); TRY = new ORTokenElementType("TRY", RmlLanguage.INSTANCE); TYPE = new ORTokenElementType("TYPE", RmlLanguage.INSTANCE); UNPACK = new ORTokenElementType("UNPACK", RmlLanguage.INSTANCE); UIDENT = new ORTokenElementType("UIDENT", RmlLanguage.INSTANCE); UNIT = new ORTokenElementType("UNIT", RmlLanguage.INSTANCE); VAL = new ORTokenElementType("VAL", RmlLanguage.INSTANCE); PUB = new ORTokenElementType("PUB", RmlLanguage.INSTANCE); PRI = new ORTokenElementType("PRI", RmlLanguage.INSTANCE); WHEN = new ORTokenElementType("WHEN", RmlLanguage.INSTANCE); WHILE = new ORTokenElementType("WHILE", RmlLanguage.INSTANCE); WITH = new ORTokenElementType("WITH", RmlLanguage.INSTANCE); RAW = new ORTokenElementType("RAW", RmlLanguage.INSTANCE); ASR = new ORTokenElementType("ASR", RmlLanguage.INSTANCE); CLASS = new ORTokenElementType("CLASS", RmlLanguage.INSTANCE); CONSTRAINT = new ORTokenElementType("CONSTRAINT", RmlLanguage.INSTANCE); DOWNTO = new ORTokenElementType("DOWNTO", RmlLanguage.INSTANCE); INHERIT = new ORTokenElementType("INHERIT", RmlLanguage.INSTANCE); INITIALIZER = new ORTokenElementType("INITIALIZER", RmlLanguage.INSTANCE); LAND = new ORTokenElementType("LAND", RmlLanguage.INSTANCE); LOR = new ORTokenElementType("LOR", RmlLanguage.INSTANCE); LSL = new ORTokenElementType("LSL", RmlLanguage.INSTANCE); LSR = new ORTokenElementType("LSR", RmlLanguage.INSTANCE); LXOR = new ORTokenElementType("LXOR", RmlLanguage.INSTANCE); METHOD = new ORTokenElementType("METHOD", RmlLanguage.INSTANCE); MOD = new ORTokenElementType("MOD", RmlLanguage.INSTANCE); NEW = new ORTokenElementType("NEW", RmlLanguage.INSTANCE); NONREC = new ORTokenElementType("NONREC", RmlLanguage.INSTANCE); OR = new ORTokenElementType("OR", RmlLanguage.INSTANCE); PRIVATE = new ORTokenElementType("PRIVATE", RmlLanguage.INSTANCE); VIRTUAL = new ORTokenElementType("VIRTUAL", RmlLanguage.INSTANCE); COLON_EQ = new ORTokenElementType("COLON_EQ", RmlLanguage.INSTANCE); COLON_GT = new ORTokenElementType("COLON_GT", RmlLanguage.INSTANCE); DOTDOT = new ORTokenElementType("DOTDOT", RmlLanguage.INSTANCE); SEMISEMI = new ORTokenElementType("SEMISEMI", RmlLanguage.INSTANCE); GT_BRACKET = new ORTokenElementType("GT_BRACKET", RmlLanguage.INSTANCE); GT_BRACE = new ORTokenElementType("GT_BRACE", RmlLanguage.INSTANCE); LEFT_ARROW = new ORTokenElementType("LEFT_ARROW", RmlLanguage.INSTANCE); RIGHT_ARROW = new ORTokenElementType("RIGHT_ARROW", RmlLanguage.INSTANCE); OBJECT = new ORTokenElementType("OBJECT", RmlLanguage.INSTANCE); RECORD = new ORTokenElementType("RECORD", RmlLanguage.INSTANCE); AMPERSAND = new ORTokenElementType("AMPERSAND", RmlLanguage.INSTANCE); BRACKET_GT = new ORTokenElementType("BRACKET_GT", RmlLanguage.INSTANCE); BRACKET_LT = new ORTokenElementType("BRACKET_LT", RmlLanguage.INSTANCE); BRACE_LT = new ORTokenElementType("BRACE_LT", RmlLanguage.INSTANCE); ML_STRING_VALUE = new ORTokenElementType("ML_STRING_VALUE", RmlLanguage.INSTANCE); ML_STRING_OPEN = new ORTokenElementType("ML_STRING_OPEN", RmlLanguage.INSTANCE); ML_STRING_CLOSE = new ORTokenElementType("ML_STRING_CLOSE", RmlLanguage.INSTANCE); JS_STRING_OPEN = new ORTokenElementType("JS_STRING_OPEN", RmlLanguage.INSTANCE); JS_STRING_CLOSE = new ORTokenElementType("JS_STRING_CLOSE", RmlLanguage.INSTANCE); UNDERSCORE = new ORTokenElementType("UNDERSCORE", RmlLanguage.INSTANCE); } } ================================================ FILE: src/main/java/com/reason/lang/rescript/ResASTFactory.java ================================================ package com.reason.lang.rescript; import com.reason.lang.core.psi.impl.*; public class ResASTFactory extends ORASTFactory { public ResASTFactory() { super(ResTypes.INSTANCE); } } ================================================ FILE: src/main/java/com/reason/lang/rescript/ResFlexLexer.java ================================================ // Generated by JFlex 1.9.2 http://jflex.de/ (tweaked for IntelliJ platform) // source: Rescript.flex package com.reason.lang.rescript; import com.intellij.lexer.*; import com.intellij.psi.tree.*; import com.reason.lang.core.type.*; import static com.intellij.psi.TokenType.*; @SuppressWarnings("ALL") public class ResFlexLexer implements FlexLexer { /** 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 INITIAL = 2; public static final int IN_LOWER_DECLARATION = 4; public static final int IN_TEMPLATE = 6; public static final int IN_STRING = 8; public static final int IN_ML_COMMENT = 10; public static final int IN_SL_COMMENT = 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\37\u0100\1\u0200\267\u0100\10\u0300\u1020\u0100"; 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\3\1\4\1\5\22\0\1\1"+ "\1\6\1\7\1\10\1\11\1\12\1\13\1\14\1\15"+ "\1\16\1\17\1\20\1\21\1\22\1\23\1\24\1\25"+ "\1\26\2\27\4\30\2\31\1\32\1\33\1\34\1\35"+ "\1\36\1\37\1\40\1\41\1\42\2\41\1\43\1\41"+ "\7\44\1\45\1\46\1\47\2\44\1\50\4\44\1\51"+ "\2\44\1\52\1\53\1\54\1\55\1\56\1\57\1\60"+ "\1\61\1\62\1\63\1\64\1\65\1\66\1\67\1\70"+ "\1\71\1\72\1\73\1\74\1\75\1\76\1\77\1\100"+ "\1\101\1\102\1\103\1\104\1\105\1\106\1\107\1\110"+ "\1\111\1\112\1\113\1\114\1\115\6\0\1\3\u01a2\0"+ "\2\3\326\0\u0100\3"; private static int [] zzUnpackcmap_blocks() { int [] result = new int[1024]; 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\1\3\2\4\1\5\1\6\1\7"+ "\1\10\1\11\1\12\1\13\1\14\1\15\1\16\1\17"+ "\1\20\1\21\1\22\2\23\1\24\1\25\1\26\1\27"+ "\1\30\1\31\1\32\3\33\1\34\1\35\1\36\1\37"+ "\1\40\1\41\23\42\1\43\1\44\1\45\1\46\1\47"+ "\1\40\2\50\1\51\1\3\2\52\1\53\1\54\2\52"+ "\1\55\1\52\1\56\2\52\2\57\1\60\1\61\1\62"+ "\1\63\2\0\1\64\1\65\1\66\1\67\1\70\1\71"+ "\1\72\1\73\1\74\1\75\1\76\1\77\2\0\3\23"+ "\1\100\1\101\1\102\1\103\1\104\1\105\1\106\1\107"+ "\2\33\1\42\1\110\5\42\1\111\6\42\1\112\1\113"+ "\15\42\1\114\1\42\1\115\17\42\1\116\1\117\1\50"+ "\5\52\1\120\1\121\1\122\2\123\3\0\1\123\1\64"+ "\1\124\1\77\1\23\1\0\1\77\2\23\1\125\2\33"+ "\1\126\1\127\12\42\1\130\3\42\1\131\6\42\1\132"+ "\1\42\1\133\1\134\1\135\3\42\1\136\1\42\1\137"+ "\4\42\1\140\1\141\1\42\1\142\1\143\1\144\1\145"+ "\4\42\1\146\3\42\1\147\4\42\1\143\6\0\1\77"+ "\1\23\1\150\1\151\7\42\1\152\1\42\1\153\6\42"+ "\1\154\1\155\1\156\1\157\6\42\1\160\5\42\1\161"+ "\1\162\1\163\1\164\2\42\1\165\1\42\1\166\2\0"+ "\1\77\1\42\1\167\1\170\1\171\1\172\1\173\10\42"+ "\1\174\7\42\1\175\4\42\1\176\1\177\1\42\1\200"+ "\6\42\1\201\1\202\1\42\1\203\1\204\1\205\1\42"+ "\1\206\1\207\1\210\4\42\1\211\1\212\1\213\1\42"+ "\1\214\1\215\1\216\2\42\1\217\2\42\1\220\1\42"+ "\1\221\1\42\1\222"; private static int [] zzUnpackAction() { int [] result = new int[375]; 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\116\0\234\0\352\0\u0138\0\u0186\0\u01d4\0\u0222"+ "\0\u0222\0\u0270\0\u0222\0\u02be\0\u030c\0\u0222\0\u035a\0\u0222"+ "\0\u03a8\0\u03f6\0\u0222\0\u0222\0\u0444\0\u0492\0\u0222\0\u04e0"+ "\0\u052e\0\u057c\0\u05ca\0\u0618\0\u0666\0\u0222\0\u06b4\0\u0702"+ "\0\u0222\0\u0222\0\u0222\0\u0750\0\u079e\0\u07ec\0\u0222\0\u0222"+ "\0\u0222\0\u0222\0\u083a\0\u0222\0\u0888\0\u08d6\0\u0924\0\u0972"+ "\0\u09c0\0\u0a0e\0\u083a\0\u0a5c\0\u0aaa\0\u0af8\0\u0b46\0\u0b94"+ "\0\u0be2\0\u0c30\0\u0c7e\0\u0ccc\0\u0d1a\0\u0d68\0\u0db6\0\u0222"+ "\0\u0e04\0\u0222\0\u0222\0\u0222\0\u0e52\0\u0e52\0\u0ea0\0\u0eee"+ "\0\u0f3c\0\u0eee\0\u0f8a\0\u0222\0\u0222\0\u0222\0\u0fd8\0\u0222"+ "\0\u1026\0\u0222\0\u1074\0\u10c2\0\u0222\0\u1110\0\u115e\0\u0222"+ "\0\u11ac\0\u0222\0\u11fa\0\u1248\0\u1296\0\u0222\0\u0222\0\u0222"+ "\0\u0222\0\u0222\0\u12e4\0\u0222\0\u0222\0\u0222\0\u0222\0\u1332"+ "\0\u1380\0\u13ce\0\u0222\0\u141c\0\u146a\0\u0222\0\u0222\0\u0222"+ "\0\u0222\0\u0222\0\u0222\0\u14b8\0\u0222\0\u1506\0\u1554\0\u15a2"+ "\0\u15f0\0\u163e\0\u168c\0\u16da\0\u1728\0\u1776\0\u17c4\0\u1812"+ "\0\u1860\0\u18ae\0\u18fc\0\u194a\0\u1998\0\u083a\0\u19e6\0\u1a34"+ "\0\u1a82\0\u1ad0\0\u1b1e\0\u1b6c\0\u1bba\0\u1c08\0\u1c56\0\u1ca4"+ "\0\u1cf2\0\u1d40\0\u1d8e\0\u1ddc\0\u083a\0\u1e2a\0\u083a\0\u1e78"+ "\0\u1ec6\0\u1f14\0\u1f62\0\u1fb0\0\u1ffe\0\u204c\0\u209a\0\u20e8"+ "\0\u2136\0\u2184\0\u21d2\0\u2220\0\u226e\0\u22bc\0\u0222\0\u0222"+ "\0\u230a\0\u2358\0\u23a6\0\u23f4\0\u2442\0\u2490\0\u0222\0\u0222"+ "\0\u0222\0\u0222\0\u11fa\0\u24de\0\u252c\0\u257a\0\u25c8\0\u25c8"+ "\0\u0222\0\u0222\0\u2616\0\u2664\0\u26b2\0\u2700\0\u274e\0\u0222"+ "\0\u279c\0\u27ea\0\u083a\0\u083a\0\u2838\0\u2886\0\u28d4\0\u2922"+ "\0\u2970\0\u29be\0\u2a0c\0\u2a5a\0\u2aa8\0\u2af6\0\u083a\0\u2b44"+ "\0\u2b92\0\u2be0\0\u083a\0\u2c2e\0\u2c7c\0\u2cca\0\u2d18\0\u2d66"+ "\0\u2db4\0\u083a\0\u2e02\0\u083a\0\u083a\0\u083a\0\u2e50\0\u2e9e"+ "\0\u2eec\0\u2f3a\0\u2f88\0\u083a\0\u2fd6\0\u3024\0\u3072\0\u30c0"+ "\0\u310e\0\u083a\0\u315c\0\u083a\0\u083a\0\u083a\0\u083a\0\u31aa"+ "\0\u31f8\0\u3246\0\u3294\0\u083a\0\u32e2\0\u3330\0\u337e\0\u083a"+ "\0\u33cc\0\u341a\0\u3468\0\u34b6\0\u0e52\0\u3504\0\u3552\0\u35a0"+ "\0\u35ee\0\u363c\0\u368a\0\u36d8\0\u13ce\0\u0750\0\u0750\0\u3726"+ "\0\u3774\0\u37c2\0\u3810\0\u385e\0\u38ac\0\u38fa\0\u083a\0\u3948"+ "\0\u083a\0\u3996\0\u39e4\0\u3a32\0\u3a80\0\u3ace\0\u3b1c\0\u083a"+ "\0\u083a\0\u083a\0\u083a\0\u3b6a\0\u3bb8\0\u3c06\0\u3c54\0\u3ca2"+ "\0\u3cf0\0\u083a\0\u3d3e\0\u3d8c\0\u3dda\0\u3e28\0\u3e76\0\u083a"+ "\0\u083a\0\u083a\0\u083a\0\u3ec4\0\u3f12\0\u083a\0\u3f60\0\u083a"+ "\0\u3fae\0\u3ffc\0\u13ce\0\u404a\0\u083a\0\u083a\0\u083a\0\u083a"+ "\0\u083a\0\u4098\0\u40e6\0\u4134\0\u4182\0\u41d0\0\u421e\0\u426c"+ "\0\u42ba\0\u083a\0\u4308\0\u4356\0\u43a4\0\u43f2\0\u4440\0\u448e"+ "\0\u44dc\0\u083a\0\u452a\0\u4578\0\u45c6\0\u4614\0\u083a\0\u083a"+ "\0\u4662\0\u083a\0\u46b0\0\u46fe\0\u474c\0\u479a\0\u47e8\0\u4836"+ "\0\u083a\0\u083a\0\u4884\0\u083a\0\u083a\0\u083a\0\u48d2\0\u083a"+ "\0\u083a\0\u083a\0\u4920\0\u496e\0\u49bc\0\u4a0a\0\u083a\0\u083a"+ "\0\u083a\0\u4a58\0\u083a\0\u083a\0\u083a\0\u4aa6\0\u4af4\0\u083a"+ "\0\u4b42\0\u4b90\0\u083a\0\u4bde\0\u083a\0\u4c2c\0\u083a"; private static int [] zzUnpackRowMap() { int [] result = new int[375]; 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 = "\116\10\1\11\1\12\1\13\1\11\1\12\1\14\1\15"+ "\1\16\1\17\1\11\1\20\1\21\1\22\1\23\1\24"+ "\1\25\1\26\1\27\1\30\1\31\1\32\1\33\4\34"+ "\1\35\1\36\1\37\1\40\1\41\1\42\1\43\4\44"+ "\1\45\2\44\1\46\1\44\1\47\1\50\1\51\1\52"+ "\1\53\1\54\1\55\1\56\1\57\1\60\1\61\1\62"+ "\2\63\1\64\2\63\1\65\1\66\1\67\1\70\1\71"+ "\1\63\1\72\1\73\1\74\1\75\1\76\1\77\3\63"+ "\1\100\1\101\1\102\1\103\1\104\1\12\1\13\1\11"+ "\1\12\1\14\45\104\1\50\2\104\1\105\1\104\21\106"+ "\1\107\10\106\4\104\1\110\1\111\1\112\1\110\1\111"+ "\1\113\3\110\1\114\45\110\1\115\32\110\1\100\1\110"+ "\1\102\1\110\3\116\2\11\1\117\1\116\1\120\43\116"+ "\1\121\45\116\2\11\1\117\1\116\1\122\7\116\1\123"+ "\4\116\1\124\73\116\1\125\2\11\1\126\110\116\117\0"+ "\1\12\2\0\1\12\113\0\1\13\150\0\1\127\70\0"+ "\1\130\30\0\11\131\4\0\1\131\1\0\32\131\17\0"+ "\1\132\102\0\2\133\4\0\45\133\1\134\2\133\1\135"+ "\1\133\32\135\4\133\23\0\1\136\112\0\1\137\2\0"+ "\1\140\115\0\1\141\12\0\1\142\102\0\1\143\111\0"+ "\1\144\3\0\1\145\1\146\11\0\1\147\102\0\1\150"+ "\1\0\5\34\10\0\1\151\1\152\2\153\1\154\2\153"+ "\1\155\4\0\1\34\2\0\1\151\2\0\1\152\1\0"+ "\10\153\1\154\10\153\1\155\2\153\27\0\1\150\1\0"+ "\5\34\11\0\1\152\6\153\4\0\1\34\5\0\1\152"+ "\1\0\24\153\36\0\1\156\2\0\1\157\1\160\101\0"+ "\1\161\1\0\1\162\10\0\1\163\115\0\1\164\1\165"+ "\73\0\1\44\10\0\5\44\7\0\11\44\4\0\1\44"+ "\1\0\32\44\20\0\1\44\10\0\5\44\7\0\11\44"+ "\4\0\1\44\1\0\16\44\1\166\13\44\20\0\1\44"+ "\10\0\5\44\7\0\11\44\4\0\1\44\1\0\16\44"+ "\1\167\13\44\20\0\1\63\10\0\5\63\7\0\11\63"+ "\4\0\1\63\1\0\32\63\20\0\1\63\10\0\5\63"+ "\7\0\11\63\4\0\1\63\1\0\15\63\1\170\4\63"+ "\1\171\3\63\1\172\3\63\20\0\1\63\10\0\5\63"+ "\7\0\11\63\4\0\1\63\1\0\4\63\1\173\25\63"+ "\20\0\1\63\10\0\5\63\7\0\11\63\4\0\1\63"+ "\1\0\1\174\12\63\1\175\2\63\1\176\13\63\20\0"+ "\1\63\10\0\5\63\7\0\11\63\4\0\1\63\1\0"+ "\16\63\1\177\13\63\20\0\1\63\10\0\5\63\7\0"+ "\11\63\4\0\1\63\1\0\13\63\1\200\1\63\1\201"+ "\11\63\1\202\2\63\20\0\1\63\10\0\5\63\7\0"+ "\11\63\4\0\1\63\1\0\1\203\15\63\1\204\5\63"+ "\1\205\5\63\20\0\1\63\10\0\5\63\7\0\11\63"+ "\4\0\1\63\1\0\5\63\1\206\7\63\1\207\14\63"+ "\20\0\1\63\10\0\5\63\7\0\11\63\4\0\1\63"+ "\1\0\1\210\3\63\1\211\3\63\1\212\5\63\1\213"+ "\3\63\1\214\4\63\1\215\2\63\20\0\1\63\10\0"+ "\5\63\7\0\11\63\4\0\1\63\1\0\1\216\3\63"+ "\1\217\11\63\1\220\5\63\1\221\5\63\20\0\1\63"+ "\10\0\5\63\7\0\11\63\4\0\1\63\1\0\4\63"+ "\1\222\11\63\1\223\13\63\20\0\1\63\10\0\5\63"+ "\7\0\11\63\4\0\1\63\1\0\1\63\1\224\3\63"+ "\1\225\11\63\1\226\1\63\1\227\10\63\20\0\1\63"+ "\10\0\5\63\7\0\11\63\4\0\1\63\1\0\21\63"+ "\1\230\2\63\1\231\5\63\20\0\1\63\10\0\5\63"+ "\7\0\11\63\4\0\1\63\1\0\1\232\3\63\1\233"+ "\25\63\20\0\1\63\10\0\5\63\7\0\11\63\4\0"+ "\1\63\1\0\10\63\1\234\12\63\1\235\2\63\1\236"+ "\3\63\20\0\1\63\10\0\5\63\7\0\11\63\4\0"+ "\1\63\1\0\7\63\1\237\11\63\1\240\6\63\1\241"+ "\1\63\20\0\1\63\10\0\5\63\7\0\11\63\4\0"+ "\1\63\1\0\15\63\1\242\14\63\20\0\1\63\10\0"+ "\5\63\7\0\11\63\4\0\1\63\1\0\1\243\7\63"+ "\1\244\21\63\20\0\1\63\10\0\5\63\7\0\11\63"+ "\4\0\1\63\1\0\7\63\1\245\1\246\21\63\42\0"+ "\1\247\54\0\1\250\16\0\1\106\10\0\5\106\7\0"+ "\11\106\4\0\1\106\1\0\32\106\20\0\1\106\10\0"+ "\5\106\7\0\11\106\4\0\1\106\1\0\4\106\1\251"+ "\25\106\4\0\11\110\1\0\45\110\1\0\32\110\1\0"+ "\1\110\1\0\2\110\1\111\2\110\1\111\4\110\1\0"+ "\45\110\1\0\32\110\1\0\1\110\1\0\3\110\1\112"+ "\6\110\1\0\45\110\1\0\32\110\1\0\1\110\1\0"+ "\1\110\2\0\1\116\113\0\2\116\1\252\2\0\1\253"+ "\17\116\5\254\44\116\1\255\10\116\1\256\6\116\24\0"+ "\1\257\110\0\1\260\100\0\1\125\150\0\1\261\74\0"+ "\1\131\10\0\5\131\7\0\11\131\4\0\1\131\1\0"+ "\32\131\20\0\1\262\110\0\1\133\4\0\1\263\10\0"+ "\5\264\21\0\1\133\5\0\1\133\13\0\1\133\1\265"+ "\2\0\1\133\1\0\1\133\3\0\1\266\22\0\1\267"+ "\10\0\5\270\7\0\11\270\4\0\1\270\1\0\32\270"+ "\27\0\1\271\117\0\5\150\11\0\1\152\6\272\4\0"+ "\1\150\5\0\1\152\1\0\24\272\31\0\2\273\107\0"+ "\1\274\1\0\1\274\2\0\5\275\111\0\4\276\112\0"+ "\5\277\7\0\3\277\14\0\6\277\65\0\1\300\74\0"+ "\1\44\10\0\5\44\7\0\11\44\4\0\1\44\1\0"+ "\15\44\1\301\14\44\20\0\1\44\10\0\5\44\7\0"+ "\11\44\4\0\1\44\1\0\14\44\1\302\15\44\20\0"+ "\1\63\10\0\5\63\7\0\11\63\4\0\1\63\1\0"+ "\3\63\1\303\26\63\20\0\1\63\10\0\5\63\7\0"+ "\11\63\4\0\1\63\1\0\21\63\1\304\1\305\5\63"+ "\1\306\1\63\20\0\1\63\10\0\5\63\7\0\11\63"+ "\4\0\1\63\1\0\1\307\31\63\20\0\1\63\10\0"+ "\5\63\7\0\11\63\4\0\1\63\1\0\6\63\1\310"+ "\23\63\20\0\1\63\10\0\5\63\7\0\11\63\4\0"+ "\1\63\1\0\23\63\1\311\6\63\20\0\1\63\10\0"+ "\5\63\7\0\11\63\4\0\1\63\1\0\1\312\31\63"+ "\20\0\1\63\10\0\5\63\7\0\11\63\4\0\1\63"+ "\1\0\15\63\1\313\14\63\20\0\1\63\10\0\5\63"+ "\7\0\11\63\4\0\1\63\1\0\15\63\1\314\10\63"+ "\1\315\3\63\20\0\1\63\10\0\5\63\7\0\11\63"+ "\4\0\1\63\1\0\22\63\1\316\7\63\20\0\1\63"+ "\10\0\5\63\7\0\11\63\4\0\1\63\1\0\3\63"+ "\1\317\26\63\20\0\1\63\10\0\5\63\7\0\11\63"+ "\4\0\1\63\1\0\2\63\1\320\20\63\1\321\6\63"+ "\20\0\1\63\10\0\5\63\7\0\11\63\4\0\1\63"+ "\1\0\13\63\1\322\16\63\20\0\1\63\10\0\5\63"+ "\7\0\11\63\4\0\1\63\1\0\21\63\1\323\10\63"+ "\20\0\1\63\10\0\5\63\7\0\11\63\4\0\1\63"+ "\1\0\15\63\1\324\14\63\20\0\1\63\10\0\5\63"+ "\7\0\11\63\4\0\1\63\1\0\2\63\1\325\4\63"+ "\1\326\1\327\21\63\20\0\1\63\10\0\5\63\7\0"+ "\11\63\4\0\1\63\1\0\15\63\1\330\13\63\1\331"+ "\20\0\1\63\10\0\5\63\7\0\11\63\4\0\1\63"+ "\1\0\23\63\1\332\6\63\20\0\1\63\10\0\5\63"+ "\7\0\11\63\4\0\1\63\1\0\22\63\1\333\7\63"+ "\20\0\1\63\10\0\5\63\7\0\11\63\4\0\1\63"+ "\1\0\21\63\1\334\10\63\20\0\1\63\10\0\5\63"+ "\7\0\11\63\4\0\1\63\1\0\13\63\1\335\5\63"+ "\1\336\10\63\20\0\1\63\10\0\5\63\7\0\11\63"+ "\4\0\1\63\1\0\16\63\1\337\13\63\20\0\1\63"+ "\10\0\5\63\7\0\11\63\4\0\1\63\1\0\23\63"+ "\1\340\6\63\20\0\1\63\10\0\5\63\7\0\11\63"+ "\4\0\1\63\1\0\23\63\1\341\6\63\20\0\1\63"+ "\10\0\5\63\7\0\11\63\4\0\1\63\1\0\3\63"+ "\1\342\26\63\20\0\1\63\10\0\5\63\7\0\11\63"+ "\4\0\1\63\1\0\23\63\1\343\6\63\20\0\1\63"+ "\10\0\5\63\7\0\11\63\4\0\1\63\1\0\26\63"+ "\1\344\3\63\20\0\1\63\10\0\5\63\7\0\11\63"+ "\4\0\1\63\1\0\15\63\1\345\14\63\20\0\1\63"+ "\10\0\5\63\7\0\11\63\4\0\1\63\1\0\11\63"+ "\1\346\20\63\20\0\1\63\10\0\5\63\7\0\11\63"+ "\4\0\1\63\1\0\4\63\1\347\16\63\1\350\6\63"+ "\20\0\1\63\10\0\5\63\7\0\11\63\4\0\1\63"+ "\1\0\10\63\1\351\21\63\20\0\1\63\10\0\5\63"+ "\7\0\11\63\4\0\1\63\1\0\1\63\1\352\30\63"+ "\20\0\1\63\10\0\5\63\7\0\11\63\4\0\1\63"+ "\1\0\10\63\1\353\15\63\1\354\3\63\20\0\1\63"+ "\10\0\5\63\7\0\11\63\4\0\1\63\1\0\2\63"+ "\1\355\2\63\1\356\24\63\20\0\1\63\10\0\5\63"+ "\7\0\11\63\4\0\1\63\1\0\6\63\1\357\23\63"+ "\20\0\1\63\10\0\5\63\7\0\11\63\4\0\1\63"+ "\1\0\21\63\1\360\10\63\20\0\1\63\10\0\5\63"+ "\7\0\11\63\4\0\1\63\1\0\10\63\1\361\21\63"+ "\20\0\1\63\10\0\5\63\7\0\11\63\4\0\1\63"+ "\1\0\4\63\1\362\25\63\20\0\1\63\10\0\5\63"+ "\7\0\11\63\4\0\1\63\1\0\24\63\1\363\3\63"+ "\1\364\1\63\20\0\1\63\10\0\5\63\7\0\11\63"+ "\4\0\1\63\1\0\17\63\1\365\12\63\20\0\1\63"+ "\10\0\5\63\7\0\11\63\4\0\1\63\1\0\10\63"+ "\1\366\6\63\1\367\12\63\20\0\1\63\10\0\5\63"+ "\7\0\11\63\4\0\1\63\1\0\13\63\1\370\16\63"+ "\20\0\1\63\10\0\5\63\7\0\11\63\4\0\1\63"+ "\1\0\21\63\1\371\10\63\20\0\1\63\10\0\5\63"+ "\7\0\11\63\4\0\1\63\1\0\4\63\1\372\3\63"+ "\1\373\21\63\20\0\1\63\10\0\5\63\7\0\11\63"+ "\4\0\1\63\1\0\23\63\1\374\6\63\20\0\1\106"+ "\10\0\5\106\7\0\11\106\4\0\1\106\1\0\2\106"+ "\1\375\27\106\5\0\1\252\115\0\2\252\140\0\5\376"+ "\111\0\3\377\113\0\5\u0100\7\0\3\u0100\14\0\6\u0100"+ "\55\0\5\u0101\111\0\3\u0102\113\0\5\u0103\7\0\3\u0103"+ "\14\0\6\u0103\44\0\1\270\10\0\5\270\7\0\11\270"+ "\4\0\1\270\1\0\32\270\31\0\2\273\15\0\6\153"+ "\4\0\1\273\7\0\24\153\31\0\5\275\111\0\5\275"+ "\12\0\6\272\4\0\1\275\7\0\24\272\31\0\4\276"+ "\13\0\6\153\4\0\1\276\7\0\24\153\27\0\1\u0104"+ "\1\0\5\277\7\0\3\277\3\153\1\u0105\2\153\4\0"+ "\1\277\1\0\6\277\11\153\1\u0105\12\153\20\0\1\44"+ "\10\0\5\44\7\0\11\44\4\0\1\44\1\0\4\44"+ "\1\u0106\25\44\20\0\1\44\10\0\5\44\7\0\11\44"+ "\4\0\1\44\1\0\4\44\1\u0107\25\44\20\0\1\63"+ "\10\0\5\63\7\0\11\63\4\0\1\63\1\0\4\63"+ "\1\u0108\25\63\20\0\1\63\10\0\5\63\7\0\11\63"+ "\4\0\1\63\1\0\15\63\1\u0109\14\63\20\0\1\63"+ "\10\0\5\63\7\0\11\63\4\0\1\63\1\0\10\63"+ "\1\u010a\21\63\20\0\1\63\10\0\5\63\7\0\11\63"+ "\4\0\1\63\1\0\10\63\1\u010b\21\63\20\0\1\63"+ "\10\0\5\63\7\0\11\63\4\0\1\63\1\0\2\63"+ "\1\u010c\27\63\20\0\1\63\10\0\5\63\7\0\11\63"+ "\4\0\1\63\1\0\22\63\1\u010d\7\63\20\0\1\63"+ "\10\0\5\63\7\0\11\63\4\0\1\63\1\0\22\63"+ "\1\u010e\7\63\20\0\1\63\10\0\5\63\7\0\11\63"+ "\4\0\1\63\1\0\4\63\1\u010f\25\63\20\0\1\63"+ "\10\0\5\63\7\0\11\63\4\0\1\63\1\0\15\63"+ "\1\u0110\14\63\20\0\1\63\10\0\5\63\7\0\11\63"+ "\4\0\1\63\1\0\4\63\1\u0111\25\63\20\0\1\63"+ "\10\0\5\63\7\0\11\63\4\0\1\63\1\0\4\63"+ "\1\u0112\25\63\20\0\1\63\10\0\5\63\7\0\11\63"+ "\4\0\1\63\1\0\4\63\1\u0113\25\63\20\0\1\63"+ "\10\0\5\63\7\0\11\63\4\0\1\63\1\0\22\63"+ "\1\363\7\63\20\0\1\63\10\0\5\63\7\0\11\63"+ "\4\0\1\63\1\0\2\63\1\u0114\27\63\20\0\1\63"+ "\10\0\5\63\7\0\11\63\4\0\1\63\1\0\13\63"+ "\1\u0115\16\63\20\0\1\63\10\0\5\63\7\0\11\63"+ "\4\0\1\63\1\0\4\63\1\u0116\25\63\20\0\1\63"+ "\10\0\5\63\7\0\11\63\4\0\1\63\1\0\23\63"+ "\1\u0117\6\63\20\0\1\63\10\0\5\63\7\0\11\63"+ "\4\0\1\63\1\0\3\63\1\u0118\26\63\20\0\1\63"+ "\10\0\5\63\7\0\11\63\4\0\1\63\1\0\30\63"+ "\1\u0119\1\63\20\0\1\63\10\0\5\63\7\0\11\63"+ "\4\0\1\63\1\0\23\63\1\u011a\6\63\20\0\1\63"+ "\10\0\5\63\7\0\11\63\4\0\1\63\1\0\21\63"+ "\1\u011b\10\63\20\0\1\63\10\0\5\63\7\0\11\63"+ "\4\0\1\63\1\0\2\63\1\u011c\27\63\20\0\1\63"+ "\10\0\5\63\7\0\11\63\4\0\1\63\1\0\7\63"+ "\1\u011d\22\63\20\0\1\63\10\0\5\63\7\0\11\63"+ "\4\0\1\63\1\0\24\63\1\u011e\5\63\20\0\1\63"+ "\10\0\5\63\7\0\11\63\4\0\1\63\1\0\1\u011f"+ "\31\63\20\0\1\63\10\0\5\63\7\0\11\63\4\0"+ "\1\63\1\0\21\63\1\u0120\10\63\20\0\1\63\10\0"+ "\5\63\7\0\11\63\4\0\1\63\1\0\4\63\1\u0121"+ "\25\63\20\0\1\63\10\0\5\63\7\0\11\63\4\0"+ "\1\63\1\0\15\63\1\u0122\14\63\20\0\1\63\10\0"+ "\5\63\7\0\11\63\4\0\1\63\1\0\10\63\1\u0123"+ "\21\63\20\0\1\63\10\0\5\63\7\0\11\63\4\0"+ "\1\63\1\0\25\63\1\u0124\4\63\20\0\1\63\10\0"+ "\5\63\7\0\11\63\4\0\1\63\1\0\22\63\1\u0125"+ "\7\63\20\0\1\63\10\0\5\63\7\0\11\63\4\0"+ "\1\63\1\0\24\63\1\u0126\5\63\20\0\1\63\10\0"+ "\5\63\7\0\11\63\4\0\1\63\1\0\23\63\1\u0127"+ "\6\63\20\0\1\63\10\0\5\63\7\0\11\63\4\0"+ "\1\63\1\0\15\63\1\u0128\14\63\20\0\1\63\10\0"+ "\5\63\7\0\11\63\4\0\1\63\1\0\4\63\1\u0129"+ "\25\63\20\0\1\63\10\0\5\63\7\0\11\63\4\0"+ "\1\63\1\0\4\63\1\u012a\25\63\20\0\1\63\10\0"+ "\5\63\7\0\11\63\4\0\1\63\1\0\23\63\1\u012b"+ "\6\63\20\0\1\63\10\0\5\63\7\0\11\63\4\0"+ "\1\63\1\0\1\u012c\31\63\20\0\1\63\10\0\5\63"+ "\7\0\11\63\4\0\1\63\1\0\23\63\1\u012d\6\63"+ "\20\0\1\63\10\0\5\63\7\0\11\63\4\0\1\63"+ "\1\0\15\63\1\u012e\14\63\20\0\1\63\10\0\5\63"+ "\7\0\11\63\4\0\1\63\1\0\13\63\1\u012f\16\63"+ "\20\0\1\63\10\0\5\63\7\0\11\63\4\0\1\63"+ "\1\0\7\63\1\u0130\22\63\31\0\5\116\111\0\4\u0131"+ "\112\0\5\116\7\0\3\116\14\0\6\116\55\0\5\133"+ "\111\0\4\u0132\112\0\5\133\7\0\3\133\14\0\6\133"+ "\55\0\5\u0104\7\0\3\u0104\3\272\1\u0133\2\272\4\0"+ "\1\u0104\1\0\6\u0104\11\272\1\u0133\12\272\20\0\1\63"+ "\10\0\5\63\7\0\11\63\4\0\1\63\1\0\21\63"+ "\1\u0134\10\63\20\0\1\63\10\0\5\63\7\0\11\63"+ "\4\0\1\63\1\0\2\63\1\u0135\27\63\20\0\1\63"+ "\10\0\5\63\7\0\11\63\4\0\1\63\1\0\23\63"+ "\1\u0136\6\63\20\0\1\63\10\0\5\63\7\0\11\63"+ "\4\0\1\63\1\0\15\63\1\u0137\14\63\20\0\1\63"+ "\10\0\5\63\7\0\11\63\4\0\1\63\1\0\7\63"+ "\1\u0138\22\63\20\0\1\63\10\0\5\63\7\0\11\63"+ "\4\0\1\63\1\0\22\63\1\u0139\7\63\20\0\1\63"+ "\10\0\5\63\7\0\11\63\4\0\1\63\1\0\23\63"+ "\1\u013a\6\63\20\0\1\63\10\0\5\63\7\0\11\63"+ "\4\0\1\63\1\0\23\63\1\u013b\6\63\20\0\1\63"+ "\10\0\5\63\7\0\11\63\4\0\1\63\1\0\17\63"+ "\1\u013c\12\63\20\0\1\63\10\0\5\63\7\0\11\63"+ "\4\0\1\63\1\0\21\63\1\u013d\10\63\20\0\1\63"+ "\10\0\5\63\7\0\11\63\4\0\1\63\1\0\23\63"+ "\1\u013e\6\63\20\0\1\63\10\0\5\63\7\0\11\63"+ "\4\0\1\63\1\0\24\63\1\u013f\5\63\20\0\1\63"+ "\10\0\5\63\7\0\11\63\4\0\1\63\1\0\21\63"+ "\1\u0140\10\63\20\0\1\63\10\0\5\63\7\0\11\63"+ "\4\0\1\63\1\0\10\63\1\u0141\21\63\20\0\1\63"+ "\10\0\5\63\7\0\11\63\4\0\1\63\1\0\7\63"+ "\1\u0142\22\63\20\0\1\63\10\0\5\63\7\0\11\63"+ "\4\0\1\63\1\0\16\63\1\u0143\13\63\20\0\1\63"+ "\10\0\5\63\7\0\11\63\4\0\1\63\1\0\13\63"+ "\1\u0144\16\63\20\0\1\63\10\0\5\63\7\0\11\63"+ "\4\0\1\63\1\0\1\63\1\u0145\30\63\20\0\1\63"+ "\10\0\5\63\7\0\11\63\4\0\1\63\1\0\4\63"+ "\1\u0146\25\63\20\0\1\63\10\0\5\63\7\0\11\63"+ "\4\0\1\63\1\0\2\63\1\u0147\27\63\20\0\1\63"+ "\10\0\5\63\7\0\11\63\4\0\1\63\1\0\16\63"+ "\1\u0148\13\63\20\0\1\63\10\0\5\63\7\0\11\63"+ "\4\0\1\63\1\0\1\u0149\31\63\20\0\1\63\10\0"+ "\5\63\7\0\11\63\4\0\1\63\1\0\4\63\1\u014a"+ "\25\63\20\0\1\63\10\0\5\63\7\0\11\63\4\0"+ "\1\63\1\0\2\63\1\u014b\27\63\20\0\1\63\10\0"+ "\5\63\7\0\11\63\4\0\1\63\1\0\2\63\1\u014c"+ "\27\63\20\0\1\63\10\0\5\63\7\0\11\63\4\0"+ "\1\63\1\0\2\63\1\u014d\27\63\20\0\1\63\10\0"+ "\5\63\7\0\11\63\4\0\1\63\1\0\24\63\1\u014e"+ "\5\63\20\0\1\63\10\0\5\63\7\0\11\63\4\0"+ "\1\63\1\0\4\63\1\u014f\25\63\31\0\4\116\112\0"+ "\4\133\101\0\1\63\10\0\5\63\7\0\11\63\4\0"+ "\1\63\1\0\23\63\1\u0150\6\63\20\0\1\63\10\0"+ "\5\63\7\0\11\63\4\0\1\63\1\0\21\63\1\u0151"+ "\10\63\20\0\1\63\10\0\5\63\7\0\11\63\4\0"+ "\1\63\1\0\16\63\1\u0152\13\63\20\0\1\63\10\0"+ "\5\63\7\0\11\63\4\0\1\63\1\0\23\63\1\u0153"+ "\6\63\20\0\1\63\10\0\5\63\7\0\11\63\4\0"+ "\1\63\1\0\15\63\1\u0154\14\63\20\0\1\63\10\0"+ "\5\63\7\0\11\63\4\0\1\63\1\0\16\63\1\u0155"+ "\13\63\20\0\1\63\10\0\5\63\7\0\11\63\4\0"+ "\1\63\1\0\3\63\1\u0156\26\63\20\0\1\63\10\0"+ "\5\63\7\0\11\63\4\0\1\63\1\0\10\63\1\u0157"+ "\21\63\20\0\1\63\10\0\5\63\7\0\11\63\4\0"+ "\1\63\1\0\1\u0158\31\63\20\0\1\63\10\0\5\63"+ "\7\0\11\63\4\0\1\63\1\0\3\63\1\u0159\26\63"+ "\20\0\1\63\10\0\5\63\7\0\11\63\4\0\1\63"+ "\1\0\4\63\1\u015a\25\63\20\0\1\63\10\0\5\63"+ "\7\0\11\63\4\0\1\63\1\0\13\63\1\u015b\16\63"+ "\20\0\1\63\10\0\5\63\7\0\11\63\4\0\1\63"+ "\1\0\2\63\1\u015c\27\63\20\0\1\63\10\0\5\63"+ "\7\0\11\63\4\0\1\63\1\0\23\63\1\u015d\6\63"+ "\20\0\1\63\10\0\5\63\7\0\11\63\4\0\1\63"+ "\1\0\15\63\1\u015e\14\63\20\0\1\63\10\0\5\63"+ "\7\0\11\63\4\0\1\63\1\0\23\63\1\u015f\6\63"+ "\20\0\1\63\10\0\5\63\7\0\11\63\4\0\1\63"+ "\1\0\23\63\1\u0160\6\63\20\0\1\63\10\0\5\63"+ "\7\0\11\63\4\0\1\63\1\0\7\63\1\u0161\22\63"+ "\20\0\1\63\10\0\5\63\7\0\11\63\4\0\1\63"+ "\1\0\12\63\1\u0162\17\63\20\0\1\63\10\0\5\63"+ "\7\0\11\63\4\0\1\63\1\0\1\u0163\31\63\20\0"+ "\1\63\10\0\5\63\7\0\11\63\4\0\1\63\1\0"+ "\1\u0164\31\63\20\0\1\63\10\0\5\63\7\0\11\63"+ "\4\0\1\63\1\0\10\63\1\u0165\21\63\20\0\1\63"+ "\10\0\5\63\7\0\11\63\4\0\1\63\1\0\1\u0166"+ "\31\63\20\0\1\63\10\0\5\63\7\0\11\63\4\0"+ "\1\63\1\0\21\63\1\u0167\10\63\20\0\1\63\10\0"+ "\5\63\7\0\11\63\4\0\1\63\1\0\4\63\1\u0168"+ "\25\63\20\0\1\63\10\0\5\63\7\0\11\63\4\0"+ "\1\63\1\0\23\63\1\u0169\6\63\20\0\1\63\10\0"+ "\5\63\7\0\11\63\4\0\1\63\1\0\13\63\1\u016a"+ "\16\63\20\0\1\63\10\0\5\63\7\0\11\63\4\0"+ "\1\63\1\0\4\63\1\u016b\25\63\20\0\1\63\10\0"+ "\5\63\7\0\11\63\4\0\1\63\1\0\4\63\1\u016c"+ "\25\63\20\0\1\63\10\0\5\63\7\0\11\63\4\0"+ "\1\63\1\0\13\63\1\u016d\16\63\20\0\1\63\10\0"+ "\5\63\7\0\11\63\4\0\1\63\1\0\10\63\1\u016e"+ "\21\63\20\0\1\63\10\0\5\63\7\0\11\63\4\0"+ "\1\63\1\0\16\63\1\u016f\13\63\20\0\1\63\10\0"+ "\5\63\7\0\11\63\4\0\1\63\1\0\13\63\1\u0170"+ "\16\63\20\0\1\63\10\0\5\63\7\0\11\63\4\0"+ "\1\63\1\0\10\63\1\u0171\21\63\20\0\1\63\10\0"+ "\5\63\7\0\11\63\4\0\1\63\1\0\15\63\1\u0172"+ "\14\63\20\0\1\63\10\0\5\63\7\0\11\63\4\0"+ "\1\63\1\0\15\63\1\u0173\14\63\20\0\1\63\10\0"+ "\5\63\7\0\11\63\4\0\1\63\1\0\31\63\1\u0174"+ "\20\0\1\63\10\0\5\63\7\0\11\63\4\0\1\63"+ "\1\0\23\63\1\u0175\6\63\20\0\1\63\10\0\5\63"+ "\7\0\11\63\4\0\1\63\1\0\4\63\1\u0176\25\63"+ "\20\0\1\63\10\0\5\63\7\0\11\63\4\0\1\63"+ "\1\0\21\63\1\u0177\10\63\4\0"; private static int [] zzUnpacktrans() { int [] result = new int[19578]; 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\2\11\1\1\1\11\2\1\1\11\1\1\1\11"+ "\2\1\2\11\2\1\1\11\6\1\1\11\2\1\3\11"+ "\3\1\4\11\1\1\1\11\23\1\1\11\1\1\3\11"+ "\7\1\3\11\1\1\1\11\1\1\1\11\2\1\1\11"+ "\2\1\1\11\1\1\1\11\2\0\1\1\5\11\1\1"+ "\4\11\1\1\2\0\1\11\2\1\6\11\1\1\1\11"+ "\61\1\2\11\6\1\4\11\1\1\3\0\2\1\2\11"+ "\1\1\1\0\3\1\1\11\75\1\6\0\55\1\2\0"+ "\105\1"; private static int [] zzUnpackAttribute() { int [] result = new int[375]; 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: */ public ResFlexLexer(ORLangTypes types) { this.types = types; } private ORLangTypes types; private int tokenStartIndex; private CharSequence quotedStringId; private int commentDepth; private boolean inCommentString = false; //Store the start index of a token private void tokenStart() { tokenStartIndex = zzStartRead; } //Set the start index of the token to the stored index private void tokenEnd() { zzStartRead = tokenStartIndex; } /** * Creates a new scanner * * @param in the java.io.Reader to read input from. */ public ResFlexLexer(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; switch (zzLexicalState) { case IN_LOWER_DECLARATION: { yybegin(INITIAL); } // fall though case 376: break; case IN_TEMPLATE: { yybegin(INITIAL); } // fall though case 377: break; case IN_STRING: { yybegin(INITIAL); tokenEnd(); return types.STRING_VALUE; } // fall though case 378: break; case IN_ML_COMMENT: { yybegin(INITIAL); tokenEnd(); return types.MULTI_COMMENT; } // fall though case 379: break; case IN_SL_COMMENT: { yybegin(INITIAL); tokenEnd(); return types.SINGLE_COMMENT; } // fall though case 380: break; default: return null; } } else { switch (zzAction < 0 ? zzAction : ZZ_ACTION[zzAction]) { case 1: { yybegin(INITIAL); yypushback(1); } // fall through case 147: break; case 2: { return BAD_CHARACTER; } // fall through case 148: break; case 3: { return WHITE_SPACE; } // fall through case 149: break; case 4: { return types.EOL; } // fall through case 150: break; case 5: { return types.EXCLAMATION_MARK; } // fall through case 151: break; case 6: { yybegin(IN_STRING); tokenStart(); } // fall through case 152: break; case 7: { return types.SHARP; } // fall through case 153: break; case 8: { return types.PERCENT; } // fall through case 154: break; case 9: { return types.AMPERSAND; } // fall through case 155: break; case 10: { return types.SINGLE_QUOTE; } // fall through case 156: break; case 11: { return types.LPAREN; } // fall through case 157: break; case 12: { return types.RPAREN; } // fall through case 158: break; case 13: { return types.STAR; } // fall through case 159: break; case 14: { return types.PLUS; } // fall through case 160: break; case 15: { return types.COMMA; } // fall through case 161: break; case 16: { return types.MINUS; } // fall through case 162: break; case 17: { return types.DOT; } // fall through case 163: break; case 18: { return types.SLASH; } // fall through case 164: break; case 19: { return types.INT_VALUE; } // fall through case 165: break; case 20: { return types.COLON; } // fall through case 166: break; case 21: { return types.SEMI; } // fall through case 167: break; case 22: { return types.LT; } // fall through case 168: break; case 23: { return types.EQ; } // fall through case 169: break; case 24: { return types.GT; } // fall through case 170: break; case 25: { return types.QUESTION_MARK; } // fall through case 171: break; case 26: { return types.ARROBASE; } // fall through case 172: break; case 27: { return types.UIDENT; } // fall through case 173: break; case 28: { return types.LBRACKET; } // fall through case 174: break; case 29: { return types.BACKSLASH; } // fall through case 175: break; case 30: { return types.RBRACKET; } // fall through case 176: break; case 31: { return types.CARRET; } // fall through case 177: break; case 32: { return types.UNDERSCORE; } // fall through case 178: break; case 33: { yybegin(IN_TEMPLATE); return types.JS_STRING_OPEN; } // fall through case 179: break; case 34: { return types.LIDENT; } // fall through case 180: break; case 35: { return types.LBRACE; } // fall through case 181: break; case 36: { return types.PIPE; } // fall through case 182: break; case 37: { return types.RBRACE; } // fall through case 183: break; case 38: { return types.TILDE; } // fall through case 184: break; case 39: { yybegin(INITIAL); yypushback(1); tokenEnd(); } // fall through case 185: break; case 40: { yybegin(INITIAL); return types.LIDENT; } // fall through case 186: break; case 41: { return types.STRING_VALUE; } // fall through case 187: break; case 42: { } // fall through case 188: break; case 43: { return types.DOLLAR; } // fall through case 189: break; case 44: { yybegin(INITIAL); return types.JS_STRING_CLOSE; } // fall through case 190: break; case 45: { yybegin(INITIAL); tokenEnd(); return types.STRING_VALUE; } // fall through case 191: break; case 46: { inCommentString = !inCommentString; } // fall through case 192: break; case 47: { yybegin(INITIAL); tokenEnd(); return types.SINGLE_COMMENT; } // fall through case 193: break; case 48: { return types.NOT_EQ; } // fall through case 194: break; case 49: { return types.SHARPSHARP; } // fall through case 195: break; case 50: { return types.POLY_VARIANT; } // fall through case 196: break; case 51: { return types.L_AND; } // fall through case 197: break; case 52: { return types.TYPE_ARGUMENT; } // fall through case 198: break; case 53: { return types.STARDOT; } // fall through case 199: break; case 54: { return types.STRING_CONCAT; } // fall through case 200: break; case 55: { return types.PLUSDOT; } // fall through case 201: break; case 56: { return types.MINUSDOT; } // fall through case 202: break; case 57: { return types.RIGHT_ARROW; } // fall through case 203: break; case 58: { return types.DOTDOT; } // fall through case 204: break; case 59: { yybegin(IN_ML_COMMENT); commentDepth = 1; tokenStart(); } // fall through case 205: break; case 60: { return types.SLASHDOT; } // fall through case 206: break; case 61: { yybegin(IN_SL_COMMENT); tokenStart(); } // fall through case 207: break; case 62: { return types.TAG_AUTO_CLOSE; } // fall through case 208: break; case 63: { return types.FLOAT_VALUE; } // fall through case 209: break; case 64: { return types.SHORTCUT; } // fall through case 210: break; case 65: { return types.COLON_EQ; } // fall through case 211: break; case 66: { return types.COLON_GT; } // fall through case 212: break; case 67: { return types.LEFT_ARROW; } // fall through case 213: break; case 68: { return types.TAG_LT_SLASH; } // fall through case 214: break; case 69: { return types.LT_OR_EQUAL; } // fall through case 215: break; case 70: { return types.EQEQ; } // fall through case 216: break; case 71: { return types.ARROW; } // fall through case 217: break; case 72: { return types.AS; } // fall through case 218: break; case 73: { return types.DO; } // fall through case 219: break; case 74: { return types.IF; } // fall through case 220: break; case 75: { return types.IN; } // fall through case 221: break; case 76: { return types.OF; } // fall through case 222: break; case 77: { return types.OR; } // fall through case 223: break; case 78: { return types.PIPE_FORWARD; } // fall through case 224: break; case 79: { return types.L_OR; } // fall through case 225: break; case 80: { if (!inCommentString) { commentDepth -= 1; if(commentDepth == 0) { yybegin(INITIAL); tokenEnd(); return types.MULTI_COMMENT; } } } // fall through case 226: break; case 81: { if (!inCommentString) { commentDepth += 1; } } // fall through case 227: break; case 82: { return types.NOT_EQEQ; } // fall through case 228: break; case 83: { return types.CHAR_VALUE; } // fall through case 229: break; case 84: { return types.DOTDOTDOT; } // fall through case 230: break; case 85: { return types.EQEQEQ; } // fall through case 231: break; case 86: { return types.AND; } // fall through case 232: break; case 87: { return types.ASR; } // fall through case 233: break; case 88: { return types.END; } // fall through case 234: break; case 89: { return types.FOR; } // fall through case 235: break; case 90: { yybegin(IN_LOWER_DECLARATION); return types.LET; } // fall through case 236: break; case 91: { return types.LOR; } // fall through case 237: break; case 92: { return types.LSL; } // fall through case 238: break; case 93: { return types.LSR; } // fall through case 239: break; case 94: { return types.MOD; } // fall through case 240: break; case 95: { return types.NEW; } // fall through case 241: break; case 96: { return types.PRI; } // fall through case 242: break; case 97: { return types.PUB; } // fall through case 243: break; case 98: { return types.RAW; } // fall through case 244: break; case 99: { return types.REC; } // fall through case 245: break; case 100: { return types.REF; } // fall through case 246: break; case 101: { return types.SIG; } // fall through case 247: break; case 102: { return types.TRY; } // fall through case 248: break; case 103: { return types.VAL; } // fall through case 249: break; case 104: { return types.NONE; } // fall through case 250: break; case 105: { return types.SOME; } // fall through case 251: break; case 106: { return types.DONE; } // fall through case 252: break; case 107: { return types.ELSE; } // fall through case 253: break; case 108: { return types.LAND; } // fall through case 254: break; case 109: { return types.LAZY; } // fall through case 255: break; case 110: { return types.LIST; } // fall through case 256: break; case 111: { return types.LXOR; } // fall through case 257: break; case 112: { return types.OPEN; } // fall through case 258: break; case 113: { return types.THEN; } // fall through case 259: break; case 114: { return types.BOOL_VALUE; } // fall through case 260: break; case 115: { return types.TYPE; } // fall through case 261: break; case 116: { return types.UNIT; } // fall through case 262: break; case 117: { return types.WHEN; } // fall through case 263: break; case 118: { return types.WITH; } // fall through case 264: break; case 119: { return types.ASYNC; } // fall through case 265: break; case 120: { return types.AWAIT; } // fall through case 266: break; case 121: { return types.BEGIN; } // fall through case 267: break; case 122: { return types.CATCH; } // fall through case 268: break; case 123: { return types.CLASS; } // fall through case 269: break; case 124: { return types.MATCH; } // fall through case 270: break; case 125: { return types.RAISE; } // fall through case 271: break; case 126: { return types.WHILE; } // fall through case 272: break; case 127: { return types.ASSERT; } // fall through case 273: break; case 128: { return types.DOWNTO; } // fall through case 274: break; case 129: { return types.METHOD; } // fall through case 275: break; case 130: { return types.MODULE; } // fall through case 276: break; case 131: { return types.NONREC; } // fall through case 277: break; case 132: { return types.OBJECT; } // fall through case 278: break; case 133: { return types.OPTION; } // fall through case 279: break; case 134: { return types.STRUCT; } // fall through case 280: break; case 135: { return types.SWITCH; } // fall through case 281: break; case 136: { return types.UNPACK; } // fall through case 282: break; case 137: { return types.FUNCTOR; } // fall through case 283: break; case 138: { return types.INCLUDE; } // fall through case 284: break; case 139: { return types.INHERIT; } // fall through case 285: break; case 140: { return types.MUTABLE; } // fall through case 286: break; case 141: { return types.PRIVATE; } // fall through case 287: break; case 142: { return types.VIRTUAL; } // fall through case 288: break; case 143: { yybegin(IN_LOWER_DECLARATION); return types.EXTERNAL; } // fall through case 289: break; case 144: { return types.EXCEPTION; } // fall through case 290: break; case 145: { return types.CONSTRAINT; } // fall through case 291: break; case 146: { return types.INITIALIZER; } // fall through case 292: break; default: zzScanError(ZZ_NO_MATCH); } } } } } ================================================ FILE: src/main/java/com/reason/lang/rescript/ResLanguage.java ================================================ package com.reason.lang.rescript; import com.intellij.lang.*; import com.reason.lang.*; import org.jetbrains.annotations.*; public class ResLanguage extends Language implements ORLanguageProperties { public static final ResLanguage INSTANCE = new ResLanguage(); private ResLanguage() { super("Rescript"); } @Override public @NotNull String getParameterSeparator() { return ", "; } @Override public @NotNull String getFunctionSeparator() { return " => "; } @Override public @NotNull String getTemplateStart() { return "<"; } @Override public @NotNull String getTemplateEnd() { return ">"; } } ================================================ FILE: src/main/java/com/reason/lang/rescript/ResLexer.java ================================================ package com.reason.lang.rescript; import com.intellij.lexer.FlexAdapter; public class ResLexer extends FlexAdapter { public ResLexer() { super(new ResFlexLexer(ResTypes.INSTANCE)); } } ================================================ FILE: src/main/java/com/reason/lang/rescript/ResParser.java ================================================ package com.reason.lang.rescript; import com.intellij.lang.*; import com.intellij.psi.*; import com.intellij.psi.tree.*; import com.reason.lang.*; import org.jetbrains.annotations.*; import static com.intellij.codeInsight.completion.CompletionUtilCore.*; public class ResParser extends CommonPsiParser { ResParser(boolean isSafe) { super(isSafe); } @Override protected ORParser getORParser(@NotNull PsiBuilder builder) { return new ResParserState(builder, myIsSafe); } static class ResParserState extends ORLanguageParser { public ResParserState(@NotNull PsiBuilder builder, boolean isSafe) { super(ResTypes.INSTANCE, builder, isSafe); } @Override public void parse() { IElementType tokenType; long parseStart = System.currentTimeMillis(); int parseCount = 0; while (!myBuilder.eof()) { parseCount++; if (parseCount > 100) { parseCount = 0; long parseTime = System.currentTimeMillis(); if (PARSE_MAX_TIME < parseTime - parseStart) { if (myIsSafe) { // Don't do that in tests error("Parsing cancelled, you should create a github issue with the source code"); break; } } } tokenType = myBuilder.getTokenType(); if (tokenType == TokenType.BAD_CHARACTER) { error("Bad character"); break; } if (tokenType == myTypes.EOL) { IElementType previousType = previousElementType(1); boolean previousOperator = previousType == myTypes.ARROW || previousType == myTypes.RIGHT_ARROW || previousType == myTypes.COLON || previousType == myTypes.COMMA || previousType == myTypes.EQ || previousType == myTypes.LPAREN || previousType == myTypes.STRING_CONCAT; myBuilder.remapCurrentToken(myTypes.WHITE_SPACE); advanceSkipEOL(); if (!previousOperator) { IElementType nextType = getTokenType(); boolean nextOperator = nextType == myTypes.QUESTION_MARK || nextType == myTypes.RIGHT_ARROW // first pipe || nextType == myTypes.COLON // ternary || nextType == myTypes.PIPE // variant || nextType == myTypes.WITH // type constraint || nextType == myTypes.AND // recursive declaration || (nextType == myTypes.LPAREN && isRawParent(myTypes.C_FUNCTION_CALL)); // function call if (!nextOperator && !myMarkers.isEmpty()) { Marker marker = myMarkers.peek(); // like popEndUntilScope while (marker != null && !marker.hasScope() && !marker.isCompositeType(myTypes.C_TAG_BODY)) { marker = pop(); if (marker != null) { marker.end(); } marker = getLatestMarker(); } if (currentHasScope() && !isHold()) { markHolder(myTypes.H_PLACE_HOLDER); } } } } // Special analysis when inside an interpolation string else if (is(myTypes.C_INTERPOLATION_EXPR) || is(myTypes.C_INTERPOLATION_PART) || is(myTypes.C_INTERPOLATION_REF)) { if (tokenType == myTypes.JS_STRING_CLOSE) { if (is(myTypes.C_INTERPOLATION_REF)) { // not closed ref dropLatest(); } popEndUntil(myTypes.C_INTERPOLATION_EXPR).advance().popEnd(); } else if (tokenType == myTypes.DOLLAR) { IElementType nextElementType = rawLookup(1); if (nextElementType == myTypes.LBRACE) { if (is(myTypes.C_INTERPOLATION_REF)) { dropLatest(); } advance().advance().markScope(myTypes.C_INTERPOLATION_REF, myTypes.LBRACE); } else { remapCurrentToken(myTypes.STRING_VALUE); } } else if (is(myTypes.C_INTERPOLATION_REF) && tokenType == myTypes.RBRACE) { popEnd().advance(); } else if (tokenType == myTypes.LBRACE || tokenType == myTypes.RBRACE) { // not a ref remapCurrentToken(myTypes.STRING_VALUE); } } else if (tokenType == myTypes.EQ) { parseEq(); } else if (tokenType == myTypes.COLON_EQ) { parseColonEq(); } else if (tokenType == myTypes.SOME) { parseSome(); } else if (tokenType == myTypes.NONE) { parseNone(); } else if (tokenType == myTypes.WITH) { parseWith(); } else if (tokenType == myTypes.ARROW) { parseArrow(); } else if (tokenType == myTypes.REF) { parseRef(); } else if (tokenType == myTypes.METHOD) { parseMethod(); } else if (tokenType == myTypes.OPTION) { parseOption(); } else if (tokenType == myTypes.MATCH) { parseMatch(); } else if (tokenType == myTypes.TRY) { parseTry(); } else if (tokenType == myTypes.CATCH) { parseCatch(); } else if (tokenType == myTypes.SWITCH) { parseSwitch(); } else if (tokenType == myTypes.LIDENT) { parseLIdent(); } else if (tokenType == myTypes.UIDENT) { parseUIdent(); } else if (tokenType == myTypes.PROPERTY_NAME) { parseLTagName(); } else if (tokenType == myTypes.A_UPPER_TAG_NAME) { parseUTagName(); } else if (tokenType == myTypes.POLY_VARIANT) { parsePolyVariant(); } else if (tokenType == myTypes.UNIT) { parseUnit(); } else if (tokenType == myTypes.ARROBASE) { parseArrobase(); } else if (tokenType == myTypes.PERCENT) { parsePercent(); } else if (tokenType == myTypes.COLON) { parseColon(); } else if (tokenType == myTypes.STRING_VALUE) { parseStringValue(); } else if (tokenType == myTypes.PIPE) { parsePipe(); } else if (tokenType == myTypes.COMMA) { parseComma(); } else if (tokenType == myTypes.AND) { parseAnd(); } else if (tokenType == myTypes.ASSERT) { parseAssert(); } else if (tokenType == myTypes.DOTDOTDOT) { parseDotDotDot(); } else if (tokenType == myTypes.QUESTION_MARK) { parseQuestionMark(); } else if (tokenType == myTypes.TILDE) { parseTilde(); } else if (tokenType == myTypes.UNDERSCORE) { parseUnderscore(); } else if (tokenType == myTypes.INT_VALUE || tokenType == myTypes.FLOAT_VALUE) { parseNumeric(); } else if (tokenType == myTypes.UNPACK) { parseUnpack(); } else if (tokenType == myTypes.TYPE_ARGUMENT) { parseTypeArgument(); } // if ... else else if (tokenType == myTypes.IF) { parseIf(); } else if (tokenType == myTypes.ELSE) { parseElse(); } // ( ... ) else if (tokenType == myTypes.LPAREN) { parseLParen(); } else if (tokenType == myTypes.RPAREN) { parseRParen(); } // { ... } // list{ ... } else if (tokenType == myTypes.LBRACE) { parseLBrace(); } else if (tokenType == myTypes.LIST) { parseList(); } else if (tokenType == myTypes.RBRACE) { parseRBrace(); } // [ ... ] else if (tokenType == myTypes.LBRACKET) { parseLBracket(); } else if (tokenType == myTypes.RBRACKET) { parseRBracket(); } // < ... > else if (tokenType == myTypes.LT) { parseLt(); } else if (tokenType == myTypes.TAG_LT_SLASH) { parseLtSlash(); } else if (tokenType == myTypes.GT) { parseGt(); } else if (tokenType == myTypes.TAG_AUTO_CLOSE) { parseGtAutoClose(); } // j` ... ` else if (tokenType == myTypes.JS_STRING_OPEN) { parseTemplateStringOpen(); } // Starts an expression else if (tokenType == myTypes.OPEN) { parseOpen(); } else if (tokenType == myTypes.INCLUDE) { parseInclude(); } else if (tokenType == myTypes.EXTERNAL) { parseExternal(); } else if (tokenType == myTypes.TYPE) { parseType(); } else if (tokenType == myTypes.MODULE) { parseModule(); } else if (tokenType == myTypes.LET) { parseLet(); } else if (tokenType == myTypes.EXCEPTION) { parseException(); } if (dontMove) { dontMove = false; } else { myBuilder.advanceLexer(); } } } private void parseTypeArgument() { if (is(myTypes.C_PARAMETERS) && isParent(myTypes.C_TYPE_DECLARATION)) { // type t< >>'a<< > ... if (!isCurrent(myTypes.C_PARAM_DECLARATION)) { mark(myTypes.C_PARAM_DECLARATION); } } } private void parseUnpack() { if (is(myTypes.C_MODULE_BINDING)) { updateComposite(myTypes.C_UNPACK); } else { mark(myTypes.C_UNPACK); } advance(); if (getTokenType() != myTypes.LPAREN) { error("Missing parenthesis"); } else { updateScopeToken(myTypes.LPAREN).advance(); } } private void parseUnit() { if (!is(myTypes.C_SIG_ITEM)) { mark(myTypes.C_SIG_ITEM); } } private void parseSome() { mark(myTypes.C_SOME); advance(); if (getTokenType() != myTypes.LPAREN) { error("Missing parenthesis"); } else { markScope(myTypes.C_PARAMETERS, myTypes.LPAREN).advance() .markHolder(myTypes.H_COLLECTION_ITEM); } } private void parseNone() { wrapWith(myTypes.C_SOME); } private void parseList() { if (lookAhead(1) == myTypes.LBRACE) { // |>list<| { ... } markScope(myTypes.C_SCOPED_EXPR, myTypes.LBRACE) .advance().advance() .markHolder(myTypes.H_COLLECTION_ITEM); // Needed to roll back to individual item in collection } } private void parseNumeric() { if (is(myTypes.C_PARAMETERS)) { boolean inCall = in(myTypes.C_FUNCTION_CALL) || in(myTypes.C_FUNCTOR_CALL); mark(inCall ? myTypes.C_PARAM : myTypes.C_PARAM_DECLARATION); } wrapAtom(myTypes.H_ATOM); } private void parseUnderscore() { if (is(myTypes.C_PARAMETERS)) { boolean inCall = isParent(myTypes.C_FUNCTION_CALL) || isParent(myTypes.C_FUNCTOR_CALL); mark(inCall ? myTypes.C_PARAM : myTypes.C_PARAM_DECLARATION); } else { IElementType nextElementType = lookAhead(1); if (nextElementType == myTypes.ARROW && strictlyInAny( myTypes.C_LET_BINDING, myTypes.C_DEFAULT_VALUE, myTypes.C_PARAM, myTypes.C_FIELD_VALUE, myTypes.C_PARAMETERS )) { // A paren-less function definition :: |>_<| => popIfHold().mark(myTypes.C_FUNCTION_EXPR) .mark(myTypes.C_PARAMETERS).mark(myTypes.C_PARAM_DECLARATION) .wrapAtom(myTypes.CA_LOWER_SYMBOL); } } } private void parseRef() { if (rawLookup(-1) == myTypes.DOT) { // .ref not a keyword remapCurrentToken(myTypes.LIDENT); } else if (isCurrent(myTypes.C_RECORD_EXPR)) { // { |>x<| ... remapCurrentToken(myTypes.LIDENT). mark(myTypes.C_RECORD_FIELD).wrapAtom(myTypes.CA_LOWER_SYMBOL); } else if (strictlyIn(myTypes.C_TAG_START)) { remapCurrentToken(myTypes.PROPERTY_NAME).mark(myTypes.C_TAG_PROPERTY); } } private void parseMethod() { if (is(myTypes.C_RECORD_EXPR)) { remapCurrentToken(myTypes.LIDENT). mark(myTypes.C_RECORD_FIELD).wrapAtom(myTypes.CA_LOWER_SYMBOL); } } private void generateJsxPropertyName() { remapCurrentToken(myTypes.PROPERTY_NAME) .mark(myTypes.C_TAG_PROPERTY) .setWhitespaceSkippedCallback(endJsxPropertyIfWhitespace()); } private void parseOption() { if (strictlyIn(myTypes.C_TAG_START)) { generateJsxPropertyName(); } else { mark(myTypes.C_OPTION).advance(); if (getTokenType() == myTypes.LPAREN) { updateScopeToken(myTypes.LPAREN).advance(); } } } private void parseMatch() { if (strictlyIn(myTypes.C_TAG_START)) { generateJsxPropertyName(); } } private void parseIf() { if (isCurrent(myTypes.C_PATTERN_MATCH_EXPR)) { // switch x { | X |>if<| ... mark(myTypes.C_GUARD).advance() .mark(myTypes.C_BINARY_CONDITION); } else { mark(myTypes.C_IF).advance() .mark(myTypes.C_BINARY_CONDITION); } } private void parseElse() { // if ... |>else<| ... popEndUntil(myTypes.C_IF) .advance().mark(myTypes.C_IF_THEN_ELSE); } private void parseDotDotDot() { if (previousElementType(1) == myTypes.LBRACE) { // Mixin // ... { |>...<| x ... updateComposite(myTypes.C_RECORD_EXPR) .mark(myTypes.C_MIXIN_FIELD); } } private void parseQuestionMark() { if (strictlyInAny(myTypes.C_TAG_START, myTypes.C_TAG_PROP_VALUE, myTypes.C_RECORD_FIELD, myTypes.C_FIELD_VALUE)) { // ?<|prop ... if (isFound(myTypes.C_TAG_START)) { mark(myTypes.C_TAG_PROPERTY) .setWhitespaceSkippedCallback(endJsxPropertyIfWhitespace()) .advance() .remapCurrentToken(myTypes.PROPERTY_NAME); } // type t = { key|>?<|: ... } //else if (isFound(myTypes.C_RECORD_FIELD)) { // skip //} // let _ = { key: a |>?<| ... } else if (isFound(myTypes.C_FIELD_VALUE)) { if (!inAny(myTypes.C_TERNARY) && previousElementType(1) != myTypes.EQ) { if (inScopeOrAny(myTypes.H_PLACE_HOLDER, myTypes.H_COLLECTION_ITEM)) { // a new ternary parseTernary(getIndex()); } } } } else if (isDone(myTypes.C_BINARY_CONDITION) || strictlyIn(myTypes.C_BINARY_CONDITION)) { // a ternary in progress // ... |>?<| ... popEndUntilFoundIndex().end() .advance() .mark(myTypes.C_IF_THEN_ELSE); } else if (!inAny(myTypes.C_TERNARY) && previousElementType(1) != myTypes.EQ) { if (inScopeOrAny(myTypes.H_PLACE_HOLDER, myTypes.H_COLLECTION_ITEM)) { // a new ternary parseTernary(getIndex()); } } } private void parseTernary(int foundPos) { if (isAtIndex(foundPos, myTypes.H_PLACE_HOLDER)) { // «placeHolder» ... |>?<| ... markBefore(foundPos, myTypes.C_TERNARY) .updateCompositeAt(foundPos, myTypes.C_BINARY_CONDITION) .popEndUntilIndex(foundPos).end() .advance().mark(myTypes.C_IF_THEN_ELSE) .markHolder(myTypes.H_PLACE_HOLDER); } else if (isAtIndex(foundPos, myTypes.H_COLLECTION_ITEM)) { markHolderBefore(foundPos, myTypes.H_COLLECTION_ITEM) .markBefore(foundPos, myTypes.C_TERNARY) .updateCompositeAt(foundPos, myTypes.C_BINARY_CONDITION) .popEndUntilIndex(foundPos).end() .advance().mark(myTypes.C_IF_THEN_ELSE); } } private void parseTilde() { if (is(myTypes.C_PARAMETERS)) { mark(myTypes.C_PARAM_DECLARATION) .mark(myTypes.C_PARAM_DECLARATION).markHolder(myTypes.H_NAMED_PARAM_DECLARATION) .advance().wrapAtom(myTypes.CA_LOWER_SYMBOL); } else if (in(myTypes.C_SIG_EXPR) && !is(myTypes.C_SIG_ITEM)) { mark(myTypes.C_SIG_ITEM) .mark(myTypes.C_PARAM_DECLARATION).markHolder(myTypes.H_NAMED_PARAM_DECLARATION) .advance().wrapAtom(myTypes.CA_LOWER_SYMBOL); } else if (isCurrent(myTypes.C_PARAM)) { updateComposite(myTypes.C_NAMED_PARAM); } else if (isCurrent(myTypes.C_PARAM_DECLARATION)) { if (isHold()) { updateLatestComposite(myTypes.H_NAMED_PARAM_DECLARATION).updateToHolder(); } } else { mark(myTypes.C_PARAM_DECLARATION).markHolder(myTypes.H_NAMED_PARAM_DECLARATION) .advance().wrapAtom(myTypes.CA_LOWER_SYMBOL); } } private void parseAssert() { mark(myTypes.C_ASSERT_STMT); } private void parseAnd() { if (in(myTypes.C_TYPE_CONSTRAINT)) { // module M = (X) : ( S with ... |>and<| ... ) = ... popEndUntilFoundIndex().popEnd(); } else if (strictlyIn(myTypes.C_TYPE_DECLARATION)) { popEndUntilFoundIndex().popEnd() .advance() .mark(myTypes.C_TYPE_DECLARATION); } else if (strictlyIn(myTypes.C_MODULE_DECLARATION)) { popEndUntilFoundIndex().popEnd() .advance() .mark(myTypes.C_MODULE_DECLARATION); } else if (strictlyIn(myTypes.C_LET_DECLARATION)) { popEndUntilFoundIndex().popEnd() .advance() .mark(myTypes.C_LET_DECLARATION); } } private void parseComma() { // remove intermediate signatures if (strictlyInAny(myTypes.C_SIG_EXPR, myTypes.C_SCOPED_EXPR)) { popEndUntilFoundIndex(); if (strictlyIn(myTypes.C_SIG_ITEM)) { popEndUntilFoundIndex().popEnd(); } } if (strictlyIn(myTypes.C_SCOPED_EXPR) && isAtIndex(getIndex() + 1, myTypes.C_LET_DECLARATION)) { // It must be a deconstruction :: let ( a |>,<| b ) = ... // We need to do it again because lower symbols must be wrapped with identifiers rollbackToFoundIndex() .updateComposite(myTypes.C_DECONSTRUCTION); } // Same priority else if (inScopeOrAny( myTypes.C_PARAM_DECLARATION, myTypes.C_PARAM, myTypes.C_NAMED_PARAM, myTypes.C_DECONSTRUCTION, myTypes.C_RECORD_FIELD, myTypes.C_MIXIN_FIELD, myTypes.C_OBJECT_FIELD, myTypes.H_COLLECTION_ITEM )) { if (isFound(myTypes.H_COLLECTION_ITEM)) { popEndUntilFoundIndex().popEnd() .advance().markHolder(myTypes.H_COLLECTION_ITEM); } else if (isFound(myTypes.C_PARAM_DECLARATION) || isFound(myTypes.C_PARAM) || isFound(myTypes.C_NAMED_PARAM)) { boolean isDeclaration = isAtIndex(getIndex(), myTypes.C_PARAM_DECLARATION) || isAtIndex(getIndex(), myTypes.H_NAMED_PARAM_DECLARATION); popEndUntilFoundIndex().popEnd(); if (is(myTypes.H_COLLECTION_ITEM) && isHold()) { popEnd(); } advanceSkipEOL(); if (getTokenType() != myTypes.RPAREN) { // not at the end of a list: ie not => (p1, p2<,> ) markHolder(myTypes.H_COLLECTION_ITEM) .mark(isDeclaration ? myTypes.C_PARAM_DECLARATION : myTypes.C_PARAM) .markHolder(myTypes.H_PLACE_HOLDER); } } else if (isFound(myTypes.C_DECONSTRUCTION)) { popEndUntilScope(); } else if (isFound(myTypes.C_RECORD_FIELD) || isFound(myTypes.C_MIXIN_FIELD) || isFound(myTypes.C_OBJECT_FIELD)) { popEndUntilFoundIndex().popEnd(); } else { popEndUntilFoundIndex(); advanceSkipEOL(); markHolder(myTypes.H_COLLECTION_ITEM); } } } private void parsePipe() { if (is(myTypes.C_TYPE_BINDING)) { // type x = |>|<| ... advance().mark(myTypes.C_VARIANT_DECLARATION); } else if (is(myTypes.C_TRY_HANDLERS)) { // Start of a try handler // try (...) { |>|<| ... } advance().mark(myTypes.C_TRY_HANDLER); } else if (is(myTypes.C_SWITCH_BODY)) { // switch x { |>|<| ... } advance().mark(myTypes.C_PATTERN_MATCH_EXPR); } else if (strictlyIn(myTypes.C_PATTERN_MATCH_BODY)) { // can be a switchBody or a 'fun' popEndUntil(myTypes.C_PATTERN_MATCH_EXPR).popEnd() .advance() .mark(myTypes.C_PATTERN_MATCH_EXPR); } else if (strictlyIn(myTypes.C_PATTERN_MATCH_EXPR)) { // pattern grouping // | X |>|<| Y => ... popEndUntilFoundIndex().popEnd() .advance() .mark(myTypes.C_PATTERN_MATCH_EXPR); } else if (strictlyIn(myTypes.C_VARIANT_DECLARATION)) { // type t = | X |>|<| Y ... popEndUntil(myTypes.C_VARIANT_DECLARATION).popEnd() .advance() .mark(myTypes.C_VARIANT_DECLARATION); } } private void parseStringValue() { if (isCurrent(myTypes.C_JS_OBJECT)) { popIfHold().mark(myTypes.C_OBJECT_FIELD); } } private void parseTemplateStringOpen() { // |>`<| ... markScope(myTypes.C_INTERPOLATION_EXPR, myTypes.JS_STRING_OPEN).advance(); } private void parseLet() { popIfHold(); if (!isCurrent(myTypes.C_PATTERN_MATCH_BODY)) { popEndUntilScope(); } mark(myTypes.C_LET_DECLARATION); } private void parseModule() { if (isCurrent(myTypes.C_DEFAULT_VALUE)) { // let fn = (~x:module(X) = »module« mark(myTypes.C_FIRST_CLASS); } else if (!is(myTypes.C_MACRO_NAME) && !is(myTypes.C_FIRST_CLASS)) { popEndUntilScope(); mark(myTypes.C_MODULE_DECLARATION); } } private void parseException() { popEndUntilScope(); mark(myTypes.C_EXCEPTION_DECLARATION); } private void parseType() { if (is(myTypes.C_CONSTRAINTS)) { // module M = (X) : ( S with |>type<| ... ) = ... mark(myTypes.C_TYPE_CONSTRAINT); } else if (!is(myTypes.C_MODULE_DECLARATION)) { popEndUntilScope(); mark(myTypes.C_TYPE_DECLARATION); } } private void parseExternal() { popEndUntilScope(); mark(myTypes.C_EXTERNAL_DECLARATION); } private void parseOpen() { popEndUntilScope(); mark(myTypes.C_OPEN); } private void parseInclude() { popEndUntilScope(); mark(myTypes.C_INCLUDE); } private void parsePercent() { // |>%<| raw ... mark(myTypes.C_MACRO_EXPR).mark(myTypes.C_MACRO_NAME).advance(); if (getTokenType() == myTypes.PERCENT) { // %|>%<| raw ... advance(); } } private void parseColon() { if (is(myTypes.C_SCOPED_EXPR) && isRawParent(myTypes.C_FIELD_VALUE)) { return; } if (strictlyInAny( myTypes.C_MODULE_DECLARATION, myTypes.C_LET_DECLARATION, myTypes.C_EXTERNAL_DECLARATION, myTypes.C_PARAM_DECLARATION, myTypes.C_RECORD_FIELD, myTypes.C_OBJECT_FIELD, myTypes.H_NAMED_PARAM_DECLARATION, myTypes.C_IF_THEN_ELSE, myTypes.C_UNPACK, myTypes.C_FUNCTION_EXPR)) { if (isFound(myTypes.C_FUNCTION_EXPR)) { // a result signature ? let x = (..) |> :<| .. => .. advance(); mark(myTypes.C_SIG_EXPR).mark(myTypes.C_SIG_ITEM); } else if (isFound(myTypes.C_PARAM_DECLARATION) || isFound(myTypes.H_NAMED_PARAM_DECLARATION)) { advance(); if (getTokenType() == myTypes.MODULE) { // let _ = (~x »:« module(... mark(myTypes.C_MODULE_SIGNATURE).advance(); if (getTokenType() == myTypes.LPAREN) { markScope(myTypes.C_SCOPED_EXPR, myTypes.LPAREN).advance(); } } else { // let _ = (x »:« ... // let _ = (~x »:« ... mark(myTypes.C_SIG_EXPR).mark(myTypes.C_SIG_ITEM); } } else if (isFound(myTypes.C_MODULE_DECLARATION)) { // module M |> :<| ... popEndUntilFoundIndex().advance() .mark(myTypes.C_MODULE_SIGNATURE); } else if (isFound(myTypes.C_EXTERNAL_DECLARATION) || isFound(myTypes.C_LET_DECLARATION)) { popEndUntilFoundIndex().advance(); if (getTokenType() == myTypes.MODULE) { // let x >:< module ... mark(myTypes.C_MODULE_SIGNATURE).advance(); } else { // external/let x |> :<| ... mark(myTypes.C_SIG_EXPR); if (getTokenType() == myTypes.LPAREN) { // external/let x : |>(<| ... markDummyScope(myTypes.C_SCOPED_EXPR, myTypes.LPAREN) .advance().markHolder(myTypes.H_COLLECTION_ITEM); if (getTokenType() == myTypes.DOT) { // external/let : ( |>.<| ... advance(); } } mark(myTypes.C_SIG_ITEM); } } else if (isFound(myTypes.C_RECORD_FIELD) || isFound(myTypes.C_OBJECT_FIELD)) { advance(); if (in(myTypes.C_TYPE_BINDING)) { mark(myTypes.C_SIG_EXPR) .mark(myTypes.C_SIG_ITEM); } else { mark(myTypes.C_FIELD_VALUE) .markHolder(myTypes.H_PLACE_HOLDER); } } else if (isFound(myTypes.C_IF_THEN_ELSE)) { // ternary :: cond ? x |> :<| ... popEndUntilFoundIndex().popEnd() .advance() .mark(myTypes.C_IF_THEN_ELSE); } else if (isFound(myTypes.C_UNPACK)) { advance(); mark(myTypes.C_MODULE_SIGNATURE); } } } private void parseArrobase() { if (!is(myTypes.C_VARIANT_DECLARATION)) { popEndUntilScope(); } mark(myTypes.C_ANNOTATION).mark(myTypes.C_MACRO_NAME); } private void parseLt() { if (is(myTypes.C_OPTION) || in(myTypes.C_SIG_EXPR)) { markScope(myTypes.C_SCOPED_EXPR, myTypes.LT).advance() .markHolder(myTypes.H_PLACE_HOLDER); } else if (in(myTypes.C_TYPE_DECLARATION)) { // type parameters // type t |> < <| 'a > markScope(myTypes.C_PARAMETERS, myTypes.LT); } else if (in(myTypes.C_VARIANT_DECLARATION)) { // type t = #X(array |> < <| ... markScope(myTypes.C_PARAMETERS, myTypes.LT); } // If there is a lower ident just before a tag, it can’t be a jsx else if (rawLookup(-1) != myTypes.LIDENT) { // Can be a symbol or a JSX tag IElementType nextTokenType = rawLookup(1); if (nextTokenType == myTypes.LIDENT || nextTokenType == myTypes.UIDENT || nextTokenType == myTypes.OPTION) { // Note that option is a keyword but also a JSX keyword ! mark(myTypes.C_TAG) .markScope(myTypes.C_TAG_START, myTypes.LT); } else if (nextTokenType == myTypes.GT) { // a React fragment start mark(myTypes.C_TAG) .mark(myTypes.C_TAG_START) .advance() .advance() .popEnd() .mark(myTypes.C_TAG_BODY); } } } private void parseGt() { if (isCurrent(myTypes.C_TAG_PROP_VALUE)) { // ?prop=value |> > <| ... popEndUntil(myTypes.C_TAG_PROP_VALUE).popEnd().popEnd(); } else if (is(myTypes.C_TAG_PROPERTY)) { // ?prop |> > <| ... popEnd(); } if (strictlyInAny(myTypes.C_PARAMETERS, myTypes.C_SCOPED_EXPR, myTypes.C_TAG_START, myTypes.C_TAG_CLOSE)) { if (isFound(myTypes.C_PARAMETERS)) { if (isFoundScope(myTypes.LT)) { // type x< ... |> ><| popEndUntilFoundIndex().advance().end(); } } else if (isFound(myTypes.C_SCOPED_EXPR)) { if (isFoundScope(myTypes.LT)) { popEndUntilFoundIndex().advance().popEnd(); if (strictlyIn(myTypes.C_OPTION)) { // option < ... |> > <| ... popEnd(); } } } else if (isFound(myTypes.C_TAG_START)) { // ><| popEndUntilFoundIndex().advance().end(); mark(myTypes.C_TAG_BODY); } else if (isFound(myTypes.C_TAG_CLOSE)) { // ><| advance().popEndUntil(myTypes.C_TAG).end(); } } } private void parseGtAutoClose() { if (is(myTypes.C_TAG_PROP_VALUE)) { // ?prop=value |> /> <| ... popEndUntil(myTypes.C_TAG_PROPERTY).popEnd(); } else if (is(myTypes.C_TAG_PROPERTY)) { // ?prop |> /> <| ... popEnd(); } if (in(myTypes.C_TAG_START)) { popEndUntilFoundIndex() .advance().popEnd() .end(); } } private void parseLtSlash() { if (in(myTypes.C_TAG)) { if (in(myTypes.C_TAG_BODY)) { popEndUntilFoundIndex().end(); } remapCurrentToken(myTypes.TAG_LT_SLASH) .mark(myTypes.C_TAG_CLOSE); } } private void parseLIdent() { // external |>x<| ... if (is(myTypes.C_EXTERNAL_DECLARATION)) { wrapAtom(myTypes.CA_LOWER_SYMBOL); } // let |>x<| ... else if (is(myTypes.C_LET_DECLARATION)) { wrapAtom(myTypes.CA_LOWER_SYMBOL); } // type |>x<| ... else if (is(myTypes.C_TYPE_DECLARATION)) { wrapAtom(myTypes.CA_LOWER_SYMBOL); } // else if (is(myTypes.C_FUNCTION_EXPR)) { mark(myTypes.C_PARAMETERS) .mark(myTypes.C_PARAM_DECLARATION).wrapAtom(myTypes.CA_LOWER_SYMBOL); } // else if (isCurrent(myTypes.C_PARAMETERS) && (isParent(myTypes.C_FUNCTION_EXPR) || isParent(myTypes.C_FUNCTION_CALL))) { mark(myTypes.C_PARAM_DECLARATION).wrapAtom(myTypes.CA_LOWER_SYMBOL); } // { |>x<| ... else if (isCurrent(myTypes.C_RECORD_EXPR)) { mark(myTypes.C_RECORD_FIELD) .wrapAtom(myTypes.CA_LOWER_SYMBOL); } // tag name else if (is(myTypes.C_TAG_START)) { remapCurrentToken(myTypes.A_LOWER_TAG_NAME).wrapAtom(myTypes.CA_LOWER_SYMBOL); } // else if (is(myTypes.C_DECONSTRUCTION)) { wrapWith(myTypes.C_LOWER_NAME); } // This is a property else if (strictlyIn(myTypes.C_TAG_START) && !isCurrent(myTypes.C_TAG_PROP_VALUE)) { // no scope generateJsxPropertyName(); } // else { IElementType nextElementType = lookAheadSkipEOL(); if (isCurrent(myTypes.C_MACRO_NAME)) { // @ |>x<| or @x. |>y<| if (nextElementType != myTypes.DOT && nextElementType != myTypes.LPAREN) { wrapAtom(myTypes.CA_LOWER_SYMBOL). popEndUntil(myTypes.C_ANNOTATION).popEnd(); } } else if (isCurrent(myTypes.C_SCOPED_EXPR) && isCurrentScope(myTypes.LBRACE) && nextElementType == myTypes.COLON) { // this is a record usage :: { |>x<| : ... updateComposite(myTypes.C_RECORD_EXPR) .mark(myTypes.C_RECORD_FIELD) .wrapAtom(myTypes.CA_LOWER_SYMBOL); } else if (nextElementType == myTypes.QUESTION_MARK && isHold()) { if (!isCurrent(myTypes.C_TAG_PROP_VALUE) || currentHasScope()) { // not an inline tag value like v<| ?w> // a ternary :: |>x<| ? ... wrapAtom(myTypes.CA_LOWER_SYMBOL); parseTernary(1); } } else if (nextElementType == myTypes.LPAREN && !is(myTypes.C_MACRO_NAME)) { // a function call // |>x<| ( ... mark(myTypes.C_FUNCTION_CALL).wrapAtom(myTypes.CA_LOWER_SYMBOL); } else if (is(myTypes.C_SIG_EXPR) || (isScopeAtIndex(1, myTypes.LPAREN) && isAtIndex(2, myTypes.C_SIG_EXPR))) { mark(myTypes.C_SIG_ITEM) .wrapAtom(myTypes.CA_LOWER_SYMBOL).popEnd(); } else if (nextElementType == myTypes.ARROW && strictlyInAny( myTypes.C_LET_BINDING, myTypes.C_DEFAULT_VALUE, myTypes.C_PARAM, myTypes.C_FIELD_VALUE, myTypes.C_SCOPED_EXPR, myTypes.C_FUNCTION_EXPR )) { // Already a function if (isFound(myTypes.C_FUNCTION_EXPR)) { wrapAtom(myTypes.CA_LOWER_SYMBOL); popEndUntil(myTypes.C_FUNCTION_EXPR); advance().mark(myTypes.C_FUNCTION_BODY) .markHolder(myTypes.H_PLACE_HOLDER); } else { // A paren-less function definition :: |>x<| => mark(myTypes.C_FUNCTION_EXPR) .mark(myTypes.C_PARAMETERS) .mark(myTypes.C_PARAM_DECLARATION) .wrapAtom(myTypes.CA_LOWER_SYMBOL); } } else { wrapAtom(myTypes.CA_LOWER_SYMBOL); } } } private void parseLBracket() { IElementType nextType = rawLookup(1); if (nextType == myTypes.GT) { // |> [ <| > ... ] markScope(myTypes.C_OPEN_VARIANT, myTypes.LBRACKET).advance().advance(); if (getTokenType() != myTypes.RBRACKET) { mark(myTypes.C_VARIANT_DECLARATION); } } else if (nextType == myTypes.LT) { // |> [ <| < ... ] markScope(myTypes.C_CLOSED_VARIANT, myTypes.LBRACKET).advance().advance(); if (getTokenType() != myTypes.RBRACKET) { mark(myTypes.C_VARIANT_DECLARATION); } } else { markScope(myTypes.C_ARRAY, myTypes.LBRACKET).advance(); if (getTokenType() != myTypes.PIPE && getTokenType() != myTypes.POLY_VARIANT) { markHolder(myTypes.H_COLLECTION_ITEM); // Needed to roll back to individual item in collection } } } private void parseRBracket() { Marker scope = popEndUntilScopeToken(myTypes.LBRACKET); advance(); if (scope != null) { popEnd(); } } private void parseLBrace() { if (previousElementType(1) == myTypes.DOT && previousElementType(2) == myTypes.A_MODULE_NAME) { // Local open a js object or a record // Xxx.|>{<| ... } mark(myTypes.C_LOCAL_OPEN); IElementType nextElementType = lookAhead(1); if (nextElementType == myTypes.LIDENT) { markScope(myTypes.C_RECORD_EXPR, myTypes.LBRACE); } else { markScope(myTypes.C_JS_OBJECT, myTypes.LBRACE); } } else if (is(myTypes.C_LET_DECLARATION)) { // let |>{<| .. markScope(myTypes.C_DECONSTRUCTION, myTypes.LBRACE); } else if (is(myTypes.C_TYPE_BINDING)) { boolean isJsObject = lookAheadSkipEOL() == myTypes.STRING_VALUE; markScope(isJsObject ? myTypes.C_JS_OBJECT : myTypes.C_RECORD_EXPR, myTypes.LBRACE); } else if (is(myTypes.C_MODULE_BINDING)) { // module M = |>{<| ... updateScopeToken(myTypes.LBRACE); } else if (is(myTypes.C_FUNCTOR_BINDING)) { // module M = (...) => |>{<| ... updateScopeToken(myTypes.LBRACE); } else if (isCurrent(myTypes.C_TAG_PROP_VALUE) || isCurrent(myTypes.C_TAG_BODY)) { // A scoped property popIfHold().markScope(myTypes.C_SCOPED_EXPR, myTypes.LBRACE) .advance().markHolder(myTypes.H_PLACE_HOLDER); } else if (is(myTypes.C_MODULE_SIGNATURE)) { // module M : |>{<| ... updateScopeToken(myTypes.LBRACE); } else if (isDone(myTypes.C_TRY_BODY)) { // A try expression // try ... |>{<| ... } markScope(myTypes.C_TRY_HANDLERS, myTypes.LBRACE); } else if (isDone(myTypes.C_BINARY_CONDITION) && isRawParent(myTypes.C_IF)) { // if x |>{<| ... } markScope(myTypes.C_IF_THEN_ELSE, myTypes.LBRACE); } else if (isDone(myTypes.C_BINARY_CONDITION) && isRawParent(myTypes.C_SWITCH_EXPR)) { // switch (x) |>{<| ... } markScope(myTypes.C_SWITCH_BODY, myTypes.LBRACE); } else if (strictlyIn(myTypes.C_BINARY_CONDITION)) { popEndUntilFoundIndex().popEnd(); if (isCurrent(myTypes.C_IF)) { // if ... |>{<| markScope(myTypes.C_IF_THEN_ELSE, myTypes.LBRACE); } else if (strictlyIn(myTypes.C_SWITCH_EXPR)) { // switch x |>{<| ... } markScope(myTypes.C_SWITCH_BODY, myTypes.LBRACE); } } else { // it might be a js object IElementType nextElement = lookAheadSkipEOL(); if (nextElement == myTypes.STRING_VALUE || nextElement == myTypes.DOT) { boolean hasDot = nextElement == myTypes.DOT; // js object detected :: |>{<| ./"x" ___ } markScope(myTypes.C_JS_OBJECT, myTypes.LBRACE).advance(); if (hasDot) { advance(); } } else { popIfHold(); markScope(myTypes.C_SCOPED_EXPR, myTypes.LBRACE); } } } private void parseRBrace() { Marker scope = popEndUntilOneOfElementType(myTypes.LBRACE, myTypes.RECORD, myTypes.SWITCH); advance(); if (scope != null) { popEnd(); if (is(myTypes.C_LOCAL_OPEN) && !rawHasScope()) { // X.{ ... |>}<| popEnd(); } else if (is(myTypes.C_TAG_PROP_VALUE)) { popEndUntil(myTypes.C_TAG_PROPERTY).popEnd(); } else if (scope.isCompositeType(myTypes.C_RECORD_EXPR) && is(myTypes.C_TYPE_BINDING)) { // Record type, end the type itself popEndUntil(myTypes.C_TYPE_DECLARATION).popEnd(); } //endInstruction(getTokenType()); } } private void parseLParen() { if (previousElementType(2) == myTypes.A_MODULE_NAME && previousElementType(1) == myTypes.DOT) { // Local open // M. |>(<| ... ) markScope(myTypes.C_LOCAL_OPEN, myTypes.LPAREN); } else if (is(myTypes.C_MODULE_BINDING) && !in(myTypes.C_FUNCTOR_DECLARATION)) { if (myBuilder.lookAhead(1) == myTypes.UNPACK) { // module M = |>(<| unpack .. ) markParenthesisScope(true); } else { // This is a functor // module M = |>(<| .. ) int moduleIndex = latestIndexOfCompositeAtMost(3, myTypes.C_MODULE_DECLARATION); dropLatest() .popEndUntilIndex(moduleIndex) .updateComposite(myTypes.C_FUNCTOR_DECLARATION) .markScope(myTypes.C_PARAMETERS, myTypes.LPAREN).advance() .mark(myTypes.C_PARAM_DECLARATION) .markHolder(myTypes.H_PLACE_HOLDER); } } else if (is(myTypes.C_FUNCTION_EXPR)) { // A function // |>(<| . OR |>(<| ~ markScope(myTypes.C_PARAMETERS, myTypes.LPAREN); } // x |>(<| ... else if (isCurrent(myTypes.C_FUNCTION_CALL)) { popEnd().markScope(myTypes.C_PARAMETERS, myTypes.LPAREN).advance(); if (getTokenType() == myTypes.DOT) { advance(); } markHolder(myTypes.H_COLLECTION_ITEM); if (getTokenType() != myTypes.RPAREN) { mark(myTypes.C_PARAM) .markHolder(myTypes.H_PLACE_HOLDER); } } else if (isCurrent(myTypes.C_PARAM_DECLARATION) || isCurrent(myTypes.C_PARAM)) { popIfHold(); markScope(myTypes.C_SCOPED_EXPR, myTypes.LPAREN); advance().markHolder(myTypes.H_COLLECTION_ITEM); } else if (isCurrent(myTypes.C_PARAMETERS) && !isParent(myTypes.C_SOME)) { if (!currentHasScope()) { // |>(<| ... ) => ... updateScopeToken(myTypes.LPAREN).advance() .markHolder(myTypes.H_COLLECTION_ITEM); } else { // ( |>(<| ... ) , ... ) => ... mark(isParent(myTypes.C_FUNCTION_CALL) ? myTypes.C_PARAM : myTypes.C_PARAM_DECLARATION) .markScope(myTypes.C_SCOPED_EXPR, myTypes.LPAREN) .markHolder(myTypes.H_PLACE_HOLDER); } } else if (is(myTypes.C_MACRO_NAME) && isRawParent(myTypes.C_ANNOTATION)) { // @ann |>(<| ... ) popEnd() .markScope(myTypes.C_SCOPED_EXPR, myTypes.LPAREN); } else if (is(myTypes.C_MACRO_NAME)) { // %raw |>(<| ... popEnd() .markDummyScope(myTypes.C_SCOPED_EXPR, myTypes.LPAREN) .advance() .mark(myTypes.C_MACRO_BODY); } else if (is(myTypes.C_BINARY_CONDITION) && !currentHasScope()) { updateScopeToken(myTypes.LPAREN); } else if (is(myTypes.C_DECONSTRUCTION) && isRawParent(myTypes.C_LET_DECLARATION)) { // let ((x |>,<| ... markScope(myTypes.C_DECONSTRUCTION, myTypes.LPAREN); } // else if (strictlyInAny( myTypes.C_OPEN, myTypes.C_INCLUDE, myTypes.C_FUNCTOR_DECLARATION, myTypes.C_FUNCTOR_RESULT )) { if (isFound(myTypes.C_OPEN) || isFound(myTypes.C_INCLUDE)) { // Functor call // open M |>(<| ... markBefore(0, myTypes.C_FUNCTOR_CALL) .markScope(myTypes.C_PARAMETERS, myTypes.LPAREN).advance(); } else if (isFound(myTypes.C_FUNCTOR_DECLARATION)) { // module M = |>(<| ... // module M = ( ... ) : |>(<| ... boolean isCall = isFound(myTypes.C_FUNCTOR_CALL); markScope(myTypes.C_PARAMETERS, myTypes.LPAREN).advance() .mark(isCall ? myTypes.C_PARAM : myTypes.C_PARAM_DECLARATION); } } else { Marker marker = getActiveMarker(); if (marker != null && marker.isCompositeType(myTypes.C_LET_BINDING) && !marker.hasScope()) { if (rawHasScope() && isParent(myTypes.C_LET_BINDING)) { markHolder(myTypes.H_PLACE_HOLDER); } } markScope(myTypes.C_SCOPED_EXPR, myTypes.LPAREN).advance() .markHolder(myTypes.H_COLLECTION_ITEM); } } private void parseRParen() { Marker lParen = popEndUntilScopeToken(myTypes.LPAREN); advance(); IElementType nextTokenType = getTokenType(); if (is(myTypes.C_BINARY_CONDITION)) { end(); if (isRawParent(myTypes.C_IF) && nextTokenType != myTypes.LBRACE) { // if ( x ) |><| ... mark(myTypes.C_IF_THEN_ELSE); } } else if (lParen != null) { if (isRawParent(myTypes.C_FUNCTOR_DECLARATION)) { popEnd(); if (nextTokenType == myTypes.COLON) { // module M = (P) |> :<| R ... advance(); markParenthesisScope(true); mark(myTypes.C_FUNCTOR_RESULT); } else if (nextTokenType == myTypes.ARROW) { // module M = (P) |>=><| ... advance().mark(myTypes.C_FUNCTOR_BINDING); } } else if (isRawParent(myTypes.C_MODULE_SIGNATURE)) { popEnd(); } else if ((nextTokenType == myTypes.ARROW || nextTokenType == myTypes.COLON) && !isParent(myTypes.C_FUNCTION_EXPR)) { popEnd(); if (strictlyInAny(myTypes.C_PATTERN_MATCH_EXPR, myTypes.C_TRY_HANDLER, myTypes.C_PARAM, myTypes.C_PARAMETERS, myTypes.C_SIG_ITEM, myTypes.C_SIG_EXPR, myTypes.C_DEFAULT_VALUE, myTypes.C_SCOPED_EXPR, myTypes.C_LET_BINDING, myTypes.H_PLACE_HOLDER)) { int foundIndex = getIndex(); if (isFound(myTypes.C_DEFAULT_VALUE) && isHold()) { // fn(~p=() |>=><| ... rollbackToIndex(0); mark(myTypes.C_FUNCTION_EXPR); mark(myTypes.C_PARAMETERS).markHolder(myTypes.H_COLLECTION_ITEM); } else if (isFound(myTypes.C_PARAM) || isFound(myTypes.H_PLACE_HOLDER) || (isFound(myTypes.C_PARAMETERS) && isAtIndex(foundIndex + 1, myTypes.C_SOME))) { rollbackToFoundIndex(); mark(myTypes.C_FUNCTION_EXPR); if (getTokenType() == myTypes.LPAREN) { markScope(myTypes.C_PARAMETERS, myTypes.LPAREN).advance(); } else { mark(myTypes.C_PARAMETERS); } markHolder(myTypes.H_COLLECTION_ITEM); } else if (isFound(myTypes.C_LET_BINDING) || isFound(myTypes.C_SCOPED_EXPR)) { // a missed function expression int foundHolderIndex = latestIndexOfCompositeAtMost(getIndex(), myTypes.H_PLACE_HOLDER, myTypes.H_COLLECTION_ITEM); if (-1 < foundHolderIndex) { rollbackToIndex(foundHolderIndex); updateLatestComposite(myTypes.C_FUNCTION_EXPR); mark(myTypes.C_PARAMETERS).markHolder(myTypes.H_COLLECTION_ITEM); } } else { popEndUntilFoundIndex(); } } } else { popEnd(); if (is(myTypes.C_ANNOTATION)) { popEnd(); } else if (is(myTypes.C_FUNCTION_CALL)) { if (nextTokenType == myTypes.LPAREN) { // Chaining function calls fn()( ... markBefore(0, myTypes.C_FUNCTION_CALL); } else { popEnd(); if (isCurrent(myTypes.C_LET_BINDING) && !currentHasScope()) { IElementType nextValidToken = nextTokenType == myTypes.EOL ? lookAheadSkipEOL() : nextTokenType; if (nextValidToken != myTypes.QUESTION_MARK && nextValidToken != myTypes.RIGHT_ARROW) { // let _ = fn(|>)<| popEndUntil(myTypes.C_LET_DECLARATION).popEnd(); } else if (nextTokenType == myTypes.EOL) { advanceSkipEOL(); } } } } else if (strictlyIn(myTypes.C_TAG_PROP_VALUE)) { popEndUntil(myTypes.C_TAG_PROPERTY).popEnd(); } } } } private void parseEq() { if (strictlyInAny( myTypes.C_TYPE_DECLARATION, myTypes.C_LET_DECLARATION, myTypes.C_MODULE_SIGNATURE, myTypes.C_MODULE_DECLARATION, myTypes.C_TAG_PROPERTY, myTypes.C_SIG_EXPR, myTypes.H_NAMED_PARAM_DECLARATION, myTypes.C_NAMED_PARAM, myTypes.C_TYPE_CONSTRAINT, myTypes.C_TYPE_BINDING )) { if (isFound(myTypes.C_TYPE_DECLARATION)) { // type t |> = <| ... popEndUntilFoundIndex().advance() .mark(myTypes.C_TYPE_BINDING); } else if (isFound(myTypes.C_LET_DECLARATION)) { // let x |> = <| ... popEndUntilFoundIndex().advance() .mark(myTypes.C_LET_BINDING) .markHolder(myTypes.H_PLACE_HOLDER); } else if (isFound(myTypes.C_MODULE_SIGNATURE)) { popEndUntilFoundIndex().end().advance(); if (isRawParent(myTypes.C_LET_DECLARATION)) { // let x : I >=< ... mark(myTypes.C_FIRST_CLASS); } else if (isRawGrandParent(myTypes.H_NAMED_PARAM_DECLARATION)) { // let _ = (~x: module(I) »=« popEndUntilIndex(2).mark(myTypes.C_DEFAULT_VALUE).mark(myTypes.C_FIRST_CLASS); } else { // module M : T |> = <| ... mark(myTypes.C_MODULE_BINDING); } } else if (isFound(myTypes.C_MODULE_DECLARATION)) { // module M |> = <| ... advance().mark(myTypes.C_MODULE_BINDING); } else if (isFound(myTypes.C_TAG_PROPERTY)) { // = <| ... advance().mark(myTypes.C_TAG_PROP_VALUE) .markHolder(myTypes.H_PLACE_HOLDER); } else if (isFound(myTypes.C_SIG_EXPR)) { popEndUntilFoundIndex(); if (isRawGrandParent(myTypes.H_NAMED_PARAM_DECLARATION)) { popEndUntilIndex(2) .advance().mark(myTypes.C_DEFAULT_VALUE); } else if (isRawParent(myTypes.C_LET_DECLARATION)) { // let x : M.t |> =<| ... end().popEndUntilFoundIndex() .advance().mark(myTypes.C_LET_BINDING) .markHolder(myTypes.H_PLACE_HOLDER); } else { end(); } } else if (isFound(myTypes.H_NAMED_PARAM_DECLARATION) || isFound(myTypes.C_NAMED_PARAM)) { // ( ~x |> =<| ... popEndUntilFoundIndex() .advance().mark(myTypes.C_DEFAULT_VALUE) .markHolder(myTypes.H_PLACE_HOLDER); } else if (isFound(myTypes.C_TYPE_CONSTRAINT)) { // ... with type t |> =<| ... advance().mark(myTypes.C_TYPE_BINDING); } else if (isFound(myTypes.C_TYPE_BINDING) && strictlyIn(myTypes.C_CONSTRAINTS)) { // .. with type .. = .. |> =<| .. popEndUntilFoundIndex().popEnd(); if (strictlyIn(myTypes.C_MODULE_DECLARATION)) { popEndUntilFoundIndex() .advance().mark(myTypes.C_MODULE_BINDING); } } } else { // nothing found, just add a placeholder advance().markHolder(myTypes.H_PLACE_HOLDER); } } private void parseColonEq() { if (strictlyInAny(myTypes.C_TYPE_CONSTRAINT)) { if (isFound(myTypes.C_TYPE_CONSTRAINT)) { // ... with type t |> :=<| ... advance().mark(myTypes.C_TYPE_BINDING); } } } private void parseUIdent() { if (DUMMY_IDENTIFIER_TRIMMED.equals(getTokenText())) { return; } // module |>M<| ... if (is(myTypes.C_MODULE_DECLARATION)) { remapCurrentToken(myTypes.A_MODULE_NAME).wrapAtom(myTypes.CA_UPPER_SYMBOL); } // module M = ( |>P<| ... else if (isCurrent(myTypes.C_PARAM_DECLARATION) && in(myTypes.C_FUNCTOR_DECLARATION)) { remapCurrentToken(myTypes.A_MODULE_NAME).wrapAtom(myTypes.CA_UPPER_SYMBOL); } // module M = (P: |>S<| ... else if (isCurrent(myTypes.C_SIG_ITEM) && in(myTypes.C_FUNCTOR_DECLARATION, /*not*/myTypes.C_FUNCTOR_BINDING)) { remapCurrentToken(myTypes.A_MODULE_NAME).wrapAtom(myTypes.CA_UPPER_SYMBOL); } // module M = ( ... ) : |>S<| ... // module M : |>S<| ... else if (isCurrent(myTypes.C_FUNCTOR_RESULT) || isCurrent(myTypes.C_MODULE_SIGNATURE)) { remapCurrentToken(myTypes.A_MODULE_NAME).wrapAtom(myTypes.CA_UPPER_SYMBOL); } // else if (isCurrent(myTypes.C_MODULE_BINDING)) { IElementType nextElement = lookAhead(1); if (nextElement == myTypes.LPAREN) { // functor call :: |>X<| ( ... // functor call with path :: A.B.|>X<| ( ... mark(myTypes.C_FUNCTOR_CALL) .remapCurrentToken(myTypes.A_MODULE_NAME).wrapAtom(myTypes.CA_UPPER_SYMBOL) .markScope(myTypes.C_PARAMETERS, myTypes.LPAREN).advance() .mark(myTypes.C_PARAM) .markHolder(myTypes.H_PLACE_HOLDER); } else { // module M = |>X<| // module M = X.|>Y<| remapCurrentToken(myTypes.A_MODULE_NAME).wrapAtom(myTypes.CA_UPPER_SYMBOL); if (nextElement != myTypes.DOT) { popEndUntil(myTypes.C_MODULE_DECLARATION).popEnd(); } } } // exception |>E<| ... else if (is(myTypes.C_EXCEPTION_DECLARATION)) { remapCurrentToken(myTypes.A_EXCEPTION_NAME).wrapAtom(myTypes.CA_UPPER_SYMBOL); } // try .. catch { | |>X<| .. else if (isCurrent(myTypes.C_TRY_HANDLER)) { remapCurrentToken(myTypes.A_EXCEPTION_NAME).wrapAtom(myTypes.CA_UPPER_SYMBOL); } // tag name else if (isCurrent(myTypes.C_TAG_START) || isCurrent(myTypes.C_TAG_CLOSE)) { remapCurrentToken(myTypes.A_UPPER_TAG_NAME).wrapAtom(myTypes.CA_UPPER_SYMBOL); } // open |>M<| ... else if (isCurrent(myTypes.C_OPEN)) { // It is a module name/path, or maybe a functor call remapCurrentToken(myTypes.A_MODULE_NAME).wrapAtom(myTypes.CA_UPPER_SYMBOL); IElementType nextToken = getTokenType(); if (nextToken != myTypes.LPAREN && nextToken != myTypes.DOT) { popEndUntil(myTypes.C_OPEN).popEnd(); } } // include |>M<| ... else if (isCurrent(myTypes.C_INCLUDE)) { // It is a module name/path, or maybe a functor call remapCurrentToken(myTypes.A_MODULE_NAME).wrapAtom(myTypes.CA_UPPER_SYMBOL); IElementType nextToken = getTokenType(); if (nextToken != myTypes.LPAREN && nextToken != myTypes.DOT) { popEndUntil(myTypes.C_INCLUDE).popEnd(); } } // type t = | |>X<| .. else if (is(myTypes.C_VARIANT_DECLARATION)) { // Declaring a variant IElementType nextElementType = rawLookup(1); remapCurrentToken(nextElementType == myTypes.DOT ? myTypes.A_MODULE_NAME : myTypes.A_VARIANT_NAME); wrapAtom(myTypes.CA_UPPER_SYMBOL); if (getTokenType() == myTypes.LPAREN) { markScope(myTypes.C_PARAMETERS, myTypes.LPAREN).advance() .mark(myTypes.C_PARAM_DECLARATION); } } // type |>M<|.t += ... else if (is(myTypes.C_TYPE_DECLARATION)) { remapCurrentToken(myTypes.A_MODULE_NAME).wrapAtom(myTypes.CA_UPPER_SYMBOL); } // else if (is(myTypes.C_TYPE_BINDING)) { IElementType nextToken = lookAhead(1); if (nextToken == myTypes.DOT) { // a path remapCurrentToken(myTypes.A_MODULE_NAME).wrapAtom(myTypes.CA_UPPER_SYMBOL); } else { // We are declaring a variant without a pipe before // type t = |>X<| | ... // type t = |>X<| (...) | ... mark(myTypes.C_VARIANT_DECLARATION) .remapCurrentToken(myTypes.A_VARIANT_NAME).wrapAtom(myTypes.CA_UPPER_SYMBOL); if (getTokenType() == myTypes.LPAREN) { markScope(myTypes.C_PARAMETERS, myTypes.LPAREN).advance() .mark(myTypes.C_PARAM_DECLARATION); } } } else if (in(myTypes.C_FIRST_CLASS)) { remapCurrentToken(myTypes.A_MODULE_NAME).wrapAtom(myTypes.CA_UPPER_SYMBOL); } // else { IElementType nextToken = lookAhead(1); if (((isCurrent(myTypes.C_PATTERN_MATCH_EXPR) || isCurrent(myTypes.C_LET_BINDING))) && nextToken != myTypes.DOT) { // Pattern matching a variant or using it // switch c { | |>X<| ... / let x = |>X<| ... remapCurrentToken(myTypes.A_VARIANT_NAME).wrapAtom(myTypes.CA_UPPER_SYMBOL); if (getTokenType() == myTypes.LPAREN) { markScope(myTypes.C_PARAMETERS, myTypes.LPAREN).advance() .markHolder(myTypes.H_COLLECTION_ITEM); } } else { boolean isModule = nextToken == myTypes.DOT || nextToken == myTypes.WITH || isParent(myTypes.C_MODULE_SIGNATURE); remapCurrentToken(isModule ? myTypes.A_MODULE_NAME : myTypes.A_VARIANT_NAME) .wrapAtom(myTypes.CA_UPPER_SYMBOL); } } } private void parseLTagName() { // LIdent might have already incorrectly been remapped to a tag name. need to revert that update. if (in(myTypes.C_SIG_ITEM)) { remapCurrentToken(myTypes.LIDENT).wrapAtom(myTypes.CA_LOWER_SYMBOL); } } private void parseUTagName() { // UIdent might have already incorrectly been remapped to a tag name. need to revert that update. if (in(myTypes.C_SIG_ITEM)) { remapCurrentToken(myTypes.A_MODULE_NAME).wrapAtom(myTypes.CA_UPPER_SYMBOL); } } private void parsePolyVariant() { if (isRawParent(myTypes.C_TYPE_BINDING)) { // type t = [ |>#xxx<| ... mark(myTypes.C_VARIANT_DECLARATION); } advance(); markParenthesisScope(false); } private void parseSwitch() { mark(myTypes.C_SWITCH_EXPR) .advance() .mark(myTypes.C_BINARY_CONDITION); } private void parseTry() { mark(myTypes.C_TRY_EXPR).advance() .mark(myTypes.C_TRY_BODY); } private void parseCatch() { if (strictlyIn(myTypes.C_TRY_BODY)) { popEndUntilFoundIndex().end(); } } private void parseWith() { if (strictlyInAny(myTypes.C_FUNCTOR_RESULT, myTypes.C_MODULE_SIGNATURE)) { // module M (X) : ( S |>with<| ... ) = ... popEndUntilFoundIndex().popEnd().advance() .mark(myTypes.C_CONSTRAINTS); } } private void parseArrow() { if (inScopeOrAny( myTypes.C_PATTERN_MATCH_EXPR, myTypes.C_TRY_HANDLER, myTypes.C_PARAM_DECLARATION, myTypes.C_PARAMETERS, myTypes.C_FUNCTION_EXPR, myTypes.C_SIG_EXPR, myTypes.C_SIG_ITEM, myTypes.C_FUNCTOR_RESULT )) { if (isFound(myTypes.C_PARAMETERS) || isFound(myTypes.C_FUNCTION_EXPR)) { popEndUntilOneOf(myTypes.C_PARAMETERS, myTypes.C_FUNCTION_EXPR); if (isRawParent(myTypes.C_FUNCTION_EXPR) || isRawParent(myTypes.C_FUNCTION_CALL)) { popEnd(); } advance().mark(myTypes.C_FUNCTION_BODY) .markHolder(myTypes.H_PLACE_HOLDER); } else if (isFound(myTypes.C_SIG_EXPR)) { advance().mark(myTypes.C_SIG_ITEM); } else if (isFound(myTypes.C_SIG_ITEM)) { popEndUntilFoundIndex().popEnd(); advance().mark(myTypes.C_SIG_ITEM); } else if (isFound(myTypes.C_PATTERN_MATCH_EXPR)) { // switch ( ... ) { | ... |>=><| ... } popEndUntilFoundIndex().advance() .markScope(myTypes.C_PATTERN_MATCH_BODY, myTypes.ARROW) .markHolder(myTypes.H_PLACE_HOLDER); } else if (isFound(myTypes.C_TRY_HANDLER)) { // try .. { | X |>=><| .. } popEndUntilFoundIndex().advance() .mark(myTypes.C_TRY_HANDLER_BODY); } else if (isFound(myTypes.C_PARAM_DECLARATION)) { // anonymous function if (isParent(myTypes.C_SIG_ITEM)) { } else if (in(myTypes.C_FUNCTION_EXPR)) { // x( y |>=><| ... ) popEndUntil(myTypes.C_FUNCTION_EXPR).advance() .mark(myTypes.C_FUNCTION_BODY) .markHolder(myTypes.H_PLACE_HOLDER); } } else if (isFound(myTypes.C_FUNCTOR_RESULT)) { // module Make = (M) : R |>=><| ... popEndUntilFoundIndex().popEnd() .advance().mark(myTypes.C_FUNCTOR_BINDING); } } } private @Nullable IElementType lookAheadSkipEOL() { IElementType elementType = lookAhead(1); if (elementType == myTypes.EOL) { elementType = lookAhead(1 + 1); } return elementType; } private void advanceSkipEOL() { advance(); while (getTokenType() == myTypes.EOL) { myBuilder.remapCurrentToken(myTypes.WHITE_SPACE); advance(); } } } } ================================================ FILE: src/main/java/com/reason/lang/rescript/ResParserDefinition.java ================================================ package com.reason.lang.rescript; import com.intellij.lang.*; import com.intellij.lexer.*; import com.intellij.openapi.project.*; import com.intellij.psi.*; import com.intellij.psi.tree.*; import com.reason.ide.files.*; import com.reason.lang.core.stub.type.*; import org.jetbrains.annotations.*; public class ResParserDefinition implements ParserDefinition { @Override public @NotNull Lexer createLexer(Project project) { return new ResLexer(); } public @NotNull TokenSet getWhitespaceTokens() { return TokenSet.create(TokenType.WHITE_SPACE); } public @NotNull TokenSet getCommentTokens() { return TokenSet.create(ResTypes.INSTANCE.MULTI_COMMENT, ResTypes.INSTANCE.SINGLE_COMMENT); } public @NotNull TokenSet getStringLiteralElements() { return TokenSet.create(ResTypes.INSTANCE.STRING_VALUE); } public @NotNull PsiParser createParser(Project project) { return new ResParser(false); } @Override public @NotNull IFileElementType getFileNodeType() { return ResFileStubElementType.INSTANCE; } public @NotNull PsiFile createFile(@NotNull FileViewProvider viewProvider) { return viewProvider.getFileType() instanceof ResInterfaceFileType ? new ResInterfaceFile(viewProvider) : new ResFile(viewProvider); } public @NotNull SpaceRequirements spaceExistenceTypeBetweenTokens(ASTNode left, ASTNode right) { return SpaceRequirements.MAY; } public @NotNull PsiElement createElement(@NotNull ASTNode node) { IElementType type = node.getElementType(); if (type instanceof ORStubElementType) { //noinspection rawtypes return ((ORStubElementType) node.getElementType()).createPsi(node); } throw new IllegalArgumentException("Not a Rescript node: " + node + " (" + type + ", " + type.getLanguage() + ")"); } } ================================================ FILE: src/main/java/com/reason/lang/rescript/ResSafeParserDefinition.java ================================================ package com.reason.lang.rescript; import com.intellij.lang.*; import com.intellij.openapi.project.*; import org.jetbrains.annotations.*; public class ResSafeParserDefinition extends ResParserDefinition { @Override public @NotNull PsiParser createParser(Project project) { return new ResParser(true); } } ================================================ FILE: src/main/java/com/reason/lang/rescript/ResTypes.java ================================================ package com.reason.lang.rescript; import com.reason.lang.core.stub.*; import com.reason.lang.core.type.*; public class ResTypes extends ORLangTypes { public static final ResTypes INSTANCE = new ResTypes(); private ResTypes() { // Stub element types C_CLASS_DECLARATION = (ORCompositeType) ResStubBasedElementTypes.C_CLASS_DECLARATION; C_CLASS_METHOD = (ORCompositeType) ResStubBasedElementTypes.C_CLASS_METHOD; C_EXCEPTION_DECLARATION = (ORCompositeType) ResStubBasedElementTypes.C_EXCEPTION_DECLARATION; C_EXTERNAL_DECLARATION = (ORCompositeType) ResStubBasedElementTypes.C_EXTERNAL_DECLARATION; C_FUNCTOR_DECLARATION = (ORCompositeType) ResStubBasedElementTypes.C_FUNCTOR_DECLARATION; C_INCLUDE = (ORCompositeType) ResStubBasedElementTypes.C_INCLUDE; C_LET_DECLARATION = (ORCompositeType) ResStubBasedElementTypes.C_LET_DECLARATION; C_MODULE_DECLARATION = (ORCompositeType) ResStubBasedElementTypes.C_MODULE_DECLARATION; C_OBJECT_FIELD = (ORCompositeType) ResStubBasedElementTypes.C_OBJECT_FIELD; C_OPEN = (ORCompositeType) ResStubBasedElementTypes.C_OPEN; C_PARAM_DECLARATION = (ORCompositeType) ResStubBasedElementTypes.C_PARAM_DECLARATION; C_RECORD_FIELD = (ORCompositeType) ResStubBasedElementTypes.C_RECORD_FIELD; C_TYPE_DECLARATION = (ORCompositeType) ResStubBasedElementTypes.C_TYPE_DECLARATION; C_VAL_DECLARATION = (ORCompositeType) ResStubBasedElementTypes.C_VAL_DECLARATION; C_VARIANT_DECLARATION = (ORCompositeType) ResStubBasedElementTypes.C_VARIANT_DECLARATION; // Composite element types C_ANNOTATION = new ORCompositeElementType("C_ANNOTATION", ResLanguage.INSTANCE); C_ARRAY = new ORCompositeElementType("C_ARRAY", ResLanguage.INSTANCE); C_ASSERT_STMT = new ORCompositeElementType("C_ASSERT_STMT", ResLanguage.INSTANCE); C_BINARY_CONDITION = new ORCompositeElementType("C_BINARY_CONDITION", ResLanguage.INSTANCE); C_CLASS_CONSTR = new ORCompositeElementType("C_CLASS_CONSTR", ResLanguage.INSTANCE); C_CLASS_FIELD = new ORCompositeElementType("C_CLASS_FIELD", ResLanguage.INSTANCE); C_CLASS_INITIALIZER = new ORCompositeElementType("C_CLASS_INITIALIZER", ResLanguage.INSTANCE); C_CLOSED_VARIANT = new ORCompositeElementType("C_CLOSED_VARIANT", ResLanguage.INSTANCE); C_CONSTRAINTS = new ORCompositeElementType("C_CONSTRAINTS", ResLanguage.INSTANCE); C_TYPE_CONSTRAINT = new ORCompositeElementType("C_TYPE_CONSTRAINT", ResLanguage.INSTANCE); C_CUSTOM_OPERATOR = new ORCompositeElementType("C_CUSTOM_OPERATOR", ResLanguage.INSTANCE); C_DECONSTRUCTION = new ORCompositeElementType("C_DECONSTRUCTION", ResLanguage.INSTANCE); C_DEFAULT_VALUE = new ORCompositeElementType("C_DEFAULT_VALUE", ResLanguage.INSTANCE); C_DIRECTIVE = new ORCompositeElementType("C_DIRECTIVE", ResLanguage.INSTANCE); C_DO_LOOP = new ORCompositeElementType("C_DO_LOOP", ResLanguage.INSTANCE); C_FIRST_CLASS = new ORCompositeElementType("C_FIRST_CLASS", ResLanguage.INSTANCE); C_FOR_LOOP = new ORCompositeElementType("C_FOR_LOOP", ResLanguage.INSTANCE); C_FIELD_VALUE = new ORCompositeElementType("C_FIELD_VALUE", ResLanguage.INSTANCE); C_FUN_EXPR = new ORCompositeElementType("C_FUN_EXPR", ResLanguage.INSTANCE); C_FUNCTION_BODY = new ORCompositeElementType("C_FUNCTION_BODY", ResLanguage.INSTANCE); C_FUNCTION_CALL = new ORCompositeElementType("C_FUNCTION_CALL", ResLanguage.INSTANCE); C_FUNCTION_EXPR = new ORCompositeElementType("C_FUNCTION_EXPR", ResLanguage.INSTANCE); C_FUNCTOR_BINDING = new ORCompositeElementType("C_FUNCTOR_BINDING", ResLanguage.INSTANCE); C_FUNCTOR_CALL = new ORCompositeElementType("C_FUNCTOR_CALL", ResLanguage.INSTANCE); C_FUNCTOR_RESULT = new ORCompositeElementType("C_FUNCTOR_RESULT", ResLanguage.INSTANCE); C_GUARD = new ORCompositeElementType("C_GUARD", ResLanguage.INSTANCE); C_IF = new ORCompositeElementType("C_IF", ResLanguage.INSTANCE); C_IF_THEN_ELSE = new ORCompositeElementType("C_IF_THEN_ELSE", ResLanguage.INSTANCE); C_INHERIT = new ORCompositeElementType("C_INHERIT", ResLanguage.INSTANCE); C_INTERPOLATION_EXPR = new ORCompositeElementType("C_INTERPOLATION_EXPR", ResLanguage.INSTANCE); C_INTERPOLATION_PART = new ORCompositeElementType("C_INTERPOLATION_PART", ResLanguage.INSTANCE); C_INTERPOLATION_REF = new ORCompositeElementType("C_INTERPOLATION_REF", ResLanguage.INSTANCE); C_JS_OBJECT = new ORCompositeElementType("C_JS_OBJECT", ResLanguage.INSTANCE); C_LET_ATTR = new ORCompositeElementType("C_LET_ATTR", ResLanguage.INSTANCE); C_LET_BINDING = new ORCompositeElementType("C_LET_BINDING", ResLanguage.INSTANCE); C_LOCAL_OPEN = new ORCompositeElementType("C_LOCAL_OPEN", ResLanguage.INSTANCE); C_TYPE_VARIABLE = new ORCompositeElementType("C_TYPE_VARIABLE", ResLanguage.INSTANCE); C_MACRO_EXPR = new ORCompositeElementType("C_MACRO_EXPR", ResLanguage.INSTANCE); C_MACRO_NAME = new ORCompositeElementType("C_MACRO_NAME", ResLanguage.INSTANCE); C_MACRO_BODY = new ORCompositeElementType("C_MACRO_RAW_BODY", ResLanguage.INSTANCE); C_METHOD_CALL = new ORCompositeElementType("C_METHOD_CALL", ResLanguage.INSTANCE); C_MIXIN_FIELD = new ORCompositeElementType("C_MIXIN_FIELD", ResLanguage.INSTANCE); C_MODULE_BINDING = new ORCompositeElementType("C_MODULE_BINDING", ResLanguage.INSTANCE); C_MODULE_SIGNATURE = new ORCompositeElementType("C_MODULE_SIGNATURE", ResLanguage.INSTANCE); C_ML_INTERPOLATOR = new ORCompositeElementType("C_ML_INTERPOLATOR", ResLanguage.INSTANCE); C_NAMED_PARAM = new ORCompositeElementType("C_NAMED_PARAM", ResLanguage.INSTANCE); C_NONE = new ORCompositeElementType("C_NONE", ResLanguage.INSTANCE); C_OBJECT = new ORCompositeElementType("C_OBJECT", ResLanguage.INSTANCE); C_OPEN_VARIANT = new ORCompositeElementType("C_OPEN_VARIANT", ResLanguage.INSTANCE); C_OPTION = new ORCompositeElementType("C_OPTION", ResLanguage.INSTANCE); C_LOWER_NAME = new ORCompositeElementType("C_LOWER_NAME", ResLanguage.INSTANCE); C_PARAM = new ORCompositeElementType("C_PARAM", ResLanguage.INSTANCE); C_PARAMETERS = new ORCompositeElementType("C_PARAMETERS", ResLanguage.INSTANCE); C_PATTERN_MATCH_BODY = new ORCompositeElementType("C_PATTERN_MATCH_BODY", ResLanguage.INSTANCE); C_PATTERN_MATCH_EXPR = new ORCompositeElementType("C_PATTERN_MATCH_EXPR", ResLanguage.INSTANCE); C_RECORD_EXPR = new ORCompositeElementType("C_RECORD_EXPR", ResLanguage.INSTANCE); C_SIG_EXPR = new ORCompositeElementType("C_SIG_EXPR", ResLanguage.INSTANCE); C_SIG_ITEM = new ORCompositeElementType("C_SIG_ITEM", ResLanguage.INSTANCE); C_SOME = new ORCompositeElementType("C_SOME", ResLanguage.INSTANCE); C_SCOPED_EXPR = new ORCompositeElementType("C_SCOPED_EXPR", ResLanguage.INSTANCE); C_STRUCT_EXPR = new ORCompositeElementType("C_STRUCT_EXPR", ResLanguage.INSTANCE); C_SWITCH_BODY = new ORCompositeElementType("C_SWITCH_BODY", ResLanguage.INSTANCE); C_SWITCH_EXPR = new ORCompositeElementType("C_SWITCH_EXPR", ResLanguage.INSTANCE); C_TAG = new ORCompositeElementType("C_TAG", ResLanguage.INSTANCE); C_TAG_PROP_VALUE = new ORCompositeElementType("C_TAG_PROP_VALUE", ResLanguage.INSTANCE); C_TAG_BODY = new ORCompositeElementType("C_TAG_BODY", ResLanguage.INSTANCE); C_TAG_START = new ORCompositeElementType("C_TAG_START", ResLanguage.INSTANCE); C_TAG_CLOSE = new ORCompositeElementType("C_TAG_CLOSE", ResLanguage.INSTANCE); C_TAG_PROPERTY = new ORCompositeElementType("C_TAG_PROPERTY", ResLanguage.INSTANCE); C_TERNARY = new ORCompositeElementType("C_TERNARY", ResLanguage.INSTANCE); C_TRY_EXPR = new ORCompositeElementType("C_TRY_EXPR", ResLanguage.INSTANCE); C_TRY_BODY = new ORCompositeElementType("C_TRY_BODY", ResLanguage.INSTANCE); C_TRY_HANDLER = new ORCompositeElementType("C_TRY_HANDLER", ResLanguage.INSTANCE); C_TRY_HANDLER_BODY = new ORCompositeElementType("C_TRY_HANDLER_BODY", ResLanguage.INSTANCE); C_TRY_HANDLERS = new ORCompositeElementType("C_TRY_HANDLERS", ResLanguage.INSTANCE); C_TUPLE = new ORCompositeElementType("C_TUPLE", ResLanguage.INSTANCE); C_TYPE_BINDING = new ORCompositeElementType("C_TYPE_BINDING", ResLanguage.INSTANCE); C_UNIT = new ORCompositeElementType("C_UNIT", ResLanguage.INSTANCE); C_UNPACK = new ORCompositeElementType("C_UNPACK", ResLanguage.INSTANCE); C_VARIANT_CONSTRUCTOR = new ORCompositeElementType("C_VARIANT_CONSTRUCTOR", ResLanguage.INSTANCE); C_WHILE = new ORCompositeElementType("C_WHILE", ResLanguage.INSTANCE); // Atom types CA_LOWER_SYMBOL = new ORCompositeElementType("CA_LOWER_SYMBOL", ResLanguage.INSTANCE); CA_UPPER_SYMBOL = new ORCompositeElementType("CA_UPPER_SYMBOL", ResLanguage.INSTANCE); A_LOWER_TAG_NAME = new ORTokenElementType("A_LOWER_TAG_NAME", ResLanguage.INSTANCE); A_UPPER_TAG_NAME = new ORTokenElementType("A_UPPER_TAG_NAME", ResLanguage.INSTANCE); A_VARIANT_NAME = new ORTokenElementType("A_VARIANT_NAME", ResLanguage.INSTANCE); A_MODULE_NAME = new ORTokenElementType("A_MODULE_NAME", ResLanguage.INSTANCE); A_EXCEPTION_NAME = new ORTokenElementType("A_EXCEPTION_NAME", ResLanguage.INSTANCE); // Dummy types H_ATOM = new ORCompositeElementType("H_ATOM", ResLanguage.INSTANCE); H_PLACE_HOLDER = new ORCompositeElementType("H_PLACE_HOLDER", ResLanguage.INSTANCE); H_COLLECTION_ITEM = new ORCompositeElementType("H_COLLECTION_ITEM", ResLanguage.INSTANCE); H_NAMED_PARAM_DECLARATION = new ORCompositeElementType("H_NAMED_PARAM_DECLARATION", ResLanguage.INSTANCE); // Token element types from lexer ASYNC = new ORTokenElementType("ASYNC", ResLanguage.INSTANCE); AWAIT = new ORTokenElementType("AWAIT", ResLanguage.INSTANCE); AND = new ORTokenElementType("AND", ResLanguage.INSTANCE); L_AND = new ORTokenElementType("L_AND", ResLanguage.INSTANCE); L_OR = new ORTokenElementType("L_OR", ResLanguage.INSTANCE); ARROBASE = new ORTokenElementType("ARROBASE", ResLanguage.INSTANCE); ARROBASE_2 = new ORTokenElementType("ARROBASE_2", ResLanguage.INSTANCE); ARROBASE_3 = new ORTokenElementType("ARROBASE_3", ResLanguage.INSTANCE); ARROW = new ORTokenElementType("ARROW", ResLanguage.INSTANCE); ASSERT = new ORTokenElementType("ASSERT", ResLanguage.INSTANCE); AS = new ORTokenElementType("AS", ResLanguage.INSTANCE); BACKSLASH = new ORTokenElementType("BACKSLASH", ResLanguage.INSTANCE); BOOL_VALUE = new ORTokenElementType("BOOL_VALUE", ResLanguage.INSTANCE); CATCH = new ORTokenElementType("CATCH", ResLanguage.INSTANCE); CHAR_VALUE = new ORTokenElementType("CHAR_VALUE", ResLanguage.INSTANCE); PIPE_FIRST = new ORTokenElementType("FAST_PIPE", ResLanguage.INSTANCE); FLOAT_VALUE = new ORTokenElementType("FLOAT_VALUE", ResLanguage.INSTANCE); FUNCTION = new ORTokenElementType("FUNCTION", ResLanguage.INSTANCE); FUN = new ORTokenElementType("FUN", ResLanguage.INSTANCE); FUNCTOR = new ORTokenElementType("FUNCTOR", ResLanguage.INSTANCE); INT_VALUE = new ORTokenElementType("INT_VALUE", ResLanguage.INSTANCE); PROPERTY_NAME = new ORTokenElementType("PROPERTY_NAME", ResLanguage.INSTANCE); STRING_VALUE = new ORTokenElementType("STRING_VALUE", ResLanguage.INSTANCE); SWITCH = new ORTokenElementType("SWITCH", ResLanguage.INSTANCE); IF = new ORTokenElementType("IF", ResLanguage.INSTANCE); BACKTICK = new ORTokenElementType("BACKTICK", ResLanguage.INSTANCE); BEGIN = new ORTokenElementType("BEGIN", ResLanguage.INSTANCE); CARRET = new ORTokenElementType("CARRET", ResLanguage.INSTANCE); COLON = new ORTokenElementType("COLON", ResLanguage.INSTANCE); COMMA = new ORTokenElementType("COMMA", ResLanguage.INSTANCE); SINGLE_COMMENT = new ORTokenElementType("SINGLE_COMMENT", ResLanguage.INSTANCE); MULTI_COMMENT = new ORTokenElementType("MULTI_COMMENT", ResLanguage.INSTANCE); DIFF = new ORTokenElementType("DIFF", ResLanguage.INSTANCE); DIRECTIVE_IF = new ORTokenElementType("DIRECTIVE_IF", ResLanguage.INSTANCE); DIRECTIVE_ELSE = new ORTokenElementType("DIRECTIVE_ELSE", ResLanguage.INSTANCE); DIRECTIVE_ELIF = new ORTokenElementType("DIRECTIVE_ELIF", ResLanguage.INSTANCE); DIRECTIVE_END = new ORTokenElementType("DIRECTIVE_END", ResLanguage.INSTANCE); DIRECTIVE_ENDIF = new ORTokenElementType("DIRECTIVE_ENDIF", ResLanguage.INSTANCE); LT_OR_EQUAL = new ORTokenElementType("LT_OR_EQUAL", ResLanguage.INSTANCE); GT_OR_EQUAL = new ORTokenElementType("GT_OR_EQUAL", ResLanguage.INSTANCE); DOLLAR = new ORTokenElementType("DOLLAR", ResLanguage.INSTANCE); DOT = new ORTokenElementType("DOT", ResLanguage.INSTANCE); DOTDOTDOT = new ORTokenElementType("DOTDOTDOT", ResLanguage.INSTANCE); DO = new ORTokenElementType("DO", ResLanguage.INSTANCE); DONE = new ORTokenElementType("DONE", ResLanguage.INSTANCE); EOL = new ORTokenElementType("EOL", ResLanguage.INSTANCE); ELSE = new ORTokenElementType("ELSE", ResLanguage.INSTANCE); END = new ORTokenElementType("END", ResLanguage.INSTANCE); NOT_EQ = new ORTokenElementType("EQ", ResLanguage.INSTANCE); NOT_EQEQ = new ORTokenElementType("EQEQ", ResLanguage.INSTANCE); EQ = new ORTokenElementType("EQ", ResLanguage.INSTANCE); EQEQ = new ORTokenElementType("EQEQ", ResLanguage.INSTANCE); EQEQEQ = new ORTokenElementType("EQEQEQ", ResLanguage.INSTANCE); EXCEPTION = new ORTokenElementType("EXCEPTION", ResLanguage.INSTANCE); EXCLAMATION_MARK = new ORTokenElementType("EXCLAMATION_MARK", ResLanguage.INSTANCE); EXTERNAL = new ORTokenElementType("EXTERNAL", ResLanguage.INSTANCE); FOR = new ORTokenElementType("FOR", ResLanguage.INSTANCE); TYPE_ARGUMENT = new ORTokenElementType("TYPE_ARGUMENT", ResLanguage.INSTANCE); GT = new ORTokenElementType("GT", ResLanguage.INSTANCE); IN = new ORTokenElementType("IN", ResLanguage.INSTANCE); LAZY = new ORTokenElementType("LAZY", ResLanguage.INSTANCE); INCLUDE = new ORTokenElementType("INCLUDE", ResLanguage.INSTANCE); LARRAY = new ORTokenElementType("LARRAY", ResLanguage.INSTANCE); LBRACE = new ORTokenElementType("LBRACE", ResLanguage.INSTANCE); LBRACKET = new ORTokenElementType("LBRACKET", ResLanguage.INSTANCE); LET = new ORTokenElementType("LET", ResLanguage.INSTANCE); LIST = new ORTokenElementType("LIST", ResLanguage.INSTANCE); LIDENT = new ORTokenElementType("LIDENT", ResLanguage.INSTANCE); LPAREN = new ORTokenElementType("LPAREN", ResLanguage.INSTANCE); LT = new ORTokenElementType("LT", ResLanguage.INSTANCE); MATCH = new ORTokenElementType("MATCH", ResLanguage.INSTANCE); MINUS = new ORTokenElementType("MINUS", ResLanguage.INSTANCE); MINUSDOT = new ORTokenElementType("MINUSDOT", ResLanguage.INSTANCE); MODULE = new ORTokenElementType("MODULE", ResLanguage.INSTANCE); MUTABLE = new ORTokenElementType("MUTABLE", ResLanguage.INSTANCE); NONE = new ORTokenElementType("NONE", ResLanguage.INSTANCE); OF = new ORTokenElementType("OF", ResLanguage.INSTANCE); OPEN = new ORTokenElementType("OPEN", ResLanguage.INSTANCE); OPTION = new ORTokenElementType("OPTION", ResLanguage.INSTANCE); POLY_VARIANT = new ORTokenElementType("POLY_VARIANT", ResLanguage.INSTANCE); PIPE = new ORTokenElementType("PIPE", ResLanguage.INSTANCE); PIPE_FORWARD = new ORTokenElementType("PIPE_FORWARD", ResLanguage.INSTANCE); PLUS = new ORTokenElementType("PLUS", ResLanguage.INSTANCE); PERCENT = new ORTokenElementType("PERCENT", ResLanguage.INSTANCE); PLUSDOT = new ORTokenElementType("PLUSDOT", ResLanguage.INSTANCE); QUESTION_MARK = new ORTokenElementType("QUESTION_MARK", ResLanguage.INSTANCE); SINGLE_QUOTE = new ORTokenElementType("SINGLE_QUOTE", ResLanguage.INSTANCE); DOUBLE_QUOTE = new ORTokenElementType("DOUBLE_QUOTE", ResLanguage.INSTANCE); RAISE = new ORTokenElementType("RAISE", ResLanguage.INSTANCE); RARRAY = new ORTokenElementType("RARRAY", ResLanguage.INSTANCE); RBRACE = new ORTokenElementType("RBRACE", ResLanguage.INSTANCE); RBRACKET = new ORTokenElementType("RBRACKET", ResLanguage.INSTANCE); REC = new ORTokenElementType("REC", ResLanguage.INSTANCE); REF = new ORTokenElementType("REF", ResLanguage.INSTANCE); RPAREN = new ORTokenElementType("RPAREN", ResLanguage.INSTANCE); SEMI = new ORTokenElementType("SEMI", ResLanguage.INSTANCE); SIG = new ORTokenElementType("SIG", ResLanguage.INSTANCE); SHARP = new ORTokenElementType("SHARP", ResLanguage.INSTANCE); SHARPSHARP = new ORTokenElementType("SHARPSHARP", ResLanguage.INSTANCE); SHORTCUT = new ORTokenElementType("SHORTCUT", ResLanguage.INSTANCE); SLASH = new ORTokenElementType("SLASH", ResLanguage.INSTANCE); SLASH_2 = new ORTokenElementType("SLASH_2", ResLanguage.INSTANCE); SLASHDOT = new ORTokenElementType("SLASHDOT", ResLanguage.INSTANCE); SOME = new ORTokenElementType("SOME", ResLanguage.INSTANCE); STAR = new ORTokenElementType("STAR", ResLanguage.INSTANCE); STARDOT = new ORTokenElementType("STARDOT", ResLanguage.INSTANCE); STRING_CONCAT = new ORTokenElementType("STRING_CONCAT", ResLanguage.INSTANCE); STRUCT = new ORTokenElementType("STRUCT", ResLanguage.INSTANCE); OP_STRUCT_DIFF = new ORTokenElementType("OP_STRUCT_DIFF", ResLanguage.INSTANCE); TAG_AUTO_CLOSE = new ORTokenElementType("TAG_AUTO_CLOSE", ResLanguage.INSTANCE); TAG_LT_SLASH = new ORTokenElementType("TAG_LT_SLASH", ResLanguage.INSTANCE); TILDE = new ORTokenElementType("TILDE", ResLanguage.INSTANCE); TO = new ORTokenElementType("TO", ResLanguage.INSTANCE); THEN = new ORTokenElementType("THEN", ResLanguage.INSTANCE); TRY = new ORTokenElementType("TRY", ResLanguage.INSTANCE); TYPE = new ORTokenElementType("TYPE", ResLanguage.INSTANCE); UNPACK = new ORTokenElementType("UNPACK", ResLanguage.INSTANCE); UIDENT = new ORTokenElementType("UIDENT", ResLanguage.INSTANCE); UNIT = new ORTokenElementType("UNIT", ResLanguage.INSTANCE); VAL = new ORTokenElementType("VAL", ResLanguage.INSTANCE); PUB = new ORTokenElementType("PUB", ResLanguage.INSTANCE); PRI = new ORTokenElementType("PRI", ResLanguage.INSTANCE); WHEN = new ORTokenElementType("WHEN", ResLanguage.INSTANCE); WHILE = new ORTokenElementType("WHILE", ResLanguage.INSTANCE); WITH = new ORTokenElementType("WITH", ResLanguage.INSTANCE); RAW = new ORTokenElementType("RAW", ResLanguage.INSTANCE); ASR = new ORTokenElementType("ASR", ResLanguage.INSTANCE); CLASS = new ORTokenElementType("CLASS", ResLanguage.INSTANCE); CONSTRAINT = new ORTokenElementType("CONSTRAINT", ResLanguage.INSTANCE); DOWNTO = new ORTokenElementType("DOWNTO", ResLanguage.INSTANCE); INHERIT = new ORTokenElementType("INHERIT", ResLanguage.INSTANCE); INITIALIZER = new ORTokenElementType("INITIALIZER", ResLanguage.INSTANCE); LAND = new ORTokenElementType("LAND", ResLanguage.INSTANCE); LOR = new ORTokenElementType("LOR", ResLanguage.INSTANCE); LSL = new ORTokenElementType("LSL", ResLanguage.INSTANCE); LSR = new ORTokenElementType("LSR", ResLanguage.INSTANCE); LXOR = new ORTokenElementType("LXOR", ResLanguage.INSTANCE); METHOD = new ORTokenElementType("METHOD", ResLanguage.INSTANCE); MOD = new ORTokenElementType("MOD", ResLanguage.INSTANCE); NEW = new ORTokenElementType("NEW", ResLanguage.INSTANCE); NONREC = new ORTokenElementType("NONREC", ResLanguage.INSTANCE); OR = new ORTokenElementType("OR", ResLanguage.INSTANCE); PRIVATE = new ORTokenElementType("PRIVATE", ResLanguage.INSTANCE); VIRTUAL = new ORTokenElementType("VIRTUAL", ResLanguage.INSTANCE); COLON_EQ = new ORTokenElementType("COLON_EQ", ResLanguage.INSTANCE); COLON_GT = new ORTokenElementType("COLON_GT", ResLanguage.INSTANCE); DOTDOT = new ORTokenElementType("DOTDOT", ResLanguage.INSTANCE); SEMISEMI = new ORTokenElementType("SEMISEMI", ResLanguage.INSTANCE); GT_BRACKET = new ORTokenElementType("GT_BRACKET", ResLanguage.INSTANCE); GT_BRACE = new ORTokenElementType("GT_BRACE", ResLanguage.INSTANCE); LEFT_ARROW = new ORTokenElementType("LEFT_ARROW", ResLanguage.INSTANCE); RIGHT_ARROW = new ORTokenElementType("RIGHT_ARROW", ResLanguage.INSTANCE); OBJECT = new ORTokenElementType("OBJECT", ResLanguage.INSTANCE); RECORD = new ORTokenElementType("RECORD", ResLanguage.INSTANCE); AMPERSAND = new ORTokenElementType("AMPERSAND", ResLanguage.INSTANCE); BRACKET_GT = new ORTokenElementType("BRACKET_GT", ResLanguage.INSTANCE); BRACKET_LT = new ORTokenElementType("BRACKET_LT", ResLanguage.INSTANCE); BRACE_LT = new ORTokenElementType("BRACE_LT", ResLanguage.INSTANCE); ML_STRING_VALUE = new ORTokenElementType("ML_STRING_VALUE", ResLanguage.INSTANCE); ML_STRING_OPEN = new ORTokenElementType("ML_STRING_OPEN", ResLanguage.INSTANCE); ML_STRING_CLOSE = new ORTokenElementType("ML_STRING_CLOSE", ResLanguage.INSTANCE); JS_STRING_OPEN = new ORTokenElementType("JS_STRING_OPEN", ResLanguage.INSTANCE); JS_STRING_CLOSE = new ORTokenElementType("JS_STRING_CLOSE", ResLanguage.INSTANCE); UNDERSCORE = new ORTokenElementType("UNDERSCORE", ResLanguage.INSTANCE); } } ================================================ FILE: src/main/java/com/reason/lang/rescript/Rescript.flex ================================================ package com.reason.lang.rescript; import com.intellij.lexer.*; import com.intellij.psi.tree.*; import com.reason.lang.core.type.*; import static com.intellij.psi.TokenType.*; @SuppressWarnings("ALL") %% %{ public ResFlexLexer(ORLangTypes types) { this.types = types; } private ORLangTypes types; private int tokenStartIndex; private CharSequence quotedStringId; private int commentDepth; private boolean inCommentString = false; //Store the start index of a token private void tokenStart() { tokenStartIndex = zzStartRead; } //Set the start index of the token to the stored index private void tokenEnd() { zzStartRead = tokenStartIndex; } %} %public %class ResFlexLexer %implements FlexLexer %unicode %function advance %type IElementType EOL=\n|\r|\r\n WHITE_SPACE_CHAR=[\ \t\f] WHITE_SPACE={WHITE_SPACE_CHAR}+ LOWERCASE=[a-z_] UPPERCASE=[A-Z] IDENTCHAR=[A-Za-z_0-9'] DECIMAL=[0-9] DECIMAL_SEP=[0-9_] HEXA=[0-9A-Fa-f] HEXA_SEP=[0-9A-Fa-f_] OCTAL=[0-7] OCTAL_SEP=[0-7_] DECIMAL_LITERAL={DECIMAL} {DECIMAL_SEP}* HEXA_LITERAL="0" [xX] {HEXA} {HEXA_SEP}* OCT_LITERAL="0" [oO] {OCTAL} {OCTAL_SEP}* BIN_LITERAL="0" [bB] [0-1] [0-1_]* INT_LITERAL= {DECIMAL_LITERAL} | {HEXA_LITERAL} | {OCT_LITERAL} | {BIN_LITERAL} FLOAT_LITERAL={DECIMAL} {DECIMAL_SEP}* ("." {DECIMAL_SEP}* )? ([eE] [+-]? {DECIMAL} {DECIMAL_SEP}* )? HEXA_FLOAT_LITERAL="0" [xX] {HEXA} {HEXA_SEP}* ("." {HEXA_SEP}* )? ([pP] [+-]? {DECIMAL} {DECIMAL_SEP}* )? LITERAL_MODIFIER=[G-Zg-z] LIDENT={LOWERCASE}{IDENTCHAR}* ESCAPE_BACKSLASH="\\\\" ESCAPE_SINGLE_QUOTE="\\'" ESCAPE_LF="\\n" ESCAPE_TAB="\\t" ESCAPE_BACKSPACE="\\b" ESCAPE_CR="\\r" ESCAPE_QUOTE="\\\"" ESCAPE_DECIMAL="\\" {DECIMAL} {DECIMAL} {DECIMAL} ESCAPE_HEXA="\\x" {HEXA} {HEXA} ESCAPE_OCTAL="\\o" [0-3] {OCTAL} {OCTAL} ESCAPE_CHAR= {ESCAPE_BACKSLASH} | {ESCAPE_SINGLE_QUOTE} | {ESCAPE_LF} | {ESCAPE_TAB} | {ESCAPE_BACKSPACE } | { ESCAPE_CR } | { ESCAPE_QUOTE } | {ESCAPE_DECIMAL} | {ESCAPE_HEXA} | {ESCAPE_OCTAL} %state INITIAL %state IN_LOWER_DECLARATION %state IN_TEMPLATE %state IN_STRING %state IN_ML_COMMENT %state IN_SL_COMMENT %% { [^] { yybegin(INITIAL); yypushback(1); } } { {WHITE_SPACE} { return WHITE_SPACE; } {EOL} { return types.EOL; } "async" { return types.ASYNC; } "await" { return types.AWAIT; } "and" { return types.AND; } "as" { return types.AS; } "assert" { return types.ASSERT; } "begin" { return types.BEGIN; } "catch" { return types.CATCH; } "class" { return types.CLASS; } "constraint" { return types.CONSTRAINT; } "do" { return types.DO; } "done" { return types.DONE; } "downto" { return types.DOWNTO; } "else" { return types.ELSE; } "end" { return types.END; } "exception" { return types.EXCEPTION; } "external" { yybegin(IN_LOWER_DECLARATION); return types.EXTERNAL; } "for" { return types.FOR; } "functor" { return types.FUNCTOR; } "if" { return types.IF; } "in" { return types.IN; } "include" { return types.INCLUDE; } "inherit" { return types.INHERIT; } "initializer" { return types.INITIALIZER; } "lazy" { return types.LAZY; } "let" { yybegin(IN_LOWER_DECLARATION); return types.LET; } "list" { return types.LIST; } "module" { return types.MODULE;} "mutable" { return types.MUTABLE; } "new" { return types.NEW; } "nonrec" { return types.NONREC; } "object" { return types.OBJECT; } "of" { return types.OF; } "open" { return types.OPEN; } "or" { return types.OR; } "pub" { return types.PUB; } "pri" { return types.PRI; } "raw" { return types.RAW; } "rec" { return types.REC; } "sig" { return types.SIG; } "struct" { return types.STRUCT; } "switch" { return types.SWITCH; } "then" { return types.THEN; } "try" { return types.TRY; } "type" { return types.TYPE; } "unpack" { return types.UNPACK; } "val" { return types.VAL; } "virtual" { return types.VIRTUAL; } "when" { return types.WHEN; } "while" { return types.WHILE; } "with" { return types.WITH; } "mod" { return types.MOD; } "land" { return types.LAND; } "lor" { return types.LOR; } "lxor" { return types.LXOR; } "lsl" { return types.LSL; } "lsr" { return types.LSR; } "asr" { return types.ASR; } "unit" { return types.UNIT; } "ref" { return types.REF; } "raise" { return types.RAISE; } "method" { return types.METHOD; } "private" { return types.PRIVATE; } "match" { return types.MATCH; } "option" { return types.OPTION; } "None" { return types.NONE; } "Some" { return types.SOME; } "false" { return types.BOOL_VALUE; } "true" { return types.BOOL_VALUE; } "_" { return types.UNDERSCORE; } "'" ( {ESCAPE_CHAR} | . ) "'" { return types.CHAR_VALUE; } {LIDENT} { return types.LIDENT; } {UPPERCASE}{IDENTCHAR}* { return types.UIDENT; } {INT_LITERAL}{LITERAL_MODIFIER}? { return types.INT_VALUE; } ({FLOAT_LITERAL} | {HEXA_FLOAT_LITERAL}){LITERAL_MODIFIER}? { return types.FLOAT_VALUE; } "'"{LOWERCASE}{IDENTCHAR}* { return types.TYPE_ARGUMENT; } "#"{UPPERCASE}{IDENTCHAR}* { return types.POLY_VARIANT; } "#"{LOWERCASE}{IDENTCHAR}* { return types.POLY_VARIANT; } "`" { yybegin(IN_TEMPLATE); return types.JS_STRING_OPEN; } "\"" { yybegin(IN_STRING); tokenStart(); } "/*" { yybegin(IN_ML_COMMENT); commentDepth = 1; tokenStart(); } "//" { yybegin(IN_SL_COMMENT); tokenStart(); } "++" { return types.STRING_CONCAT; } "##" { return types.SHARPSHARP; } "::" { return types.SHORTCUT; } "=>" { return types.ARROW; } "->" { return types.RIGHT_ARROW; } "<-" { return types.LEFT_ARROW; } "|>" { return types.PIPE_FORWARD; } "" { return types.TAG_AUTO_CLOSE; } "===" { return types.EQEQEQ; } "==" { return types.EQEQ; } "=" { return types.EQ; } "!==" { return types.NOT_EQEQ; } "!=" { return types.NOT_EQ; } ":=" { return types.COLON_EQ; } ":>" { return types.COLON_GT; } "<=" { return types.LT_OR_EQUAL; } //">=" { return types.GT_OR_EQUAL; } // Incompatible with type argument -> external x : (~props: Js.t<{..}>=?) "&&" { return types.L_AND; } "||" { return types.L_OR; } "," { return types.COMMA; } ":" { return types.COLON; } ";" { return types.SEMI; } "'" { return types.SINGLE_QUOTE; } "..." { return types.DOTDOTDOT; } ".." { return types.DOTDOT; } "." { return types.DOT; } "|" { return types.PIPE; } "(" { return types.LPAREN; } ")" { return types.RPAREN; } "{" { return types.LBRACE; } "}" { return types.RBRACE; } "[" { return types.LBRACKET; } "]" { return types.RBRACKET; } "@" { return types.ARROBASE; } "#" { return types.SHARP; } "?" { return types.QUESTION_MARK; } "!" { return types.EXCLAMATION_MARK; } "~" { return types.TILDE; } "&" { return types.AMPERSAND; } "<" { return types.LT; } ">" { return types.GT; } "\^" { return types.CARRET; } "+." { return types.PLUSDOT; } "-." { return types.MINUSDOT; } "/." { return types.SLASHDOT; } "*." { return types.STARDOT; } "+" { return types.PLUS; } "-" { return types.MINUS; } "/" { return types.SLASH; } "*" { return types.STAR; } "%" { return types.PERCENT; } "\\" { return types.BACKSLASH; } } { "rec" { return types.REC; } "\\" { return types.BACKSLASH; } "_" { return types.UNDERSCORE; } {LIDENT} { yybegin(INITIAL); return types.LIDENT; } {WHITE_SPACE} { return WHITE_SPACE; } {EOL} { return types.EOL; } <> { yybegin(INITIAL); } . { yybegin(INITIAL); yypushback(1); tokenEnd();} } { {WHITE_SPACE} { return WHITE_SPACE; } "$" { return types.DOLLAR; } "{" { return types.LBRACE; } "}" { return types.RBRACE; } "`" { yybegin(INITIAL); return types.JS_STRING_CLOSE; } {EOL} { } <> { yybegin(INITIAL); } ([^`{}$])+ { return types.STRING_VALUE; } } { "\"" { yybegin(INITIAL); tokenEnd(); return types.STRING_VALUE; } "\\" {EOL} ([ \t] *) { } "\\" [\\\'\"ntbr ] { } "\\" [0-9] [0-9] [0-9] { } "\\" "o" [0-3] [0-7] [0-7] { } "\\" "x" [0-9a-fA-F] [0-9a-fA-F] { } "\\" . { } {EOL} { } . { } <> { yybegin(INITIAL); tokenEnd(); return types.STRING_VALUE; } } { "/*" { if (!inCommentString) { commentDepth += 1; } } "*/" { if (!inCommentString) { commentDepth -= 1; if(commentDepth == 0) { yybegin(INITIAL); tokenEnd(); return types.MULTI_COMMENT; } } } "\"" { inCommentString = !inCommentString; } . | {EOL} { } <> { yybegin(INITIAL); tokenEnd(); return types.MULTI_COMMENT; } } { . { } {EOL} { yybegin(INITIAL); tokenEnd(); return types.SINGLE_COMMENT; } <> { yybegin(INITIAL); tokenEnd(); return types.SINGLE_COMMENT; } } [^] { return BAD_CHARACTER; } ================================================ FILE: src/main/java/jpsplugin/com/reason/AutoDeletingTempFile.java ================================================ package jpsplugin.com.reason; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.io.*; import org.jetbrains.annotations.*; import java.io.*; import java.nio.charset.*; public class AutoDeletingTempFile implements AutoCloseable { private final File myFile; public AutoDeletingTempFile(@NotNull String prefix, @NotNull String extension) throws IOException { myFile = FileUtilRt.createTempFile(prefix, extension, true); } public @NotNull String getPath() { return myFile.getPath(); } public void write(@NotNull String text, @NotNull Charset charset) throws IOException { FileUtil.writeToFile(myFile, text, charset); } @Override public void close() { FileUtilRt.delete(myFile); } } ================================================ FILE: src/main/java/jpsplugin/com/reason/FileUtil.java ================================================ package jpsplugin.com.reason; import com.intellij.openapi.vfs.VirtualFile; import java.io.*; import org.jetbrains.annotations.NotNull; public class FileUtil { private FileUtil() { } public static String readFileContent(@NotNull VirtualFile file) { try { return Streams.inputToString(file.getInputStream()); } catch (IOException e) { throw new UncheckedIOException(e); } } } ================================================ FILE: src/main/java/jpsplugin/com/reason/Interrupted.java ================================================ package jpsplugin.com.reason; public class Interrupted { private Interrupted() { } public static void sleep(int timeToWait) { try { Thread.sleep(timeToWait); } catch (InterruptedException e) { // Do nothing } } } ================================================ FILE: src/main/java/jpsplugin/com/reason/Joiner.java ================================================ package jpsplugin.com.reason; import org.jetbrains.annotations.*; import java.util.function.*; public class Joiner { private Joiner() { } @NotNull public static String join(@NotNull String separator, @Nullable Iterable items) { return join(separator, items, Object::toString); } @NotNull public static String join( @NotNull String separator, @Nullable Iterable items, @NotNull Function fn) { if (items == null) { return ""; } StringBuilder sb = new StringBuilder(); boolean first = true; for (T item : items) { if (!first) { sb.append(separator); } sb.append(fn.apply(item)); first = false; } return sb.toString(); } public static @NotNull String join(@NotNull String separator, @Nullable Object[] items) { if (items == null) { return ""; } StringBuilder sb = new StringBuilder(); boolean first = true; for (Object item : items) { if (!first) { sb.append(separator); } sb.append(item); first = false; } return sb.toString(); } } ================================================ FILE: src/main/java/jpsplugin/com/reason/Log.java ================================================ package jpsplugin.com.reason; import com.intellij.execution.process.*; import com.intellij.lang.*; import com.intellij.openapi.diagnostic.*; import com.intellij.openapi.module.Module; import com.intellij.openapi.project.*; import com.intellij.openapi.vfs.*; import com.intellij.psi.*; import com.intellij.psi.search.*; import org.jetbrains.annotations.*; import java.io.*; import java.nio.file.*; import java.util.*; public class Log { private static final String SEP = ": "; @NotNull private final Logger m_log; private Log(String name) { m_log = Logger.getInstance("ReasonML." + name); } @NotNull public static Log create(String name) { return new Log(name); } public boolean isDebugEnabled() { return m_log.isDebugEnabled(); } public boolean isTraceEnabled() { return m_log.isTraceEnabled(); } public void debug(String comment) { if (m_log.isDebugEnabled()) { m_log.debug(comment); } } public void debug(String msg, Project t) { if (m_log.isDebugEnabled()) { m_log.debug(msg + SEP + t); } } public void debug(String comment, Integer t) { if (m_log.isDebugEnabled()) { m_log.debug(comment + SEP + t); } } public void debug(String comment, int t) { if (m_log.isDebugEnabled()) { m_log.debug(comment + SEP + t); } } public void debug(String comment, long t) { if (m_log.isDebugEnabled()) { m_log.debug(comment + SEP + t); } } public void debug(String comment, int t, @Nullable Collection t1) { if (m_log.isDebugEnabled()) { m_log.debug( comment + SEP + t + (t1 != null && 1 == t1.size() ? " " + t1.iterator().next() : "")); } } public void debug(String comment, String t) { if (m_log.isDebugEnabled()) { m_log.debug(comment + SEP + t); } } public void debug(String comment, int t, String t1) { if (m_log.isDebugEnabled()) { m_log.debug(comment + SEP + t + " " + t1); } } public void debug(String comment, String t, String t1) { if (m_log.isDebugEnabled()) { m_log.debug(comment + SEP + t + " " + t1); } } public void debug(String comment, boolean t) { if (m_log.isDebugEnabled()) { m_log.debug(comment + SEP + t); } } public void debug(String comment, @Nullable PsiFile[] t) { if (m_log.isDebugEnabled()) { m_log.debug(comment + SEP + (t == null ? "" : t.length + " ")); } } public void debug(String comment, @Nullable Path t) { if (m_log.isDebugEnabled()) { m_log.debug(comment + SEP + (t == null ? "" : t + " ")); } } public void debug(String comment, @Nullable ResolveResult[] t) { if (m_log.isDebugEnabled()) { m_log.debug(comment + SEP + (t == null ? "" : t.length + " [" + Joiner.join(", ", t) + "]")); } } public void debug(@NotNull String comment, @Nullable PsiFile t) { if (m_log.isDebugEnabled()) { VirtualFile vFile = t == null ? null : t.getVirtualFile(); debug(comment, vFile == null ? " (" + t + ")" : vFile.toString()); } } public void debug(@NotNull String comment, @NotNull String t, @Nullable PsiFile t1) { if (m_log.isDebugEnabled()) { debug(comment + SEP + t, t1 == null ? null : t1.getVirtualFile()); } } public void debug(@NotNull String comment, @NotNull String t, @Nullable VirtualFile t1) { if (m_log.isDebugEnabled()) { debug(comment + SEP + t, t1); } } public void debug(@NotNull String comment, @Nullable VirtualFile t) { if (m_log.isDebugEnabled()) { m_log.debug(comment + SEP + (t == null ? "" : t.getCanonicalPath() + " ")); } } public void debug(@NotNull String comment, @Nullable File t) { if (m_log.isDebugEnabled()) { m_log.debug(comment + SEP + (t == null ? "" : t.getPath() + " ")); } } public void debug(String comment, @Nullable Collection t) { if (m_log.isDebugEnabled()) { m_log.debug( comment + SEP + (t == null ? "" : t.size() + " ") + "[" + Joiner.join(", ", t) + "]"); } } public void debug(String comment, @NotNull PsiQualifiedNamedElement element) { if (m_log.isDebugEnabled()) { m_log.debug( comment + SEP + element.getQualifiedName() + " (" + element.getContainingFile().getVirtualFile().getPath() + ")"); } } public void debug(String comment, @NotNull PsiQualifiedNamedElement element, int position) { if (m_log.isDebugEnabled()) { m_log.debug( comment + SEP + element.getQualifiedName() + " (" + element.getContainingFile().getVirtualFile().getPath() + ") pos=" + position); } } public void debug(String comment, String t, boolean t1) { if (m_log.isDebugEnabled()) { m_log.debug(comment + SEP + t + " " + t1); } } public void debug(@NotNull String comment, @NotNull Map map) { if (m_log.isDebugEnabled()) { StringBuilder sb = new StringBuilder(); boolean start = true; for (Map.Entry entry : map.entrySet()) { if (!start) { sb.append(", "); } sb.append(entry.getKey()).append(":").append(entry.getValue()); start = false; } m_log.debug(comment + SEP + "[" + sb + "]"); } } public void debug(String comment, String[] values) { if (m_log.isDebugEnabled()) { m_log.debug(comment + SEP + "[" + Joiner.join(", ", values) + "]"); } } public void debug(String msg, VirtualFile[] files) { if (m_log.isDebugEnabled()) { m_log.debug(msg + SEP + "[" + Joiner.join(", ", files) + "]"); } } public void debug(String msg, PsiElement element) { if (m_log.isDebugEnabled()) { m_log.debug(msg + SEP + element + (element instanceof PsiNamedElement ? ", name=[" + ((PsiNamedElement) element).getName() + "]" : "")); } } public void debug(String msg, String value, SearchScope scope, Project project) { if (m_log.isDebugEnabled()) { m_log.debug(msg + SEP + value + ", scope=[" + scope + "] in project " + project); } } public void info(String msg) { m_log.info(msg); } public void info(String msg, @NotNull Exception e) { m_log.info(msg); m_log.info(e); } public void info(String msg, @NotNull Map rootContents) { StringBuilder sb = new StringBuilder(); for (Map.Entry entry : rootContents.entrySet()) { sb.append("(module ") .append(entry.getKey()) .append(", file ") .append(entry.getValue()) .append(")"); } m_log.info(msg + SEP + sb); } public void warn(String msg) { m_log.warn(msg); } public void warn(@NotNull Exception e) { m_log.warn(e); } public void trace(String msg) { if (m_log.isTraceEnabled()) { m_log.trace(msg); } } public void trace(String comment, @Nullable Collection t) { if (m_log.isTraceEnabled()) { m_log.trace(comment + SEP + (t == null ? "" : t.size() + " ") + "[" + Joiner.join(", ", t) + "]"); } } public void trace(@NotNull String msg, @Nullable VirtualFile t) { if (m_log.isTraceEnabled()) { m_log.trace(msg + SEP + (t == null ? "" : t)); } } public void trace(@NotNull String comment, @Nullable PsiFile t) { if (m_log.isTraceEnabled()) { m_log.trace(comment + SEP + (t == null ? "" : t.getVirtualFile())); } } public void trace(String msg, String t) { if (m_log.isTraceEnabled()) { m_log.trace(msg + SEP + " " + t); } } public void trace(String msg, boolean t) { if (m_log.isTraceEnabled()) { m_log.trace(msg + SEP + " " + t); } } public void trace(String msg, Language t) { if (m_log.isTraceEnabled()) { m_log.trace(msg + SEP + t); } } public void trace(@NotNull String comment, @Nullable File t) { if (m_log.isTraceEnabled()) { m_log.trace(comment + SEP + (t == null ? "" : t.getPath() + " ")); } } public void trace(@NotNull String comment, @Nullable ProcessEvent t) { if (m_log.isTraceEnabled()) { m_log.trace(comment + SEP + (t == null ? "" : t + " ")); } } public void trace(@NotNull String comment, @NotNull PsiQualifiedNamedElement element) { if (m_log.isTraceEnabled()) { m_log.trace(comment + SEP + element.getQualifiedName() + " (" + element.getContainingFile().getVirtualFile().getPath() + ")"); } } public void trace(@NotNull String msg, @NotNull PsiElement element) { if (m_log.isTraceEnabled()) { m_log.trace(msg + SEP + element + (element instanceof PsiNamedElement ? ", name=[" + ((PsiNamedElement) element).getName() + "]" : "")); } } public void trace(String msg, PsiElement element, Project project, GlobalSearchScope scope) { if (m_log.isTraceEnabled()) { m_log.trace(msg + SEP + element + " <" + element.getClass().getSimpleName() + ">, project=[" + project.getName() + "], scope=[" + scope + "]"); } } public void error(String message, Exception e) { m_log.error(message, e); } public void error(String msg) { m_log.error(msg); } } ================================================ FILE: src/main/java/jpsplugin/com/reason/OCamlExecutable.java ================================================ package jpsplugin.com.reason; import com.intellij.execution.*; import com.intellij.execution.configurations.*; import com.intellij.execution.wsl.*; import com.intellij.openapi.util.*; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.impl.wsl.*; import com.intellij.util.containers.*; import org.jetbrains.annotations.*; import java.util.*; public abstract class OCamlExecutable { private static final Log LOG = Log.create("ocaml"); protected String myId; public static @NotNull OCamlExecutable getExecutable(@Nullable String opamRootPath, @Nullable String cygwinBash) { if (opamRootPath == null) { return new OCamlExecutable.Unknown(); } if (Platform.isWindows()) { Pair pair = OCamlExecutable.parseWslPath(opamRootPath.replace("/", "\\")); if (pair != null) { if (pair.second == null) { LOG.debug("Opam home not found", opamRootPath); return new OCamlExecutable.Unknown(); } else { return new OCamlExecutable.Wsl(pair.second); } } } return new OCamlExecutable.Local(cygwinBash); } public abstract @NotNull GeneralCommandLine patchCommandLine(@NotNull GeneralCommandLine commandLine, String pathToBinary, boolean login); // From GitExecutableManager @Nullable public static Pair parseWslPath(@NotNull String path) { if (!WSLUtil.isSystemCompatible()) { return null; } if (!path.startsWith(WslConstants.UNC_PREFIX)) { return null; } path = StringUtil.trimStart(path, WslConstants.UNC_PREFIX); int index = path.indexOf('\\'); if (index == -1) { return null; } String distName = path.substring(0, index); String wslPath = FileUtil.toSystemIndependentName(path.substring(index)); WSLDistribution distribution = WslDistributionManager.getInstance().getOrCreateDistributionByMsId(distName); return Pair.create(wslPath, distribution); } public static class Wsl extends OCamlExecutable { private final WSLDistribution m_distribution; public Wsl(@NotNull WSLDistribution distribution) { m_distribution = distribution; myId = "wsl-" + distribution.getId(); } @Override @NotNull public GeneralCommandLine patchCommandLine(@NotNull GeneralCommandLine commandLine, @Nullable String pathToBinary, boolean login) { try { return m_distribution.patchCommandLine(commandLine, null, new WSLCommandLineOptions()); } catch (ExecutionException e) { throw new IllegalStateException("Cannot patch command line for WSL", e); } } @Override public @NotNull String toString() { return m_distribution.getPresentableName(); } } public static class Local extends OCamlExecutable { private final String myCygwinBash; public Local(@Nullable String cygwinBash) { myId = "local"; myCygwinBash = cygwinBash; } @Override public @NotNull GeneralCommandLine patchCommandLine(@NotNull GeneralCommandLine commandLine, @Nullable String pathToBinary, boolean login) { ParametersList parametersList = commandLine.getParametersList(); List realParamsList = parametersList.getList(); boolean isCygwin = myCygwinBash != null; String extension = Platform.isWindows() && isCygwin ? Platform.WINDOWS_EXECUTABLE_SUFFIX : ""; String exe = commandLine.getExePath() + extension; String exePath = pathToBinary == null ? exe : pathToBinary + "/" + exe; if (isCygwin) { commandLine.setExePath(myCygwinBash); String bashParameters = StringUtil.join(ContainerUtil.prepend(realParamsList, exePath), CommandLineUtil::posixQuote, " "); parametersList.clearAll(); if (login) { parametersList.add("--login"); } parametersList.add("-c"); parametersList.add(bashParameters); } else { commandLine.setExePath(exePath); } LOG.debug("[" + myId + (isCygwin ? "/cygwin" : "") + "] " + "Patched as: " + commandLine.getCommandLineString()); return commandLine; } @Override public String toString() { return myId; } } public static class Unknown extends OCamlExecutable { public Unknown() { myId = "wsl-unknown"; } @Override public @NotNull GeneralCommandLine patchCommandLine(@NotNull GeneralCommandLine commandLine, String pathToBinary, boolean login) { throw new RuntimeException("Unknown WSL distribution"); } @Override public String toString() { return myId; } } } ================================================ FILE: src/main/java/jpsplugin/com/reason/OClSourcesOrderRootTypeUIFactory.java ================================================ package jpsplugin.com.reason; import com.intellij.icons.*; import com.intellij.openapi.fileChooser.*; import com.intellij.openapi.fileTypes.*; import com.intellij.openapi.progress.*; import com.intellij.openapi.projectRoots.*; import com.intellij.openapi.projectRoots.ui.*; import com.intellij.openapi.roots.*; import com.intellij.openapi.roots.libraries.*; import com.intellij.openapi.roots.libraries.ui.*; import com.intellij.openapi.roots.libraries.ui.impl.*; import com.intellij.openapi.roots.ui.*; import com.intellij.openapi.vfs.*; import com.intellij.util.containers.*; import org.jetbrains.annotations.*; import javax.swing.*; import java.awt.*; import java.util.List; import java.util.*; public class OClSourcesOrderRootTypeUIFactory implements OrderRootTypeUIFactory { @Override public SdkPathEditor createPathEditor(final Sdk sdk) { FileChooserDescriptor descriptor = new FileChooserDescriptor(true, true, false, false, false, true); return new OClSourcesOrderRootTypeUIFactory.SourcesPathEditor(descriptor); } @Override public @NotNull Icon getIcon() { return AllIcons.Nodes.Package; } @Override public @NotNull String getNodeText() { return "Sources"; } private static class SourcesPathEditor extends SdkPathEditor { SourcesPathEditor(@NotNull FileChooserDescriptor descriptor) { super("Sourcepath", OclSourcesOrderRootType.getInstance(), descriptor); } @Override protected @NotNull VirtualFile[] adjustAddedFileSet(final Component component, final VirtualFile[] files) { java.util.List orderRoots = RootDetectionUtil.detectRoots(Arrays.asList(files), component, null, new OCamlRootsDetector(), new OrderRootType[]{OclSourcesOrderRootType.getInstance()}); List result = new ArrayList<>(); for (OrderRoot root : orderRoots) { result.add(root.getFile()); } return VfsUtil.toVirtualFileArray(result); } private static class OCamlRootsDetector extends LibraryRootsDetector { @Override public @NotNull Collection detectRoots(@NotNull VirtualFile rootCandidate, @NotNull ProgressIndicator progressIndicator) { List result = new ArrayList<>(); OrderRootType OCAML_SOURCES = OclSourcesOrderRootType.getInstance(); Collection files = suggestOCamlRoots(rootCandidate, progressIndicator); for (VirtualFile file : files) { result.add(new DetectedLibraryRoot(file, OCAML_SOURCES, false)); } return result; } @NotNull List suggestOCamlRoots( @NotNull VirtualFile dir, @NotNull final ProgressIndicator progressIndicator) { if (!dir.isDirectory()) { return ContainerUtil.emptyList(); } final FileTypeManager typeManager = FileTypeManager.getInstance(); final ArrayList foundDirectories = new ArrayList<>(); try { VfsUtilCore.visitChildrenRecursively( dir, new VirtualFileVisitor() { @Override public @NotNull Result visitFileEx(@NotNull VirtualFile file) { progressIndicator.checkCanceled(); if (!file.isDirectory()) { FileType type = typeManager.getFileTypeByFileName(file.getName()); if (type.getDefaultExtension().equals("ml")) { VirtualFile root = file.getParent(); if (root != null) { foundDirectories.add(root); return skipTo(root); } } } return CONTINUE; } }); } catch (ProcessCanceledException ignore) { } return foundDirectories; } @Override public @Nullable String getRootTypeName(@NotNull LibraryRootType rootType) { if (OclSourcesOrderRootType.getInstance().equals(rootType.getType()) && !rootType.isJarDirectory()) { return "sources"; } return null; } } } } ================================================ FILE: src/main/java/jpsplugin/com/reason/ORNotification.java ================================================ package jpsplugin.com.reason; import com.intellij.notification.*; import com.reason.ide.*; import org.jetbrains.annotations.*; import javax.swing.*; import static com.intellij.notification.NotificationType.*; public final class ORNotification extends Notification { private static final String REASON_ML_GROUP_DISPLAY = "Reason"; public static void notifyError(@NotNull String title, @Nullable String subtitle, @NotNull String content) { Notifications.Bus.notify(new ORNotification(title, subtitle, content, ERROR)); } public ORNotification(@NotNull String title, @Nullable String subtitle, @NotNull String content, @NotNull NotificationType type) { super(REASON_ML_GROUP_DISPLAY, title, content, type); setIcon(getIcon(type)); setSubtitle(subtitle); } public ORNotification(@NotNull String title, @NotNull String content, @NotNull NotificationType type) { super(REASON_ML_GROUP_DISPLAY, title, content, type); setIcon(getIcon(type)); } @Override public @NotNull Icon getIcon() { return getIcon(getType()); } private static @NotNull Icon getIcon(@NotNull NotificationType type) { return type == NotificationType.INFORMATION ? ORIcons.RML_BLUE : (type == NotificationType.WARNING ? ORIcons.RML_YELLOW : ORIcons.RML_FILE); } } ================================================ FILE: src/main/java/jpsplugin/com/reason/ORProcessTerminated.java ================================================ package jpsplugin.com.reason; import org.jetbrains.annotations.*; @FunctionalInterface public interface ORProcessTerminated { void run(@Nullable T data); } ================================================ FILE: src/main/java/jpsplugin/com/reason/OclSourcesOrderRootType.java ================================================ package jpsplugin.com.reason; import com.intellij.openapi.roots.*; import org.jetbrains.annotations.*; public class OclSourcesOrderRootType extends PersistentOrderRootType { public OclSourcesOrderRootType() { super("OCAML_SOURCES", null, null, null); } @NotNull public static OclSourcesOrderRootType getInstance() { return getOrderRootType(OclSourcesOrderRootType.class); } } ================================================ FILE: src/main/java/jpsplugin/com/reason/Platform.java ================================================ package jpsplugin.com.reason; import com.intellij.execution.configurations.*; import com.intellij.ide.plugins.*; import com.intellij.openapi.extensions.*; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.*; import com.intellij.openapi.project.*; import com.intellij.openapi.roots.*; import com.intellij.openapi.util.*; import com.intellij.openapi.vfs.*; import com.intellij.psi.*; import com.reason.ide.*; import org.jetbrains.annotations.*; import java.io.*; import java.nio.charset.*; import java.nio.file.*; import java.util.*; public class Platform { public static final Charset UTF8 = StandardCharsets.UTF_8; public static final String WINDOWS_EXECUTABLE_SUFFIX = ".exe"; private static final Log LOG = Log.create("platform"); private Platform() { } @NotNull public static String getOsPrefix() { if (SystemInfo.isWindows) { return "w"; } if (SystemInfo.isLinux) { return "l"; } if (SystemInfo.isMac) { return "o"; } return ""; } public static boolean isWindows() { return SystemInfo.isWindows; } @Nullable public static Path getPluginLocation() { IdeaPluginDescriptor plugin = PluginManagerCore.getPlugin(PluginId.getId("reasonml")); return plugin == null ? null : plugin.getPluginPath(); } public static @NotNull Map findModulesFor(@NotNull Project project, @NotNull String filename) { Map rootContents = new HashMap<>(); ModuleManager moduleManager = ModuleManager.getInstance(project); for (Module module : moduleManager.getModules()) { for (VirtualFile contentRoot : ModuleRootManager.getInstance(module).getContentRoots()) { VirtualFile child = contentRoot.findChild(filename); if (child != null && !rootContents.containsKey(module)) { rootContents.put(module, child); } } } if (LOG.isDebugEnabled()) { LOG.debug("Modules for file [" + filename + "] in project=\"" + project.getName() + "\": [" + Joiner.join(",", rootContents.entrySet(), entry -> entry.getKey().getName() + " -> " + entry.getValue().getCanonicalPath()) + "]"); } return rootContents; } public static @NotNull Optional findExecutableInPath(String filename, String shellPath) { if (SystemInfo.isWindows) { filename += WINDOWS_EXECUTABLE_SUFFIX; } File exeFile = PathEnvironmentVariableUtil.findInPath(filename, shellPath, null); return exeFile == null ? Optional.empty() : Optional.of(exeFile.toPath()); } public static @NotNull String getRelativePathToModule(@NotNull String path, @NotNull Project project) { String relativePath = path; VirtualFile vFile = VirtualFileManager.getInstance().findFileByNioPath(Path.of(path)); Module module = vFile == null ? null : ModuleUtil.findModuleForFile(vFile, project); if (module != null) { ModuleRootManagerEx moduleRootManager = ModuleRootManagerEx.getInstanceEx(module); for (VirtualFile contentRoot : moduleRootManager.getContentRoots()) { String contentRootPath = contentRoot.getPath(); if (path.startsWith(contentRootPath)) { relativePath = path.substring(contentRootPath.length()); } } } return relativePath; } public static @NotNull String getRelativePathToModule(@NotNull PsiFile file) { VirtualFile virtualFile = ORFileUtils.getVirtualFile(file); String relativePath = virtualFile == null ? file.getName() : virtualFile.getPath(); Module module = ModuleUtil.findModuleForFile(file); if (module != null) { String fileName = file.getName(); ModuleRootManagerEx moduleRootManager = ModuleRootManagerEx.getInstanceEx(module); for (VirtualFile sourceRoot : moduleRootManager.getSourceRoots()) { VirtualFile child = sourceRoot.findChild(fileName); if (child != null) { relativePath = child.getPath().replace(sourceRoot.getPath(), sourceRoot.getName()); break; } } } return relativePath; } public static @Nullable Module getModule(@NotNull Project project, @Nullable VirtualFile file) { PsiFile psiFile = file == null ? null : PsiManager.getInstance(project).findFile(file); return psiFile == null ? null : ModuleUtil.findModuleForFile(psiFile); } public static boolean isElementInSourceContent(@Nullable PsiElement element) { if (element == null) { return false; } VirtualFile targetFile = ORFileUtils.getVirtualFile(element); Module moduleForTarget = ModuleUtil.findModuleForPsiElement(element); ModuleRootManagerEx moduleRootManager = moduleForTarget != null ? ModuleRootManagerEx.getInstanceEx(moduleForTarget) : null; return moduleRootManager != null && targetFile != null && moduleRootManager.getFileIndex().isInSourceContent(targetFile); } } ================================================ FILE: src/main/java/jpsplugin/com/reason/Streams.java ================================================ package jpsplugin.com.reason; import static java.nio.charset.StandardCharsets.UTF_8; import java.io.*; import java.util.stream.*; import org.jetbrains.annotations.NotNull; public class Streams { public static final String LINE_SEPARATOR = System.lineSeparator(); private Streams() { } public static void waitUntilReady(@NotNull BufferedReader reader, @NotNull BufferedReader errorReader) throws IOException { long start = System.currentTimeMillis(); boolean isReady = reader.ready() || errorReader.ready(); while (!isReady) { if (200 < (System.currentTimeMillis() - start)) { // max 1s isReady = true; } else { Interrupted.sleep(20); isReady = reader.ready() || errorReader.ready(); } } } public static String inputToString(@NotNull InputStream is) { try (BufferedReader br = new BufferedReader(new InputStreamReader(is, UTF_8))) { return br.lines().collect(Collectors.joining(LINE_SEPARATOR)); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } catch (IOException e) { throw new UncheckedIOException(e); } } } ================================================ FILE: src/main/java/jpsplugin/com/reason/StringUtil.java ================================================ package jpsplugin.com.reason; import org.jetbrains.annotations.*; import java.util.*; public class StringUtil { private StringUtil() { } @NotNull public static String toFirstUpper(@Nullable String value) { if (value == null || value.isEmpty()) { return ""; } return value.substring(0, 1).toUpperCase(Locale.getDefault()) + value.substring(1); } @NotNull public static String toFirstLower(@Nullable String value) { if (value == null || value.isEmpty()) { return ""; } return value.substring(0, 1).toLowerCase(Locale.getDefault()) + value.substring(1); } public static @Nullable String trimLastCR(@Nullable String value) { if (value == null || value.isEmpty()) { return value; } int length = value.length(); if (value.charAt(length - 1) == '\n') { return value.substring(0, length - 1); } return value; } public static boolean isUpper(char c) { return 'A' <= c && c <= 'Z'; } } ================================================ FILE: src/main/java/jpsplugin/com/reason/WGet.java ================================================ package jpsplugin.com.reason; import com.intellij.notification.NotificationType; import com.intellij.notification.Notifications; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.util.SystemInfo; import java.io.*; import java.net.*; import java.nio.file.StandardCopyOption; import org.jetbrains.annotations.NotNull; public class WGet { private static final Log LOG = Log.create("wget"); private static final int BUFFER_SIZE = 1024; private WGet() { } public static boolean apply(@NotNull String urlString, @NotNull File targetFile, @NotNull ProgressIndicator indicator, double totalBytes) { try { // some code File partFile = new File(targetFile.getPath() + ".part"); if (partFile.exists()) { //noinspection ResultOfMethodCallIgnored partFile.delete(); } //noinspection ResultOfMethodCallIgnored partFile.createNewFile(); FileOutputStream partFileOut = new FileOutputStream(partFile); java.net.URL url = URI.create(urlString).toURL(); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod("GET"); connection.setDoOutput(true); connection.setConnectTimeout(240 * 1000); connection.setReadTimeout(240 * 1000); InputStream inputStream = connection.getInputStream(); double totalBytesDownloaded = 0.0; byte[] buffer = new byte[BUFFER_SIZE]; int bytesRead = inputStream.read(buffer); while (bytesRead >= 0) { if (totalBytes > 0.0) { indicator.setFraction(totalBytesDownloaded / totalBytes); } totalBytesDownloaded += bytesRead; partFileOut.write(buffer, 0, bytesRead); bytesRead = inputStream.read(buffer); } connection.disconnect(); partFileOut.close(); inputStream.close(); java.nio.file.Files.move(partFile.toPath(), targetFile.toPath(), StandardCopyOption.ATOMIC_MOVE); if (!SystemInfo.isWindows) { //noinspection ResultOfMethodCallIgnored targetFile.setExecutable(true); } LOG.info(targetFile.getName() + " downloaded to " + targetFile.toPath().getParent()); Notifications.Bus.notify(new ORNotification("Reason", "Downloaded " + targetFile, NotificationType.INFORMATION)); return true; } catch (IOException e) { Notifications.Bus.notify(new ORNotification("Reason", "Can't download " + targetFile + "\n" + e, NotificationType.ERROR)); return false; } } } ================================================ FILE: src/main/java/jpsplugin/com/reason/sdk/SdkDownloader.java ================================================ package jpsplugin.com.reason.sdk; import com.intellij.notification.*; import com.intellij.openapi.progress.*; import com.intellij.openapi.project.*; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.*; import com.intellij.util.io.*; import jpsplugin.com.reason.*; import org.jetbrains.annotations.*; import java.io.*; import java.util.function.*; import java.util.zip.*; import static com.intellij.notification.NotificationType.*; public class SdkDownloader { private static final Predicate KEEP_OCAML_SOURCES = s -> s.endsWith(".ml") || s.endsWith(".mli") || s.endsWith(".ml4") || s.endsWith(".mll") || s.endsWith(".mly"); private final @NotNull String m_sdk; private final @NotNull String m_major; private final @NotNull File m_sdkHome; public SdkDownloader(@NotNull String major, @NotNull String minor, @NotNull String patch, @NotNull VirtualFile sdkHome) { String canonicalPath = sdkHome.getCanonicalPath(); assert canonicalPath != null; m_sdkHome = new File(canonicalPath); m_sdk = major + "." + minor + "." + patch; m_major = major + "." + minor; } public static @NotNull Task modalTask(@NotNull String major, @NotNull String minor, @NotNull String patch, @NotNull VirtualFile sdkHome, @Nullable Project project) { return new Task.Modal(project, "Download SDK", false) { @Override public void run(@NotNull ProgressIndicator indicator) { new SdkDownloader(major, minor, patch, sdkHome).run(project, indicator); } }; } public void run(@Nullable Project project, @NotNull ProgressIndicator indicator) { String sdkFilename = "ocaml-" + m_sdk + ".tar.gz"; File targetSdkLocation = new File(m_sdkHome, sdkFilename); String sdkUrl = "https://caml.inria.fr/pub/distrib/ocaml-" + m_major + "/" + sdkFilename; indicator.setIndeterminate(true); indicator.setText("Download sdk from " + sdkUrl); boolean isDownloaded = WGet.apply(sdkUrl, targetSdkLocation, indicator, -1.0); if (isDownloaded) { try { indicator.setText("Uncompress SDK"); File tarPath = uncompress(targetSdkLocation); FileUtil.delete(targetSdkLocation); indicator.setText("Untar SDK"); new Decompressor.Tar(tarPath).filter(KEEP_OCAML_SOURCES).extract(m_sdkHome); FileUtil.delete(tarPath); } catch (IOException e) { Notifications.Bus.notify( new ORNotification("Sdk", "Cannot download sdk, error: " + e.getMessage(), ERROR), project); } } } @NotNull private File uncompress(@NotNull File source) throws IOException { byte[] buffer = new byte[1024]; String absolutePath = source.getAbsolutePath(); String tarName = absolutePath.substring(0, absolutePath.length() - 3); try (GZIPInputStream is = new GZIPInputStream(new FileInputStream(source))) { try (FileOutputStream out = new FileOutputStream(tarName)) { int len; while ((len = is.read(buffer)) > 0) { out.write(buffer, 0, len); } } } return new File(tarName); } } ================================================ FILE: src/main/java/jpsplugin/com/reason/wizard/OCamlModuleWizardStep.java ================================================ package jpsplugin.com.reason.wizard; import com.intellij.ide.util.projectWizard.*; import com.intellij.openapi.project.*; import com.intellij.openapi.roots.ui.configuration.*; import com.intellij.openapi.roots.ui.configuration.projectRoot.*; import org.jetbrains.annotations.*; import javax.swing.*; class OCamlModuleWizardStep extends ModuleWizardStep { private JPanel c_rootPanel; private @Nullable JdkComboBox c_sdk; public JComponent getComponent() { return c_rootPanel; } public @Nullable JComponent getPreferredFocusedComponent() { return c_sdk; } private void createUIComponents() { ProjectSdksModel model = new ProjectSdksModel(); Project project = ProjectManager.getInstance().getDefaultProject(); model.reset(project); c_sdk = new JdkComboBox(project, model, null, null, null, null); } public void updateDataModel() { } public boolean validate() { return true; } } ================================================ FILE: src/main/java/jpsplugin/com/reason/wizard/WizardStepForm.form ================================================
================================================ FILE: src/main/resources/META-INF/java-deps.xml ================================================ ================================================ FILE: src/main/resources/META-INF/js-deps.xml ================================================ ================================================ FILE: src/main/resources/META-INF/plugin.xml ================================================ reasonml ReasonML H.Giraud com.intellij.modules.lang com.intellij.modules.java JavaScript com.reason.ide.intentions.FunctionBracesIntention com.reason.ide.intentions.ExpandLocalOpenIntention ================================================ FILE: src/main/resources/fileTemplates/OCaml Interface.mli.ft ================================================ ================================================ FILE: src/main/resources/fileTemplates/OCaml Module.ml.ft ================================================ ================================================ FILE: src/main/resources/fileTemplates/Reason Interface.rei.ft ================================================ ================================================ FILE: src/main/resources/fileTemplates/Reason Module.re.ft ================================================ ================================================ FILE: src/main/resources/fileTemplates/Rescript Interface.resi.ft ================================================ ================================================ FILE: src/main/resources/fileTemplates/Rescript Module.res.ft ================================================ ================================================ FILE: src/main/resources/intentionDescriptions/ExpandLocalOpenIntention/after.re.template ================================================ let x = { open Module; localCall(); ); ================================================ FILE: src/main/resources/intentionDescriptions/ExpandLocalOpenIntention/before.re.template ================================================ let x = Module.( localCall(); ); ================================================ FILE: src/main/resources/intentionDescriptions/ExpandLocalOpenIntention/description.html ================================================ Expand a local open to an explicit open. ================================================ FILE: src/main/resources/intentionDescriptions/FunctionBracesIntention/after.re.template ================================================ let add (x, y) => { x + y; }; ================================================ FILE: src/main/resources/intentionDescriptions/FunctionBracesIntention/before.re.template ================================================ let add = (x, y) => x + y; ================================================ FILE: src/main/resources/intentionDescriptions/FunctionBracesIntention/description.html ================================================ Add braces to blockless function. ================================================ FILE: src/main/resources/liveTemplates/OCaml.xml ================================================ ================================================ FILE: src/main/resources/liveTemplates/Reason.xml ================================================ ================================================ FILE: src/main/resources/liveTemplates/Rescript.xml ================================================ ================================================ FILE: src/test/java/com/reason/FileHelperTest.java ================================================ package com.reason; import com.intellij.mock.MockVirtualFile; import com.reason.comp.*; import com.reason.ide.ORBasePlatformTestCase; import org.junit.*; import org.junit.runner.*; import org.junit.runners.*; @RunWith(JUnit4.class) public class FileHelperTest extends ORBasePlatformTestCase { @Test public void testIsBsConfig() { MockVirtualFile mockConfig = MockVirtualFile.file(ORConstants.BS_CONFIG_FILENAME); assertTrue(FileHelper.isBsConfigJson(mockConfig)); assertFalse(FileHelper.isBsConfigJson(MockVirtualFile.file("package.json"))); } @Test public void testIsRescriptConfig() { MockVirtualFile mockConfig = MockVirtualFile.file(ORConstants.RESCRIPT_CONFIG_FILENAME); assertTrue(FileHelper.isRescriptConfigJson(mockConfig)); assertFalse(FileHelper.isRescriptConfigJson(MockVirtualFile.file("package.json"))); } } ================================================ FILE: src/test/java/com/reason/comp/bs/BsConfigReaderTest.java ================================================ package com.reason.comp.bs; import com.reason.ide.ORBasePlatformTestCase; import java.io.*; import java.util.*; import org.jetbrains.annotations.NotNull; import org.junit.*; import org.junit.runner.*; import org.junit.runners.*; @RunWith(JUnit4.class) public class BsConfigReaderTest extends ORBasePlatformTestCase { @Override protected @NotNull String getTestDataPath() { return "src/test/testData/com/reason/bs"; } @Test public void testName() { BsConfig bsConfig = BsConfigReader.parse(toJson("{'name': 'x'}")); assertEquals("x", bsConfig.getName()); } @Test public void testTrailingComma() { BsConfig bsConfig = BsConfigReader.parse(toJson("{'sources': ['a', ],\n },")); assertContainsElements(bsConfig.getSources(), "a"); } @Test public void test_GHIssue214() throws IOException { BsConfig bsConf = BsConfigReader.parse(loadFile("issue_214.json")); assertNotNull(bsConf); } @Test public void testJsx() { BsConfig bsConfig = BsConfigReader.parse(toJson("{'name': 'x', 'reason': {'react-jsx': 2}}")); assertEquals("2", bsConfig.getJsxVersion()); BsConfig bsConfig2 = BsConfigReader.parse(toJson("{'name': 'x', 'reason': true}")); // ? assertNull(bsConfig2.getJsxVersion()); } @Test public void testNamespace() { BsConfig conf1 = BsConfigReader.parse(toJson("{'name': 'x', 'namespace': 'Foo'}")); assertTrue(conf1.hasNamespace()); assertEquals("Foo", conf1.getNamespace()); BsConfig conf2 = BsConfigReader.parse(toJson("{'name': 'auto', 'namespace': true}")); assertTrue(conf2.hasNamespace()); assertEquals("Auto", conf2.getNamespace()); assertFalse(BsConfigReader.parse(toJson("{'name': 'x', 'namespace': false}")).hasNamespace()); assertFalse(BsConfigReader.parse(toJson("{'name': 'x'}")).hasNamespace()); } @Test public void testPpx() { BsConfig bsConfig = BsConfigReader.parse(toJson("{'name': 'x', 'ppx-flags': ['graphql/ppx', 'other/ppx']}")); assertSize(2, bsConfig.getPpx()); assertEquals("graphql/ppx", bsConfig.getPpx()[0]); assertEquals("other/ppx", bsConfig.getPpx()[1]); } @Test public void testJsonWithComment() throws IOException { BsConfig bsConf = BsConfigReader.parse(loadFile("comments.json")); assertEquals("comments", bsConf.getName()); } @Test public void testBsPlatform() throws IOException { BsConfig bsConfig = BsConfigReader.parse(loadFile("bsplatform.json")); assertEquals("bs-platform", bsConfig.getName()); } @Test public void testSourcesAsString() throws IOException { BsConfig bsConfig = BsConfigReader.parse(loadFile("src_string.json")); Set sources = bsConfig.getSources(); assertSize(1, sources); assertEquals("xxx", sources.iterator().next()); } @Test public void testSourcesAsSourceItem() throws IOException { BsConfig bsConfig = BsConfigReader.parse(loadFile("src_object.json")); Set sources = bsConfig.getSources(); assertSize(1, sources); assertEquals("yyy", sources.iterator().next()); } @Test public void testSourcesAsArray() throws IOException { BsConfig bsConfig = BsConfigReader.parse(loadFile("src_array.json")); Set sources = bsConfig.getSources(); assertSize(3, sources); assertContainsElements(sources, "x", "y", "z"); } @Test public void testDepsRead() throws IOException { BsConfig bsConfig = BsConfigReader.parse(loadFile("deps.json")); assertSize(2, bsConfig.getDependencies()); } // https://github.com/giraud/reasonml-idea-plugin/issues/418 @Test public void testBscFlags() { BsConfig bsConfig = BsConfigReader.parse(toJson("{'name': 'x', 'bsc-flags': ['-no-alias-deps', '-open RescriptCore']}")); assertSize(2, bsConfig.getBscFlags()); assertSize(1, bsConfig.getOpenedDeps()); assertContainsElements(bsConfig.getOpenedDeps(), "RescriptCore"); } } ================================================ FILE: src/test/java/com/reason/comp/bs/BsLineProcessorTest.java ================================================ package com.reason.comp.bs; import static com.intellij.testFramework.UsefulTestCase.assertSize; import static org.junit.Assert.*; import jpsplugin.com.reason.Log; import com.reason.ide.annotations.OutputInfo; import org.junit.Test; import org.junit.runner.*; import org.junit.runners.*; import java.util.List; @RunWith(JUnit4.class) public class BsLineProcessorTest { @Test public void testWarningMessage() { String[] output = new String[]{ "[3/3] Building File.cmj\n", "\n", " Warning number 44\n", " C:\\__tests__\\Instant_spec.re 3:1-12\n", " \n", " 1 | open Jest;\n", " 2 | open Expect;\n", " 3 | open Instant;\n", " 4 | \n", " 5 | describe(\"Instant.millis\", () =>\n", " \n", " this open statement shadows the value identifier floor (which is later used)\n", "Compilation ended\n" }; BsLineProcessor outputListener = new BsLineProcessor(Log.create("test")); for (String line : output) { outputListener.onTextAvailable(line); } List allErrors = outputListener.getOutputInfo(); assertSize(1, allErrors); OutputInfo info = allErrors.get(0); assertFalse(info.isError); assertEquals("3:1", info.lineStart + ":" + info.colStart); assertEquals("3:13", info.lineEnd + ":" + info.colEnd); assertEquals( "this open statement shadows the value identifier floor (which is later used)", info.message); } @Test public void testSyntaxError() { String[] output = new String[]{ // "File \"C:\\sources\\File.re\", line 38, characters 2-108:", "Error: SyntaxError in block\n", "\n", " We've found a bug for you!\n", " C:\\sources\\File.re\n", " \n", " There's been an error running Reason's parser on a file.\n", " The error location should be slightly above this message.\n", " Please file an issue on github.com/facebook/reason. Thanks!\n", " \n", "Compilation ended\n" }; BsLineProcessor outputListener = new BsLineProcessor(Log.create("test")); for (String line : output) { outputListener.onTextAvailable(line); } List allErrors = outputListener.getOutputInfo(); assertSize(1, allErrors); OutputInfo info = allErrors.get(0); assertTrue(info.isError); assertEquals("38:2", info.lineStart + ":" + info.colStart); assertEquals("38:108", info.lineEnd + ":" + info.colEnd); assertEquals("SyntaxError in block", info.message); } @Test public void testError() { String[] output = new String[]{ // "[3/6] Building src\\time\\Instant.cmj\n", "FAILED: src/time/Instant.cmj C:/src/time/Instant.bs.js \n", "\"C:\\node_modules\\bs-platform\\lib\\bsc.exe\" -nostdlib -bs-package-name xxx -bs-package-output commonjs:lib\\js\\src\\time -color always -bs-suffix -bs-read-cmi -I src\\time -I src -I \"C:\\node_modules\\bs-platform\\lib\\ocaml\" -w -30-40+6+7+27+32..39+44+45+101+A-48-40-42 -warn-error,A-3-44-102 -bs-no-version-header -o src\\time\\Instant.cmj src\\time\\Instant.reast\n", "\n", " We've found a bug for you!\n", " U:\\sources\\tin-umbrella\\packages\\tin-core\\src\\time\\Instant.re 60:16-25\n", " \n", " 58 | let setMonth = dfSetMonth;\n", " 59 | let setDay = dfSetDate;\n", " 60 | let setHours = dfSetHours;\n", " 61 | let setMinutes = dfSetMinutes;\n", " 62 | let setSeconds = dfSetSeconds;\n", " \n", " The value dfSetHours can't be found\n", " \n", " Hint: Did you mean dfAddHours?\n", " \n", "FAILED: subcommand failed.\n", "Compilation ended" }; BsLineProcessor outputListener = new BsLineProcessor(Log.create("test")); for (String line : output) { outputListener.onTextAvailable(line); } List allErrors = outputListener.getOutputInfo(); assertSize(1, allErrors); OutputInfo info = allErrors.get(0); assertTrue(info.isError); assertEquals("60:16", info.lineStart + ":" + info.colStart); assertEquals("60:26", info.lineEnd + ":" + info.colEnd); assertEquals( "The value dfSetHours can't be found. Hint: Did you mean dfAddHours?", info.message); } @Test public void testErrorColonDelimeter() { String[] output = new String[]{ // "[3/6] Building src\\time\\Instant.cmj\n", "FAILED: src/time/Instant.cmj C:/src/time/Instant.bs.js \n", "\"C:\\node_modules\\bs-platform\\lib\\bsc.exe\" -nostdlib -bs-package-name xxx -bs-package-output commonjs:lib\\js\\src\\time -color always -bs-suffix -bs-read-cmi -I src\\time -I src -I \"C:\\node_modules\\bs-platform\\lib\\ocaml\" -w -30-40+6+7+27+32..39+44+45+101+A-48-40-42 -warn-error,A-3-44-102 -bs-no-version-header -o src\\time\\Instant.cmj src\\time\\Instant.reast\n", "\n", " We've found a bug for you!\n", " U:\\sources\\tin-umbrella\\packages\\tin-core\\src\\time\\Instant.re:60:16-25\n", " \n", " 58 | let setMonth = dfSetMonth;\n", " 59 | let setDay = dfSetDate;\n", " 60 | let setHours = dfSetHours;\n", " 61 | let setMinutes = dfSetMinutes;\n", " 62 | let setSeconds = dfSetSeconds;\n", " \n", " The value dfSetHours can't be found\n", " \n", " Hint: Did you mean dfAddHours?\n", " \n", "FAILED: subcommand failed.\n", "Compilation ended" }; BsLineProcessor outputListener = new BsLineProcessor(Log.create("test")); for (String line : output) { outputListener.onTextAvailable(line); } List allErrors = outputListener.getOutputInfo(); assertSize(1, allErrors); OutputInfo info = allErrors.get(0); assertTrue(info.isError); assertEquals("60:16", info.lineStart + ":" + info.colStart); assertEquals("60:26", info.lineEnd + ":" + info.colEnd); assertEquals( "The value dfSetHours can't be found. Hint: Did you mean dfAddHours?", info.message); } @Test public void testWarningError() { String[] output = new String[]{ // "[3/6] Building src\\time\\Instant.cmj\n", "FAILED: src/time/Instant.cmj C:/src/lib/js/src/time/Instant.bs.js\n", "\"C:\\node_modules\\bs-platform\\lib\\bsc.exe\" -nostdlib -bs-package-name tin-core -bs-package-output commonjs:lib\\js\\src\\time -color always -bs-suffix -bs-read-cmi -I src\\time -I src -I \"C:\\node_modules\\bs-platform\\lib\\ocaml\" -w -30-40+6+7+27+32..39+44+45+101+A-48-40-42 -warn-error,A-3-44-102 -bs-no-version-header -o src\\time\\Instant.cmj src\\time\\Instant.reast\n", "\n", " Warning number 32\n", " C:\\src\\time\\Instant.re 3:1-58\n", "\n", " 1 | type t = Js.Date.t;\n", " 2 |\n", " 3 | [@bs.module] external parseDate: t => t = \"date-fns/parse\";\n", " 4 | [@bs.module] external dfIsEqual: (t, t) => bool = \"date-fns/is_equal\";\n", " 5 | [@bs.module] external dfIsBefore: (t, t) => bool = \"date-fns/is_before\"\n", " ;\n", "\n", " unused value parseDate.\n", "\n", " We've found a bug for you!\n", " C:\\src\\time\\Instant.re\n", "\n", " Some fatal warnings were triggered (1 occurrences)\n", "\n", "FAILED: subcommand failed.\n", "Compilation ended" }; BsLineProcessor outputListener = new BsLineProcessor(Log.create("test")); long start = System.nanoTime(); for (String line : output) { outputListener.onTextAvailable(line); } long end = System.nanoTime(); System.out.println("process in " + (end - start) + "ns"); List allErrors = outputListener.getOutputInfo(); assertSize(1, allErrors); OutputInfo info = allErrors.get(0); // assertTrue(info.isError); assertEquals("3:1", info.lineStart + ":" + info.colStart); assertEquals("3:59", info.lineEnd + ":" + info.colEnd); assertEquals("unused value parseDate.", info.message); } @Test public void testError2() { String[] output = new String[]{ // "[5/287] Building \\src\\UUID.cmj\n", "\n", " We've found a bug for you!\n", " C:\\src\\UUID.re 18:11-14\n", " \n", " 16 | \n", " 17 | let eq = (uuid1, uuid2) => {\n", " 18 | compare(uuid, uuid2) === 0;\n", " 19 | };\n", " 20 | \n", " \n", " The value uuid can't be found\n", " \n", " Hint: Did you mean uuid2 or uuid1?\n", " \n", "Compilation ended\n" }; BsLineProcessor outputListener = new BsLineProcessor(Log.create("test")); for (String line : output) { outputListener.onTextAvailable(line); } List allErrors = outputListener.getOutputInfo(); assertSize(1, allErrors); OutputInfo info = allErrors.get(0); assertTrue(info.isError); assertEquals("18:11", info.lineStart + ":" + info.colStart); assertEquals("18:15", info.lineEnd + ":" + info.colEnd); assertEquals("The value uuid can't be found. Hint: Did you mean uuid2 or uuid1?", info.message); } @Test public void testPpxError() { String[] output = new String[]{ // "File \"C:\\reason\\src\\Demo.re\", line 6, characters 2-12:\n", "6 | ..}\n", "6 | allUs.......\n", "Error: Unknown field 'allUsessrs' on type Query\n", "\n", " We've found a bug for you!\n", " C:\\reason\\src\\Demo.re\n", " \n", " Error while running external preprocessor\n" }; BsLineProcessor outputListener = new BsLineProcessor(Log.create("test")); for (String line : output) { outputListener.onTextAvailable(line); } List allErrors = outputListener.getOutputInfo(); assertSize(1, allErrors); OutputInfo info = allErrors.get(0); assertTrue(info.isError); assertEquals("6:2", info.lineStart + ":" + info.colStart); assertEquals("6:12", info.lineEnd + ":" + info.colEnd); assertEquals("Unknown field 'allUsessrs' on type Query", info.message); } } ================================================ FILE: src/test/java/com/reason/comp/bs/NinjaTest.java ================================================ package com.reason.comp.bs; import com.reason.ide.*; import org.jetbrains.annotations.*; import org.junit.*; import org.junit.runner.*; import org.junit.runners.*; import java.io.*; @RunWith(JUnit4.class) public class NinjaTest extends ORBasePlatformTestCase { @Override protected @NotNull String getTestDataPath() { return "src/test/testData/com/reason/bs"; } @Test public void test_read_rescript_format() throws IOException { String content = loadFile("ninja-rescript.build"); Ninja ninja = new Ninja(content); } } ================================================ FILE: src/test/java/com/reason/comp/bs/ResConfigReaderTest.java ================================================ package com.reason.comp.bs; import com.reason.ide.*; import org.junit.*; import org.junit.runner.*; import org.junit.runners.*; // All other tests in BsConfigReaderTest @RunWith(JUnit4.class) public class ResConfigReaderTest extends ORBasePlatformTestCase { @Test public void testJsx_4() { BsConfig bsConfig = BsConfigReader.parse(toJson("{'name': 'x', 'jsx': {'version': 4}}")); assertEquals("4", bsConfig.getJsxVersion()); assertNull(bsConfig.getJsxMode()); BsConfig bsConfig1 = BsConfigReader.parse(toJson("{'name': 'x', 'jsx': {'version': 4, 'mode': 'automatic'}}")); assertEquals("4", bsConfig1.getJsxVersion()); assertEquals("automatic", bsConfig1.getJsxMode()); } @Test public void testUncurried() { BsConfig bsConfig = BsConfigReader.parse(toJson("{'name': 'x', 'uncurried': false}")); assertFalse(bsConfig.isUncurried()); } } ================================================ FILE: src/test/java/com/reason/comp/dune/DuneOutputAnalyzerTest.java ================================================ package com.reason.comp.dune; import com.reason.comp.*; import com.reason.ide.*; import com.reason.ide.annotations.*; import com.reason.ide.console.*; import org.jetbrains.annotations.*; import org.junit.*; import org.junit.runner.*; import org.junit.runners.*; @RunWith(JUnit4.class) public class DuneOutputAnalyzerTest extends ORBasePlatformTestCase { /* | File "lib/InputTest.ml", line 3, characters 0-1: unknown -> fileLocation | 3 | X + y fileLocation -> sourceCode | ^ - | Error: Unbound constructor X sourceCode -> ErrorMessage */ @Test public void test_error_01() { CompilerOutputAnalyzer analyzer = analyze( "File \"lib/InputTest.ml\", line 3, characters 0-1:", "3 | X + y", " ^", "Error: Unbound constructor X" ); assertSize(1, analyzer.getOutputInfo()); OutputInfo outputInfo = analyzer.getOutputInfo().get(0); assertTrue(outputInfo.isError); assertEquals("Unbound constructor X", outputInfo.message); assertEquals(3, outputInfo.lineStart); assertEquals(3, outputInfo.lineEnd); assertEquals(0, outputInfo.colStart); assertEquals(1, outputInfo.colEnd); } /* | File "lib/InputTest.ml", line 3, characters 10-12: | Error: Syntax error */ @Test public void test_error_02() { CompilerOutputAnalyzer analyzer = analyze( "File \"lib/InputTest.ml\", line 3, characters 10-12:", "Error: Syntax error" ); assertSize(1, analyzer.getOutputInfo()); OutputInfo outputInfo = analyzer.getOutputInfo().get(0); assertTrue(outputInfo.isError); assertEquals("Syntax error", outputInfo.message); assertEquals(3, outputInfo.lineStart); assertEquals(3, outputInfo.lineEnd); assertEquals(10, outputInfo.colStart); assertEquals(12, outputInfo.colEnd); } @Test public void test_common_0() { CompilerOutputAnalyzer analyzer = analyze(OCamlMessages.common.get(0)); assertSize(1, analyzer.getOutputInfo()); OutputInfo outputInfo = analyzer.getOutputInfo().get(0); assertTrue(outputInfo.isError); assertLines(4, 4, outputInfo); assertCols(6, 7, outputInfo); assertEquals("This expression has type int This is not a function; it cannot be applied.", outputInfo.message); } @Test public void test_common_1() { CompilerOutputAnalyzer analyzer = analyze(OCamlMessages.common.get(1)); assertSize(1, analyzer.getOutputInfo()); OutputInfo outputInfo = analyzer.getOutputInfo().get(0); assertFalse(outputInfo.isError); assertLines(3, 3, outputInfo); assertCols(6, 7, outputInfo); assertEquals("unused variable y.", outputInfo.message); } @Test public void test_common_2() { CompilerOutputAnalyzer analyzer = analyze(OCamlMessages.common.get(2)); assertSize(1, analyzer.getOutputInfo()); OutputInfo outputInfo = analyzer.getOutputInfo().get(0); assertTrue(outputInfo.isError); assertLines(6, 6, outputInfo); assertCols(15, 38, outputInfo); assertEquals("Signature mismatch: Modules do not match: sig val x : float end is not included in X Values do not match: val x : float is not included in val x : int", outputInfo.message); } @Test public void test_since408_0() { CompilerOutputAnalyzer analyzer = analyze(OCamlMessages.since408.get(0)); assertSize(1, analyzer.getOutputInfo()); OutputInfo outputInfo = analyzer.getOutputInfo().get(0); assertTrue(outputInfo.isError); assertLines(2, 2, outputInfo); assertCols(36, 64, outputInfo); assertEquals("Cannot safely evaluate the definition of the following cycle of recursively-defined modules: A -> B -> A.", outputInfo.message); } @Test public void test_since408_1() { CompilerOutputAnalyzer analyzer = analyze(OCamlMessages.since408.get(1)); assertSize(1, analyzer.getOutputInfo()); OutputInfo outputInfo = analyzer.getOutputInfo().get(0); assertTrue(outputInfo.isError); assertLines(4, 7, outputInfo); assertCols(6, 3, outputInfo); assertEquals("Cannot safely evaluate the definition of the following cycle of recursively-defined modules: A -> B -> A.", outputInfo.message); } @Test public void test_since408_2() { CompilerOutputAnalyzer analyzer = analyze(OCamlMessages.since408.get(2)); assertSize(1, analyzer.getOutputInfo()); OutputInfo outputInfo = analyzer.getOutputInfo().get(0); assertFalse(outputInfo.isError); assertLines(33, 37, outputInfo); assertCols(6, 23, outputInfo); assertEquals("this pattern-matching is not exhaustive.", outputInfo.message); } @Test public void test_since408_3() { CompilerOutputAnalyzer analyzer = analyze(OCamlMessages.since408.get(3)); assertSize(1, analyzer.getOutputInfo()); OutputInfo outputInfo = analyzer.getOutputInfo().get(0); assertTrue(outputInfo.isError); assertLines(2, 2, outputInfo); assertCols(36, 64, outputInfo); assertEquals("Cannot safely evaluate the definition of the following cycle of recursively-defined modules: A -> B -> A.", outputInfo.message); } @Test public void test_since408_4() { CompilerOutputAnalyzer analyzer = analyze(OCamlMessages.since408.get(4)); assertSize(1, analyzer.getOutputInfo()); OutputInfo outputInfo = analyzer.getOutputInfo().get(0); assertFalse(outputInfo.isError); assertLines(2, 2, outputInfo); assertCols(36, 64, outputInfo); assertEquals("Cannot safely evaluate the definition of the following cycle", outputInfo.message); } @Test public void test_since408_5() { CompilerOutputAnalyzer analyzer = analyze(OCamlMessages.since408.get(5)); assertSize(1, analyzer.getOutputInfo()); OutputInfo outputInfo = analyzer.getOutputInfo().get(0); assertFalse(outputInfo.isError); assertLines(2, 2, outputInfo); assertCols(36, 64, outputInfo); assertEquals("Cannot safely evaluate the definition of the following cycle", outputInfo.message); } @Test public void test_since408_6() { CompilerOutputAnalyzer analyzer = analyze(OCamlMessages.since408.get(6)); assertSize(1, analyzer.getOutputInfo()); OutputInfo outputInfo = analyzer.getOutputInfo().get(0); assertTrue(outputInfo.isError); assertLines(3, 3, outputInfo); assertCols(8, 50, outputInfo); assertEquals("This expression has type float but an expression was expected of type int", outputInfo.message); } @Test public void test_since408_7() { CompilerOutputAnalyzer analyzer = analyze(OCamlMessages.since408.get(7)); assertSize(1, analyzer.getOutputInfo()); OutputInfo outputInfo = analyzer.getOutputInfo().get(0); assertFalse(outputInfo.isError); assertLines(3, 3, outputInfo); assertCols(8, 50, outputInfo); assertEquals("This expression has type float but an expression was expected of type", outputInfo.message); } @Test public void test_since408_8() { CompilerOutputAnalyzer analyzer = analyze(OCamlMessages.since408.get(8)); assertSize(1, analyzer.getOutputInfo()); OutputInfo outputInfo = analyzer.getOutputInfo().get(0); assertTrue(outputInfo.isError); assertLines(13, 13, outputInfo); assertCols(34, 35, outputInfo); assertEquals("This expression has type M/2.t but an expression was expected of type M/1.t", outputInfo.message); } @Test public void test_since408_9() { CompilerOutputAnalyzer analyzer = analyze(OCamlMessages.since408.get(9)); assertSize(1, analyzer.getOutputInfo()); OutputInfo outputInfo = analyzer.getOutputInfo().get(0); assertTrue(outputInfo.isError); assertLines(13, 13, outputInfo); assertCols(34, 35, outputInfo); assertEquals("This expression has type M/2.t but an expression was expected of type M/1.t", outputInfo.message); } private void assertLines(int start, int end, OutputInfo info) { assertEquals(start, info.lineStart); assertEquals(end, info.lineEnd); } private void assertCols(int start, int end, OutputInfo info) { assertEquals(start, info.colStart); assertEquals(end, info.colEnd); } private @NotNull CompilerOutputAnalyzer analyze(String... lines) { CompilerOutputAnalyzer analyzer = new DuneOutputAnalyzer(); for (String line : lines) { analyzer.onTextAvailable(line); } return analyzer; } } ================================================ FILE: src/test/java/com/reason/comp/esy/EsyPackageJsonTest.java ================================================ package com.reason.comp.esy; import com.intellij.mock.*; import com.reason.ide.*; import org.jetbrains.annotations.*; import org.junit.*; import java.io.*; import static com.reason.comp.esy.EsyConstants.*; public class EsyPackageJsonTest extends ORBasePlatformTestCase { @Override protected @NotNull String getTestDataPath() { return "src/test/testData/com/reason/esy"; } @Test public void testIsEsyPackageJson() throws IOException { String packageJsonFilename = ESY_CONFIG_FILENAME; String mockJson = loadFile(packageJsonFilename); MockVirtualFile mockVirtualFile = MockVirtualFile.file(packageJsonFilename); mockVirtualFile.setText(mockJson); assertTrue(EsyPackageJson.isEsyPackageJson(mockVirtualFile)); mockVirtualFile.setText("{}"); assertFalse(EsyPackageJson.isEsyPackageJson(mockVirtualFile)); } } ================================================ FILE: src/test/java/com/reason/comp/ocaml/OpamProcessTest.java ================================================ package com.reason.comp.ocaml; import com.intellij.execution.process.*; import com.reason.ide.*; import jpsplugin.com.reason.*; import org.junit.*; import java.util.*; public class OpamProcessTest extends ORBasePlatformTestCase { /* Windows: # switch compiler description → 4.08.0 ocaml-base-compiler = 4.08.0 | ocaml-system = 4.08.0 default arch-x86_64.1,ocaml-base-compiler.5.2.0,ocaml-options-vanilla.1,system-mingw.1 ocaml >= 4.05.0 [WARNING] The environment is not in sync with the current switch. */ @Test public void testListSwitches() { final List result = new ArrayList<>(); ORProcessTerminated> listORProcessTerminated = data -> result.addAll(data); OpamProcess.ListProcessListener listProcessListener = new OpamProcess.ListProcessListener(listORProcessTerminated); listProcessListener.onTextAvailable(new ProcessEvent(OpamProcess.NULL_HANDLER, "# switch compiler description"), ProcessOutputTypes.STDOUT); listProcessListener.onTextAvailable(new ProcessEvent(OpamProcess.NULL_HANDLER, "→ 4.08.0 ocaml-base-compiler = 4.08.0 | ocaml-system = 4.08.0"), ProcessOutputTypes.STDOUT); listProcessListener.onTextAvailable(new ProcessEvent(OpamProcess.NULL_HANDLER, " default arch-x86_64.1,ocaml-base-compiler.5.2.0,ocaml-options-vanilla.1,system-mingw.1 ocaml >= 4.05.0"), ProcessOutputTypes.STDOUT); listProcessListener.onTextAvailable(new ProcessEvent(OpamProcess.NULL_HANDLER, ""), ProcessOutputTypes.STDOUT); listProcessListener.onTextAvailable(new ProcessEvent(OpamProcess.NULL_HANDLER, "[WARNING] The environment is not in sync with the current switch."), ProcessOutputTypes.STDOUT); listProcessListener.processTerminated(new ProcessEvent(OpamProcess.NULL_HANDLER)); assertSize(2, result); assertEquals(result.get(0).name(), "4.08.0"); assertTrue(result.get(0).isSelected()); assertEquals(result.get(1).name(), "default"); assertFalse(result.get(1).isSelected()); } } ================================================ FILE: src/test/java/com/reason/comp/rescript/RescriptOutputAnalyzerTest.java ================================================ package com.reason.comp.rescript; import com.reason.ide.*; import com.reason.ide.annotations.*; import org.junit.*; public class RescriptOutputAnalyzerTest extends ORBasePlatformTestCase { /* FAILED: src/InputTest.ast Syntax error! unknown -> syntaxError C:\bla\bla\src\InputTest.res:1:11-12 syntaxError -> syntaxErrorLocation syntaxErrorLocation -> syntaxErrorSourceCode 1 │ let x = 1 + - 2 │ - 3 │ start of line... - │ ...end of line - syntaxErrorSourceCode -> syntaxErrorMessage Did you forget to write an expression here? - FAILED: cannot make progress due to previous errors. */ @Test public void test_error_01() { String[] lines = new String[]{ "FAILED: src/InputTest.ast", // "", // " Syntax error!", // " C:\\bla\\bla\\src\\InputTest.res:1:11-12", // "", // " 1 │ let x = 1 +", // "", // " Did you forget to write an expression here?", // "", // "FAILED: cannot make progress due to previous errors.", // }; RescriptOutputAnalyzer analyzer = new RescriptOutputAnalyzer(); for (String line : lines) { analyzer.onTextAvailable(line); } assertSize(1, analyzer.getOutputInfo()); OutputInfo outputInfo = analyzer.getOutputInfo().get(0); assertTrue(outputInfo.isError); assertEquals("Did you forget to write an expression here?", outputInfo.message); assertEquals(1, outputInfo.lineStart); assertEquals(1, outputInfo.lineEnd); assertEquals(11, outputInfo.colStart); assertEquals(12, outputInfo.colEnd); } /* FAILED: src/InputTest.ast Syntax error! C:\bla\bla\src\InputTest.res:1:11 1 │ let x = [1|] Did you forget a `,` here? FAILED: cannot make progress due to previous errors. */ @Test public void test_error_02() { String[] lines = new String[]{ "FAILED: src/InputTest.ast", // "", // " Syntax error!", // " C:\\bla\\bla\\src\\InputTest.res:1:11", // "", // " 1 │ let x = [1|]", // "", // " Did you forget a `,` here?", // "", // "FAILED: cannot make progress due to previous errors.", // }; RescriptOutputAnalyzer analyzer = new RescriptOutputAnalyzer(); for (String line : lines) { analyzer.onTextAvailable(line); } assertSize(1, analyzer.getOutputInfo()); OutputInfo outputInfo = analyzer.getOutputInfo().get(0); assertTrue(outputInfo.isError); assertEquals("Did you forget a `,` here?", outputInfo.message); assertEquals(1, outputInfo.lineStart); assertEquals(1, outputInfo.lineEnd); assertEquals(11, outputInfo.colStart); assertEquals(12, outputInfo.colEnd); } /* File "C:\bla\bla\src\InputTest.res", line 1, characters 9-10: unknown -> fileLocation Error: This expression has type int but an expression was expected of type fileLocation -> errorMessage float - FAILED: cannot make progress due to previous errors. * -> unknown */ @Test public void test_error_03() { String[] lines = new String[]{ "File \"C:\\bla\\bla\\src\\InputTest.res\", line 1, characters 9-10:", // "Error: This expression has type int but an expression was expected of type", // " float", // "FAILED: cannot make progress due to previous errors.", // }; RescriptOutputAnalyzer analyzer = new RescriptOutputAnalyzer(); for (String line : lines) { analyzer.onTextAvailable(line); } assertSize(1, analyzer.getOutputInfo()); OutputInfo outputInfo = analyzer.getOutputInfo().get(0); assertTrue(outputInfo.isError); assertEquals("This expression has type int but an expression was expected of type float", outputInfo.message); assertEquals(1, outputInfo.lineStart); assertEquals(1, outputInfo.lineEnd); assertEquals(9, outputInfo.colStart); assertEquals(10, outputInfo.colEnd); } /* File "C:\bla\bla\src\InputTest.res", line 2, characters 5-9: unknown -> fileLocation Error (warning 32): unused value make. fileLocation -> errorMessage(warning) File "C:\bla\bla\src\InputTest.res", line 8, characters 13-17: errorMessage -> fileLocation Error (warning 27): unused variable sss. fileLocation -> errorMessage(warning) */ @Test public void test_error_04() { String[] lines = new String[]{ "File \"C:\\bla\\bla\\src\\InputTest.res\", line 2, characters 5-9:", // "Error (warning 32): unused value make.", // "File \"C:\\bla\\bla\\src\\InputTest.res\", line 8, characters 13-17:", // "Error (warning 27): unused variable xxx.", // }; RescriptOutputAnalyzer analyzer = new RescriptOutputAnalyzer(); for (String line : lines) { analyzer.onTextAvailable(line); } assertSize(2, analyzer.getOutputInfo()); OutputInfo outputInfo = analyzer.getOutputInfo().get(0); assertFalse(outputInfo.isError); assertEquals("unused value make.", outputInfo.message); assertEquals(2, outputInfo.lineStart); assertEquals(2, outputInfo.lineEnd); assertEquals(5, outputInfo.colStart); assertEquals(9, outputInfo.colEnd); outputInfo = analyzer.getOutputInfo().get(1); assertFalse(outputInfo.isError); assertEquals("unused variable xxx.", outputInfo.message); assertEquals(8, outputInfo.lineStart); assertEquals(8, outputInfo.lineEnd); assertEquals(13, outputInfo.colStart); assertEquals(17, outputInfo.colEnd); } /* File "C:\bla\bla\src\InputTest.res", line 185, characters 8-17: unknown -> fileLocation Warning 27: unused variable onCreated. fileLocation -> warningMessage File "C:\bla\bla\src\InputTest.res", line 186, characters 8-18: errorMessage -> fileLocation Warning 27: unused variable onCanceled. fileLocation -> warningMessage */ @Test public void test_error_05() { String[] lines = new String[]{ "File \"C:\\bla\\bla\\src\\InputTest.res\", line 185, characters 8-17:", // "Warning 27: unused variable onCreated.", // "File \"C:\\bla\\bla\\src\\InputTest.res\", line 186, characters 8-18:", // "Warning 27: unused variable onCanceled.", // }; RescriptOutputAnalyzer analyzer = new RescriptOutputAnalyzer(); for (String line : lines) { analyzer.onTextAvailable(line); } assertSize(2, analyzer.getOutputInfo()); OutputInfo outputInfo = analyzer.getOutputInfo().get(0); assertFalse(outputInfo.isError); assertEquals("unused variable onCreated.", outputInfo.message); assertEquals(185, outputInfo.lineStart); assertEquals(185, outputInfo.lineEnd); assertEquals(8, outputInfo.colStart); assertEquals(17, outputInfo.colEnd); outputInfo = analyzer.getOutputInfo().get(1); assertFalse(outputInfo.isError); assertEquals("unused variable onCanceled.", outputInfo.message); assertEquals(186, outputInfo.lineStart); assertEquals(186, outputInfo.lineEnd); assertEquals(8, outputInfo.colStart); assertEquals(18, outputInfo.colEnd); } /* */ @Test public void test_error_06() { String error = """ FAILED: src/NotGood.cmj We've found a bug for you! C:\\project\\src\\NotGood.res:3:9 1 │ let x = (a, b) => a + b 2 │ 3 │ let y = x(10) This uncurried function has type (int, int) => int It is applied with 1 arguments but it requires 2. FAILED: cannot make progress due to previous errors. """; String[] lines = error.split("\n"); RescriptOutputAnalyzer analyzer = new RescriptOutputAnalyzer(); for (String line : lines) { analyzer.onTextAvailable(line); } assertSize(1, analyzer.getOutputInfo()); OutputInfo outputInfo = analyzer.getOutputInfo().get(0); assertTrue(outputInfo.isError); assertEquals("This uncurried function has type (int, int) => int It is applied with 1 arguments but it requires 2.", outputInfo.message); assertEquals(3, outputInfo.lineStart); assertEquals(3, outputInfo.lineEnd); assertEquals(9, outputInfo.colStart); assertEquals(10, outputInfo.colEnd); } @Test public void test_multi() { String error = """ FAILED: src/Colors.ast Syntax error! C:\\myProject\\src\\Colors.res:31:23-28 29 │ let mainColor = #hex("9D4B70") 30 │ let dark = #hex("6C1D45) 31 │ let lighter = #hex("EFE7EB") 32 │ let light = #hex("C3A4B4") 33 │ } Did you forget a `,` here? Syntax error! C:\\myProject\\src\\Colors.res:163:13-165:1 161 │ "#FFFFFF" 162 │ } else { 163 │ "#000000" 164 │ } 165 │ } This string is missing a double quote at the end Syntax error! C:\\myProject\\src\\Colors.res:165:2 163 │ "#000000" 164 │ } 165 │ } Did you forget a `}` here? FAILED: cannot make progress due to previous errors. Process finished in 97ms """; String[] lines = error.split("\n"); RescriptOutputAnalyzer analyzer = new RescriptOutputAnalyzer(); for (String line : lines) { analyzer.onTextAvailable(line); } assertSize(3, analyzer.getOutputInfo()); OutputInfo outputInfo = analyzer.getOutputInfo().get(0); assertTrue(outputInfo.isError); assertEquals("Did you forget a `,` here?", outputInfo.message); assertEquals(31, outputInfo.lineStart); assertEquals(31, outputInfo.lineEnd); assertEquals(23, outputInfo.colStart); assertEquals(28, outputInfo.colEnd); outputInfo = analyzer.getOutputInfo().get(1); assertTrue(outputInfo.isError); assertEquals("This string is missing a double quote at the end", outputInfo.message); assertEquals(163, outputInfo.lineStart); assertEquals(165, outputInfo.lineEnd); assertEquals(13, outputInfo.colStart); assertEquals(1, outputInfo.colEnd); outputInfo = analyzer.getOutputInfo().get(2); assertTrue(outputInfo.isError); assertEquals("Did you forget a `}` here?", outputInfo.message); assertEquals(165, outputInfo.lineStart); assertEquals(165, outputInfo.lineEnd); assertEquals(2, outputInfo.colStart); assertEquals(3, outputInfo.colEnd); } @Test public void test_warning_as_error() { String error = """ rescript: [3/27] src/a/File.cmj FAILED: src/a/File.cmj Warning number 27 (configured as error)\s C:\\dev\\src\\a\\File.res:632:3-12 630 ┆ ~currentOperationIndex, 631 ┆ ~keys, 632 ┆ ~allInputs: UUID.Map.t, 633 ┆ ~getValue: MappingValueSelector.valueGetter, 634 ┆ ~getInputs: MappingValueSelector.inputsGetter, unused variable allInputs. FAILED: cannot make progress due to previous errors. Process finished in 1s """; String[] lines = error.split("\n"); RescriptOutputAnalyzer analyzer = new RescriptOutputAnalyzer(); for (String line : lines) { analyzer.onTextAvailable(line); } assertSize(1, analyzer.getOutputInfo()); OutputInfo outputInfo = analyzer.getOutputInfo().getFirst(); assertTrue(outputInfo.isError); assertEquals("unused variable allInputs.", outputInfo.message); assertEquals(632, outputInfo.lineStart); assertEquals(632, outputInfo.lineEnd); assertEquals(3, outputInfo.colStart); assertEquals(12, outputInfo.colEnd); } } ================================================ FILE: src/test/java/com/reason/comp/rescript/error-syntax.txt ================================================ Plain (no super-errors) ----------------------- !! FAILED: src/InputTest.ast Syntax error! C:\bla\bla\src\InputTest.res:1:11-12 1 │ let x = 1 + Did you forget to write an expression here? FAILED: cannot make progress due to previous errors. !! File "C:\bla\bla\src\InputTest.res", line 1, characters 9-10: unknown -> errorLocation Error: This expression has type int but an expression was expected of type errorLocation -> error float FAILED: cannot make progress due to previous errors. * -> unknown ================================================ FILE: src/test/java/com/reason/hints/RincewindTest.java ================================================ package com.reason.hints; import org.junit.*; import org.junit.runner.*; import org.junit.runners.*; import static org.junit.Assert.*; @RunWith(JUnit4.class) public class RincewindTest { @Test public void test_bucklescript() { assertEquals("4.02", Rincewind.extractOcamlVersion("BuckleScript 4.0.6 (Using OCaml4.02.3+BS )")); assertEquals("4.06", Rincewind.extractOcamlVersion("BuckleScript 6.0.0-dev.1 (Using OCaml4.06.1+BS )")); assertEquals("4.06", Rincewind.extractOcamlVersion("BuckleScript 6.2.1 ( Using OCaml:4.06.1+BS )")); } @Test public void test_rescript() { assertEquals("4.06", Rincewind.extractOcamlVersion("ReScript 9.1.4")); } @Test public void test_dune() { assertEquals("4.07", Rincewind.extractOcamlVersion("Dune (OCaml:4.07.1)")); assertEquals("4.12", Rincewind.extractOcamlVersion("Dune (OCaml:4.12.0+mingw64c)")); } } ================================================ FILE: src/test/java/com/reason/ide/ORBasePlatformTestCase.java ================================================ package com.reason.ide; import com.intellij.codeInsight.*; import com.intellij.codeInsight.daemon.*; import com.intellij.codeInsight.daemon.impl.*; import com.intellij.lang.*; import com.intellij.lang.documentation.*; import com.intellij.openapi.editor.*; import com.intellij.openapi.util.io.*; import com.intellij.openapi.vfs.*; import com.intellij.psi.*; import com.intellij.psi.impl.*; import com.intellij.testFramework.fixtures.*; import com.intellij.usageView.*; import com.reason.ide.files.*; import org.jetbrains.annotations.*; import org.junit.runner.*; import org.junit.runners.*; import java.io.*; import java.util.*; @RunWith(JUnit4.class) public abstract class ORBasePlatformTestCase extends BasePlatformTestCase { @NotNull @SuppressWarnings("UnusedReturnValue") protected FileBase configureCode(@NotNull String fileName, @NotNull String code) { PsiFile file = myFixture.configureByText(fileName, code); System.out.println("» " + fileName + " " + this.getClass()); System.out.println(DebugUtil.psiToString(file, false, true)); return (FileBase) file; } protected @NotNull String toJson(@NotNull String value) { return value.replaceAll("'", "\"").replaceAll("@", "\n"); } protected @NotNull String loadFile(@NotNull String filename) throws IOException { return FileUtil.loadFile(new File(getTestDataPath(), filename), CharsetToolkit.UTF8, true).trim(); } protected @Nullable String getQuickDoc(@NotNull FileBase file, @NotNull Language lang) { DocumentationProvider docProvider = LanguageDocumentation.INSTANCE.forLanguage(lang); PsiElement resolvedElement = myFixture.getElementAtCaret(); PsiElement element = file.findElementAt(myFixture.getCaretOffset() - 1); return docProvider.getQuickNavigateInfo(resolvedElement, element); } protected @Nullable String getDocForElement(@NotNull FileBase file, @NotNull Language lang, PsiElement resolvedElement) { DocumentationProvider docProvider = LanguageDocumentation.INSTANCE.forLanguage(lang); PsiElement element = file.findElementAt(myFixture.getCaretOffset() - 1); return docProvider.generateDoc(resolvedElement, element); } protected @Nullable String getDoc(@NotNull FileBase file, @NotNull Language lang) { PsiElement resolvedElement = myFixture.getElementAtCaret(); return getDocForElement(file, lang, resolvedElement); } protected @NotNull List findUsages(String fileName) { return (List) myFixture.testFindUsages(fileName); } protected @NotNull PsiElement getNavigationElementAtCaret() { PsiElement elementAtCaret = myFixture.getElementAtCaret(); return TargetElementUtil.getInstance().getGotoDeclarationTarget(elementAtCaret, elementAtCaret.getNavigationElement()); } protected @NotNull List> doHighlight(FileBase f) { myFixture.openFileInEditor(f.getVirtualFile()); myFixture.doHighlighting(); Document document = myFixture.getEditor().getDocument(); return DaemonCodeAnalyzerImpl.getLineMarkers(document, myFixture.getProject()); } } ================================================ FILE: src/test/java/com/reason/ide/ORFileUtilsTest.java ================================================ package com.reason.ide; import com.intellij.psi.*; import com.reason.comp.*; import org.junit.*; import org.junit.runner.*; import org.junit.runners.*; import java.nio.file.*; @SuppressWarnings("ConstantConditions") @RunWith(JUnit4.class) public class ORFileUtilsTest extends ORBasePlatformTestCase { @Test public void test_relative_source_with_namespace() { myFixture.configureByText(ORConstants.BS_CONFIG_FILENAME, toJson("{'name': 'foo', 'namespace': 'foo'}")); PsiFile binary = myFixture.configureByText("Config-Foo.cm", "binary, should be .cmt"); FileSystem fs = FileSystems.getDefault(); String relativeSource = ORFileUtils.toRelativeSourceName(getProject(), ORFileUtils.getVirtualFile(binary), fs.getPath("src/Config-Foo.cmt")); assertEquals(fs.getPath("src", "Config.re").toString(), relativeSource); } } ================================================ FILE: src/test/java/com/reason/ide/RenameLowerTest.java ================================================ package com.reason.ide; import org.junit.*; import org.junit.runner.*; import org.junit.runners.*; @RunWith(JUnit4.class) public class RenameLowerTest extends ORBasePlatformTestCase { @Test public void test_RML_rename_let() { configureCode("A.re", "let x = 1; let z = x + 1;"); myFixture.renameElementAtCaret("y"); myFixture.checkResult("let y = 1; let z = y + 1;"); } @Test public void test_NS_rename_let() { configureCode("A.res", "let x = 1\n let z = x + 1"); myFixture.renameElementAtCaret("y"); myFixture.checkResult("let y = 1\n let z = y + 1"); } @Test public void test_OCL_rename_let() { configureCode("A.ml", "let x = 1\n let z = x + 1"); myFixture.renameElementAtCaret("y"); myFixture.checkResult("let y = 1\n let z = y + 1"); } @Test public void test_RML_rename_type() { configureCode("A.re", "type x; type z = x;"); myFixture.renameElementAtCaret("y"); myFixture.checkResult("type y; type z = y;"); } @Test public void test_NS_rename_type() { configureCode("A.res", "type x\n type z = x"); myFixture.renameElementAtCaret("y"); myFixture.checkResult("type y\n type z = y"); } @Test public void test_OCL_rename_type() { configureCode("A.ml", "type x\n type z = x"); myFixture.renameElementAtCaret("y"); myFixture.checkResult("type y\n type z = y"); } } ================================================ FILE: src/test/java/com/reason/ide/RenameUpper_OCL_Test.java ================================================ package com.reason.ide; import org.junit.*; public class RenameUpper_OCL_Test extends ORBasePlatformTestCase { @Test public void rename_module_interface_declaration() { configureCode("A.ml", """ module A1 = struct module type Intf = sig end end module Impl: A1.Intf = struct end """); myFixture.renameElementAtCaret("NewIntf"); myFixture.checkResult(""" module A1 = struct module type NewIntf = sig end end module Impl: A1.NewIntf = struct end """); } @Test public void rename_module_declaration() { configureCode("A.ml", """ module A1 = struct end module A2 = A1 """); myFixture.renameElementAtCaret("B"); myFixture.checkResult(""" module B = struct end module A2 = B """); } @Test public void rename_functor_declaration() { configureCode("A.ml", """ module Make (M:Def) = struct end module A1 = Make(struct end) """); myFixture.renameElementAtCaret("B"); myFixture.checkResult(""" module B (M:Def) = struct end module A1 = B(struct end) """); } } ================================================ FILE: src/test/java/com/reason/ide/RenameUpper_RES_Test.java ================================================ package com.reason.ide; import org.junit.*; public class RenameUpper_RES_Test extends ORBasePlatformTestCase { @Test public void rename_module_interface_declaration() { configureCode("A.res", """ module A1 = { module type Intf = {} } module Impl: A1.Intf = {} """); myFixture.renameElementAtCaret("NewIntf"); myFixture.checkResult(""" module A1 = { module type NewIntf = {} } module Impl: A1.NewIntf = {} """); } @Test public void rename_module_declaration() { configureCode("A.res", """ module A1 = {} module A2 = A1 """); myFixture.renameElementAtCaret("B"); myFixture.checkResult(""" module B = {} module A2 = B """); } @Test public void rename_functor_declaration() { configureCode("A.res", """ module Make = (M:Def) => {} module A1 = Make({}) """); myFixture.renameElementAtCaret("B"); myFixture.checkResult(""" module B = (M:Def) => {} module A1 = B({}) """); } } ================================================ FILE: src/test/java/com/reason/ide/RenameUpper_RML_Test.java ================================================ package com.reason.ide; import org.junit.*; public class RenameUpper_RML_Test extends ORBasePlatformTestCase { @Test public void rename_module_interface_declaration() { configureCode("A.re", """ module A1 = { module type Intf = {}; }; module Impl: A1.Intf = {}; """); myFixture.renameElementAtCaret("NewIntf"); myFixture.checkResult(""" module A1 = { module type NewIntf = {}; }; module Impl: A1.NewIntf = {}; """); } @Test public void rename_module_declaration() { configureCode("A.re", """ module A1 = {}; module A2 = A1; """); myFixture.renameElementAtCaret("B"); myFixture.checkResult(""" module B = {}; module A2 = B; """); } @Test public void rename_functor_declaration() { configureCode("A.re", """ module Make = (M:Def) => {}; module A1 = Make({}); """); myFixture.renameElementAtCaret("B"); myFixture.checkResult(""" module B = (M:Def) => {}; module A1 = B({}); """); } } ================================================ FILE: src/test/java/com/reason/ide/TypeConversionOclTest.java ================================================ package com.reason.ide; import com.intellij.psi.util.*; import com.reason.ide.files.*; import com.reason.lang.core.psi.*; import com.reason.lang.reason.*; import com.reason.lang.rescript.*; import org.junit.*; import org.junit.runner.*; import org.junit.runners.*; @SuppressWarnings("ConstantConditions") @RunWith(JUnit4.class) public class TypeConversionOclTest extends ORBasePlatformTestCase { public static final RmlLanguage REASON = RmlLanguage.INSTANCE; public static final ResLanguage RESCRIPT = ResLanguage.INSTANCE; @Test public void test_option() { FileBase e = configureCode("A.ml", "val x: int option"); RPsiSignature sig = PsiTreeUtil.findChildOfType(e, RPsiSignature.class); assertEquals("option(int)", sig.asText(REASON)); assertEquals("option", sig.asText(RESCRIPT)); } @Test public void test_option_named_params() { FileBase e = configureCode("A.ml", "external add : x:int option -> y:int -> int = \"\""); RPsiSignature sig = PsiTreeUtil.findChildOfType(e, RPsiSignature.class); assertEquals("(~x:option(int), ~y:int) => int", sig.asText(REASON)); assertEquals("(~x:option, ~y:int) => int", sig.asText(RESCRIPT)); } } ================================================ FILE: src/test/java/com/reason/ide/comment/OclCommenterTest.java ================================================ package com.reason.ide.comment; import com.intellij.codeInsight.generation.actions.*; import com.reason.ide.*; import org.junit.*; import org.junit.runner.*; import org.junit.runners.*; @RunWith(JUnit4.class) public class OclCommenterTest extends ORBasePlatformTestCase { @Test public void test_line_commenter() { configureCode("A.ml", " comment"); CommentByLineCommentAction action = new CommentByLineCommentAction(); action.actionPerformedImpl(getProject(), myFixture.getEditor()); myFixture.checkResult("(* comment *)"); action.actionPerformedImpl(getProject(), myFixture.getEditor()); myFixture.checkResult(" comment "); } @Test public void test_line_uncommenter() { configureCode("A.ml", " (* comment *)"); CommentByLineCommentAction action = new CommentByLineCommentAction(); action.actionPerformedImpl(getProject(), myFixture.getEditor()); myFixture.checkResult(" comment "); } @Test public void test_line_uncommenter_02() { configureCode("A.ml", " (*comment*)"); CommentByLineCommentAction action = new CommentByLineCommentAction(); action.actionPerformedImpl(getProject(), myFixture.getEditor()); myFixture.checkResult(" comment"); } @Test public void test_line_uncommenter_03() { configureCode("A.ml", "\ntest\n(* comment *) "); CommentByLineCommentAction action = new CommentByLineCommentAction(); action.actionPerformedImpl(getProject(), myFixture.getEditor()); myFixture.checkResult("\ntest\n comment "); } /*-- length: 17 / commented: 0-17 / deleteStart: 0-3 / deleteEnd: 14-17 !(* x (* y *) => x (* y *) *)! --*/ @Test public void test_GH_27() { configureCode("A.ml", "(*\n x (* y *)\n*)\n"); CommentByBlockCommentAction action = new CommentByBlockCommentAction(); action.actionPerformedImpl(getProject(), myFixture.getEditor()); myFixture.checkResult(" x (* y *)\n"); } /*-- length: 18 / commented: null (* ...(* x *) (* x *) y => y (* z *)... (* z *) *) --*/ @Test public void test_GH_27b() { configureCode("A.ml", "(* x *)\ny\n(* z *)\n"); CommentByBlockCommentAction commentAction = new CommentByBlockCommentAction(); commentAction.actionPerformedImpl(getProject(), myFixture.getEditor()); myFixture.checkResult("(*\n(* x *)\ny\n(* z *)\n*)\n"); } @Test public void test_GH_27c() { configureCode("A.ml", "(* x *)\n(* z *)\n"); CommentByBlockCommentAction commentAction = new CommentByBlockCommentAction(); commentAction.actionPerformedImpl(getProject(), myFixture.getEditor()); myFixture.checkResult("(*\n(* x *)\n(* z *)\n*)\n"); assertEquals(19, myFixture.getCaretOffset()); } // https://github.com/giraud/reasonml-idea-plugin/issues/319 @Test public void test_GH_319() { configureCode("A.ml", "line with ("); CommentByLineCommentAction a = new CommentByLineCommentAction(); a.actionPerformedImpl(getProject(), myFixture.getEditor()); myFixture.checkResult("(* line with ( *)"); } @Test public void test_GH_319_b() { configureCode("A.ml", " line with ( "); CommentByLineCommentAction a = new CommentByLineCommentAction(); a.actionPerformedImpl(getProject(), myFixture.getEditor()); myFixture.checkResult("(* line with ( *)"); } // https://github.com/giraud/reasonml-idea-plugin/issues/411 @Test public void test_GH_411_comment() { configureCode("A.ml", "Infile of dirpath"); CommentByBlockCommentAction commentAction = new CommentByBlockCommentAction(); commentAction.actionPerformedImpl(getProject(), myFixture.getEditor()); myFixture.checkResult("Infile (* of *) dirpath"); assertEquals(11, myFixture.getCaretOffset()); } @Test public void test_GH_411_uncomment() { configureCode("A.ml", "Infile (* of *) dirpath"); CommentByBlockCommentAction commentAction = new CommentByBlockCommentAction(); commentAction.actionPerformedImpl(getProject(), myFixture.getEditor()); myFixture.checkResult("Infile of dirpath"); assertEquals(9, myFixture.getCaretOffset()); } } ================================================ FILE: src/test/java/com/reason/ide/completion/CommentCompletion_OCL_Test.java ================================================ package com.reason.ide.completion; import com.intellij.codeInsight.completion.*; import com.intellij.testFramework.fixtures.*; import org.jetbrains.annotations.*; import org.junit.*; import org.junit.runner.*; import org.junit.runners.*; import java.util.*; @RunWith(JUnit4.class) public class CommentCompletion_OCL_Test extends BasePlatformTestCase { @Override protected @NotNull String getTestDataPath() { return "src/test/testData/com/reason/lang"; } @Test public void testCommentCompletion() { myFixture.configureByFiles("pervasives.ml"); myFixture.configureByText("Comment.ml", "(**)"); myFixture.complete(CompletionType.BASIC, 1); List strings = myFixture.getLookupElementStrings(); assertNullOrEmpty(strings); } @Test public void testCommentStartCompletion() { myFixture.configureByFiles("pervasives.ml"); myFixture.configureByText("Comment.ml", "(*"); myFixture.complete(CompletionType.BASIC, 1); List strings = myFixture.getLookupElementStrings(); assertNullOrEmpty(strings); } } ================================================ FILE: src/test/java/com/reason/ide/completion/CommentCompletion_RES_Test.java ================================================ package com.reason.ide.completion; import com.intellij.codeInsight.completion.*; import com.intellij.testFramework.fixtures.*; import org.jetbrains.annotations.*; import org.junit.*; import org.junit.runner.*; import org.junit.runners.*; import java.util.*; @RunWith(JUnit4.class) public class CommentCompletion_RES_Test extends BasePlatformTestCase { @Override protected @NotNull String getTestDataPath() { return "src/test/testData/com/reason/lang"; } @Test public void testCommentCompletion() { myFixture.configureByFiles("pervasives.ml"); myFixture.configureByText("Comment.res", "/**/"); myFixture.complete(CompletionType.BASIC, 1); List strings = myFixture.getLookupElementStrings(); assertNullOrEmpty(strings); } @Test public void testCommentStartCompletion() { myFixture.configureByFiles("pervasives.ml"); myFixture.configureByText("Comment.res", "/*"); myFixture.complete(CompletionType.BASIC, 1); List strings = myFixture.getLookupElementStrings(); assertNullOrEmpty(strings); } } ================================================ FILE: src/test/java/com/reason/ide/completion/CommentCompletion_RML_Test.java ================================================ package com.reason.ide.completion; import com.intellij.codeInsight.completion.CompletionType; import com.intellij.testFramework.fixtures.BasePlatformTestCase; import java.util.List; import org.jetbrains.annotations.NotNull; import org.junit.*; import org.junit.runner.*; import org.junit.runners.*; @RunWith(JUnit4.class) public class CommentCompletion_RML_Test extends BasePlatformTestCase { @Override protected @NotNull String getTestDataPath() { return "src/test/testData/com/reason/lang"; } @Test public void testCommentCompletion() { myFixture.configureByFiles("pervasives.ml"); myFixture.configureByText("Comment.re", "/**/"); myFixture.complete(CompletionType.BASIC, 1); List strings = myFixture.getLookupElementStrings(); assertNullOrEmpty(strings); } @Test public void testCommentStartCompletion() { myFixture.configureByFiles("pervasives.ml"); myFixture.configureByText("Comment.re", "/*"); myFixture.complete(CompletionType.BASIC, 1); List strings = myFixture.getLookupElementStrings(); assertNullOrEmpty(strings); } } ================================================ FILE: src/test/java/com/reason/ide/completion/DotCompletion_OCL_Test.java ================================================ package com.reason.ide.completion; import com.intellij.codeInsight.completion.*; import com.reason.ide.*; import org.junit.*; import java.util.*; @SuppressWarnings("ConstantConditions") public class DotCompletion_OCL_Test extends ORBasePlatformTestCase { @Test public void test_basic() { configureCode("A.ml", "let x = 1"); configureCode("B.ml", "type t let y = 2 module B = struct end let _ = A."); myFixture.complete(CompletionType.BASIC, 1); List strings = myFixture.getLookupElementStrings(); assertSameElements(strings, "x"); } @Test public void test_module_override() { configureCode("A.ml", "let x = 1"); configureCode("B.ml", "module A = struct let y = 2 end let _ = A."); myFixture.complete(CompletionType.BASIC, 1); List strings = myFixture.getLookupElementStrings(); assertSameElements(strings, "y"); } @Test public void test_before_caret() { configureCode("A.ml", "type x"); configureCode("B.ml", "let _ = A."); myFixture.complete(CompletionType.BASIC, 1); List strings = myFixture.getLookupElementStrings(); assertSameElements(strings, "x"); } @Test public void test_end_of_file() { configureCode("A.ml", "type t"); configureCode("B.ml", "let _ = A."); myFixture.complete(CompletionType.BASIC, 1); List strings = myFixture.getLookupElementStrings(); assertSameElements(strings, "t"); } @Test public void test_single_alias() { configureCode("C.mli", "type t"); configureCode("B.mli", "module B1 = C"); configureCode("A.ml", "let _ = B.B1."); myFixture.complete(CompletionType.BASIC, 1); List strings = myFixture.getLookupElementStrings(); assertSize(1, strings); assertEquals("t", strings.getFirst()); } @Test public void test_multiple_alias() { // like Belt configureCode("string.mli", "external length : string -> int = \"%string_length\""); configureCode("belt_MapString.mli", "type key = string"); configureCode("belt_Map.mli", "module String = Belt_MapString"); configureCode("belt.ml", "module Map = Belt_Map"); configureCode("Dummy.re", "Belt.Map.String."); myFixture.complete(CompletionType.BASIC, 1); List strings = myFixture.getLookupElementStrings(); assertSize(1, strings); assertEquals("key", strings.getFirst()); } @Test public void test_alias() { configureCode("A.ml", "module A1 = struct end"); configureCode("B.ml", "module B1 = struct include A end"); configureCode("C.ml", "module C1 = B.B1."); myFixture.complete(CompletionType.BASIC, 1); List strings = myFixture.getLookupElementStrings(); assertSize(1, strings); assertEquals("A1", strings.getFirst()); } @Test public void test_alias_of_alternates() { configureCode("A.ml", """ module A1 = struct module A2 = struct let id = "_new_" end end """); configureCode("B.ml", """ module B1 = struct module B2 = struct module B3 = struct let id = A.A1.A2.id end end end module B4 = struct include A module B5 = B1.B2 end """); configureCode("C.ml", """ module C1 = B.B4 let _ = C1. """); myFixture.complete(CompletionType.BASIC, 1); List elements = myFixture.getLookupElementStrings(); assertContainsElements(elements, "A1", "B5"); assertSize(2, elements); } @Test public void test_no_pervasives() { configureCode("pervasives.mli", "val int_of_string : str -> int"); configureCode("belt_Array.mli", "val length: t -> int"); configureCode("belt.ml", "module Array = Belt_Array"); configureCode("Dummy.re", "Belt.Array."); myFixture.complete(CompletionType.BASIC, 1); List strings = myFixture.getLookupElementStrings(); assertSize(1, strings); assertEquals("length", strings.getFirst()); } @Test public void test_functor_no_return_type() { configureCode("A.ml", "module type Intf = sig val x : bool end\n module MakeOcl(I:Intf) = struct let y = 1 end"); configureCode("B.ml", "open A\n module Instance = MakeOcl(struct let x = true end)"); configureCode("C.ml", "open B let _ = Instance."); myFixture.complete(CompletionType.BASIC, 1); List elements = myFixture.getLookupElementStrings(); assertSameElements(elements, "y"); } @Test public void test_functor_with_return_type() { configureCode("A.ml", "module type Intf = sig val x : bool end\n module MakeIntf(I:Intf) : Intf = struct let y = 1 end"); configureCode("B.ml", "open A\n module Instance = MakeIntf(struct let x = true end)"); configureCode("C.ml", "open B let _ = Instance."); myFixture.complete(CompletionType.BASIC, 1); List elements = myFixture.getLookupElementStrings(); assertSameElements(elements, "x"); } @Test public void test_functor_include() { configureCode("A.ml", "module type Intf = sig val x : bool end\n module MakeIntf(I:Intf) = struct let y = 1 end"); configureCode("B.ml", "include A.MakeIntf(struct let x = true end)"); configureCode("C.ml", "let _ = B."); myFixture.complete(CompletionType.BASIC, 1); List elements = myFixture.getLookupElementStrings(); assertSameElements(elements, "y"); } @Test public void test_parameter() { configureCode("A.ml", """ type store = {x: int; y: int} let fn (store: store) = store. """); myFixture.completeBasic(); List elements = myFixture.getLookupElementStrings(); assertContainsElements(elements, "x", "y"); } // https://github.com/giraud/reasonml-idea-plugin/issues/452 @Test public void test_GH_452_unpacked_module() { configureCode("A.ml", """ module type I = sig val x: int end let x ~p:(p:(module I)) = let module S = (val p) in S. }; """); myFixture.completeBasic(); List elements = myFixture.getLookupElementStrings(); assertSize(1, elements); assertContainsElements(elements, "x"); } } ================================================ FILE: src/test/java/com/reason/ide/completion/DotCompletion_RES_Test.java ================================================ package com.reason.ide.completion; import com.intellij.codeInsight.completion.*; import com.reason.ide.*; import org.junit.*; import java.util.*; @SuppressWarnings("ConstantConditions") public class DotCompletion_RES_Test extends ORBasePlatformTestCase { @Test public void test_basic() { configureCode("A.res", "let x = 1"); configureCode("B.res", """ type t let y = 2 module B = {} A. """); myFixture.complete(CompletionType.BASIC, 1); List strings = myFixture.getLookupElementStrings(); assertSameElements(strings, "x"); } @Test public void test_module_override() { configureCode("A.res", "let x = 1"); configureCode("B.res", """ module A = { let y = 2 } A. """); myFixture.complete(CompletionType.BASIC, 1); List strings = myFixture.getLookupElementStrings(); assertSameElements(strings, "y"); } @Test public void test_before_caret() { configureCode("A.res", "type x"); configureCode("B.res", "A."); myFixture.complete(CompletionType.BASIC, 1); List strings = myFixture.getLookupElementStrings(); assertSameElements(strings, "x"); } @Test public void test_end_of_file() { configureCode("A.res", "type x"); configureCode("B.res", "A."); myFixture.complete(CompletionType.BASIC, 1); List elements = myFixture.getLookupElementStrings(); assertSameElements(elements, "x"); } @Test public void test_single_alias() { configureCode("C.resi", "type t"); configureCode("B.resi", "module B1 = C"); configureCode("A.res", "B.B1."); myFixture.complete(CompletionType.BASIC, 1); List strings = myFixture.getLookupElementStrings(); assertSize(1, strings); assertEquals("t", strings.getFirst()); } @Test public void test_alias_in_file() { // like ReasonReact.Router configureCode("View.res", "module Detail = { let alias = \"a\" }"); configureCode("Dummy.res", """ module V = View.Detail V. """); myFixture.complete(CompletionType.BASIC, 1); List strings = myFixture.getLookupElementStrings(); assertSize(1, strings); assertEquals("alias", strings.getFirst()); } @Test public void test_alias() { configureCode("A.res", "module A1 = {}"); configureCode("B.res", "module B1 = { include A }"); configureCode("C.res", "module C1 = B.B1."); myFixture.complete(CompletionType.BASIC, 1); List strings = myFixture.getLookupElementStrings(); assertSize(1, strings); assertEquals("A1", strings.getFirst()); } @Test public void test_alias_of_alternates() { configureCode("A.res", """ module A1 = { module A2 = { let id = "_new_" } } """); configureCode("B.res", """ module B1 = { module B2 = { module B3 = { let id = A.A1.A2.id } } } module B4 = { include A module B5 = B1.B2 } """); configureCode("C.res", """ module C1 = B.B4 let _ = C1. """); myFixture.complete(CompletionType.BASIC, 1); List elements = myFixture.getLookupElementStrings(); assertContainsElements(elements, "A1", "B5"); assertSize(2, elements); } @Test public void test_uncurried() { configureCode("Aa.res", "let x = 1"); configureCode("B.res", "send(. )"); // should use free completion myFixture.complete(CompletionType.BASIC, 1); List strings = myFixture.getLookupElementStrings(); assertSameElements(strings, "Aa"); } @Test public void test_functor_no_return_type() { configureCode("A.res", "module type Intf = { let x: bool }\n module MakeIntf = (I:Intf) => { let y = 1 }"); configureCode("B.res", "open A\n module Instance = MakeIntf({let x = true})"); configureCode("C.res", "open B\n Instance."); myFixture.complete(CompletionType.BASIC, 1); List elements = myFixture.getLookupElementStrings(); assertSameElements(elements, "y"); } @Test public void test_functor_with_return_type() { configureCode("A.res", "module type Intf = { let x: bool }\n module MakeIntf = (I:Intf) : Intf => { let y = 1 }"); configureCode("B.res", "open A\n module Instance = MakeIntf({let x = true})"); configureCode("C.res", "open B\n Instance."); myFixture.complete(CompletionType.BASIC, 1); List elements = myFixture.getLookupElementStrings(); assertSameElements(elements, "x"); } @Test public void test_functor_include() { configureCode("A.res", "module type Intf = { let x: bool }\n module MakeIntf = (I:Intf) => { let y = 1 }"); configureCode("B.res", "include A.MakeIntf({ let x = true })"); configureCode("C.res", "B."); myFixture.complete(CompletionType.BASIC, 1); List elements = myFixture.getLookupElementStrings(); assertSameElements(elements, "y"); } @Test public void test_functor_include_multiple_choice() { configureCode("A.res", "module Make = (I:{}) => { let a = 1 }"); configureCode("B.res", "module Make = (I:{}) => { let b = 1 }\n include Make({})"); configureCode("C.res", "B."); myFixture.complete(CompletionType.BASIC, 1); List elements = myFixture.getLookupElementStrings(); assertSameElements(elements, "Make", "b"); } @Test public void test_functor_include_alias() { configureCode("A.res", "module type Intf = { let x: bool }\n module MakeIntf = (I:Intf) => { let y = 1 }"); configureCode("B.res", "module Instance = A.MakeIntf({let x = true})\n include Instance"); configureCode("C.res", "B."); myFixture.complete(CompletionType.BASIC, 1); List elements = myFixture.getLookupElementStrings(); assertSameElements(elements, "Instance", "y"); } @Test public void test_result_with_alias() { configureCode("A.res", "module type Result = { let a: int }"); configureCode("B.res", "module T = A\n module Make = (M:Intf): T.Result => { let b = 3 }"); configureCode("C.res", "module Instance = B.Make({})\n let c = Instance."); myFixture.complete(CompletionType.BASIC, 1); List elements = myFixture.getLookupElementStrings(); assertSameElements(elements, "a"); } @Test public void test_result_with_alias2() { configureCode("A.res", "module type Result = { let a: int }"); configureCode("B.res", "module Make = (M:Intf): (A.Result with type t := M.t) => {}\n module Instance = Make({})"); configureCode("C.res", "B.Instance."); myFixture.complete(CompletionType.BASIC, 1); List elements = myFixture.getLookupElementStrings(); assertSameElements(elements, "a"); } @Test public void test_variant() { configureCode("A.res", "type color = | Black | Red"); configureCode("B.res", "A."); myFixture.completeBasic(); List elements = myFixture.getLookupElementStrings(); assertSize(3, elements); assertContainsElements(elements, "color", "Black", "Red"); } @Test public void test_parameter() { configureCode("A.res", """ type store = {x: int, y: int} let fn = (store: store) => store. """); myFixture.completeBasic(); List elements = myFixture.getLookupElementStrings(); assertContainsElements(elements, "x", "y"); } // https://github.com/giraud/reasonml-idea-plugin/issues/452 @Test public void test_GH_452_unpacked_module() { configureCode("A.res", """ module type I = { let x: int } let x = (~p: module(I)) => { module S = unpack(p) S. }; """); myFixture.completeBasic(); List elements = myFixture.getLookupElementStrings(); assertSize(1, elements); assertContainsElements(elements, "x"); } } ================================================ FILE: src/test/java/com/reason/ide/completion/DotCompletion_RML_Test.java ================================================ package com.reason.ide.completion; import com.intellij.codeInsight.completion.*; import com.reason.ide.*; import org.junit.*; import java.util.*; @SuppressWarnings("ConstantConditions") public class DotCompletion_RML_Test extends ORBasePlatformTestCase { @Test public void test_basic() { configureCode("A.re", "let x = 1;"); configureCode("B.re", "type t; let y = 2; module B = {}; A."); myFixture.complete(CompletionType.BASIC, 1); List strings = myFixture.getLookupElementStrings(); assertSameElements(strings, "x"); } @Test public void test_module_override() { configureCode("A.re", "let x = 1;"); configureCode("B.re", "module A = { let y = 2; }; A."); myFixture.complete(CompletionType.BASIC, 1); List strings = myFixture.getLookupElementStrings(); assertSameElements(strings, "y"); } @Test public void test_before_caret() { configureCode("A.re", "type x;"); configureCode("B.re", "A.;"); myFixture.complete(CompletionType.BASIC, 1); List strings = myFixture.getLookupElementStrings(); assertSameElements(strings, "x"); } @Test public void test_end_of_file() { configureCode("A.re", "type x;"); configureCode("B.re", "A."); myFixture.complete(CompletionType.BASIC, 1); List elements = myFixture.getLookupElementStrings(); assertSameElements(elements, "x"); } @Test public void test_single_alias() { configureCode("C.rei", "type t;"); configureCode("B.rei", "module B1 = C;"); configureCode("A.re", "B.B1."); myFixture.complete(CompletionType.BASIC, 1); List strings = myFixture.getLookupElementStrings(); assertSize(1, strings); assertEquals("t", strings.getFirst()); } @Test public void test_alias_in_file() { // like ReasonReact.Router configureCode("View.re", "module Detail = { let alias = \"a\"; };"); configureCode("Dummy.re", "module V = View.Detail; V."); myFixture.complete(CompletionType.BASIC, 1); List strings = myFixture.getLookupElementStrings(); assertSize(1, strings); assertEquals("alias", strings.getFirst()); } @Test public void test_alias() { configureCode("A.re", "module A1 = {};"); configureCode("B.re", "module B1 = { include A; };"); configureCode("C.re", "module C1 = B.B1."); myFixture.complete(CompletionType.BASIC, 1); List elements = myFixture.getLookupElementStrings(); assertSize(1, elements); assertEquals("A1", elements.getFirst()); } @Test public void test_alias_and_alternates() { configureCode("A.res", """ module A1 = { module A2 = { let id = "_new_" } } """); configureCode("B.res", """ module B1 = { module B2 = { module B3 = { let id = A.A1.A2.id } } } module B4 = { include A module B5 = B1.B2 } """); configureCode("C.res", """ module C1 = B.B4 let _ = C1. """); myFixture.complete(CompletionType.BASIC, 1); List elements = myFixture.getLookupElementStrings(); assertContainsElements(elements, "A1", "B5"); assertSize(2, elements); } @Test public void test_uncurried() { configureCode("A.re", "let x = 1;"); configureCode("B.re", "send(. )"); // should use free completion myFixture.complete(CompletionType.BASIC, 1); List strings = myFixture.getLookupElementStrings(); assertSameElements(strings, "A"); } @Test public void test_let_private() { configureCode("A.re", "let x%private = 1;"); configureCode("B.re", "A."); myFixture.complete(CompletionType.BASIC, 1); List strings = myFixture.getLookupElementStrings(); assertEmpty(strings); } @Test public void test_functor_no_return_type() { configureCode("A.re", "module type Intf = { let x: bool; }; module MakeIntf = (I:Intf) => { let y = 1; };"); configureCode("B.re", "open A; module Instance = MakeIntf({let x = true});"); configureCode("C.re", "open B; Instance."); myFixture.complete(CompletionType.BASIC, 1); List elements = myFixture.getLookupElementStrings(); assertSameElements(elements, "y"); } @Test public void test_functor_with_return_type() { configureCode("A.re", "module type Intf = { let x: bool; }; module type Sig = { let y: int}; module MakeIntf = (I:Intf) : Sig => { let y = 1; };"); configureCode("B.re", "open A; module Instance = MakeIntf({let x = true});"); configureCode("C.re", "open B; Instance."); myFixture.complete(CompletionType.BASIC, 1); List elements = myFixture.getLookupElementStrings(); assertSameElements(elements, "y"); } @Test public void test_functor_include() { configureCode("A.re", "module type Intf = { let x: bool; }; module MakeIntf = (I:Intf) => { let y = 1; };"); configureCode("B.re", "include A.MakeIntf({ let x = true; });"); configureCode("C.re", "B."); myFixture.complete(CompletionType.BASIC, 1); List elements = myFixture.getLookupElementStrings(); assertSameElements(elements, "y"); } @Test public void test_functor_include_multiple_choice() { configureCode("A.re", "module Make = (I:{}) => { let a = 1; };"); configureCode("B.re", "module Make = (I:{}) => { let b = 1; }; include Make({});"); configureCode("C.re", "B."); myFixture.complete(CompletionType.BASIC, 1); List elements = myFixture.getLookupElementStrings(); assertSameElements(elements, "Make", "b"); } @Test public void test_functor_include_alias() { configureCode("A.re", "module type Intf = { let x: bool; }; module MakeIntf = (I:Intf) => { let y = 1; };"); configureCode("B.re", "module Instance = A.MakeIntf({let x = true}); include Instance;"); configureCode("C.re", "B."); myFixture.complete(CompletionType.BASIC, 1); List elements = myFixture.getLookupElementStrings(); assertSameElements(elements, "Instance", "y"); } @Test public void test_result_with_alias() { configureCode("A.re", "module type Result = { let a: int; };"); configureCode("B.re", "module T = A; module Make = (M:Intf): T.Result => { let b = 3; };"); configureCode("C.re", "module Instance = B.Make({}); let c = Instance.;"); myFixture.complete(CompletionType.BASIC, 1); List elements = myFixture.getLookupElementStrings(); assertSameElements(elements, "a"); } @Test public void test_result_with_alias2() { configureCode("A.re", "module type Result = { let a: int; };"); configureCode("B.re", "module Make = (M:Intf): (A.Result with type t := M.t) => {}; module Instance = Make({});"); configureCode("C.re", "B.Instance."); myFixture.complete(CompletionType.BASIC, 1); List elements = myFixture.getLookupElementStrings(); assertSameElements(elements, "a"); } @Test public void test_variant() { configureCode("A.re", "type color = | Black | Red;"); configureCode("B.re", "A."); myFixture.completeBasic(); List elements = myFixture.getLookupElementStrings(); assertSize(3, elements); assertContainsElements(elements, "color", "Black", "Red"); } @Test public void test_parameter() { configureCode("A.re", """ type store = {x: int, y: int}; let fn = (store: store) => store. """); myFixture.completeBasic(); List elements = myFixture.getLookupElementStrings(); assertContainsElements(elements, "x", "y"); } // https://github.com/giraud/reasonml-idea-plugin/issues/452 @Test public void test_GH_452_unpacked_module() { configureCode("A.re", """ module type I = { let x: int; }; let x = (~p: (module I)) => { module S = (val p); S. }; """); myFixture.completeBasic(); List elements = myFixture.getLookupElementStrings(); assertSize(1, elements); assertContainsElements(elements, "x"); } } ================================================ FILE: src/test/java/com/reason/ide/completion/FreeCompletion_OCL_Test.java ================================================ package com.reason.ide.completion; import com.intellij.codeInsight.completion.*; import com.reason.ide.*; import com.reason.ide.insight.*; import org.junit.*; import java.util.*; @SuppressWarnings("DataFlowIssue") public class FreeCompletion_OCL_Test extends ORBasePlatformTestCase { @Test public void test_pervasives() { configureCode("pervasives.mli", "val int_of_string : str -> int"); configureCode("pervasives.ml", "let int_of_string : x -> 42"); configureCode("belt_Array.mli", "val length: t -> int"); configureCode("belt.ml", "module Array = Belt_Array"); configureCode("Dummy.ml", "let x = "); myFixture.complete(CompletionType.BASIC, 1); List elements = myFixture.getLookupElementStrings(); assertContainsElements(elements, "int_of_string", "Belt", "Belt_Array", "Pervasives"); assertSize(4, elements); } @Test public void test_underscore() { configureCode("Dummy.re", "let _ = 1; "); myFixture.complete(CompletionType.BASIC, 1); List elements = myFixture.getLookupElementStrings(); assertSameElements(elements, OclKeywordCompletionContributor.KEYWORDS); assertSize(OclKeywordCompletionContributor.KEYWORDS.length, elements); } @Test public void test_deconstruction() { configureCode("Dummy.ml", """ let (first, second) = myVar """); myFixture.complete(CompletionType.BASIC, 1); List elements = myFixture.getLookupElementStrings(); assertContainsElements(elements, "first", "second"); } @Test public void test_include() { configureCode("Aa.ml", "let x = 1"); configureCode("B.ml", """ include Aa """); myFixture.complete(CompletionType.BASIC, 1); List strings = myFixture.getLookupElementStrings(); assertContainsElements(strings, "x", "Aa"); } @Test public void test_include_after() { myFixture.configureByText("A.ml", "let x = 1;"); myFixture.configureByText("B.ml", """ include A """); myFixture.complete(CompletionType.BASIC, 1); List strings = myFixture.getLookupElementStrings(); assertContainsElements(strings, "A"); } @Test public void test_include_eof() { myFixture.configureByText("A.ml", "let x = 1"); myFixture.configureByText("B.ml", """ include A let y = 2 """); myFixture.complete(CompletionType.BASIC, 1); List strings = myFixture.getLookupElementStrings(); assertSameElements(strings, "exception", "external", "include", "let", "module", "open", "type", "A", "y", "x"); assertSize(10, strings); } @Test public void test_include_functor() { configureCode("A.ml", """ module type I = sig type renderer end module type R = sig type rule val style : unit -> rule array end module Core = struct let color = "red" module Make(_:I) : R = struct type rule let style () = [||] end end module Css = struct include Core include Core.Make(struct type renderer end) end open Css let y = """); myFixture.complete(CompletionType.BASIC, 1); List strings = myFixture.getLookupElementStrings(); assertSameElements(strings, "color", "Core", "Css", "I", "Make", "R", "style", "y"); // <- y because caret is not inside the let binding } @Test public void test_open_include() { configureCode("A.ml", "let x = 1"); configureCode("B.ml", "include A"); configureCode("C.ml", "include B"); configureCode("D.ml", """ open C let _ = """); myFixture.complete(CompletionType.BASIC, 1); List strings = myFixture.getLookupElementStrings(); assertContainsElements(strings, "A", "B", "C", "x"); } @Test public void test_parameters() { configureCode("A.ml", "let fn newValue newUnit = n"); myFixture.complete(CompletionType.BASIC, 1); List strings = myFixture.getLookupElementStrings(); assertContainsElements(strings, "newValue", "newUnit"); } @Test public void test_named_parameters() { configureCode("A.ml", "let fn ?newOther ~newValue ~newUnit:(newUnit : string option) = let x = n"); myFixture.complete(CompletionType.BASIC, 1); List strings = myFixture.getLookupElementStrings(); assertContainsElements(strings, "newValue", "newUnit", "newOther"); } @Test public void test_GH_246() { configureCode("A.ml", """ let fn newValue newUnit = setSomething(fun _ -> { value = n """); myFixture.complete(CompletionType.BASIC, 1); List strings = myFixture.getLookupElementStrings(); assertContainsElements(strings, "newValue", "newUnit"); } @Test public void test_GH_496_local_polyvariant() { configureCode("A.ml", """ type color = [ | `blue | `red ] type font = [ | `normal | `bold ] let x = """); myFixture.complete(CompletionType.BASIC, 1); List strings = myFixture.getLookupElementStrings(); assertContainsElements(strings, "`blue", "`red", "`normal", "`bold"); assertSize(4, strings); } @Test public void test_GH_496_external_polyvariant() { configureCode("A.ml", """ type color = [ `blue | `red ] type font = [ `normal | `bold ]"""); configureCode("B.ml", """ open A let x = """); myFixture.complete(CompletionType.BASIC, 1); List strings = myFixture.getLookupElementStrings(); assertContainsElements(strings, "A", "`blue", "`red", "`normal", "`bold"); assertSize(5, strings); } } ================================================ FILE: src/test/java/com/reason/ide/completion/FreeCompletion_RES_Test.java ================================================ package com.reason.ide.completion; import com.intellij.codeInsight.completion.*; import com.reason.ide.*; import com.reason.ide.insight.*; import org.junit.*; import java.util.*; @SuppressWarnings("ConstantConditions") public class FreeCompletion_RES_Test extends ORBasePlatformTestCase { @Test public void test_pervasives() { configureCode("pervasives.mli", "val int_of_string : str -> int"); configureCode("belt_Array.mli", "val length: t -> int"); configureCode("belt.ml", "module Array = Belt_Array"); configureCode("Dummy.res", "let x = "); myFixture.complete(CompletionType.BASIC, 1); List elements = myFixture.getLookupElementStrings(); assertContainsElements(elements, "int_of_string", "Belt", "Belt_Array", "Pervasives"); assertSize(4, elements); } @Test public void test_underscore() { configureCode("Dummy.res", """ let _ = 1 """); myFixture.complete(CompletionType.BASIC, 1); List elements = myFixture.getLookupElementStrings(); assertSameElements(elements, ResKeywordCompletionContributor.KEYWORDS); assertSize(ResKeywordCompletionContributor.KEYWORDS.length, elements); } @Test public void test_deconstruction() { configureCode("Dummy.res", """ let (first, second) = myVar """); myFixture.complete(CompletionType.BASIC, 1); List elements = myFixture.getLookupElementStrings(); assertContainsElements(elements, "first", "second"); } @Test public void test_include() { configureCode("Aa.res", "let x = 1"); configureCode("B.res", """ include Aa """); myFixture.complete(CompletionType.BASIC, 1); List strings = myFixture.getLookupElementStrings(); assertContainsElements(strings, "x", "Aa"); } @Test public void test_include_after() { myFixture.configureByText("A.res", "let x = 1"); myFixture.configureByText("B.res", """ include A """); myFixture.complete(CompletionType.BASIC, 1); List strings = myFixture.getLookupElementStrings(); assertContainsElements(strings, "A"); } @Test public void test_include_eof() { myFixture.configureByText("A.res", "let x = 1"); myFixture.configureByText("B.res", """ include A let y = 2 """); myFixture.complete(CompletionType.BASIC, 1); List strings = myFixture.getLookupElementStrings(); assertSameElements(strings, "exception", "external", "include", "let", "module", "open", "type", "A", "y", "x"); assertSize(10, strings); } @Test public void test_include_functor() { configureCode("A.res", """ module type I = { type renderer } module type R = { type rule let style: unit => array } module Core = { let color = "red" module Make = (I): R => { type rule let style = () => [] } } module Css = { include Core include Core.Make({ type renderer }) }; open Css let y = """); myFixture.complete(CompletionType.BASIC, 1); List strings = myFixture.getLookupElementStrings(); assertSameElements(strings, "color", "Core", "Css", "I", "Make", "R", "style", "y"); // <- y because caret is not inside the let binding } @Test public void test_open_include() { configureCode("A.res", "let x = 1"); configureCode("B.res", "include A"); configureCode("C.res", "include B"); configureCode("D.res", """ open C """); myFixture.complete(CompletionType.BASIC, 1); List strings = getLookupStrings(); assertSameElements(strings, "A", "B", "C", "x"); } @Test public void test_parameters() { configureCode("A.res", "let fn = (newValue, newUnit) => { n"); myFixture.complete(CompletionType.BASIC, 1); List strings = getLookupStrings(); assertSameElements(strings, "newValue", "newUnit"); } @Test public void test_named_parameters() { configureCode("A.res", "let fn = (~newValue, ~newUnit:option, ~newOther=?) => { n"); myFixture.complete(CompletionType.BASIC, 1); List strings = getLookupStrings(); assertSameElements(strings, "newValue", "newUnit", "newOther"); } @Test public void test_GH_246() { configureCode("A.res", """ let fn = (newValue, newUnit) => { setSomething(_ => {value: n } """); myFixture.complete(CompletionType.BASIC, 1); List strings = getLookupStrings(); assertSameElements(strings, "newValue", "newUnit"); } @Test public void test_GH_496_local_polyvariant() { configureCode("A.res", """ type color = [ | #blue | #red ]; type font = [ | #normal | #bold ]; let x = """); myFixture.complete(CompletionType.BASIC, 1); List strings = myFixture.getLookupElementStrings(); assertContainsElements(strings, "#blue", "#red", "#normal", "#bold"); assertSize(4, strings); } @Test public void test_GH_496_external_polyvariant() { configureCode("A.ml", """ type color = [ `blue | `red ] type font = [ `normal | `bold ]"""); configureCode("B.res", """ open A let x = """); myFixture.complete(CompletionType.BASIC, 1); List strings = myFixture.getLookupElementStrings(); assertContainsElements(strings, "A", "#blue", "#red", "#normal", "#bold"); assertSize(5, strings); } private List getLookupStrings() { List elements = myFixture.getLookupElementStrings(); elements.removeAll(List.of(ResKeywordCompletionContributor.KEYWORDS)); return elements; } } ================================================ FILE: src/test/java/com/reason/ide/completion/FreeCompletion_RML_Test.java ================================================ package com.reason.ide.completion; import com.intellij.codeInsight.completion.*; import com.reason.comp.*; import com.reason.ide.*; import com.reason.ide.insight.*; import org.junit.*; import java.util.*; @SuppressWarnings("ConstantConditions") public class FreeCompletion_RML_Test extends ORBasePlatformTestCase { @Override protected String getTestDataPath() { return "src/test/testData/ns"; } @Test public void test_pervasives() { configureCode("pervasives.mli", "val int_of_string : str -> int"); configureCode("belt_Array.mli", "val length: t -> int"); configureCode("belt.ml", "module Array = Belt_Array"); configureCode("Dummy.re", "let x = "); myFixture.complete(CompletionType.BASIC, 1); List elements = myFixture.getLookupElementStrings(); assertContainsElements(elements, "int_of_string", "Belt", "Belt_Array", "Pervasives"); assertSize(4, elements); } @Test public void test_underscore() { configureCode("Dummy.re", "let _ = 1; "); myFixture.complete(CompletionType.BASIC, 1); List elements = myFixture.getLookupElementStrings(); assertSameElements(elements, RmlKeywordCompletionContributor.KEYWORDS); assertSize(RmlKeywordCompletionContributor.KEYWORDS.length, elements); } @Test public void test_deconstruction() { configureCode("Dummy.re", "let (first, second) = myVar; "); myFixture.complete(CompletionType.BASIC, 1); List elements = myFixture.getLookupElementStrings(); assertContainsElements(elements, "first", "second"); } @Test public void test_let_private_from_outside() { configureCode("A.re", "let x%private = 1;"); configureCode("B.re", "open A; "); myFixture.complete(CompletionType.BASIC, 1); List elements = myFixture.getLookupElementStrings(); assertDoesntContain(elements, "x"); } @Test public void test_let_private_from_inside() { configureCode("A.re", "let x%private = 1; "); myFixture.complete(CompletionType.BASIC, 1); List elements = myFixture.getLookupElementStrings(); assertContainsElements(elements, "x"); } @Test public void test_include() { myFixture.configureByText("A.re", "let x = 1;"); myFixture.configureByText("B.re", """ include A; """); myFixture.complete(CompletionType.BASIC, 1); List strings = myFixture.getLookupElementStrings(); assertContainsElements(strings, "x", "A"); } @Test public void test_include_after() { myFixture.configureByText("A.re", "let x = 1;"); myFixture.configureByText("B.re", """ include A; """); myFixture.complete(CompletionType.BASIC, 1); List strings = myFixture.getLookupElementStrings(); assertContainsElements(strings, "A"); } @Test public void test_include_eof() { myFixture.configureByText("A.re", "let x = 1;"); myFixture.configureByText("B.re", """ include A; let y = 2; """); myFixture.complete(CompletionType.BASIC, 1); List strings = myFixture.getLookupElementStrings(); assertSameElements(strings, "exception", "external", "include", "let", "module", "open", "type", "A", "y", "x"); assertSize(10, strings); } @Test public void test_namespace() { myFixture.configureByFile(ORConstants.BS_CONFIG_FILENAME); configureCode("A.re", "module A1 = {};"); configureCode("B.re", ""); myFixture.complete(CompletionType.BASIC, 1); List strings = myFixture.getLookupElementStrings(); assertSameElements(strings, "exception", "external", "include", "let", "module", "open", "type", "MyNamespace"); assertSize(8, strings); } @Test public void test_include_functor() { configureCode("A.re", """ module type I = { type renderer; }; module type R = { type rule; let style: unit => array(rule); }; module Core = { let color = "red"; module Make = (I): R => { type rule; let style = () => [||]; }; } module Css = { include Core include Core.Make({type renderer;}); }; open Css; let y = """); myFixture.complete(CompletionType.BASIC, 1); List strings = myFixture.getLookupElementStrings(); assertSameElements(strings, "color", "Core", "Css", "I", "Make", "R", "style", "y"); // <- y because caret is not inside the let binding } @Test public void test_open_include() { configureCode("A.re", "let x = 1;"); configureCode("B.re", "include A;"); configureCode("C.re", "include B;"); configureCode("D.re", """ open C; """); myFixture.complete(CompletionType.BASIC, 1); List strings = myFixture.getLookupElementStrings(); assertContainsElements(strings, "A", "B", "C", "x"); } @Test public void test_parameters() { configureCode("A.re", "let fn = (newValue, newUnit) => { n"); myFixture.complete(CompletionType.BASIC, 1); List strings = myFixture.getLookupElementStrings(); assertContainsElements(strings, "newValue", "newUnit"); } @Test public void test_named_parameters() { configureCode("A.re", "let fn = (~newValue, ~newUnit:option(string), ~newOther=?) => { n"); myFixture.complete(CompletionType.BASIC, 1); List strings = myFixture.getLookupElementStrings(); assertContainsElements(strings, "newValue", "newUnit", "newOther"); } @Test public void test_GH_246() { configureCode("A.re", """ let fn = (newValue, newUnit) => { setSomething(_ => {value: n } """); myFixture.complete(CompletionType.BASIC, 1); List strings = myFixture.getLookupElementStrings(); assertContainsElements(strings, "newValue", "newUnit"); } @Test public void test_GH_496_local_polyvariant() { configureCode("A.re", """ type color = [ | `blue | `red ]; type font = [ | `normal | `bold ]; let x = """); myFixture.complete(CompletionType.BASIC, 1); List strings = myFixture.getLookupElementStrings(); assertContainsElements(strings, "`blue", "`red", "`normal", "`bold"); assertSize(4, strings); } @Test public void test_GH_496_external_polyvariant() { configureCode("A.ml", """ type color = [ `blue | `red ] type font = [ `normal | `bold ]"""); configureCode("B.re", """ open A; let x = """); myFixture.complete(CompletionType.BASIC, 1); List strings = myFixture.getLookupElementStrings(); assertContainsElements(strings, "A", "`blue", "`red", "`normal", "`bold"); assertSize(5, strings); } } ================================================ FILE: src/test/java/com/reason/ide/completion/JsObjectCompletion_RES_Test.java ================================================ package com.reason.ide.completion; import com.reason.ide.*; import org.junit.*; import org.junit.runner.*; import org.junit.runners.*; import java.util.*; @SuppressWarnings("ConstantConditions") @RunWith(JUnit4.class) public class JsObjectCompletion_RES_Test extends ORBasePlatformTestCase { @Test public void test_basic_bracket() { configureCode("pervasives.mli", "let max_float: float->float->float"); configureCode("JsObj.res", """ let oo = {"abc": 1, "def": 2}"""); configureCode("Dummy.res", """ let x =1 JsObj.oo["""); myFixture.completeBasic(); List elements = myFixture.getLookupElementStrings(); assertSize(2, elements); assertContainsElements(elements, "\"abc\"", "\"def\""); } @Test public void test_basic_string() { configureCode("JsObj.res", "let oo = {\"abc\": 1, \"def\": 2}"); configureCode("Dummy.res", "JsObj.oo[\""); myFixture.completeBasic(); List elements = myFixture.getLookupElementStrings(); assertSize(2, elements); assertContainsElements(elements, "\"abc\"", "\"def\""); } @Test public void test_deep() { configureCode("JsObj.res", """ let oo = {"first": {"deep": true}, "deep": {"other": {"asd": 1} } }"""); configureCode("Dummy.res", """ open JsObj oo["deep"]["other"]["""); myFixture.completeBasic(); List elements = myFixture.getLookupElementStrings(); assertContainsElements(elements, "\"asd\""); assertSize(1, elements); } @Test public void test_deep_field() { configureCode("JsObj.res", """ let o = {"asd": 1} let oo = {"deep": o}"""); configureCode("Dummy.res", """ open JsObj oo["""); myFixture.completeBasic(); List elements = myFixture.getLookupElementStrings(); assert elements != null; assertSize(1, elements); assertContainsElements(elements, "\"deep\""); } @Test public void test_deep_field_completion_order() { configureCode("JsObj.res", """ let oo = {"first": {"deep": true}, "deep": {"asd": 1} }"""); configureCode("Dummy.res", """ open JsObj oo["first"]["""); myFixture.completeBasic(); List elements = myFixture.getLookupElementStrings(); assertContainsElements(elements, "\"deep\""); assertSize(1, elements); } @Test public void test_composed() { configureCode("A.res", """ let o = {"f22": 222} let oo = {"f1": {"f11": 111}, "f2": o,"f3": {"f33": 333} }"""); configureCode("B.res", """ open A let oo = {"f1": {"f11": 111}, "f2": o, "f3": {"f33": 333} }"""); configureCode("Dummy.res", """ open B oo["f2"]["""); myFixture.completeBasic(); List elements = myFixture.getLookupElementStrings(); assertContainsElements(elements, "\"f22\""); assertSize(1, elements); } @Test public void test_composed_2() { configureCode("JsObj2.res", """ let o = {"f22": 222} let oo = {"f1": {"f11": 111}, "f2": o,"f3": {"f33": 333} }"""); configureCode("JsObj.res", """ let oo = {"f1": {"f11": 111}, "f2": JsObj2.o, "f3": {"f33": 333} }"""); configureCode("Dummy.res", """ open JsObj oo["f2"]["""); myFixture.completeBasic(); List elements = myFixture.getLookupElementStrings(); assertContainsElements(elements, "\"f22\""); assertSize(1, elements); } @Test public void test_composed_3() { configureCode("JsObj.res", """ let o = {"ooo": o, "f22": 222} let oo = {"f1": o, "f2": o, "f3": {"f33": 333}}"""); configureCode("Dummy.res", """ open JsObj oo["f2"]["""); myFixture.completeBasic(); List elements = myFixture.getLookupElementStrings(); assertContainsElements(elements, "\"ooo\"", "\"f22\""); assertSize(2, elements); } @Test public void test_path() { configureCode("A.res", """ let o = {"oo": 1}"""); configureCode("B.res", """ let o = {"oooo": 1}"""); configureCode("Dummy.res", """ open A o["""); myFixture.completeBasic(); List elements = myFixture.getLookupElementStrings(); assertSize(1, elements); assertContainsElements(elements, "\"oo\""); } @Test public void test_alias() { configureCode("A.res", "let o = {\"oo\": 1}"); configureCode("Alias.res", "module AA = A"); configureCode("B.res", "Alias.AA.o["); myFixture.completeBasic(); List elements = myFixture.getLookupElementStrings(); assertContainsElements(elements, "\"oo\""); assertSize(1, elements); } @Test public void test_with_type() { configureCode("A.res", "type t = {\"a\": int }"); configureCode("B.res", """ open A let y: t = x y["""); myFixture.completeBasic(); List elements = myFixture.getLookupElementStrings(); assertContainsElements(elements, "\"a\""); assertSize(1, elements); } } ================================================ FILE: src/test/java/com/reason/ide/completion/JsObjectCompletion_RML_Test.java ================================================ package com.reason.ide.completion; import com.reason.ide.*; import org.junit.*; import java.util.*; @SuppressWarnings("ConstantConditions") public class JsObjectCompletion_RML_Test extends ORBasePlatformTestCase { @Test public void test_basic() { configureCode("JsObj.re", "let oo = {\"asd\": 1, \"qwe\": 2}"); configureCode("Dummy.re", "open JsObj; oo##"); myFixture.completeBasic(); List elements = myFixture.getLookupElementStrings(); assertSize(2, elements); assertContainsElements(elements, "asd", "qwe"); } @Test public void test_deep() { configureCode("JsObj.re", "let oo = {\"first\": {\"deep\": true}, \"deep\": {\"other\": {\"asd\": 1} } }"); configureCode("Dummy.re", "open JsObj; oo##deep##other##"); myFixture.completeBasic(); List elements = myFixture.getLookupElementStrings(); assertSize(1, elements); assertContainsElements(elements, "asd"); } @Test public void test_deepJsField() { configureCode("JsObj.re", "let o = {\"asd\": 1};\nlet oo = {\"deep\": o};"); configureCode("Dummy.re", "open JsObj; oo##"); myFixture.completeBasic(); List elements = myFixture.getLookupElementStrings(); assert elements != null; assertSize(1, elements); assertContainsElements(elements, "deep"); } @Test public void test_deepJsFieldCompletionOrder() { configureCode("JsObj.re", "let oo = {\"first\": {\"deep\": true},\"deep\": {\"asd\": 1} }"); configureCode("Dummy.re", "open JsObj; oo##first##"); myFixture.completeBasic(); List elements = myFixture.getLookupElementStrings(); assertSize(1, elements); assertContainsElements(elements, "deep"); } @Test public void test_composed() { configureCode("A.re", "let o = {\"f22\": 222}; let oo = {\"f1\": {\"f11\": 111}, \"f2\": o,\"f3\": {\"f33\": 333} }"); configureCode("B.re", "open A; let oo = {\"f1\": {\"f11\": 111}, \"f2\": o, \"f3\": {\"f33\": 333} }"); configureCode("Dummy.re", "open B; oo##f2##"); myFixture.completeBasic(); List elements = myFixture.getLookupElementStrings(); assertSize(1, elements); assertContainsElements(elements, "f22"); } @Test public void test_composed_2() { configureCode("JsObj2.re", "let o = {\"f22\": 222}; let oo = {\"f1\": {\"f11\": 111}, \"f2\": o,\"f3\": {\"f33\": 333} }"); configureCode("JsObj.re", "let oo = {\"f1\": {\"f11\": 111}, \"f2\": JsObj2.o,\"f3\": {\"f33\": 333} }"); configureCode("Dummy.re", "open JsObj; oo##f2##"); myFixture.completeBasic(); List elements = myFixture.getLookupElementStrings(); assertContainsElements(elements, "f22"); assertSize(1, elements); } @Test public void test_composed_3() { configureCode("JsObj.re", "let o = {\"ooo\": o, \"f22\": 222}; let oo = {\"f1\": o, \"f2\": o,\"f3\": {\"f33\": 333} }"); configureCode("Dummy.re", "open JsObj; oo##f2##"); myFixture.completeBasic(); List elements = myFixture.getLookupElementStrings(); assertSize(2, elements); assertContainsElements(elements, "ooo", "f22"); } @Test public void test_path() { configureCode("A.re", "let o = {\"oo\": 1};"); configureCode("B.re", "let o = {\"oooo\": 1};"); configureCode("Dummy.re", "open A; o##"); myFixture.completeBasic(); List elements = myFixture.getLookupElementStrings(); assertSize(1, elements); assertContainsElements(elements, "oo"); } @Test public void test_alias() { configureCode("A.re", "let o = {\"oo\": 1};"); configureCode("Alias.re", "module AA = A;"); configureCode("B.re", "Alias.AA.o##"); myFixture.completeBasic(); List elements = myFixture.getLookupElementStrings(); assertSize(1, elements); assertContainsElements(elements, "oo"); } @Test public void test_with_type() { configureCode("A.re", "type t = {. \"a\": int };"); configureCode("B.re", "open A; let y:t = x; y##"); myFixture.completeBasic(); List elements = myFixture.getLookupElementStrings(); assertContainsElements(elements, "a"); assertSize(1, elements); } } ================================================ FILE: src/test/java/com/reason/ide/completion/Jsx3NameCompletion_RES_Test.java ================================================ package com.reason.ide.completion; import com.intellij.codeInsight.completion.*; import com.reason.ide.*; import org.junit.*; import org.junit.runner.*; import org.junit.runners.*; import java.util.*; @SuppressWarnings("ConstantConditions") @RunWith(JUnit4.class) public class Jsx3NameCompletion_RES_Test extends ORBasePlatformTestCase { @Test public void test_outside_components() { configureCode("DialogHeader.res", "@react.component let make = () => {
}"); configureCode("DialogFooter.res", "@react.component let make = () => {
}"); configureCode("Dialog.res", "@react.component let make = () => "); myFixture.completeBasic(); List completions = myFixture.getLookupElementStrings(); assertContainsElements(completions, "DialogHeader", "DialogFooter"); assertSize(2, completions); } @Test public void test_dont_display_properties() { configureCode("DialogHeader.res", "@react.component let make = () => {
}"); configureCode("Dummy.res", "let _ = <Dialog"); myFixture.complete(CompletionType.BASIC, 1); List completions = myFixture.getLookupElementStrings(); assertEquals(1, completions.size()); assertEquals("DialogHeader", completions.get(0)); } } ================================================ FILE: src/test/java/com/reason/ide/completion/Jsx3NameCompletion_RML_Test.java ================================================ package com.reason.ide.completion; import com.intellij.codeInsight.completion.*; import com.reason.ide.*; import org.junit.*; import org.junit.runner.*; import org.junit.runners.*; import java.util.*; @SuppressWarnings("ConstantConditions") @RunWith(JUnit4.class) public class Jsx3NameCompletion_RML_Test extends ORBasePlatformTestCase { // need multiple components because completeBasic returns null if there is only one lookup element @Test public void test_local_component() { configureCode("Dialog.re", "module DialogHeader = { [@react.component] let make = () => {
}; };\n" + "module DialogFooter = { [@react.component] let make = () => {
}; };\n" + "[@react.component] let make = () => "); myFixture.completeBasic(); List completions = myFixture.getLookupElementStrings(); assertContainsElements(completions, "DialogHeader", "DialogFooter"); assertSize(2, completions); } @Test public void test_outside_components() { configureCode("DialogHeader.re", "[@react.component] let make = () => {
};"); configureCode("DialogFooter.re", "[@react.component] let make = () => {
};"); configureCode("Dialog.re", "[@react.component] let make = () => "); myFixture.completeBasic(); List completions = myFixture.getLookupElementStrings(); assertContainsElements(completions, "DialogHeader", "DialogFooter"); assertSize(2, completions); } @Test public void test_dont_display_properties() { configureCode("DialogHeader.re", "[@react.component] let make = () => {
};"); configureCode("Dummy.re", "let _ = <Dialog"); myFixture.complete(CompletionType.BASIC, 1); List completions = myFixture.getLookupElementStrings(); assertEquals(1, completions.size()); assertEquals("DialogHeader", completions.get(0)); } @Test public void test_inner_component() { configureCode("Hidden.re", ""); configureCode("Dialog.re", "module Other = { module Title = { [@react.component] let make = () =>
; }; };"); configureCode("A.re", "let _ = <"); myFixture.complete(CompletionType.BASIC, 1); List completions = myFixture.getLookupElementStrings(); assertEquals("Dialog", completions.get(0)); assertEquals(1, completions.size()); } @Test public void test_dot() { configureCode("Dialog.re", "module Title = { [@react.component] let make = () =>
; }; [@react.component] let make = () =>
;"); configureCode("A.re", "let _ = "); myFixture.complete(CompletionType.BASIC, 1); List completions = myFixture.getLookupElementStrings(); assertEquals("Title", completions.get(0)); assertEquals(1, completions.size()); } @Test public void test_local_recursive() { configureCode("A.re", "module Confirm = = { [@react.component] let make = () =>
; };\n" + "module type ContainerType = { [@react.component] let make = () =>
; };\n" + "module rec Container:ContainerType = { [@react.component] let make = () =>
; };\n" + "[@react.component] let make = () => "); myFixture.completeBasic(); List completions = myFixture.getLookupElementStrings(); assertContainsElements(completions, "Confirm", "Container"); assertEquals(2, completions.size()); } } ================================================ FILE: src/test/java/com/reason/ide/completion/Jsx3PropertyCompletion_RES_Test.java ================================================ package com.reason.ide.completion; import com.reason.ide.*; import org.jetbrains.annotations.*; import org.junit.*; import org.junit.runner.*; import org.junit.runners.*; import java.util.*; @SuppressWarnings("ConstantConditions") @RunWith(JUnit4.class) public class Jsx3PropertyCompletion_RES_Test extends ORBasePlatformTestCase { @Override protected @NotNull String getTestDataPath() { return "src/test/testData/com/reason/lang"; } @Test public void test_display_properties_let() { myFixture.configureByFiles("pervasives.ml"); configureCode("Component.res", "@react.component let make = (~name, ~onClose=?) =>
"); configureCode("A.res", "let _ = >"); myFixture.completeBasic(); List completions = myFixture.getLookupElementStrings(); assertSize(4, completions); assertContainsElements(completions, "key", "ref", "name", "onClose"); } @Test public void test_display_properties_external() { myFixture.configureByFiles("pervasives.ml"); configureCode("Component.res", "@react.component external make : (~name:string, ~onClose: unit => unit) = \"Comp\""); configureCode("A.res", "let _ = >"); myFixture.completeBasic(); List completions = myFixture.getLookupElementStrings(); assertContainsElements(completions, "key", "ref", "name", "onClose"); assertSize(4, completions); } @Test public void test_shouldDisplayPropertiesAfterPropName() { myFixture.configureByFiles("pervasives.ml"); configureCode("Component.res", "@react.component let make = (~name, ~onClose) =>
"); configureCode("A.res", "let _ = >"); myFixture.completeBasic(); List completions = myFixture.getLookupElementStrings(); // from doc: null if the only item was auto-completed assertNull(completions); } @Test public void test_shouldDisplayProperties_nested() { myFixture.configureByFiles("pervasives.ml"); configureCode("A.res", "module Comp = { @react.component let make = (~name) =>
}"); configureCode("B.res", "@react.component let make = () => />"); myFixture.completeBasic(); List completions = myFixture.getLookupElementStrings(); assertSize(3, completions); assertContainsElements(completions, "key", "ref", "name"); } @Test public void test_shouldDisplayProperties_open() { myFixture.configureByFiles("pervasives.ml"); configureCode("A.res", "module Comp = { @react.component let make = (~name) =>
}"); configureCode("B.res", "open A\n @react.component let make = () => />\n module Comp = { @react.component let make = (~value) =>
}"); myFixture.completeBasic(); List completions = myFixture.getLookupElementStrings(); assertSize(3, completions); assertContainsElements(completions, "key", "ref", "name"); } @Test public void test_div() { myFixture.configureByFiles("ReactDOM.res"); configureCode("A.res", "let _ =
>"); myFixture.completeBasic(); List completions = myFixture.getLookupElementStrings(); assertContainsElements(completions, "key", "ref", "ariaDetails", "className", "onClick"); assertSize(5, completions); } } ================================================ FILE: src/test/java/com/reason/ide/completion/Jsx3PropertyCompletion_RML_Test.java ================================================ package com.reason.ide.completion; import com.reason.ide.*; import org.jetbrains.annotations.*; import org.junit.*; import org.junit.runner.*; import org.junit.runners.*; import java.util.*; @SuppressWarnings("ConstantConditions") @RunWith(JUnit4.class) public class Jsx3PropertyCompletion_RML_Test extends ORBasePlatformTestCase { @Override protected @NotNull String getTestDataPath() { return "src/test/testData/com/reason/lang"; } @Test public void test_display_properties_let() { myFixture.configureByFiles("pervasives.ml"); configureCode("Component.re", "[@react.component] let make = (~name, ~onClose=?) =>
;"); configureCode("A.re", "let _ = >"); myFixture.completeBasic(); List completions = myFixture.getLookupElementStrings(); assertContainsElements(completions, "key", "ref", "name", "onClose"); assertSize(4, completions); } @Test public void test_display_properties_external() { myFixture.configureByFiles("pervasives.ml"); configureCode("Component.re", "[@react.component] external make : (~name:string, ~onClose: unit => unit) = \"Comp\";"); configureCode("A.re", "let _ = >"); myFixture.completeBasic(); List completions = myFixture.getLookupElementStrings(); assertContainsElements(completions, "key", "ref", "name", "onClose"); assertSize(4, completions); } @Test public void test_props_after_name() { myFixture.configureByFiles("pervasives.ml"); configureCode("Component.re", "[@react.component] let make = (~name, ~onClose) =>
;"); configureCode("A.re", "let _ = >"); myFixture.completeBasic(); List completions = myFixture.getLookupElementStrings(); // from doc: null if the only item was auto-completed assertNull(completions); } @Test public void test_shouldDisplayProperties_nested() { myFixture.configureByFiles("pervasives.ml"); configureCode("A.re", "module Comp = { [@react.component] let make = (~name) =>
; };"); configureCode("B.re", "[@react.component] let make = () => />"); myFixture.completeBasic(); List completions = myFixture.getLookupElementStrings(); assertSize(3, completions); assertContainsElements(completions, "key", "ref", "name"); } @Test public void test_shouldDisplayProperties_open() { myFixture.configureByFiles("pervasives.ml"); configureCode("A.re", "module Comp = { [@react.component] let make = (~name) =>
; };"); configureCode("B.re", "open A; [@react.component] let make = () => />; module Comp = { [@react.component] let make = (~value) =>
; };"); myFixture.completeBasic(); List completions = myFixture.getLookupElementStrings(); assertSize(3, completions); assertContainsElements(completions, "key", "ref", "name"); } @Test public void test_interface_local() { myFixture.configureByFiles("pervasives.ml"); configureCode("A.re", """ module type CompType = { [@react.component] let make : (~name:string, ~enabled:bool) =>
; }; module rec Comp:CompType = { [@react.component] let make = (~name, ~enabled) =>
; let y = 1; }; [@react.component] let make = () => />; """); myFixture.completeBasic(); List completions = myFixture.getLookupElementStrings(); assertContainsElements(completions, "key", "ref", "name", "enabled"); assertSize(4, completions); } @Test public void test_div() { myFixture.configureByFiles("ReactDOM.res"); configureCode("A.re", "let _ =
>"); myFixture.completeBasic(); List completions = myFixture.getLookupElementStrings(); assertContainsElements(completions, "key", "ref", "ariaDetails", "className", "onClick"); assertSize(5, completions); } } ================================================ FILE: src/test/java/com/reason/ide/completion/Jsx4PropertyCompletion_RES_Test.java ================================================ package com.reason.ide.completion; import com.reason.comp.*; import com.reason.ide.*; import org.jetbrains.annotations.*; import org.junit.*; import org.junit.runner.*; import org.junit.runners.*; import java.util.*; @SuppressWarnings("ConstantConditions") @RunWith(JUnit4.class) public class Jsx4PropertyCompletion_RES_Test extends ORBasePlatformTestCase { @Override protected @NotNull String getTestDataPath() { return "src/test/testData/com/reason/lang"; } @Test public void test_div() { myFixture.configureByFiles("jsxDOMU.res"); myFixture.configureByText(ORConstants.RESCRIPT_CONFIG_FILENAME, toJson("{'name': 'foo', 'jsx': {'version': 4}}")); configureCode("A.res", "let _ =
>"); myFixture.completeBasic(); List completions = myFixture.getLookupElementStrings(); assertContainsElements(completions, "key", "ref", "ariaCurrent", "className", "dataTestId", "onClick"); assertSize(6, completions); } @Test public void test_div_used_names() { myFixture.configureByFiles("jsxDOMU.res"); myFixture.configureByText(ORConstants.RESCRIPT_CONFIG_FILENAME, toJson("{'name': 'foo', 'jsx': {'version': 4}}")); configureCode("A.res", "let _ =
>"); myFixture.completeBasic(); List completions = myFixture.getLookupElementStrings(); assertContainsElements(completions, "key", "ref", "dataTestId", "onClick"); assertSize(4, completions); } } ================================================ FILE: src/test/java/com/reason/ide/completion/KeywordCompletion_OCL_Test.java ================================================ package com.reason.ide.completion; import com.intellij.codeInsight.completion.*; import com.reason.ide.*; import org.junit.*; import org.junit.runner.*; import org.junit.runners.*; import java.util.*; @SuppressWarnings("ConstantConditions") @RunWith(JUnit4.class) public class KeywordCompletion_OCL_Test extends ORBasePlatformTestCase { @Test public void test_basic() { configureCode("B.ml", ""); myFixture.complete(CompletionType.BASIC, 1); List strings = myFixture.getLookupElementStrings(); assertSameElements(strings, "open", "include", "module", "type", "let", "external", "exception"); } } ================================================ FILE: src/test/java/com/reason/ide/completion/KeywordCompletion_RES_Test.java ================================================ package com.reason.ide.completion; import com.intellij.codeInsight.completion.*; import com.reason.ide.*; import org.junit.*; import org.junit.runner.*; import org.junit.runners.*; import java.util.*; @SuppressWarnings("ConstantConditions") @RunWith(JUnit4.class) public class KeywordCompletion_RES_Test extends ORBasePlatformTestCase { @Test public void test_basic() { configureCode("B.res", ""); myFixture.complete(CompletionType.BASIC, 1); List strings = myFixture.getLookupElementStrings(); assertSameElements(strings, "open", "include", "module", "type", "let", "external", "exception"); } } ================================================ FILE: src/test/java/com/reason/ide/completion/KeywordCompletion_RML_Test.java ================================================ package com.reason.ide.completion; import com.intellij.codeInsight.completion.*; import com.reason.ide.*; import org.junit.*; import org.junit.runner.*; import org.junit.runners.*; import java.util.*; @SuppressWarnings("ConstantConditions") @RunWith(JUnit4.class) public class KeywordCompletion_RML_Test extends ORBasePlatformTestCase { @Test public void test_basic() { configureCode("B.re", ""); myFixture.complete(CompletionType.BASIC, 1); List strings = myFixture.getLookupElementStrings(); assertSameElements(strings, "open", "include", "module", "type", "let", "external", "exception"); } } ================================================ FILE: src/test/java/com/reason/ide/completion/ModuleCompletion_OCL_Test.java ================================================ package com.reason.ide.completion; import com.intellij.codeInsight.completion.*; import com.reason.ide.*; import org.junit.*; import java.util.*; @SuppressWarnings("ConstantConditions") public class ModuleCompletion_OCL_Test extends ORBasePlatformTestCase { @Test public void test_empty() { configureCode("A.ml", "let x = 1\n module A1 = struct end"); configureCode("B.ml", "let y = 1"); configureCode("C.ml", "open "); myFixture.complete(CompletionType.BASIC, 1); List strings = myFixture.getLookupElementStrings(); assertSameElements(strings, "A", "B"); } @Test public void test_basic() { configureCode("A.ml", "let x = 1\n module A1 = struct end"); configureCode("B.ml", "open A."); myFixture.complete(CompletionType.BASIC, 1); List strings = myFixture.getLookupElementStrings(); assertSameElements(strings, "A1"); } @Test public void test_deep() { configureCode("A.ml", "module A1 = struct module A2 = struct module A3 = struct end\n module A4 = struct end end end"); configureCode("B.ml", "open A.A1.A2."); myFixture.complete(CompletionType.BASIC, 1); List strings = myFixture.getLookupElementStrings(); assertSameElements(strings, "A3", "A4"); } @Test public void test_alias() { configureCode("A.ml", "module A1 = struct end"); configureCode("B.ml", "module B1 = A"); configureCode("C.ml", "open B.B1."); myFixture.complete(CompletionType.BASIC, 1); List strings = myFixture.getLookupElementStrings(); assertSameElements(strings, "A1"); } @Test public void test_file_include() { configureCode("A.ml", "module A1 = struct end"); configureCode("B.ml", "module B1 = struct end"); configureCode("C.ml", "include A; include B"); configureCode("D.ml", "open C."); myFixture.complete(CompletionType.BASIC, 1); List strings = myFixture.getLookupElementStrings(); assertSameElements(strings, "A1", "B1"); } @Test public void test_inner_include() { configureCode("A.ml", "module A1 = struct module A2 = struct end end"); configureCode("B.ml", "module B1 = struct include A.A1 end"); configureCode("C.ml", "open B.B1."); myFixture.complete(CompletionType.BASIC, 1); List strings = myFixture.getLookupElementStrings(); assertSameElements(strings, "A2"); } } ================================================ FILE: src/test/java/com/reason/ide/completion/ModuleCompletion_RES_Test.java ================================================ package com.reason.ide.completion; import com.intellij.codeInsight.completion.*; import com.reason.ide.*; import org.junit.*; import java.util.*; @SuppressWarnings("ConstantConditions") public class ModuleCompletion_RES_Test extends ORBasePlatformTestCase { @Test public void test_empty() { configureCode("A.res", "let x = 1; module A1 = {}"); configureCode("B.res", "let y = 1"); configureCode("C.res", "open "); myFixture.complete(CompletionType.BASIC, 1); List strings = myFixture.getLookupElementStrings(); assertSameElements(strings, "A", "B"); } @Test public void test_basic() { configureCode("A.res", "let x = 1\n module A1 = {}"); configureCode("B.res", "open A."); myFixture.complete(CompletionType.BASIC, 1); List strings = myFixture.getLookupElementStrings(); assertSameElements(strings, "A1"); } @Test public void test_deep() { configureCode("A.res", "module A1 = { module A2 = { module A3 = {}\n module A4 = {} } }"); configureCode("B.res", "open A.A1.A2."); myFixture.complete(CompletionType.BASIC, 1); List strings = myFixture.getLookupElementStrings(); assertSameElements(strings, "A3", "A4"); } @Test public void test_alias() { configureCode("A.res", "module A1 = {}"); configureCode("B.res", "module B1 = A"); configureCode("C.res", "open B.B1."); myFixture.complete(CompletionType.BASIC, 1); List strings = myFixture.getLookupElementStrings(); assertSameElements(strings, "A1"); } @Test public void test_file_include() { configureCode("A.res", "module A1 = {}"); configureCode("B.res", "module B1 = {}"); configureCode("C.res", "include A\n include B"); configureCode("D.res", "open C."); myFixture.complete(CompletionType.BASIC, 1); List strings = myFixture.getLookupElementStrings(); assertSameElements(strings, "A1", "B1"); } @Test public void test_inner_include() { configureCode("A.res", "module A1 = { module A2 = {} }"); configureCode("B.res", "module B1 = { include A.A1 }"); configureCode("C.res", "open B.B1."); myFixture.complete(CompletionType.BASIC, 1); List strings = myFixture.getLookupElementStrings(); assertSameElements(strings, "A2"); } } ================================================ FILE: src/test/java/com/reason/ide/completion/ModuleCompletion_RML_Test.java ================================================ package com.reason.ide.completion; import com.intellij.codeInsight.completion.*; import com.reason.ide.*; import org.junit.*; import org.junit.runner.*; import org.junit.runners.*; import java.util.*; @SuppressWarnings("ConstantConditions") public class ModuleCompletion_RML_Test extends ORBasePlatformTestCase { @Test public void test_empty() { configureCode("A.re", "let x = 1; module A1 = {};"); configureCode("B.re", "let y = 1;"); configureCode("C.re", "open "); myFixture.complete(CompletionType.BASIC, 1); List strings = myFixture.getLookupElementStrings(); assertSameElements(strings, "A", "B"); } @Test public void test_basic() { configureCode("A.re", "let x = 1; module A1 = {};"); configureCode("B.re", "open A."); myFixture.complete(CompletionType.BASIC, 1); List strings = myFixture.getLookupElementStrings(); assertSameElements(strings, "A1"); } @Test public void test_deep() { configureCode("A.re", "module A1 = { module A2 = { module A3 = {}; module A4 = {}; }; };"); configureCode("B.re", "open A.A1.A2."); myFixture.complete(CompletionType.BASIC, 1); List strings = myFixture.getLookupElementStrings(); assertSameElements(strings, "A3", "A4"); } @Test public void test_alias() { configureCode("A.re", "module A1 = {};"); configureCode("B.re", "module B1 = A;"); configureCode("C.re", "open B.B1."); myFixture.complete(CompletionType.BASIC, 1); List strings = myFixture.getLookupElementStrings(); assertSameElements(strings, "A1"); } @Test public void test_file_include() { configureCode("A.re", "module A1 = {};"); configureCode("B.re", "module B1 = {};"); configureCode("C.re", "include A; include B;"); configureCode("D.re", "open C."); myFixture.complete(CompletionType.BASIC, 1); List strings = myFixture.getLookupElementStrings(); assertSameElements(strings, "A1", "B1"); } @Test public void test_inner_include() { configureCode("A.re", "module A1 = { module A2 = {}; };"); configureCode("B.re", "module B1 = { include A.A1; };"); configureCode("C.re", "open B.B1."); myFixture.complete(CompletionType.BASIC, 1); List strings = myFixture.getLookupElementStrings(); assertSameElements(strings, "A2"); } } ================================================ FILE: src/test/java/com/reason/ide/completion/RecordCompletion_OCL_Test.java ================================================ package com.reason.ide.completion; import com.reason.ide.*; import org.junit.*; import org.junit.runner.*; import org.junit.runners.*; import java.util.*; @SuppressWarnings("ConstantConditions") @RunWith(JUnit4.class) public class RecordCompletion_OCL_Test extends ORBasePlatformTestCase { @Test public void test_basic() { configureCode("A.ml", "type r = { a:float; b:int }"); configureCode("B.ml", "let b: A.r = { a=1.; b=2 }\nlet _ = b."); myFixture.completeBasic(); List elements = myFixture.getLookupElementStrings(); assertContainsElements(elements, "a", "b"); assertSize(2, elements); } @Test public void test_deep() { configureCode("A.ml", """ let record = { a=1; b={ c={ d=2; e=3 }; f=4 }} let _ = record.b.c."""); myFixture.completeBasic(); List elements = myFixture.getLookupElementStrings(); assertContainsElements(elements, "d", "e"); assertSize(2, elements); } // https://github.com/giraud/reasonml-idea-plugin/issues/453 @Test public void test_GH_453_mixin() { configureCode("A.ml", """ let default = { abc=1; def=2 } let x = { default with """); myFixture.completeBasic(); List elements = myFixture.getLookupElementStrings(); assertContainsElements(elements, "abc", "def"); assertSize(2, elements); } // https://github.com/giraud/reasonml-idea-plugin/issues/453 @Test public void test_GH_453_mixin_2() { configureCode("A.ml", """ let default = { abc1=1; abc2=2; def=2 } let x = {default with ab}; """); myFixture.completeBasic(); List elements = myFixture.getLookupElementStrings(); assertContainsElements(elements, "abc1", "abc2"); assertSize(2, elements); } } ================================================ FILE: src/test/java/com/reason/ide/completion/RecordCompletion_RES_Test.java ================================================ package com.reason.ide.completion; import com.reason.ide.*; import org.junit.*; import org.junit.runner.*; import org.junit.runners.*; import java.util.*; @SuppressWarnings("ConstantConditions") @RunWith(JUnit4.class) public class RecordCompletion_RES_Test extends ORBasePlatformTestCase { @Test public void test_basic() { configureCode("A.res", "type r = { a: float, b: int }"); configureCode("B.res", "let b: A.r = { a: 1., b: 2 }\n b."); myFixture.completeBasic(); List elements = myFixture.getLookupElementStrings(); assertSize(2, elements); assertContainsElements(elements, "a", "b"); } @Test public void test_deep() { configureCode("A.res", """ let record = { a:1, b:{ c:{ d:2, e:3 }, f:4 }} let _ = record.b.c."""); myFixture.completeBasic(); List elements = myFixture.getLookupElementStrings(); assertContainsElements(elements, "d", "e"); assertSize(2, elements); } // https://github.com/giraud/reasonml-idea-plugin/issues/453 @Test public void test_GH_453_mixin() { configureCode("A.res", """ let default = {abc: 1, def: 2} let x = {...default, } """); myFixture.completeBasic(); List elements = myFixture.getLookupElementStrings(); assertSize(2, elements); assertContainsElements(elements, "abc", "def"); } // https://github.com/giraud/reasonml-idea-plugin/issues/453 @Test public void test_GH_453_mixin_2() { configureCode("A.res", """ let default = {abc1: 1, abc2: 2, def: 2} let x = {...default, ab} """); myFixture.completeBasic(); List elements = myFixture.getLookupElementStrings(); assertSize(2, elements); assertContainsElements(elements, "abc1", "abc2"); } } ================================================ FILE: src/test/java/com/reason/ide/completion/RecordCompletion_RML_Test.java ================================================ package com.reason.ide.completion; import com.reason.ide.*; import org.junit.*; import org.junit.runner.*; import org.junit.runners.*; import java.util.*; @SuppressWarnings("ConstantConditions") @RunWith(JUnit4.class) public class RecordCompletion_RML_Test extends ORBasePlatformTestCase { @Test public void test_basic() { configureCode("A.re", "type r = { a: float, b: int };"); configureCode("B.re", "let b: A.r = { a: 1., b: 2 }; b."); myFixture.completeBasic(); List elements = myFixture.getLookupElementStrings(); assertSize(2, elements); assertContainsElements(elements, "a", "b"); } @Test public void test_deep() { configureCode("A.re", """ let record = { a:1, b:{ c:{ d:2, e:3 }, f:4 }}; let _ = record.b.c."""); myFixture.completeBasic(); List elements = myFixture.getLookupElementStrings(); assertContainsElements(elements, "d", "e"); assertSize(2, elements); } // https://github.com/giraud/reasonml-idea-plugin/issues/453 @Test public void test_GH_453_mixin() { configureCode("A.re", """ let default = {abc: 1, def: 2}; let x = {...default, }; """); myFixture.completeBasic(); List elements = myFixture.getLookupElementStrings(); assertSize(2, elements); assertContainsElements(elements, "abc", "def"); } // https://github.com/giraud/reasonml-idea-plugin/issues/453 @Test public void test_GH_453_mixin_2() { configureCode("A.re", """ let default = {abc1: 1, abc2: 2, def: 2}; let x = {...default, ab}; """); myFixture.completeBasic(); List elements = myFixture.getLookupElementStrings(); assertSize(2, elements); assertContainsElements(elements, "abc1", "abc2"); } } ================================================ FILE: src/test/java/com/reason/ide/console/OCamlMessages.java ================================================ package com.reason.ide.console; import java.util.*; // Shows different OCaml error messages and how they are rendered. // Copied from https://github.com/Chris00/tuareg/blob/master/compilation.txt public class OCamlMessages { public static final List common = new ArrayList<>(); public static final List since408 = new ArrayList<>(); public static final List since412 = new ArrayList<>(); public static final List ancillary = new ArrayList<>(); static { common.add(new String[]{ // 0 "File \"file.ml\", line 4, characters 6-7:", "Error: This expression has type int", " This is not a function; it cannot be applied."}); common.add(new String[]{ // 1 "File \"file.ml\", line 3, characters 6-7:", "Warning 26: unused variable y."}); common.add(new String[]{ // 2 "File \"file.ml\", line 6, characters 15-38:", "Error: Signature mismatch:", " Modules do not match: sig val x : float end is not included in X", " Values do not match: val x : float is not included in val x : int", " File \"file.ml\", line 3, characters 2-13: Expected declaration", " File \"file.ml\", line 7, characters 6-7: Actual declaration" }); // Since OCaml 4.08, the error messages have the following form. since408.add(new String[]{ // 0 "File \"helloworld.ml\", line 2, characters 36-64:", "2 | module rec A: sig type t += A end = struct type t += A = B.A end", " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^", "Error: Cannot safely evaluate the definition of the following cycle", " of recursively-defined modules: A -> B -> A.", " There are no safe modules in this cycle (see manual section 8.2)." }); since408.add(new String[]{ // 1 "File \"helloworld.ml\", lines 4-7, characters 6-3:", "4 | ......struct", "5 | module F(X:sig end) = struct end", "6 | let f () = B.value", "7 | end", "Error: Cannot safely evaluate the definition of the following cycle", " of recursively-defined modules: A -> B -> A.", " There are no safe modules in this cycle (see manual section 8.2)." }); since408.add(new String[]{ // 2 "File \"robustmatch.ml\", lines 33-37, characters 6-23:", " 9 | ......match t1, t2, x with", "10 | | AB, AB, A -> ()", "11 | | MAB, _, A -> ()", "12 | | _, AB, B -> ()", "13 | | _, MAB, B -> ()", "Warning 8: this pattern-matching is not exhaustive.", "Here is an example of a case that is not matched:", "(AB, MAB, A)" }); since408.add(new String[]{ // 3 "File \"helloworld.ml\", line 2, characters 36-64:", "Error: Cannot safely evaluate the definition of the following cycle", " of recursively-defined modules: A -> B -> A.", " There are no safe modules in this cycle (see manual section 8.2)." }); since408.add(new String[]{ // 4 "File \"helloworld.ml\", line 2, characters 36-64:", "Warning 3: Cannot safely evaluate the definition of the following cycle", " of recursively-defined modules: A -> B -> A.", " There are no safe modules in this cycle (see manual section 8.2)." }); since408.add(new String[]{ // 5 "File \"helloworld.ml\", line 2, characters 36-64:", "2 | module rec A: sig type t += A end = struct type t += A = B.A end", " ^^^^^^^^^^^^^^^^^^^^^^^^", "Warning: Cannot safely evaluate the definition of the following cycle", " of recursively-defined modules: A -> B -> A.", " There are no safe modules in this cycle (see manual section 8.2)." }); since408.add(new String[]{ // 6 "File \"main.ml\", line 3, characters 8-50:", "Error: This expression has type float but an expression was expected of type", " int" }); since408.add(new String[]{ // 7 "File \"main.ml\", line 3, characters 8-50:", "Warning 3: This expression has type float but an expression was expected of type", " int" }); since408.add(new String[]{ // 8 "File \"main.ml\", line 13, characters 34-35:", "13 | let f : M.t -> M.t = fun M.C -> y", " ^", "Error: This expression has type M/2.t but an expression was expected of type", " M/1.t", " File \"main.ml\", line 10, characters 2-41:", " Definition of module M/1", " File \"main.ml\", line 7, characters 0-32:", " Definition of module M/2" }); since408.add(new String[]{ // 9 "File \"main.ml\", line 13, characters 34-35:", "13 | let f : M.t -> M.t = fun M.C -> y", " ^", "Error: This expression has type M/2.t but an expression was expected of type", " M/1.t", " File \"main.ml\", line 10, characters 2-41:", " Definition of module M/1", " File \"main.ml\", line 7, characters 0-32:", " Definition of module M/2" }); // Since OCaml 4.12, warnings come with mnemonics. since412.add(new String[]{ // 0 "File \"moo.ml\", line 6, characters 6-10:", "6 | let fish = 13 in", " ^^^^", "Warning 26 [unused-var]: unused variable fish." }); // Example of a warning with ancillary locations ancillary.add(new String[]{ // 0 "File \"urk.ml\", line 1:", "Warning 63 [erroneous-printed-signature]: The printed interface differs from the inferred interface.", "The inferred interface contained items which could not be printed", "properly due to name collisions between identifiers.", "File \"urk.ml\", lines 23-25, characters 2-5:", " Definition of module M/1", "File \"urk.ml\", lines 17-20, characters 0-3:", " Definition of module M/2", "Beware that this warning is purely informational and will not catch", "all instances of erroneous printed interface.", "module M : sig type t val v : t end", "module F : sig module M : sig val v : M.t end val v : M/2.t end" }); ancillary.add(new String[]{ // 1 Alert: treat like warning "File \"alrt.ml\", line 25, characters 9-10:", "25 | val x: t [@@ocaml.deprecated]", " ^", "Alert deprecated: t" }); } private OCamlMessages() { } } ================================================ FILE: src/test/java/com/reason/ide/console/ocaml/OCamlConsoleFilterTest.java ================================================ package com.reason.ide.console.ocaml; import com.intellij.execution.filters.*; import com.intellij.openapi.project.*; import com.intellij.openapi.vfs.*; import com.intellij.testFramework.*; import com.intellij.testFramework.fixtures.*; import com.reason.ide.console.*; import com.reason.ide.console.dune.*; import org.jetbrains.annotations.*; import org.junit.*; import org.junit.runner.*; import org.junit.runners.*; @SuppressWarnings({"ConstantConditions", "SameParameterValue"}) @RunWith(JUnit4.class) public class OCamlConsoleFilterTest extends BasePlatformTestCase { @Test public void test_common_0() { String[] message = OCamlMessages.common.get(0); OCamlConsoleFilter filter = new MemoryRescriptConsoleFilter(myFixture.getProject()); Filter.Result[] results = applyMessage(message, filter); Filter.ResultItem resultItem = results[0].getResultItems().get(0); assertHighlight(6, 13, resultItem); assertHyperlink("file.ml", 4, 6, resultItem.getHyperlinkInfo()); assertNullResult(results, 1); } @Test public void test_common_1() { String[] message = OCamlMessages.common.get(1); OCamlConsoleFilter filter = new MemoryRescriptConsoleFilter(myFixture.getProject()); Filter.Result[] results = applyMessage(message, filter); Filter.ResultItem resultItem = results[0].getResultItems().get(0); assertHighlight(6, 13, resultItem); assertHyperlink("file.ml", 3, 6, resultItem.getHyperlinkInfo()); assertNullResult(results, 1); } @Test public void test_common_2() { String[] message = OCamlMessages.common.get(2); OCamlConsoleFilter filter = new MemoryRescriptConsoleFilter(myFixture.getProject()); Filter.Result[] results = applyMessage(message, filter); Filter.ResultItem resultItem = results[0].getResultItems().get(0); assertHighlight(6, 13, resultItem); assertHyperlink("file.ml", 6, 15, resultItem.getHyperlinkInfo()); assertNullResult(results, 1); } @Test public void test_408_0() { String[] message = OCamlMessages.since408.get(0); OCamlConsoleFilter filter = new MemoryRescriptConsoleFilter(myFixture.getProject()); Filter.Result[] results = applyMessage(message, filter); Filter.ResultItem resultItem = results[0].getResultItems().get(0); assertHighlight(6, 19, resultItem); assertHyperlink("helloworld.ml", 2, 36, resultItem.getHyperlinkInfo()); assertNullResult(results, 1); } @Test public void test_408_1() { String[] message = OCamlMessages.since408.get(1); OCamlConsoleFilter filter = new MemoryRescriptConsoleFilter(myFixture.getProject()); Filter.Result[] results = applyMessage(message, filter); Filter.ResultItem resultItem = results[0].getResultItems().get(0); assertHighlight(6, 19, resultItem); assertHyperlink("helloworld.ml", 4, 6, resultItem.getHyperlinkInfo()); assertNullResult(results, 1); } @Test public void test_408_2() { String[] message = OCamlMessages.since408.get(2); OCamlConsoleFilter filter = new MemoryRescriptConsoleFilter(myFixture.getProject()); Filter.Result[] results = applyMessage(message, filter); Filter.ResultItem resultItem = results[0].getResultItems().get(0); assertHighlight(6, 20, resultItem); assertHyperlink("robustmatch.ml", 33, 6, resultItem.getHyperlinkInfo()); assertNullResult(results, 1); } @Test public void test_408_3() { String[] message = OCamlMessages.since408.get(3); OCamlConsoleFilter filter = new MemoryRescriptConsoleFilter(myFixture.getProject()); Filter.Result[] results = applyMessage(message, filter); Filter.ResultItem resultItem = results[0].getResultItems().get(0); assertHighlight(6, 19, resultItem); assertHyperlink("helloworld.ml", 2, 36, resultItem.getHyperlinkInfo()); assertNullResult(results, 1); } @Test public void test_408_4() { String[] message = OCamlMessages.since408.get(4); OCamlConsoleFilter filter = new MemoryRescriptConsoleFilter(myFixture.getProject()); Filter.Result[] results = applyMessage(message, filter); Filter.ResultItem resultItem = results[0].getResultItems().get(0); assertHighlight(6, 19, resultItem); assertHyperlink("helloworld.ml", 2, 36, resultItem.getHyperlinkInfo()); assertNullResult(results, 1); } @Test public void test_408_5() { String[] message = OCamlMessages.since408.get(5); OCamlConsoleFilter filter = new MemoryRescriptConsoleFilter(myFixture.getProject()); Filter.Result[] results = applyMessage(message, filter); Filter.ResultItem resultItem = results[0].getResultItems().get(0); assertHighlight(6, 19, resultItem); assertHyperlink("helloworld.ml", 2, 36, resultItem.getHyperlinkInfo()); assertNullResult(results, 1); } @Test public void test_408_6() { String[] message = OCamlMessages.since408.get(6); OCamlConsoleFilter filter = new MemoryRescriptConsoleFilter(myFixture.getProject()); Filter.Result[] results = applyMessage(message, filter); Filter.ResultItem resultItem = results[0].getResultItems().get(0); assertHighlight(6, 13, resultItem); assertHyperlink("main.ml", 3, 8, resultItem.getHyperlinkInfo()); assertNullResult(results, 1); } @Test public void test_408_7() { String[] message = OCamlMessages.since408.get(7); OCamlConsoleFilter filter = new MemoryRescriptConsoleFilter(myFixture.getProject()); Filter.Result[] results = applyMessage(message, filter); Filter.ResultItem resultItem = results[0].getResultItems().get(0); assertHighlight(6, 13, resultItem); assertHyperlink("main.ml", 3, 8, resultItem.getHyperlinkInfo()); assertNullResult(results, 1); } @Test public void test_408_8() { String[] message = OCamlMessages.since408.get(8); OCamlConsoleFilter filter = new MemoryRescriptConsoleFilter(myFixture.getProject()); Filter.Result[] results = applyMessage(message, filter); Filter.ResultItem resultItem = results[0].getResultItems().get(0); assertHighlight(6, 13, resultItem); assertHyperlink("main.ml", 13, 34, resultItem.getHyperlinkInfo()); assertNullResult(results, 1); } @Test public void test_408_9() { String[] message = OCamlMessages.since408.get(9); OCamlConsoleFilter filter = new MemoryRescriptConsoleFilter(myFixture.getProject()); Filter.Result[] results = applyMessage(message, filter); Filter.ResultItem resultItem = results[0].getResultItems().get(0); assertHighlight(6, 13, resultItem); assertHyperlink("main.ml", 13, 34, resultItem.getHyperlinkInfo()); assertNullResult(results, 1); } @Test public void test_412_0() { String[] message = OCamlMessages.since412.get(0); OCamlConsoleFilter filter = new MemoryRescriptConsoleFilter(myFixture.getProject()); Filter.Result[] results = applyMessage(message, filter); Filter.ResultItem resultItem = results[0].getResultItems().get(0); assertHighlight(6, 12, resultItem); assertHyperlink("moo.ml", 6, 6, resultItem.getHyperlinkInfo()); assertNullResult(results, 1); } @Test public void test_ancillary_0() { String[] message = OCamlMessages.ancillary.get(0); OCamlConsoleFilter filter = new MemoryRescriptConsoleFilter(myFixture.getProject()); Filter.Result[] results = applyMessage(message, filter); assertNull(results[0]); Filter.ResultItem resultItem = results[4].getResultItems().get(0); assertHighlight(6, 12, resultItem); assertHyperlink("urk.ml", 23, 2, resultItem.getHyperlinkInfo()); resultItem = results[6].getResultItems().get(0); assertHighlight(6, 12, resultItem); assertHyperlink("urk.ml", 17, 0, resultItem.getHyperlinkInfo()); } @Test public void test_ancillary_1() { String[] message = OCamlMessages.ancillary.get(1); OCamlConsoleFilter filter = new MemoryRescriptConsoleFilter(myFixture.getProject()); Filter.Result[] results = applyMessage(message, filter); Filter.ResultItem resultItem = results[0].getResultItems().get(0); assertHighlight(6, 13, resultItem); assertHyperlink("alrt.ml", 25, 9, resultItem.getHyperlinkInfo()); assertNullResult(results, 1); } private Filter.Result[] applyMessage(String[] message, OCamlConsoleFilter filter) { Filter.Result[] results = new Filter.Result[message.length]; for (int i = 0; i < message.length; i++) { String line = message[i]; results[i] = filter.applyFilter(line, line.length()); } return results; } private void assertNullResult(Filter.Result[] results, int start) { for (int i = start; i < results.length; i++) { assertNull(results[i]); } } private void assertHyperlink(String filename, int line, int col, HyperlinkInfo hyperlinkInfo) { OpenFileHyperlinkInfo info = (OpenFileHyperlinkInfo) hyperlinkInfo; assertEquals(filename, info.getVirtualFile().getName()); assertEquals(line, info.getDescriptor().getLine() + 1); assertEquals(col, info.getDescriptor().getColumn()); } private void assertHighlight(int start, int end, Filter.ResultItem item) { // file path assertEquals(start, item.getHighlightStartOffset()); assertEquals(end, item.getHighlightEndOffset()); } static class MemoryRescriptConsoleFilter extends OCamlConsoleFilter { public MemoryRescriptConsoleFilter(@NotNull Project project) { super(project); } @Override protected @Nullable OpenFileHyperlinkInfo getHyperlinkInfo(String filePath, int documentLine, int documentColumn) { VirtualFile sourceFile = new LightVirtualFile(filePath); return new OpenFileHyperlinkInfo(myProject, sourceFile, documentLine, documentColumn); } } } ================================================ FILE: src/test/java/com/reason/ide/console/rescript/RescriptConsoleFilterTest.java ================================================ package com.reason.ide.console.rescript; import com.intellij.execution.filters.*; import com.intellij.openapi.project.*; import com.intellij.testFramework.fixtures.*; import org.jetbrains.annotations.*; import org.junit.*; import org.junit.runner.*; import org.junit.runners.*; @SuppressWarnings("ConstantConditions") @RunWith(JUnit4.class) public class RescriptConsoleFilterTest extends BasePlatformTestCase { @Test public void test_line_01() { String line = "File \"C:\\dev\\src\\MyFile.re\", line 234, characters 10-11:\n"; RescriptConsoleFilter filter = new MemoryRescriptConsoleFilter(myFixture.getProject()); Filter.Result result = filter.applyFilter(line, line.length()); Filter.ResultItem resultItem = result.getResultItems().get(0); assertEquals(6, resultItem.getHighlightStartOffset()); assertEquals(26, resultItem.getHighlightEndOffset()); } static class MemoryRescriptConsoleFilter extends RescriptConsoleFilter { public MemoryRescriptConsoleFilter(@NotNull Project project) { super(project); } @Override protected @Nullable OpenFileHyperlinkInfo getHyperlinkInfo(String filePath, int documentLine, int documentColumn) { return null; } } } ================================================ FILE: src/test/java/com/reason/ide/docs/ORDocumentationProviderTest.java ================================================ package com.reason.ide.docs; import com.intellij.lang.*; import com.intellij.lang.documentation.*; import com.intellij.psi.*; import com.intellij.psi.util.*; import com.reason.ide.*; import com.reason.ide.files.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import com.reason.lang.reason.*; import com.reason.lang.rescript.*; import org.junit.*; import org.junit.runner.*; import org.junit.runners.*; /** @noinspection ConstantConditions*/ @RunWith(JUnit4.class) public class ORDocumentationProviderTest extends ORBasePlatformTestCase { @Test public void test_customDocumentationElement_empty_parens_RML() { FileBase a = configureCode("A.re", "/** Doc for fn */ \nlet fn = (x) => x;"); FileBase b = configureCode("B.re", "let _ = A.fn();"); int caretOffset = myFixture.getCaretOffset(); PsiElement rParen = PsiTreeUtil.findChildOfType(b, RPsiParameters.class).getLastChild(); DocumentationProvider docProvider = LanguageDocumentation.INSTANCE.forLanguage(RmlLanguage.INSTANCE); PsiElement docElement = docProvider.getCustomDocumentationElement(myFixture.getEditor(), b, rParen, caretOffset); assertEquals("A.fn", ((RPsiQualifiedPathElement) docElement).getQualifiedName()); } @Test public void test_customDocumentationElement_empty_parens_RES() { FileBase a = configureCode("A.res", "/** Doc for fn */ \nlet fn = (x) => x"); FileBase b = configureCode("B.res", "let _ = A.fn()"); int caretOffset = myFixture.getCaretOffset(); PsiElement rParen = PsiTreeUtil.findChildOfType(b, RPsiParameters.class).getLastChild(); DocumentationProvider docProvider = LanguageDocumentation.INSTANCE.forLanguage(ResLanguage.INSTANCE); PsiElement docElement = docProvider.getCustomDocumentationElement(myFixture.getEditor(), b, rParen, caretOffset); assertEquals("A.fn", ((RPsiQualifiedPathElement) docElement).getQualifiedName()); } } ================================================ FILE: src/test/java/com/reason/ide/docs/QuickDocRESTest.java ================================================ package com.reason.ide.docs; import com.intellij.lang.*; import com.reason.ide.*; import com.reason.ide.files.*; import com.reason.lang.rescript.*; import org.junit.*; import org.junit.runner.*; import org.junit.runners.*; @RunWith(JUnit4.class) public class QuickDocRESTest extends ORBasePlatformTestCase { public static final Language LANG = ResLanguage.INSTANCE; @Test public void test_abstract_type() { FileBase a = configureCode("A.res", "type t\n let x: t"); String info = getQuickDoc(a, LANG); assertEquals("A
type t
This is an abstract type", info); } @Test public void test_external() { FileBase a = configureCode("A.res", "external e : string -> int = \"name\"\n let x = e"); String info = getQuickDoc(a, LANG); assertEquals("A
external e
string -> int", info); } } ================================================ FILE: src/test/java/com/reason/ide/docs/QuickDocRMLTest.java ================================================ package com.reason.ide.docs; import com.reason.ide.ORBasePlatformTestCase; import com.reason.ide.files.FileBase; import com.reason.lang.reason.RmlLanguage; import org.junit.*; import org.junit.runner.*; import org.junit.runners.*; @RunWith(JUnit4.class) public class QuickDocRMLTest extends ORBasePlatformTestCase { public static final RmlLanguage LANG = RmlLanguage.INSTANCE; @Test public void test_abstract_type() { FileBase a = configureCode("A.re", "type t; let x: t"); String info = getQuickDoc(a, LANG); assertEquals("A
type t
This is an abstract type", info); } @Test public void test_external() { FileBase a = configureCode("A.re", "external e : string -> int = \"name\"; let x = e"); String info = getQuickDoc(a, LANG); assertEquals("A
external e
string -> int", info); } } ================================================ FILE: src/test/java/com/reason/ide/docs/ShowDocOCLTest.java ================================================ package com.reason.ide.docs; import com.intellij.lang.*; import com.reason.ide.*; import com.reason.ide.files.*; import com.reason.lang.ocaml.*; import org.junit.*; import org.junit.runner.*; import org.junit.runners.*; @RunWith(JUnit4.class) public class ShowDocOCLTest extends ORBasePlatformTestCase { public static final Language LANG = OclLanguage.INSTANCE; @Test public void test_multiple_spaces_below() { configureCode("Doc.ml", "let x = 1; \t\n (** doc for x *)"); FileBase a = configureCode("A.ml", "Doc.x"); String doc = getDoc(a, LANG); assertEquals("
Doc

let x

doc for x

", doc); } @Test public void test_type() { FileBase a = configureCode("A.ml", "(** my type *) type t = string"); String doc = getDoc(a, LANG); assertEquals("
A

type t

my type

", doc); } @Test public void test_GH_350() { configureCode("A.mli", "val compare : string -> string -> int\n(** compare doc *)"); FileBase a = configureCode("A.ml", "let compare s1 s2 = 1"); String doc = getDoc(a, LANG); assertEquals("
A

let compare

compare doc

", doc); } } ================================================ FILE: src/test/java/com/reason/ide/docs/ShowDocRESTest.java ================================================ package com.reason.ide.docs; import com.intellij.lang.*; import com.reason.ide.*; import com.reason.ide.files.*; import com.reason.lang.core.psi.*; import com.reason.lang.rescript.*; import org.junit.*; import org.junit.runner.*; import org.junit.runners.*; @RunWith(JUnit4.class) public class ShowDocRESTest extends ORBasePlatformTestCase { public static final Language LANG = ResLanguage.INSTANCE; @Test public void test_GH_155() { FileBase doc = configureCode("Doc.res", "/** add 1 */\nlet fn = x => x + 1"); FileBase a = configureCode("A.res", "Mod.fn()"); RPsiLet resolvedELement = doc.getQualifiedExpressions("Doc.fn", RPsiLet.class).get(0); assertEquals("
Doc

let fn

add 1

", getDocForElement(a, LANG, resolvedELement)); } @Test public void test_GH_156() { configureCode("Doc.res", "/** Doc for y */\nlet y = 1"); FileBase a = configureCode("A.res", "let x = Doc.y\nlet z = x"); assertEquals("
Doc

let y

Doc for y

", getDoc(a, LANG)); } @Test public void test_GH_350() { configureCode("A.resi", "/** compare doc */\nlet compare : string -> string -> int"); FileBase a = configureCode("A.res", "let compare = (s1, s2) => 1"); String doc = getDoc(a, LANG); assertEquals("
A

let compare

compare doc

", doc); } @Test public void test_GH_359() { FileBase a = configureCode("A.res", """ module InnerComp = { /** Doc for my component @param text Label */ @react.component let make = (~text) =>
{text->React.string}
} @react.component let make = () => text="my text" /> """); String doc = getDoc(a, LANG); assertEquals("
A.InnerComp

let make

Doc for my component

Param:

text - Label

", doc); } } ================================================ FILE: src/test/java/com/reason/ide/docs/ShowDocRMLTest.java ================================================ package com.reason.ide.docs; import com.intellij.lang.*; import com.reason.ide.*; import com.reason.ide.files.*; import com.reason.lang.core.psi.*; import com.reason.lang.reason.*; import org.junit.*; import org.junit.runner.*; import org.junit.runners.*; @RunWith(JUnit4.class) public class ShowDocRMLTest extends ORBasePlatformTestCase { public static final Language LANG = RmlLanguage.INSTANCE; @Test public void test_GH_155() { FileBase doc = configureCode("Doc.re", "/** add 1 */\nlet fn = x => x + 1;"); FileBase a = configureCode("A.re", "Mod.fn();"); RPsiLet resolvedElement = doc.getQualifiedExpressions("Doc.fn", RPsiLet.class).get(0); assertEquals("
Doc

let fn

add 1

", getDocForElement(a, LANG, resolvedElement)); } @Test public void test_GH_156() { configureCode("Doc.re", "/** Doc for y */\nlet y = 1;"); FileBase a = configureCode("A.re", "let x = Doc.y;\nx"); assertEquals("
Doc

let y

Doc for y

", getDoc(a, LANG)); } @Test public void test_GH_350() { configureCode("A.rei", "/** compare doc */\n let compare : string -> string -> int;"); FileBase a = configureCode("A.re", "let compare = (s1, s2) => 1;"); String doc = getDoc(a, LANG); assertEquals("
A

let compare

compare doc

", doc); } @Test public void test_GH_359() { FileBase a = configureCode("A.re", "module InnerComp = {\n" + " /**\n" + " Doc for my component\n" + " @param text Label\n" + " */\n" + " [@react.component]\n" + " let make = (~text) =>
text->React.string
;\n" + "};\n" + "\n" + "[@react.component]\n" + "let make = () => text=\"my text\" />;"); assertEquals("
A.InnerComp

let make

Doc for my component

Param:

text - Label

", getDoc(a, LANG)); } } ================================================ FILE: src/test/java/com/reason/ide/go/GotoImplementationOCLTest.java ================================================ package com.reason.ide.go; import com.intellij.psi.*; import com.intellij.testFramework.fixtures.*; import com.reason.ide.*; import com.reason.ide.files.*; import com.reason.lang.core.psi.*; import org.junit.*; import org.junit.runner.*; import org.junit.runners.*; @RunWith(JUnit4.class) public class GotoImplementationOCLTest extends ORBasePlatformTestCase { @Test public void should_goto_implementation() { configureCode("A.mli", "val x: int"); configureCode("A.ml", "let x = 1"); FileBase ref = configureCode("B.ml", "let y = A.x"); PsiElement[] targets = CodeInsightTestUtil.gotoImplementation(myFixture.getEditor(), ref).targets; assertSize(1, targets); assertEquals("A.x", ((RPsiQualifiedPathElement) targets[0]).getQualifiedName()); assertEquals("A.ml", targets[0].getContainingFile().getName()); } } ================================================ FILE: src/test/java/com/reason/ide/go/GotoImplementationRESTest.java ================================================ package com.reason.ide.go; import com.intellij.psi.*; import com.intellij.testFramework.fixtures.*; import com.reason.ide.*; import com.reason.ide.files.*; import com.reason.lang.core.psi.*; import org.junit.*; public class GotoImplementationRESTest extends ORBasePlatformTestCase { @Test public void should_goto_implementation() { configureCode("A.resi", "let x: int"); configureCode("A.res", "let x = 1"); FileBase b = configureCode("B.res", "let y = A.x"); PsiElement[] targets = CodeInsightTestUtil.gotoImplementation(myFixture.getEditor(), b).targets; assertSize(1, targets); assertEquals("A.x", ((RPsiQualifiedPathElement) targets[0]).getQualifiedName()); //assertEquals("A.res", targets[0].getContainingFile().getName()); } } ================================================ FILE: src/test/java/com/reason/ide/go/GotoImplementationRMLTest.java ================================================ package com.reason.ide.go; import org.junit.Test; import com.intellij.psi.PsiElement; import com.intellij.testFramework.fixtures.CodeInsightTestUtil; import com.reason.ide.ORBasePlatformTestCase; import com.reason.ide.files.FileBase; import com.reason.lang.core.psi.RPsiQualifiedPathElement; public class GotoImplementationRMLTest extends ORBasePlatformTestCase { @Test public void should_goto_implementation() { configureCode("A.rei", "let x: int;"); configureCode("A.re", "let x = 1;"); FileBase ref = configureCode("B.re", "let y = A.x;"); PsiElement[] targets = CodeInsightTestUtil.gotoImplementation(myFixture.getEditor(), ref).targets; assertSize(1, targets); assertEquals("A.x", ((RPsiQualifiedPathElement) targets[0]).getQualifiedName()); //assertEquals("A.re", targets[0].getContainingFile().getName()); } } ================================================ FILE: src/test/java/com/reason/ide/go/LineMarkerProviderOCLTest.java ================================================ package com.reason.ide.go; import com.intellij.codeInsight.daemon.*; import com.intellij.codeInsight.daemon.impl.*; import com.intellij.navigation.*; import com.reason.ide.*; import com.reason.ide.files.*; import com.reason.lang.core.psi.*; import org.junit.*; import org.junit.runner.*; import org.junit.runners.*; import java.util.*; @SuppressWarnings("DataFlowIssue") public class LineMarkerProviderOCLTest extends ORBasePlatformTestCase { @Test public void test_let_val_files() { FileBase intf = configureCode("A.mli", "val x: int"); FileBase impl = configureCode("A.ml", "let x = 1"); List> markers = doHighlight(intf); assertEquals(ORIcons.IMPLEMENTED, markers.getFirst().getIcon()); assertEquals("Implements let/val", markers.getFirst().getLineMarkerTooltip()); markers = doHighlight(impl); assertEquals(ORIcons.IMPLEMENTING, markers.getFirst().getIcon()); assertEquals("Declare let/val", markers.getFirst().getLineMarkerTooltip()); } @Test public void test_let_val() { FileBase f = configureCode("A.ml", """ module type I = sig val x: int end module M1 : I = struct let x = 1 end module M2 : I = struct let x = 2 end """); List> markers = doHighlight(f); assertSize(6, markers); RelatedItemLineMarkerInfo m0 = (RelatedItemLineMarkerInfo) markers.getFirst(); assertEquals(ORIcons.IMPLEMENTED, m0.getIcon()); assertEquals("Implements module", m0.getLineMarkerTooltip()); List m0Targets = new ArrayList<>(m0.createGotoRelatedItems()); assertSize(2, m0Targets); assertInstanceOf(m0Targets.getFirst().getElement(), RPsiInnerModule.class); assertInstanceOf(m0Targets.get(1).getElement(), RPsiInnerModule.class); RelatedItemLineMarkerInfo m1 = (RelatedItemLineMarkerInfo) markers.get(1); assertEquals(ORIcons.IMPLEMENTED, m1.getIcon()); assertEquals("Implements let/val", m1.getLineMarkerTooltip()); List m1Targets = new ArrayList<>(m1.createGotoRelatedItems()); assertSize(2, m1Targets); assertInstanceOf(m1Targets.getFirst().getElement(), RPsiLet.class); assertInstanceOf(m1Targets.get(1).getElement(), RPsiLet.class); assertEquals(ORIcons.IMPLEMENTING, markers.get(2).getIcon()); assertEquals("Declare module", markers.get(2).getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTING, markers.get(3).getIcon()); assertEquals("Declare let/val", markers.get(3).getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTING, markers.get(4).getIcon()); assertEquals("Declare module", markers.get(4).getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTING, markers.get(5).getIcon()); assertEquals("Declare let/val", markers.get(5).getLineMarkerTooltip()); } @Test public void test_type_files() { FileBase intf = configureCode("A.mli", "type t"); FileBase impl = configureCode("A.ml", """ type t module Inner = struct type t end """); List> markers = doHighlight(intf); assertEquals(ORIcons.IMPLEMENTED, markers.getFirst().getIcon()); assertEquals("Implements type", markers.getFirst().getLineMarkerTooltip()); markers = doHighlight(impl); assertEquals(ORIcons.IMPLEMENTING, markers.getFirst().getIcon()); assertEquals("Declare type", markers.getFirst().getLineMarkerTooltip()); assertSize(1, markers); } @Test public void test_type() { FileBase f = configureCode("A.ml", """ module type I = struct type t end module M1 : I = struct type t end module M2 : I = struct type t end """); List> markers = doHighlight(f); assertEquals(ORIcons.IMPLEMENTED, markers.getFirst().getIcon()); assertEquals("Implements module", markers.getFirst().getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTED, markers.get(1).getIcon()); assertEquals("Implements type", markers.get(1).getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTING, markers.get(2).getIcon()); assertEquals("Declare module", markers.get(2).getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTING, markers.get(3).getIcon()); assertEquals("Declare type", markers.get(3).getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTING, markers.get(2).getIcon()); assertEquals("Declare module", markers.get(2).getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTING, markers.get(5).getIcon()); assertEquals("Declare type", markers.get(5).getLineMarkerTooltip()); assertSize(6, markers); } @Test public void test_external_files() { FileBase intf = configureCode("A.mli", "external t: int -> unit = \"\";"); FileBase impl = configureCode("A.ml", "external t: int -> unit = \"\";"); List> markers = doHighlight(intf); assertEquals(ORIcons.IMPLEMENTED, markers.getFirst().getIcon()); assertEquals("Implements external", markers.getFirst().getLineMarkerTooltip()); markers = doHighlight(impl); assertEquals(ORIcons.IMPLEMENTING, markers.getFirst().getIcon()); assertEquals("Declare external", markers.getFirst().getLineMarkerTooltip()); } @Test public void test_external() { FileBase f = configureCode("A.ml", """ module type I = sig external t: int = "" end module M1 : I = struct external t: int = "" end module M2 : I = struct external t: int = "" end """); List> markers = doHighlight(f); assertEquals(ORIcons.IMPLEMENTED, markers.get(1).getIcon()); assertEquals("Implements external", markers.get(1).getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTING, markers.get(3).getIcon()); assertEquals("Declare external", markers.get(3).getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTING, markers.get(5).getIcon()); assertEquals("Declare external", markers.get(5).getLineMarkerTooltip()); assertSize(6, markers); } @Test public void test_module_type() { FileBase f = configureCode("A.ml", """ module type Intf = sig end module Impl : Intf = struct end """); List> lineMarkers = doHighlight(f); assertEquals(ORIcons.IMPLEMENTED, lineMarkers.getFirst().getIcon()); assertEquals("Implements module", lineMarkers.getFirst().getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTING, lineMarkers.get(1).getIcon()); assertEquals("Declare module", lineMarkers.get(1).getLineMarkerTooltip()); assertSize(2, lineMarkers); } @Test public void test_module_type_files() { FileBase intf = configureCode("A.mli", "module type A1 = sig end"); FileBase impl = configureCode("A.ml", "module type A1 = sig end"); List> markers = doHighlight(intf); assertEquals(ORIcons.IMPLEMENTED, markers.getFirst().getIcon()); assertEquals("Implements module", markers.getFirst().getLineMarkerTooltip()); markers = doHighlight(impl); assertEquals(ORIcons.IMPLEMENTING, markers.getFirst().getIcon()); assertEquals("Declare module", markers.getFirst().getLineMarkerTooltip()); } @Test public void test_module_type_both_direction() { FileBase intf = configureCode("A.mli", "module type A1 = sig end"); FileBase impl = configureCode("A.ml", """ // ... module type A1 = sig end module A2 : A1 = struct end """); List> markers = doHighlight(intf); assertSize(1, markers); assertEquals(ORIcons.IMPLEMENTED, markers.getFirst().getIcon()); assertEquals("Implements module", markers.getFirst().getLineMarkerTooltip()); markers = doHighlight(impl); assertSize(3, markers); assertEquals(ORIcons.IMPLEMENTED, markers.getFirst().getIcon()); assertEquals(19, markers.getFirst().startOffset); // A1->A2 assertEquals(ORIcons.IMPLEMENTING, markers.get(1).getIcon()); assertEquals(19, markers.get(1).startOffset); // A1->A1.mli assertEquals(ORIcons.IMPLEMENTING, markers.get(2).getIcon()); assertEquals(40, markers.get(2).startOffset); // A2->A1 } @Test public void test_module_deep() { configureCode("A.ml", """ module B = struct module type Intf = sig end end module IncorrectImpl : Intf = struct end module CorrectImpl : B.Intf = struct end """); myFixture.doHighlighting(); List> lineMarkers = DaemonCodeAnalyzerImpl.getLineMarkers(myFixture.getEditor().getDocument(), myFixture.getProject()); LineMarkerInfo m0 = lineMarkers.getFirst(); assertEquals(ORIcons.IMPLEMENTED, m0.getIcon()); assertEquals("Implements module", m0.getLineMarkerTooltip()); LineMarkerInfo m1 = lineMarkers.get(1); assertEquals(ORIcons.IMPLEMENTING, m1.getIcon()); assertEquals("Declare module", m1.getLineMarkerTooltip()); assertSize(2, lineMarkers); } @Test public void test_modules() { FileBase f = configureCode("A.ml", """ module type Intf = sig end module ImplA : Intf = struct end module ImplB : Intf = struct end """); List> markers = doHighlight(f); assertEquals(ORIcons.IMPLEMENTED, markers.getFirst().getIcon()); assertEquals("Implements module", markers.getFirst().getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTING, markers.get(1).getIcon()); assertEquals("Declare module", markers.get(1).getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTING, markers.get(2).getIcon()); assertEquals("Declare module", markers.get(2).getLineMarkerTooltip()); assertSize(3, markers); } @Test public void test_exception_files() { FileBase intf = configureCode("A.mli", "exception X"); FileBase impl = configureCode("A.ml", "exception X"); List> markers = doHighlight(intf); assertEquals(ORIcons.IMPLEMENTED, markers.getFirst().getIcon()); assertEquals("Implements exception", markers.getFirst().getLineMarkerTooltip()); markers = doHighlight(impl); assertEquals(ORIcons.IMPLEMENTING, markers.getFirst().getIcon()); assertEquals("Declare exception", markers.getFirst().getLineMarkerTooltip()); } @Test public void test_exception() { FileBase f = configureCode("A.ml", """ module type I = sig exception X end module M : I = struct exception X end """); List> markers = doHighlight(f); assertEquals(ORIcons.IMPLEMENTED, markers.get(1).getIcon()); assertEquals("Implements exception", markers.get(1).getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTING, markers.get(3).getIcon()); assertEquals("Declare exception", markers.get(3).getLineMarkerTooltip()); assertSize(4, markers); } // https://github.com/giraud/reasonml-idea-plugin/issues/322 // Class types in .mli files should link to the corresponding definition in the .ml file @Test public void test_GH_322() { configureCode("A.mli", """ class type proof_view = object end """); configureCode("A.ml", """ class type proof_view = object end """); myFixture.doHighlighting(); List> lineMarkers = DaemonCodeAnalyzerImpl.getLineMarkers(myFixture.getEditor().getDocument(), myFixture.getProject()); LineMarkerInfo m0 = lineMarkers.getFirst(); assertEquals(ORIcons.IMPLEMENTING, m0.getIcon()); assertEquals("Declare class", m0.getLineMarkerTooltip()); assertSize(1, lineMarkers); } // https://github.com/giraud/reasonml-idea-plugin/issues/323 // Method declarations in .ml files should link to their implementations @Test public void test_GH_323() { configureCode("A.mli", """ class type proof_view = object inherit GObj.widget method buffer: GText.buffer end """); configureCode("A.ml", """ class type proof_view = object inherit GObj.widget method buffer: GText.buffer end"""); myFixture.doHighlighting(); List> lineMarkers = DaemonCodeAnalyzerImpl.getLineMarkers(myFixture.getEditor().getDocument(), myFixture.getProject()); LineMarkerInfo m1 = lineMarkers.get(1); assertEquals("Declare method", m1.getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTING, m1.getIcon()); assertSize(2, lineMarkers); } // https://github.com/giraud/reasonml-idea-plugin/issues/485 // Module signature with inline signature @Test public void test_GH_485_signature_in_different_files() { FileBase mli = configureCode("A.mli", """ module M: sig val x: int end """); FileBase ml = configureCode("A.ml", """ module M: sig val x: int end = struct let x = 1 end """); myFixture.openFileInEditor(mli.getVirtualFile()); myFixture.doHighlighting(); List> mliMarkers = DaemonCodeAnalyzerImpl.getLineMarkers(myFixture.getEditor().getDocument(), getProject()); myFixture.openFileInEditor(ml.getVirtualFile()); myFixture.doHighlighting(); List> mlMarkers = DaemonCodeAnalyzerImpl.getLineMarkers(myFixture.getEditor().getDocument(), getProject()); assertSize(2, mliMarkers); assertSize(4, mlMarkers); // val x in A.mli RelatedItemLineMarkerInfo mi1 = (RelatedItemLineMarkerInfo) mliMarkers.get(1); assertEquals("Implements let/val", mi1.getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTED, mi1.getIcon()); // targets definition in signature and implementation in body List mi1RelatedItems = new ArrayList<>(mi1.createGotoRelatedItems()); assertSize(2, mi1RelatedItems); assertInstanceOf(mi1RelatedItems.getFirst().getElement(), RPsiVal.class); assertInstanceOf(mi1RelatedItems.get(1).getElement(), RPsiLet.class); assertEquals(ORIcons.IMPLEMENTING, mlMarkers.getFirst().getIcon()); assertEquals(7, mlMarkers.getFirst().startOffset); // M->M.mli assertEquals("Declare module", mlMarkers.getFirst().getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTED, mlMarkers.get(1).getIcon()); assertEquals("Implements let/val", mlMarkers.get(1).getLineMarkerTooltip()); assertEquals(21, mlMarkers.get(1).startOffset); // x[sig] -> x[impl] assertEquals(ORIcons.IMPLEMENTING, mlMarkers.get(2).getIcon()); assertEquals("Declare let/val", mlMarkers.get(2).getLineMarkerTooltip()); assertEquals(21, mlMarkers.get(2).startOffset); // x[sig] -> x.rei RelatedItemLineMarkerInfo r3 = (RelatedItemLineMarkerInfo) mlMarkers.get(3); assertTrue(r3.getLineMarkerTooltip().contains("Declare let/val")); assertEquals(ORIcons.IMPLEMENTING, r3.getIcon()); // targets both definitions (signatures in ml and mli) List r3RelatedItems = new ArrayList<>(r3.createGotoRelatedItems()); assertSize(2, r3RelatedItems); assertInstanceOf(r3RelatedItems.getFirst().getElement(), RPsiVal.class); assertInstanceOf(r3RelatedItems.get(1).getElement(), RPsiVal.class); } // https://github.com/giraud/reasonml-idea-plugin/issues/485 // Module signature with named signature type @Test public void test_GH_485_named_signature_module_type() { FileBase mli = configureCode("A.mli", """ module type MT = sig val x: int end """); FileBase ml = configureCode("A.ml", """ module type MT = sig val x : int end module Mm: MT = struct let x = 1 end """); myFixture.openFileInEditor(mli.getVirtualFile()); myFixture.doHighlighting(); List> mliMarkers = DaemonCodeAnalyzerImpl.getLineMarkers(myFixture.getEditor().getDocument(), getProject()); myFixture.openFileInEditor(ml.getVirtualFile()); myFixture.doHighlighting(); List> mlMarkers = DaemonCodeAnalyzerImpl.getLineMarkers(myFixture.getEditor().getDocument(), getProject()); assertSize(2, mliMarkers); assertSize(6, mlMarkers); // module type in A.mli RelatedItemLineMarkerInfo mi1 = (RelatedItemLineMarkerInfo) mliMarkers.getFirst(); assertTextEquals("Implements module", mi1.getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTED, mi1.getIcon()); List mi1RelatedItems = new ArrayList<>(mi1.createGotoRelatedItems()); assertSize(2, mi1RelatedItems); assertContainsElements(mi1RelatedItems.stream().map(m -> ((RPsiInnerModule) m.getElement()).getQualifiedName()).toList(), "A.MT", "A.Mm"); // module type in A.ml LineMarkerInfo m0 = mlMarkers.getFirst(); assertEquals("Implements module", m0.getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTED, m0.getIcon()); LineMarkerInfo m1 = mlMarkers.get(1); assertEquals("Declare module", m1.getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTING, m1.getIcon()); // val x in module type in A.ml LineMarkerInfo m2 = mlMarkers.get(2); assertEquals("Implements let/val", m2.getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTED, m2.getIcon()); // val x in module type in A.ml LineMarkerInfo m3 = mlMarkers.get(3); assertEquals("Declare let/val", m3.getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTING, m3.getIcon()); // val x in A.ml (to mli) LineMarkerInfo m4 = mlMarkers.get(4); assertEquals("Declare module", m4.getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTING, m4.getIcon()); // let in A.ml LineMarkerInfo m5 = mlMarkers.get(5); assertEquals("Declare let/val", m5.getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTING, m5.getIcon()); } // https://github.com/giraud/reasonml-idea-plugin/issues/485 @Test public void test_GH_485_anonymous_signature() { configureCode("A.ml", """ module type M = sig val x: int end module M : sig val x: int end = struct let x = 1 end """); myFixture.doHighlighting(); List> lineMarkers = DaemonCodeAnalyzerImpl.getLineMarkers(myFixture.getEditor().getDocument(), myFixture.getProject()); assertSize(2, lineMarkers); // val x LineMarkerInfo m0 = lineMarkers.getFirst(); assertEquals("Implements let/val", m0.getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTED, m0.getIcon()); // let x LineMarkerInfo m1 = lineMarkers.get(1); assertEquals("Declare let/val", m1.getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTING, m1.getIcon()); } } ================================================ FILE: src/test/java/com/reason/ide/go/LineMarkerProviderRESTest.java ================================================ package com.reason.ide.go; import com.intellij.codeInsight.daemon.*; import com.intellij.codeInsight.daemon.impl.*; import com.intellij.navigation.*; import com.reason.ide.*; import com.reason.ide.files.*; import com.reason.lang.core.psi.*; import org.junit.*; import java.util.*; @SuppressWarnings("DataFlowIssue") public class LineMarkerProviderRESTest extends ORBasePlatformTestCase { @Test public void test_let_files() { FileBase intf = configureCode("A.resi", "let x: int"); FileBase impl = configureCode("A.res", "let x = 1"); List> markers = doHighlight(intf); assertEquals(ORIcons.IMPLEMENTED, markers.getFirst().getIcon()); assertEquals("Implements let/val", markers.getFirst().getLineMarkerTooltip()); markers = doHighlight(impl); assertEquals(ORIcons.IMPLEMENTING, markers.getFirst().getIcon()); assertEquals("Declare let/val", markers.getFirst().getLineMarkerTooltip()); } @Test public void test_let() { FileBase f = configureCode("A.res", """ module type I = { let x : int } module M1 : I = { let x = 1 } module M2 : I = { let x = 2 } """); List> markers = doHighlight(f); assertEquals(ORIcons.IMPLEMENTED, markers.getFirst().getIcon()); assertEquals("Implements module", markers.getFirst().getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTED, markers.get(1).getIcon()); assertEquals("Implements let/val", markers.get(1).getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTING, markers.get(2).getIcon()); assertEquals("Declare module", markers.get(2).getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTING, markers.get(3).getIcon()); assertEquals("Declare let/val", markers.get(3).getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTING, markers.get(2).getIcon()); assertEquals("Declare module", markers.get(2).getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTING, markers.get(5).getIcon()); assertEquals("Declare let/val", markers.get(5).getLineMarkerTooltip()); } @Test public void test_type_files() { FileBase intf = configureCode("A.resi", "type t"); FileBase impl = configureCode("A.res", """ type t module Inner = { type t } """); List> markers = doHighlight(intf); assertEquals(ORIcons.IMPLEMENTED, markers.getFirst().getIcon()); assertEquals("Implements type", markers.getFirst().getLineMarkerTooltip()); markers = doHighlight(impl); assertEquals(ORIcons.IMPLEMENTING, markers.getFirst().getIcon()); assertEquals("Declare type", markers.getFirst().getLineMarkerTooltip()); assertSize(1, markers); } @Test public void test_type() { FileBase f = configureCode("A.res", """ module type I = { type t } module M1 : I = { type t } module M2 : I = { type t } """); List> markers = doHighlight(f); assertEquals(ORIcons.IMPLEMENTED, markers.getFirst().getIcon()); assertEquals("Implements module", markers.getFirst().getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTED, markers.get(1).getIcon()); assertEquals("Implements type", markers.get(1).getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTING, markers.get(2).getIcon()); assertEquals("Declare module", markers.get(2).getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTING, markers.get(3).getIcon()); assertEquals("Declare type", markers.get(3).getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTING, markers.get(2).getIcon()); assertEquals("Declare module", markers.get(2).getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTING, markers.get(5).getIcon()); assertEquals("Declare type", markers.get(5).getLineMarkerTooltip()); assertSize(6, markers); } @Test public void test_external_files() { FileBase intf = configureCode("A.resi", "external t: int = \"\""); FileBase impl = configureCode("A.res", "external t: int = \"\""); List> markers = doHighlight(intf); assertEquals(ORIcons.IMPLEMENTED, markers.getFirst().getIcon()); assertEquals("Implements external", markers.getFirst().getLineMarkerTooltip()); markers = doHighlight(impl); assertEquals(ORIcons.IMPLEMENTING, markers.getFirst().getIcon()); assertEquals("Declare external", markers.getFirst().getLineMarkerTooltip()); } @Test public void test_external() { FileBase f = configureCode("A.res", """ module type I = { external t: int = "" } module M1 : I = { external t: int = "" } module M2 : I = { external t: int = "" } """); List> markers = doHighlight(f); assertEquals(ORIcons.IMPLEMENTED, markers.get(1).getIcon()); assertEquals("Implements external", markers.get(1).getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTING, markers.get(3).getIcon()); assertEquals("Declare external", markers.get(3).getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTING, markers.get(5).getIcon()); assertEquals("Declare external", markers.get(5).getLineMarkerTooltip()); assertSize(6, markers); } @Test public void test_module_type() { FileBase f = configureCode("A.res", """ module type Intf = {} module Impl : Intf = {} """); List> lineMarkers = doHighlight(f); assertEquals(ORIcons.IMPLEMENTED, lineMarkers.getFirst().getIcon()); assertEquals("Implements module", lineMarkers.getFirst().getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTING, lineMarkers.get(1).getIcon()); assertEquals("Declare module", lineMarkers.get(1).getLineMarkerTooltip()); assertSize(2, lineMarkers); } @Test public void test_module_type_files() { FileBase intf = configureCode("A.resi", "module type A1 = {}"); FileBase impl = configureCode("A.res", "module type A1 = {}"); List> markers = doHighlight(intf); assertEquals(ORIcons.IMPLEMENTED, markers.getFirst().getIcon()); assertEquals("Implements module", markers.getFirst().getLineMarkerTooltip()); markers = doHighlight(impl); assertEquals(ORIcons.IMPLEMENTING, markers.getFirst().getIcon()); assertEquals("Declare module", markers.getFirst().getLineMarkerTooltip()); } @Test public void test_module_type_both_direction() { FileBase intf = configureCode("A.resi", "module type A1 = {}"); FileBase impl = configureCode("A.res", """ // ... module type A1 = {} module A2 : A1 = {} """); List> markers = doHighlight(intf); assertSize(1, markers); assertEquals(ORIcons.IMPLEMENTED, markers.getFirst().getIcon()); assertEquals("Implements module", markers.getFirst().getLineMarkerTooltip()); markers = doHighlight(impl); assertSize(3, markers); assertEquals(ORIcons.IMPLEMENTED, markers.getFirst().getIcon()); assertEquals("Implements module", markers.getFirst().getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTING, markers.get(1).getIcon()); assertEquals("Declare module", markers.get(1).getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTING, markers.get(2).getIcon()); assertEquals("Declare module", markers.get(2).getLineMarkerTooltip()); } @Test public void test_module_deep() { configureCode("A.res", """ module B = { module type Intf = {} } module IncorrectImpl : Intf = {} module CorrectImpl : B.Intf = {} """); myFixture.doHighlighting(); List> lineMarkers = DaemonCodeAnalyzerImpl.getLineMarkers(myFixture.getEditor().getDocument(), myFixture.getProject()); LineMarkerInfo m0 = lineMarkers.getFirst(); assertEquals(ORIcons.IMPLEMENTED, m0.getIcon()); assertEquals("Implements module", m0.getLineMarkerTooltip()); LineMarkerInfo m1 = lineMarkers.get(1); assertEquals(ORIcons.IMPLEMENTING, m1.getIcon()); assertEquals("Declare module", m1.getLineMarkerTooltip()); assertSize(2, lineMarkers); } @Test public void test_modules() { FileBase f = configureCode("A.res", """ module type Intf = {} module ImplA : Intf = {} module ImplB : Intf = {} """); List> markers = doHighlight(f); assertEquals(ORIcons.IMPLEMENTED, markers.getFirst().getIcon()); assertEquals("Implements module", markers.getFirst().getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTING, markers.get(1).getIcon()); assertEquals("Declare module", markers.get(1).getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTING, markers.get(2).getIcon()); assertEquals("Declare module", markers.get(2).getLineMarkerTooltip()); assertSize(3, markers); } @Test public void test_exception_files() { FileBase intf = configureCode("A.resi", "exception X"); FileBase impl = configureCode("A.res", "exception X"); List> markers = doHighlight(intf); assertEquals(ORIcons.IMPLEMENTED, markers.getFirst().getIcon()); assertEquals("Implements exception", markers.getFirst().getLineMarkerTooltip()); markers = doHighlight(impl); assertEquals(ORIcons.IMPLEMENTING, markers.getFirst().getIcon()); assertEquals("Declare exception", markers.getFirst().getLineMarkerTooltip()); } @Test public void test_exception() { FileBase f = configureCode("A.res", """ module type I = { exception X } module M : I = { exception X } """); List> markers = doHighlight(f); assertEquals(ORIcons.IMPLEMENTED, markers.get(1).getIcon()); assertEquals("Implements exception", markers.get(1).getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTING, markers.get(3).getIcon()); assertEquals("Declare exception", markers.get(3).getLineMarkerTooltip()); assertSize(4, markers); } // https://github.com/giraud/reasonml-idea-plugin/issues/485 // Module signature with inline signature @Test public void test_GH_485_signature_in_different_files() { FileBase resi = configureCode("A.resi", """ module M: { let x: int }; """); FileBase res = configureCode("A.res", """ module M: { let x: int } = { let x = 1 }; """); myFixture.openFileInEditor(resi.getVirtualFile()); myFixture.doHighlighting(); List> resiMarkers = DaemonCodeAnalyzerImpl.getLineMarkers(myFixture.getEditor().getDocument(), getProject()); myFixture.openFileInEditor(res.getVirtualFile()); myFixture.doHighlighting(); List> resMarkers = DaemonCodeAnalyzerImpl.getLineMarkers(myFixture.getEditor().getDocument(), getProject()); assertSize(2, resiMarkers); assertSize(4, resMarkers); // val x in A.rei RelatedItemLineMarkerInfo ri1 = (RelatedItemLineMarkerInfo) resiMarkers.get(1); assertEquals("Implements let/val", ri1.getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTED, ri1.getIcon()); // targets definition in signature and implementation in body List ri1RelatedItems = new ArrayList<>(ri1.createGotoRelatedItems()); assertSize(2, ri1RelatedItems); assertInstanceOf(ri1RelatedItems.getFirst().getElement(), RPsiLet.class); assertInstanceOf(ri1RelatedItems.get(1).getElement(), RPsiLet.class); assertEquals(ORIcons.IMPLEMENTING, resMarkers.getFirst().getIcon()); assertEquals(7, resMarkers.getFirst().startOffset); // M->M.mli assertEquals("Declare module", resMarkers.getFirst().getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTED, resMarkers.get(1).getIcon()); assertEquals("Implements let/val", resMarkers.get(1).getLineMarkerTooltip()); assertEquals(19, resMarkers.get(1).startOffset); // x[sig] -> x[impl] assertEquals(ORIcons.IMPLEMENTING, resMarkers.get(2).getIcon()); assertEquals("Declare let/val", resMarkers.get(2).getLineMarkerTooltip()); assertEquals(19, resMarkers.get(2).startOffset); // x[sig] -> x.rei RelatedItemLineMarkerInfo r3 = (RelatedItemLineMarkerInfo) resMarkers.get(3); assertTrue(r3.getLineMarkerTooltip().contains("Declare let/val")); assertEquals(ORIcons.IMPLEMENTING, r3.getIcon()); // targets both definitions (signatures in ml and mli) List r3RelatedItems = new ArrayList<>(r3.createGotoRelatedItems()); assertSize(2, r3RelatedItems); assertInstanceOf(r3RelatedItems.getFirst().getElement(), RPsiLet.class); assertInstanceOf(r3RelatedItems.get(1).getElement(), RPsiLet.class); } // https://github.com/giraud/reasonml-idea-plugin/issues/485 // Module signature with named signature type @Test public void test_GH_485_named_signature_module_type() { FileBase resi = configureCode("A.resi", """ module type MT = { let x: int }; """); FileBase res = configureCode("A.res", """ module type MT = { let x : int }; module Mm: MT = { let x = 1 }; """); myFixture.openFileInEditor(resi.getVirtualFile()); myFixture.doHighlighting(); List> resiMarkers = DaemonCodeAnalyzerImpl.getLineMarkers(myFixture.getEditor().getDocument(), getProject()); myFixture.openFileInEditor(res.getVirtualFile()); myFixture.doHighlighting(); List> resMarkers = DaemonCodeAnalyzerImpl.getLineMarkers(myFixture.getEditor().getDocument(), getProject()); assertSize(2, resiMarkers); assertSize(6, resMarkers); // module type in A.rei RelatedItemLineMarkerInfo mi1 = (RelatedItemLineMarkerInfo) resiMarkers.getFirst(); assertTextEquals("Implements module", mi1.getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTED, mi1.getIcon()); List mi1RelatedItems = new ArrayList<>(mi1.createGotoRelatedItems()); assertSize(2, mi1RelatedItems); assertContainsElements(mi1RelatedItems.stream().map(m -> ((RPsiInnerModule) m.getElement()).getQualifiedName()).toList(), "A.MT", "A.Mm"); // module type in A.re LineMarkerInfo m0 = resMarkers.getFirst(); assertEquals("Implements module", m0.getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTED, m0.getIcon()); LineMarkerInfo m1 = resMarkers.get(1); assertEquals("Declare module", m1.getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTING, m1.getIcon()); // val x in module type in A.re LineMarkerInfo m2 = resMarkers.get(2); assertEquals("Implements let/val", m2.getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTED, m2.getIcon()); // val x in module type in A.re LineMarkerInfo m3 = resMarkers.get(3); assertEquals("Declare let/val", m3.getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTING, m3.getIcon()); // val x in A.re (to mli) LineMarkerInfo m4 = resMarkers.get(4); assertEquals("Declare module", m4.getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTING, m4.getIcon()); // let in A.re LineMarkerInfo m5 = resMarkers.get(5); assertEquals("Declare let/val", m5.getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTING, m5.getIcon()); } // https://github.com/giraud/reasonml-idea-plugin/issues/485 @Test public void test_GH_485_anonymous_signature() { configureCode("A.re", """ module type M = { let x: int; }; module M : { let x: int; } = { let x = 1; }; """); myFixture.doHighlighting(); List> lineMarkers = DaemonCodeAnalyzerImpl.getLineMarkers(myFixture.getEditor().getDocument(), myFixture.getProject()); assertSize(2, lineMarkers); // let x LineMarkerInfo m0 = lineMarkers.getFirst(); assertEquals("Implements let/val", m0.getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTED, m0.getIcon()); // let x LineMarkerInfo m1 = lineMarkers.get(1); assertEquals("Declare let/val", m1.getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTING, m1.getIcon()); } } ================================================ FILE: src/test/java/com/reason/ide/go/LineMarkerProviderRMLTest.java ================================================ package com.reason.ide.go; import com.intellij.codeInsight.daemon.*; import com.intellij.codeInsight.daemon.impl.*; import com.intellij.navigation.*; import com.reason.ide.*; import com.reason.ide.files.*; import com.reason.lang.core.psi.*; import org.junit.*; import java.util.*; @SuppressWarnings("DataFlowIssue") public class LineMarkerProviderRMLTest extends ORBasePlatformTestCase { @Test public void test_let_files() { FileBase intf = configureCode("A.rei", "let x : int;"); FileBase impl = configureCode("A.re", "let x = 1;"); List> markers = doHighlight(intf); assertEquals(ORIcons.IMPLEMENTED, markers.getFirst().getIcon()); assertEquals("Implements let/val", markers.getFirst().getLineMarkerTooltip()); markers = doHighlight(impl); assertEquals(ORIcons.IMPLEMENTING, markers.getFirst().getIcon()); assertEquals("Declare let/val", markers.getFirst().getLineMarkerTooltip()); } @Test public void test_let() { FileBase f = configureCode("A.re", """ module type I = { let x : int; }; module M1 : I = { let x = 1; }; module M2 : I = { let x = 2; }; """); List> markers = doHighlight(f); assertSize(6, markers); RelatedItemLineMarkerInfo m0 = (RelatedItemLineMarkerInfo) markers.getFirst(); assertEquals(ORIcons.IMPLEMENTED, m0.getIcon()); assertEquals("Implements module", m0.getLineMarkerTooltip()); List m0Targets = new ArrayList<>(m0.createGotoRelatedItems()); assertSize(2, m0Targets); assertInstanceOf(m0Targets.getFirst().getElement(), RPsiInnerModule.class); assertInstanceOf(m0Targets.get(1).getElement(), RPsiInnerModule.class); RelatedItemLineMarkerInfo m1 = (RelatedItemLineMarkerInfo) markers.get(1); assertEquals(ORIcons.IMPLEMENTED, m1.getIcon()); assertEquals("Implements let/val", m1.getLineMarkerTooltip()); List m1Targets = new ArrayList<>(m1.createGotoRelatedItems()); assertSize(2, m1Targets); assertInstanceOf(m1Targets.getFirst().getElement(), RPsiLet.class); assertInstanceOf(m1Targets.get(1).getElement(), RPsiLet.class); assertEquals(ORIcons.IMPLEMENTING, markers.get(2).getIcon()); assertEquals("Declare module", markers.get(2).getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTING, markers.get(3).getIcon()); assertEquals("Declare let/val", markers.get(3).getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTING, markers.get(4).getIcon()); assertEquals("Declare module", markers.get(4).getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTING, markers.get(5).getIcon()); assertEquals("Declare let/val", markers.get(5).getLineMarkerTooltip()); } @Test public void test_type_files() { FileBase intf = configureCode("A.rei", "type t;"); FileBase impl = configureCode("A.re", """ type t; module Inner = { type t; }; """); List> markers = doHighlight(intf); assertEquals(ORIcons.IMPLEMENTED, markers.getFirst().getIcon()); assertEquals("Implements type", markers.getFirst().getLineMarkerTooltip()); markers = doHighlight(impl); assertEquals(ORIcons.IMPLEMENTING, markers.getFirst().getIcon()); assertEquals("Declare type", markers.getFirst().getLineMarkerTooltip()); assertSize(1, markers); } @Test public void test_type() { FileBase f = configureCode("A.re", """ module type I = { type t; }; module M1 : I = { type t; }; module M2 : I = { type t; }; """); List> markers = doHighlight(f); assertEquals(ORIcons.IMPLEMENTED, markers.getFirst().getIcon()); assertEquals("Implements module", markers.getFirst().getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTED, markers.get(1).getIcon()); assertEquals("Implements type", markers.get(1).getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTING, markers.get(2).getIcon()); assertEquals("Declare module", markers.get(2).getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTING, markers.get(3).getIcon()); assertEquals("Declare type", markers.get(3).getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTING, markers.get(2).getIcon()); assertEquals("Declare module", markers.get(2).getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTING, markers.get(5).getIcon()); assertEquals("Declare type", markers.get(5).getLineMarkerTooltip()); assertSize(6, markers); } @Test public void test_external_files() { FileBase intf = configureCode("A.rei", "external t: int = \"\";"); FileBase impl = configureCode("A.re", "external t: int = \"\";"); List> markers = doHighlight(intf); assertEquals(ORIcons.IMPLEMENTED, markers.getFirst().getIcon()); assertEquals("Implements external", markers.getFirst().getLineMarkerTooltip()); markers = doHighlight(impl); assertEquals(ORIcons.IMPLEMENTING, markers.getFirst().getIcon()); assertEquals("Declare external", markers.getFirst().getLineMarkerTooltip()); } @Test public void test_external() { FileBase f = configureCode("A.re", """ module type I = { external t: int = ""; }; module M1 : I = { external t: int = ""; }; module M2 : I = { external t: int = ""; }; """); List> markers = doHighlight(f); assertEquals(ORIcons.IMPLEMENTED, markers.get(1).getIcon()); assertEquals("Implements external", markers.get(1).getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTING, markers.get(3).getIcon()); assertEquals("Declare external", markers.get(3).getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTING, markers.get(5).getIcon()); assertEquals("Declare external", markers.get(5).getLineMarkerTooltip()); assertSize(6, markers); } @Test public void test_module_type() { FileBase f = configureCode("A.re", """ module type Intf = {}; module Impl: Intf = {}; """); List> lineMarkers = doHighlight(f); assertEquals(ORIcons.IMPLEMENTED, lineMarkers.getFirst().getIcon()); assertEquals("Implements module", lineMarkers.getFirst().getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTING, lineMarkers.get(1).getIcon()); assertEquals("Declare module", lineMarkers.get(1).getLineMarkerTooltip()); assertSize(2, lineMarkers); } @Test public void test_module_type_files() { FileBase intf = configureCode("A.rei", "module type A1 = {};"); FileBase impl = configureCode("A.re", "module type A1 = {};"); List> markers = doHighlight(intf); assertEquals(ORIcons.IMPLEMENTED, markers.getFirst().getIcon()); assertEquals("Implements module", markers.getFirst().getLineMarkerTooltip()); markers = doHighlight(impl); assertEquals(ORIcons.IMPLEMENTING, markers.getFirst().getIcon()); assertEquals("Declare module", markers.getFirst().getLineMarkerTooltip()); } @Test public void test_module_type_both_direction() { FileBase intf = configureCode("A.rei", "module type A1 = {};"); FileBase impl = configureCode("A.re", """ // ... module type A1 = {}; module A2 : A1 = {}; """); List> markers = doHighlight(intf); assertSize(1, markers); assertEquals(ORIcons.IMPLEMENTED, markers.getFirst().getIcon()); assertEquals("Implements module", markers.getFirst().getLineMarkerTooltip()); markers = doHighlight(impl); assertSize(3, markers); assertEquals(ORIcons.IMPLEMENTED, markers.getFirst().getIcon()); assertEquals("Implements module", markers.getFirst().getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTING, markers.get(1).getIcon()); assertEquals("Declare module", markers.get(1).getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTING, markers.get(2).getIcon()); assertEquals("Declare module", markers.get(2).getLineMarkerTooltip()); } @Test public void test_module_deep() { configureCode("A.re", """ module B = { module type Intf = {}; }; module IncorrectImpl : Intf = {}; module CorrectImpl : B.Intf = {}; """); myFixture.doHighlighting(); List> lineMarkers = DaemonCodeAnalyzerImpl.getLineMarkers(myFixture.getEditor().getDocument(), myFixture.getProject()); LineMarkerInfo m0 = lineMarkers.getFirst(); assertEquals(ORIcons.IMPLEMENTED, m0.getIcon()); assertEquals("Implements module", m0.getLineMarkerTooltip()); LineMarkerInfo m1 = lineMarkers.get(1); assertEquals(ORIcons.IMPLEMENTING, m1.getIcon()); assertEquals("Declare module", m1.getLineMarkerTooltip()); assertSize(2, lineMarkers); } @Test public void test_modules() { FileBase f = configureCode("A.re", """ module type Intf = {}; module ImplA : Intf = {}; module ImplB : Intf = {}; """); List> markers = doHighlight(f); assertEquals(ORIcons.IMPLEMENTED, markers.getFirst().getIcon()); assertEquals("Implements module", markers.getFirst().getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTING, markers.get(1).getIcon()); assertEquals("Declare module", markers.get(1).getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTING, markers.get(2).getIcon()); assertEquals("Declare module", markers.get(2).getLineMarkerTooltip()); assertSize(3, markers); } @Test public void test_exception_files() { FileBase intf = configureCode("A.rei", "exception X;"); FileBase impl = configureCode("A.re", "exception X;"); List> markers = doHighlight(intf); assertEquals(ORIcons.IMPLEMENTED, markers.getFirst().getIcon()); assertEquals("Implements exception", markers.getFirst().getLineMarkerTooltip()); markers = doHighlight(impl); assertEquals(ORIcons.IMPLEMENTING, markers.getFirst().getIcon()); assertEquals("Declare exception", markers.getFirst().getLineMarkerTooltip()); } @Test public void test_exception() { FileBase f = configureCode("A.re", """ module type I = { exception X; }; module M : I = { exception X; }; """); List> markers = doHighlight(f); assertEquals(ORIcons.IMPLEMENTED, markers.get(1).getIcon()); assertEquals("Implements exception", markers.get(1).getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTING, markers.get(3).getIcon()); assertEquals("Declare exception", markers.get(3).getLineMarkerTooltip()); assertSize(4, markers); } // https://github.com/giraud/reasonml-idea-plugin/issues/322 // Class types in .rei files should link to the corresponding definition in the .re file @Test public void test_GH_322() { configureCode("A.rei", """ class type proof_view = {}; """); configureCode("A.re", """ class type proof_view = {}; """); myFixture.doHighlighting(); List> lineMarkers = DaemonCodeAnalyzerImpl.getLineMarkers(myFixture.getEditor().getDocument(), myFixture.getProject()); LineMarkerInfo m0 = lineMarkers.getFirst(); assertEquals(ORIcons.IMPLEMENTING, m0.getIcon()); assertEquals("Declare class", m0.getLineMarkerTooltip()); assertSize(1, lineMarkers); } // https://github.com/giraud/reasonml-idea-plugin/issues/323 // Method declarations in .re files should link to their implementations @Test public void test_GH_323_class() { configureCode("A.rei", """ class type proof_view = { inherit GObj.widget; pub buffer: GText.buffer; }; """); configureCode("A.re", """ class type proof_view = { inherit GObj.widget; pub buffer: GText.buffer; }; """); myFixture.doHighlighting(); List> lineMarkers = DaemonCodeAnalyzerImpl.getLineMarkers(myFixture.getEditor().getDocument(), myFixture.getProject()); assertSize(2, lineMarkers); LineMarkerInfo m1 = lineMarkers.get(1); assertEquals("Declare method", m1.getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTING, m1.getIcon()); } // https://github.com/giraud/reasonml-idea-plugin/issues/485 // Module signature with inline signature @Test public void test_GH_485_signature_in_different_files() { FileBase rei = configureCode("A.rei", """ module M: { let x: int; }; """); FileBase re = configureCode("A.re", """ module M: { let x: int; } = { let x = 1; }; """); myFixture.openFileInEditor(rei.getVirtualFile()); myFixture.doHighlighting(); List> reiMarkers = DaemonCodeAnalyzerImpl.getLineMarkers(myFixture.getEditor().getDocument(), getProject()); myFixture.openFileInEditor(re.getVirtualFile()); myFixture.doHighlighting(); List> reMarkers = DaemonCodeAnalyzerImpl.getLineMarkers(myFixture.getEditor().getDocument(), getProject()); assertSize(2, reiMarkers); assertSize(4, reMarkers); // val x in A.rei RelatedItemLineMarkerInfo ri1 = (RelatedItemLineMarkerInfo) reiMarkers.get(1); assertEquals("Implements let/val", ri1.getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTED, ri1.getIcon()); // targets definition in signature and implementation in body List ri1RelatedItems = new ArrayList<>(ri1.createGotoRelatedItems()); assertSize(2, ri1RelatedItems); assertInstanceOf(ri1RelatedItems.getFirst().getElement(), RPsiLet.class); assertInstanceOf(ri1RelatedItems.get(1).getElement(), RPsiLet.class); assertEquals(ORIcons.IMPLEMENTING, reMarkers.getFirst().getIcon()); assertEquals(7, reMarkers.getFirst().startOffset); // M->M.mli assertEquals("Declare module", reMarkers.getFirst().getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTED, reMarkers.get(1).getIcon()); assertEquals("Implements let/val", reMarkers.get(1).getLineMarkerTooltip()); assertEquals(19, reMarkers.get(1).startOffset); // x[sig] -> x[impl] assertEquals(ORIcons.IMPLEMENTING, reMarkers.get(2).getIcon()); assertEquals("Declare let/val", reMarkers.get(2).getLineMarkerTooltip()); assertEquals(19, reMarkers.get(2).startOffset); // x[sig] -> x.rei RelatedItemLineMarkerInfo r3 = (RelatedItemLineMarkerInfo) reMarkers.get(3); assertTrue(r3.getLineMarkerTooltip().contains("Declare let/val")); assertEquals(ORIcons.IMPLEMENTING, r3.getIcon()); // targets both definitions (signatures in ml and mli) List r3RelatedItems = new ArrayList<>(r3.createGotoRelatedItems()); assertSize(2, r3RelatedItems); assertInstanceOf(r3RelatedItems.getFirst().getElement(), RPsiLet.class); assertInstanceOf(r3RelatedItems.get(1).getElement(), RPsiLet.class); } // https://github.com/giraud/reasonml-idea-plugin/issues/485 // Module signature with named signature type @Test public void test_GH_485_named_signature_module_type() { FileBase mli = configureCode("A.rei", """ module type MT = { let x: int; }; """); FileBase ml = configureCode("A.re", """ module type MT = { let x : int; }; module Mm: MT = { let x = 1; }; """); myFixture.openFileInEditor(mli.getVirtualFile()); myFixture.doHighlighting(); List> mliMarkers = DaemonCodeAnalyzerImpl.getLineMarkers(myFixture.getEditor().getDocument(), getProject()); myFixture.openFileInEditor(ml.getVirtualFile()); myFixture.doHighlighting(); List> mlMarkers = DaemonCodeAnalyzerImpl.getLineMarkers(myFixture.getEditor().getDocument(), getProject()); assertSize(2, mliMarkers); assertSize(6, mlMarkers); // module type in A.rei RelatedItemLineMarkerInfo mi1 = (RelatedItemLineMarkerInfo) mliMarkers.getFirst(); assertTextEquals("Implements module", mi1.getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTED, mi1.getIcon()); List mi1RelatedItems = new ArrayList<>(mi1.createGotoRelatedItems()); assertSize(2, mi1RelatedItems); assertContainsElements(mi1RelatedItems.stream().map(m -> ((RPsiInnerModule) m.getElement()).getQualifiedName()).toList(), "A.MT", "A.Mm"); // module type in A.re LineMarkerInfo m0 = mlMarkers.getFirst(); assertEquals("Implements module", m0.getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTED, m0.getIcon()); LineMarkerInfo m1 = mlMarkers.get(1); assertEquals("Declare module", m1.getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTING, m1.getIcon()); // val x in module type in A.re LineMarkerInfo m2 = mlMarkers.get(2); assertEquals("Implements let/val", m2.getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTED, m2.getIcon()); // val x in module type in A.re LineMarkerInfo m3 = mlMarkers.get(3); assertEquals("Declare let/val", m3.getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTING, m3.getIcon()); // val x in A.re (to mli) LineMarkerInfo m4 = mlMarkers.get(4); assertEquals("Declare module", m4.getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTING, m4.getIcon()); // let in A.re LineMarkerInfo m5 = mlMarkers.get(5); assertEquals("Declare let/val", m5.getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTING, m5.getIcon()); } // https://github.com/giraud/reasonml-idea-plugin/issues/485 @Test public void test_GH_485_anonymous_signature() { configureCode("A.re", """ module type M = { let x: int; }; module M : { let x: int; } = { let x = 1; }; """); myFixture.doHighlighting(); List> lineMarkers = DaemonCodeAnalyzerImpl.getLineMarkers(myFixture.getEditor().getDocument(), myFixture.getProject()); assertSize(2, lineMarkers); // let x LineMarkerInfo m0 = lineMarkers.getFirst(); assertEquals("Implements let/val", m0.getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTED, m0.getIcon()); // let x LineMarkerInfo m1 = lineMarkers.get(1); assertEquals("Declare let/val", m1.getLineMarkerTooltip()); assertEquals(ORIcons.IMPLEMENTING, m1.getIcon()); } } ================================================ FILE: src/test/java/com/reason/ide/hints/OclParameterInfoHandlerTest.java ================================================ package com.reason.ide.hints; import com.intellij.psi.*; import com.intellij.testFramework.utils.parameterInfo.*; import com.reason.ide.*; import com.reason.lang.core.psi.impl.*; import org.junit.*; import org.junit.runner.*; import org.junit.runners.*; @RunWith(JUnit4.class) public class OclParameterInfoHandlerTest extends ORBasePlatformTestCase { @Test public void test_before() { configureCode("A.mli", "val add: int -> int -> int"); configureCode("B.ml", "A.add 1"); UIInfoContext context = getParameterInfoUI(); assertEquals("int -> int -> int", context.text); assertEquals(-1, context.currentParam); } @Test public void test_basic() { configureCode("A.mli", "val add: int -> int -> int"); configureCode("B.ml", "A.add 1 1"); UIInfoContext context = getParameterInfoUI(); assertEquals("int -> int -> int", context.text); assertEquals(0, context.currentParam); } @Test public void test_eof() { configureCode("A.mli", "val add: int -> int -> int"); configureCode("B.ml", "A.add 1"); UIInfoContext context = getParameterInfoUI(); assertEquals("int -> int -> int", context.text); assertEquals(0, context.currentParam); } @Test public void test_whitespace() { configureCode("A.mli", "val add: int -> int -> int"); configureCode("B.ml", "A.add 1 "); UIInfoContext context = getParameterInfoUI(); assertEquals("int -> int -> int", context.text); assertEquals(0/*1?*/, context.currentParam); } @Test public void test_intf_impl() { configureCode("A.mli", "val add: int -> int -> int"); configureCode("A.ml", "let add x y = x + y"); configureCode("B.ml", "let _ = A.add 1"); UIInfoContext context = getParameterInfoUI(); assertEquals("int -> int -> int", context.text); assertEquals(0, context.currentParam); } @SuppressWarnings("ConstantConditions") private UIInfoContext getParameterInfoUI() { ORParameterInfoHandler handler = new OclParameterInfoHandler(); MockCreateParameterInfoContext infoContext = new MockCreateParameterInfoContext(myFixture.getEditor(), myFixture.getFile()); RPsiParameters paramsOwner = handler.findElementForParameterInfo(infoContext); if (paramsOwner == null) { return new UIInfoContext("NULL", -1); } handler.showParameterInfo(paramsOwner, infoContext); MockParameterInfoUIContext context = new MockParameterInfoUIContext<>(paramsOwner); handler.updateUI((RmlParameterInfoHandler.ArgumentsDescription) infoContext.getItemsToShow()[0], context); MockUpdateParameterInfoContext updateContext = new MockUpdateParameterInfoContext(myFixture.getEditor(), myFixture.getFile()); RPsiParameters updateParamsOwner = handler.findElementForUpdatingParameterInfo(updateContext); updateContext.setParameterOwner(updateParamsOwner); handler.updateParameterInfo(updateParamsOwner, updateContext); return new UIInfoContext(context.getText(), updateContext.getCurrentParameter()); } static class UIInfoContext { String text; int currentParam; public UIInfoContext(String text, int currentParam) { this.text = text; this.currentParam = currentParam; } } } ================================================ FILE: src/test/java/com/reason/ide/hints/ResParameterInfoHandlerTest.java ================================================ package com.reason.ide.hints; import com.intellij.psi.*; import com.intellij.testFramework.utils.parameterInfo.*; import com.reason.ide.*; import com.reason.lang.core.psi.impl.*; import org.junit.*; public class ResParameterInfoHandlerTest extends ORBasePlatformTestCase { @Test public void test_basic() { configureCode("A.resi", "let add : (int, int) => int"); configureCode("B.res", "A.add()"); UIInfoContext context = getParameterInfoUI(); assertEquals("(int, int) => int", context.text); assertEquals(0, context.currentParam); } @Test public void test_intf_impl() { configureCode("A.resi", "let add : (int, int) => int"); configureCode("A.res", "let add = (x, y) => x + y"); configureCode("B.res", "A.add()"); UIInfoContext context = getParameterInfoUI(); assertEquals("(int, int) => int", context.text); assertEquals(0, context.currentParam); } @Test public void test_empty() { configureCode("A.resi", "let fn : unit => string"); configureCode("B.res", "A.fn()"); UIInfoContext context = getParameterInfoUI(); assertEquals("unit => string", context.text); assertEquals(0, context.currentParam); } @Test public void test_item() { configureCode("A.resi", "let add : (int, int) => int"); configureCode("B.res", "A.add(1, )"); UIInfoContext context = getParameterInfoUI(); assertEquals("(int, int) => int", context.text); assertEquals(1, context.currentParam); } @SuppressWarnings("ConstantConditions") private UIInfoContext getParameterInfoUI() { ResParameterInfoHandler handler = new ResParameterInfoHandler(); MockCreateParameterInfoContext infoContext = new MockCreateParameterInfoContext(myFixture.getEditor(), myFixture.getFile()); RPsiParameters paramsOwner = handler.findElementForParameterInfo(infoContext); handler.showParameterInfo(paramsOwner, infoContext); MockParameterInfoUIContext context = new MockParameterInfoUIContext<>(paramsOwner); handler.updateUI((RmlParameterInfoHandler.ArgumentsDescription) infoContext.getItemsToShow()[0], context); MockUpdateParameterInfoContext updateContext = new MockUpdateParameterInfoContext(myFixture.getEditor(), myFixture.getFile()); RPsiParameters updateParamsOwner = handler.findElementForUpdatingParameterInfo(updateContext); updateContext.setParameterOwner(updateParamsOwner); handler.updateParameterInfo(updateParamsOwner, updateContext); return new UIInfoContext(context.getText(), updateContext.getCurrentParameter()); } static class UIInfoContext { String text; int currentParam; public UIInfoContext(String text, int currentParam) { this.text = text; this.currentParam = currentParam; } } } ================================================ FILE: src/test/java/com/reason/ide/hints/RmlParameterInfoHandlerTest.java ================================================ package com.reason.ide.hints; import com.intellij.psi.*; import com.intellij.testFramework.utils.parameterInfo.*; import com.reason.ide.*; import com.reason.lang.core.psi.impl.*; import org.junit.*; import org.junit.runner.*; import org.junit.runners.*; @RunWith(JUnit4.class) public class RmlParameterInfoHandlerTest extends ORBasePlatformTestCase { @Test public void test_basic() { configureCode("A.rei", "let add : (int, int) => int;"); configureCode("B.re", "A.add()"); UIInfoContext context = getParameterInfoUI(); assertEquals("(int, int) => int", context.text); assertEquals(0, context.currentParam); } @Test public void test_intf_impl() { configureCode("A.rei", "let add : (int, int) => int;"); configureCode("A.re", "let add = (x, y) => x + y;"); configureCode("B.re", "A.add()"); UIInfoContext context = getParameterInfoUI(); assertEquals("(int, int) => int", context.text); assertEquals(0, context.currentParam); } @Test public void test_empty() { configureCode("A.rei", "let fn : unit => string;"); configureCode("B.re", "A.fn()"); UIInfoContext context = getParameterInfoUI(); assertEquals("unit => string", context.text); assertEquals(0, context.currentParam); } @Test public void test_item() { configureCode("A.rei", "let add : (int, int) => int;"); configureCode("B.re", "A.add(1, )"); UIInfoContext context = getParameterInfoUI(); assertEquals("(int, int) => int", context.text); assertEquals(1, context.currentParam); } @SuppressWarnings("ConstantConditions") private UIInfoContext getParameterInfoUI() { RmlParameterInfoHandler handler = new RmlParameterInfoHandler(); MockCreateParameterInfoContext infoContext = new MockCreateParameterInfoContext(myFixture.getEditor(), myFixture.getFile()); RPsiParameters paramsOwner = handler.findElementForParameterInfo(infoContext); handler.showParameterInfo(paramsOwner, infoContext); MockParameterInfoUIContext context = new MockParameterInfoUIContext<>(paramsOwner); handler.updateUI((RmlParameterInfoHandler.ArgumentsDescription) infoContext.getItemsToShow()[0], context); MockUpdateParameterInfoContext updateContext = new MockUpdateParameterInfoContext(myFixture.getEditor(), myFixture.getFile()); RPsiParameters updateParamsOwner = handler.findElementForUpdatingParameterInfo(updateContext); updateContext.setParameterOwner(updateParamsOwner); handler.updateParameterInfo(updateParamsOwner, updateContext); return new UIInfoContext(context.getText(), updateContext.getCurrentParameter()); } static class UIInfoContext { String text; int currentParam; public UIInfoContext(String text, int currentParam) { this.text = text; this.currentParam = currentParam; } } } ================================================ FILE: src/test/java/com/reason/ide/intention/ExpandLocalOpenIntentionRMLTest.java ================================================ package com.reason.ide.intention; import com.intellij.codeInsight.intention.IntentionAction; import com.reason.ide.ORBasePlatformTestCase; import com.reason.ide.files.RmlFileType; import org.junit.*; import org.junit.runner.*; import org.junit.runners.*; @SuppressWarnings("ConstantConditions") @RunWith(JUnit4.class) public class ExpandLocalOpenIntentionRMLTest extends ORBasePlatformTestCase { @Test public void test_basic() { myFixture.configureByText(RmlFileType.INSTANCE, "let x = Js.Promise.(Api.all());"); IntentionAction expandAction = myFixture.getAvailableIntention("Expand local open"); myFixture.launchAction(expandAction); myFixture.checkResult("let x = { open Js.Promise; Api.all(); };"); } // https://github.com/giraud/reasonml-idea-plugin/issues/67 @Test public void test_inner() { myFixture.configureByText(RmlFileType.INSTANCE, "Js.Promise.(Api.all() |> then_(result => if (!result) { Js.log(result); () }));"); IntentionAction expandAction = myFixture.getAvailableIntention("Expand local open"); myFixture.launchAction(expandAction); myFixture.checkResult("{ open Js.Promise; Api.all() |> then_(result => if (!result) { Js.log(result); () }); };"); } } ================================================ FILE: src/test/java/com/reason/ide/intention/FunctionBracesIntentionRESTest.java ================================================ package com.reason.ide.intention; import com.intellij.codeInsight.intention.*; import com.reason.ide.*; import org.junit.*; import org.junit.runner.*; import org.junit.runners.*; @SuppressWarnings("ConstantConditions") @RunWith(JUnit4.class) public class FunctionBracesIntentionRESTest extends ORBasePlatformTestCase { public static final String ADD_BRACES = "Add braces to blockless function"; @Test public void test_basic() { configureCode("A.res", "let add = (x, y) => x + y"); IntentionAction bracesAction = myFixture.getAvailableIntention(ADD_BRACES); myFixture.launchAction(bracesAction); myFixture.checkResult("let add = (x, y) => { x + y }"); } @Test public void test_with_signature() { configureCode("A.res", "let add = (x: int => int, y: int) => x + y"); IntentionAction bracesAction = myFixture.getAvailableIntention(ADD_BRACES); myFixture.launchAction(bracesAction); myFixture.checkResult("let add = (x: int => int, y: int) => { x + y }"); } // https://github.com/giraud/reasonml-idea-plugin/issues/67 @Test public void test_GH_67() { configureCode("A.res", "Js.Promise.( Api.all() |> then_(result => Js.log(result)) )"); IntentionAction bracesAction = myFixture.getAvailableIntention(ADD_BRACES); myFixture.launchAction(bracesAction); myFixture.checkResult("Js.Promise.( Api.all() |> then_(result => { Js.log(result) }) )"); } } ================================================ FILE: src/test/java/com/reason/ide/intention/FunctionBracesIntentionRMLTest.java ================================================ package com.reason.ide.intention; import com.intellij.codeInsight.intention.*; import com.reason.ide.*; import org.junit.*; import org.junit.runner.*; import org.junit.runners.*; @SuppressWarnings("ConstantConditions") @RunWith(JUnit4.class) public class FunctionBracesIntentionRMLTest extends ORBasePlatformTestCase { public static final String ADD_BRACES = "Add braces to blockless function"; @Test public void test_basic() { configureCode("A.re", "let add = (x, y) => x + y;"); IntentionAction bracesAction = myFixture.getAvailableIntention(ADD_BRACES); myFixture.launchAction(bracesAction); myFixture.checkResult("let add = (x, y) => { x + y; };"); } @Test public void test_with_signature() { configureCode("A.re", "let add = (x: int => int, y: int) => x + y;"); IntentionAction bracesAction = myFixture.getAvailableIntention(ADD_BRACES); myFixture.launchAction(bracesAction); myFixture.checkResult("let add = (x: int => int, y: int) => { x + y; };"); } @Test public void test_jsx2ComponentFunction() { configureCode("A.re", "let make = (children) => { ...component, render: self => v/>, };"); IntentionAction bracesAction = myFixture.getAvailableIntention(ADD_BRACES); myFixture.launchAction(bracesAction); myFixture.checkResult("let make = (children) => { ...component, render: self => {
; }, };"); } // https://github.com/giraud/reasonml-idea-plugin/issues/67 @Test public void test_GH_67() { configureCode("A.re", "Js.Promise.( Api.all() |> then_(result => Js.log(result)) );"); IntentionAction bracesAction = myFixture.getAvailableIntention(ADD_BRACES); myFixture.launchAction(bracesAction); myFixture.checkResult("Js.Promise.( Api.all() |> then_(result => { Js.log(result); }) );"); } } ================================================ FILE: src/test/java/com/reason/ide/search/reference/FindLIdentUsagesOCLTest.java ================================================ package com.reason.ide.search.reference; import com.intellij.openapi.util.*; import com.intellij.usageView.*; import com.reason.ide.*; import com.reason.lang.core.psi.impl.*; import org.junit.*; import java.util.*; @SuppressWarnings("ConstantConditions") public class FindLIdentUsagesOCLTest extends ORBasePlatformTestCase { @Test public void test_from_module() { configureCode("FLIA.ml", "let x = 1"); configureCode("FLIB.ml", "let y = FLIA.x + 2;"); Collection usages = myFixture.testFindUsages("FLIA.ml"); assertSize(1, usages); } @Test public void test_same_module() { configureCode("FLIC.ml", "let x = 1\n let y = x + 1"); List usages = (List) myFixture.testFindUsages("FLIC.ml"); assertSize(1, usages); UsageInfo usageInfo = usages.getFirst(); assertEquals("x + 1", usageInfo.getElement().getParent().getText()); } @Test public void test_val() { configureCode("A.mli", "val x: int"); configureCode("B.ml", "let y = A.x + 2"); List usages = findUsages("A.mli"); assertSize(1, usages); assertInstanceOf(usages.getFirst().getElement().getParent(), RPsiLetBinding.class); } @Test public void test_destructuration() { configureCode("A.ml", """ let (dialogStatus,setDialogStatus) = x let _ = fun () -> setDialogStatus () """); List usages = findUsages("A.ml"); assertSize(1, usages); UsageInfo usageInfo = usages.getFirst(); assertEquals(TextRange.create(58, 73), usageInfo.getSegment()); } } ================================================ FILE: src/test/java/com/reason/ide/search/reference/FindLIdentUsagesRESTest.java ================================================ package com.reason.ide.search.reference; import java.util.*; import org.junit.Test; import com.intellij.openapi.util.TextRange; import com.intellij.usageView.UsageInfo; import com.reason.ide.ORBasePlatformTestCase; import com.reason.lang.core.psi.RPsiQualifiedPathElement; @SuppressWarnings("DataFlowIssue") public class FindLIdentUsagesRESTest extends ORBasePlatformTestCase { @Test public void test_from_module() { configureCode("FLIA.res", "let x = 1"); configureCode("FLIB.res", "let y = FLIA.x + 2"); Collection usages = myFixture.testFindUsages("FLIA.res"); assertSize(1, usages); } @Test public void test_same_module() { configureCode("FLIC.res", "let x = 1\n let y = x + 1"); List usages = (List) myFixture.testFindUsages("FLIC.res"); assertSize(1, usages); UsageInfo usageInfo = usages.getFirst(); assertEquals("x + 1", usageInfo.getElement().getParent().getText()); } @Test public void test_module_signature() { configureCode("A.resi", "module B: { type t\n let toString: t => string\n }\n module C: { type t\n let toString: t => string\n }"); List usages = (List) myFixture.testFindUsages("A.resi"); assertSize(1, usages); UsageInfo usageInfo = usages.getFirst(); assertEquals("t", usageInfo.getElement().getParent().getText()); assertEquals("A.B.toString", ((RPsiQualifiedPathElement) usageInfo.getElement().getParent().getParent().getParent()).getQualifiedName()); } @Test public void test_record() { configureCode("A.res", """ type t = { f1: bool, f2: int } let x = { f1: true, f2: 421 } """); List usages = findUsages("A.res"); assertSize(1, usages); UsageInfo usageInfo = usages.getFirst(); assertEquals("A.x.f2", ((RPsiQualifiedPathElement) usageInfo.getElement().getParent()).getQualifiedName()); } //@Test TODO make it work public void test_object_field() { configureCode("A.res", """ let obj = { "f1": true, "f2": 421 } let _ = obj["f2"] """); List usages = findUsages("A.res"); assertSize(1, usages); UsageInfo usageInfo = usages.getFirst(); assertEquals(TextRange.create(48, 52), usageInfo.getSegment()); } @Test public void test_destructuration() { configureCode("A.res", """ let (dialogStatus, setDialogStatus) = x let _ = () => setDialogStatus() """); List usages = findUsages("A.res"); assertSize(1, usages); UsageInfo usageInfo = usages.getFirst(); assertEquals(TextRange.create(54, 69), usageInfo.getSegment()); } } ================================================ FILE: src/test/java/com/reason/ide/search/reference/FindLIdentUsagesRMLTest.java ================================================ package com.reason.ide.search.reference; import com.intellij.openapi.util.*; import com.intellij.usageView.*; import com.reason.ide.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import org.junit.*; import java.util.*; @SuppressWarnings("ConstantConditions") public class FindLIdentUsagesRMLTest extends ORBasePlatformTestCase { @Test public void test_let() { configureCode("A.re", "let x = 1; let z = x + 2;"); List usages = findUsages("A.re"); assertSize(1, usages); assertInstanceOf(usages.getFirst().getElement().getParent(), RPsiLetBinding.class); } @Test public void test_type() { configureCode("A.re", "type t; type x = t;"); List usages = findUsages("A.re"); assertSize(1, usages); assertInstanceOf(usages.getFirst().getElement().getParent(), RPsiTypeBinding.class); } @Test public void test_external() { configureCode("A.re", "external width : unit => int = \"\"; let x = width();"); List usages = findUsages("A.re"); assertSize(1, usages); assertInstanceOf(usages.getFirst().getElement().getParent(), RPsiFunctionCall.class); } @Test public void test_from_module() { configureCode("FLIA.re", "let x = 1;"); configureCode("FLIB.re", "let y = FLIA.x + 2;"); List usages = findUsages("FLIA.re"); assertSize(1, usages); } @Test public void test_same_module() { configureCode("FLIC.re", "let x = 1; let y = x + 1;"); List usages = findUsages("FLIC.re"); assertSize(1, usages); UsageInfo usageInfo = usages.getFirst(); assertEquals("x + 1", usageInfo.getElement().getParent().getText()); } @Test public void test_module_signature() { configureCode("A.rei", "module B: { type t; let toString: t => string; }; module C: { type t; let toString: t => string; };"); List usages = findUsages("A.rei"); assertSize(1, usages); UsageInfo usageInfo = usages.getFirst(); assertEquals("t", usageInfo.getElement().getParent().getText()); assertEquals("A.B.toString", ((RPsiQualifiedPathElement) usageInfo.getElement().getParent().getParent().getParent()).getQualifiedName()); } @Test public void test_record_field() { configureCode("A.re", """ type t = { f1: bool, f2: int }; let x = { f1: true, f2: 421 }; """); List usages = findUsages("A.re"); assertSize(1, usages); UsageInfo usageInfo = usages.getFirst(); assertEquals("A.x.f2", ((RPsiQualifiedPathElement) usageInfo.getElement().getParent()).getQualifiedName()); } @Test public void test_object_field() { configureCode("A.re", """ let obj = { "f1": true, "f2": 421 }; let _ = obj##f2; """); List usages = findUsages("A.re"); assertSize(1, usages); UsageInfo usageInfo = usages.getFirst(); assertEquals(TextRange.create(50, 52), usageInfo.getSegment()); } @Test public void test_destructuration() { configureCode("A.re", """ let (dialogStatus, setDialogStatus) = x; let _ = () => setDialogStatus(); """); List usages = findUsages("A.re"); assertSize(1, usages); UsageInfo usageInfo = usages.getFirst(); assertEquals(TextRange.create(55, 70), usageInfo.getSegment()); } } ================================================ FILE: src/test/java/com/reason/ide/search/reference/FindUIdentUsagesOCLTest.java ================================================ package com.reason.ide.search.reference; import com.intellij.psi.util.*; import com.intellij.usageView.*; import com.reason.ide.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import org.jetbrains.annotations.*; import org.junit.*; import org.junit.runner.*; import org.junit.runners.*; import java.util.*; @SuppressWarnings("ConstantConditions") @RunWith(JUnit4.class) public class FindUIdentUsagesOCLTest extends ORBasePlatformTestCase { @Test public void test_exception() { configureCode("A.ml", "exception ExceptionName\n let _ = raise ExceptionName"); Collection usages = myFixture.testFindUsages("A.ml"); assertSize(1, usages); assertEquals("raise ExceptionName", usages.iterator().next().getElement().getParent().getText()); } @Test public void test_module() { configureCode("A.ml", "module M\n let x = M.x"); List usages = findUsages("A.ml"); assertEquals("M.x", usages.get(0).getElement().getParent().getText()); } @Test public void test_functor() { configureCode("A.ml", "module M () = struct module X = struct end end\n module X = M()"); List usages = findUsages("A.ml"); assertEquals("M()", usages.get(0).getElement().getParent().getText()); } @Test public void test_variant() { configureCode("A.ml", "type t = | Red\n let color = Red"); List usages = findUsages("A.ml"); assertSize(1, usages); assertEquals("A.color", PsiTreeUtil.getParentOfType(usages.get(0).getElement(), RPsiLet.class).getQualifiedName()); } } ================================================ FILE: src/test/java/com/reason/ide/search/reference/FindUIdentUsagesRESTest.java ================================================ package com.reason.ide.search.reference; import com.intellij.psi.util.*; import com.intellij.usageView.*; import com.reason.ide.*; import com.reason.lang.core.psi.*; import org.jetbrains.annotations.*; import org.junit.*; import org.junit.runner.*; import org.junit.runners.*; import java.util.*; @SuppressWarnings("ConstantConditions") @RunWith(JUnit4.class) public class FindUIdentUsagesRESTest extends ORBasePlatformTestCase { @Test public void test_exception() { configureCode("A.res", "exception ExceptionName\n raise(ExceptionName)"); List usages = findUsages("A.res"); assertSize(1, usages); assertEquals("(ExceptionName)", usages.iterator().next().getElement().getParent().getText()); } @Test public void test_module() { configureCode("A.res", "module M\n let x = M.x"); List usages = findUsages("A.res"); assertSize(1, usages); assertEquals("M.x", usages.get(0).getElement().getParent().getText()); } @Test public void test_functor() { configureCode("A.res", "module M = () => { let x = true }\n module X = M()"); List usages = findUsages("A.res"); assertEquals("M()", usages.get(0).getElement().getParent().getText()); } @Test public void test_variant() { configureCode("A.res", "type t = | Red\n let color = Red"); List usages = findUsages("A.res"); assertSize(1, usages); assertEquals("A.color", PsiTreeUtil.getParentOfType(usages.get(0).getElement(), RPsiLet.class).getQualifiedName()); } } ================================================ FILE: src/test/java/com/reason/ide/search/reference/FindUIdentUsagesRMLTest.java ================================================ package com.reason.ide.search.reference; import com.intellij.psi.*; import com.intellij.psi.util.*; import com.intellij.usageView.*; import com.reason.ide.*; import com.reason.lang.core.psi.*; import org.junit.*; import org.junit.runner.*; import org.junit.runners.*; import java.util.*; @SuppressWarnings("ConstantConditions") @RunWith(JUnit4.class) public class FindUIdentUsagesRMLTest extends ORBasePlatformTestCase { @Test public void test_exception() { configureCode("A.re", "exception ExceptionName; raise(ExceptionName);"); List usages = findUsages("A.re"); assertEquals("(ExceptionName)", usages.get(0).getElement().getParent().getText()); } @Test public void test_module() { configureCode("A.re", "module M; let x = M.x;"); List usages = findUsages("A.re"); assertEquals("M.x", usages.get(0).getElement().getParent().getText()); } @Test public void test_functor() { configureCode("A.re", "module M = () => { let x = true; }; module X = M();"); List usages = findUsages("A.re"); assertEquals("M()", usages.get(0).getElement().getParent().getText()); } @Test public void test_variant() { configureCode("A.re", "type t = | Red; let color = Red;"); List usages = findUsages("A.re"); assertSize(1, usages); assertEquals("A.color", PsiTreeUtil.getParentOfType(usages.get(0).getElement(), RPsiLet.class).getQualifiedName()); } } ================================================ FILE: src/test/java/com/reason/ide/search/reference/ORModuleResolutionPsiGist_OCL_Test.java ================================================ package com.reason.ide.search.reference; import com.intellij.psi.util.*; import com.reason.ide.*; import com.reason.ide.files.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import org.junit.*; import java.util.*; import static java.util.List.*; @SuppressWarnings("ConstantConditions") public class ORModuleResolutionPsiGist_OCL_Test extends ORBasePlatformTestCase { @Test public void test_include_no_resolution() { FileBase e = configureCode("A.ml", """ module A1 = struct end include B.B1 """); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); RPsiInclude ei = PsiTreeUtil.findChildOfType(e, RPsiInclude.class); assertEmpty(data.getValues(ei)); assertEmpty(data.getValues(e)); } @Test public void test_open_no_resolution() { FileBase e = configureCode("A.ml", """ module A1 = struct end open B.B1 """); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); RPsiOpen eo = PsiTreeUtil.findChildOfType(e, RPsiOpen.class); assertEmpty(data.getValues(eo)); assertEmpty(data.getValues(e)); } @Test public void test_include_in_file() { FileBase e = configureCode("A.ml", """ module A1 = struct module A2 = struct end end include A1.A2 """); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); RPsiInclude ei = PsiTreeUtil.findChildOfType(e, RPsiInclude.class); assertOrderedEquals(data.getValues(ei/*A1.A2*/), "A.A1.A2"); assertOrderedEquals(data.getValues(e/*A.re*/), "A.A1.A2"); } @Test public void test_open_in_file() { FileBase e = configureCode("A.ml", """ module A1 = struct module A2 = struct end end open A1.A2 """); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); RPsiOpen eo = PsiTreeUtil.findChildOfType(e, RPsiOpen.class); assertOrderedEquals(data.getValues(eo/*A1.A11*/), "A.A1.A2"); assertEmpty(data.getValues(e/*A.re*/)); } @Test public void test_include_in_file_steps() { FileBase e = configureCode("A.ml", """ module A1 = struct module A2 = struct end end include A1 include A2 """); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); ArrayList eis = new ArrayList<>(PsiTreeUtil.findChildrenOfType(e, RPsiInclude.class)); assertOrderedEquals(data.getValues(eis.get(0/*A1*/)), "A.A1"); assertOrderedEquals(data.getValues(eis.get(1/*A2*/)), "A.A1.A2"); assertOrderedEquals(data.getValues(e/*A.re*/), "A.A1", "A.A1.A2"); } @Test public void test_open_in_file_steps() { FileBase e = configureCode("A.ml", """ module A1 = struct module A2 = struct end end open A1 open A2 """); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); ArrayList eos = new ArrayList<>(PsiTreeUtil.findChildrenOfType(e, RPsiOpen.class)); assertOrderedEquals(data.getValues(eos.get(0/*A1*/)), "A.A1"); assertOrderedEquals(data.getValues(eos.get(1/*A2*/)), "A.A1.A2"); assertEmpty(data.getValues(e/*A.re*/)); } @Test public void test_include_in_steps() { configureCode("A.ml", """ module A1 = struct module A2 = struct end end """); FileBase e = configureCode("B.ml", """ include A.A1 include A2 """); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); ArrayList eis = new ArrayList<>(PsiTreeUtil.findChildrenOfType(e, RPsiInclude.class)); assertEmpty(data.getValues(eis.get(0/*A.A1*/))); // Same assertOrderedEquals(data.getValues(eis.get(1/*A2*/)), "A.A1.A2"); assertOrderedEquals(data.getValues(e/*B.re*/), "A.A1", "A.A1.A2"); } @Test public void test_open_in_steps() { configureCode("A.ml", """ module A1 = struct module A2 = struct end end """); FileBase e = configureCode("B.ml", """ open A.A1 open A2 """); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); ArrayList eos = new ArrayList<>(PsiTreeUtil.findChildrenOfType(e, RPsiOpen.class)); assertEmpty(data.getValues(eos.get(0/*A.A1*/))); // Same assertOrderedEquals(data.getValues(eos.get(1/*A2*/)), "A.A1.A2"); assertEmpty(data.getValues(e/*B*/)); } @Test public void test_aliases_01() { FileBase e = configureCode("A.ml", """ module A1 = struct module A2 = struct end end module B1 = A1 include B1 module B2 = A2 include B2 """); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); List eis = new ArrayList<>(PsiTreeUtil.findChildrenOfType(e, RPsiInclude.class)); assertOrderedEquals(data.getValues(eis.get(0/*B1*/)), "A.A1"); assertOrderedEquals(data.getValues(eis.get(1/*B2*/)), "A.A1.A2"); List ems = new ArrayList<>(PsiTreeUtil.findChildrenOfType(e, RPsiModule.class)); assertOrderedEquals(data.getValues(ems.get(2/*B1*/)), "A.A1"); assertOrderedEquals(data.getValues(ems.get(3/*B2*/)), "A.A1.A2"); assertOrderedEquals(data.getValues(e/*A*/), "A.A1", "A.A1.A2"); } @Test public void test_aliases_02() { FileBase e = configureCode("A.ml", """ module A1 = struct module A2 = struct end module B2 = A2 end module B1 = A1 include B1.B2 """); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); RPsiInclude ei = PsiTreeUtil.findChildOfType(e, RPsiInclude.class); assertOrderedEquals(data.getValues(ei/*B1.B2*/), "A.A1.A2"); assertOrderedEquals(data.getValues(e/*A*/), "A.A1.A2"); } @Test public void test_aliases_03() { configureCode("A.ml", ""); FileBase e = configureCode("B.ml", "module B1 = A"); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); RPsiModule em = PsiTreeUtil.findChildOfType(e, RPsiModule.class); assertOrderedEquals(data.getValues(em/*B1*/), "A"); assertEmpty(data.getValues(e/*B*/)); } @Test public void test_aliases_04() { configureCode("A.ml", """ module W = struct module X = struct module Y = struct module Z = struct let z = 1 end end end end """); FileBase e = configureCode("B.ml", """ module C = A.W.X module D = C.Y.Z """); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); List ems = copyOf(PsiTreeUtil.findChildrenOfType(e, RPsiModule.class)); assertOrderedEquals(data.getValues(ems.get(0/*C*/)), "A.W.X"); assertOrderedEquals(data.getValues(ems.get(1/*D*/)), "A.W.X.Y.Z"); assertEmpty(data.getValues(e/*B*/)); } @Test public void test_aliases_05() { configureCode("A.ml", "module A1 = struct module A11 = struct type id = string end end"); configureCode("B.ml", "module B1 = A.A1"); FileBase e = configureCode("Dummy.ml", "module X = B.B1.A11"); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); RPsiModule em = PsiTreeUtil.findChildOfType(e, RPsiModule.class); assertOrderedEquals(data.getValues(em/*ELayout*/), "A.A1.A11"); } @Test public void test_alias_of_alias() { configureCode("A.ml", """ module A1 = struct module A2 = struct let id = "_new_" end end """); configureCode("B.ml", """ module B1 = struct module B2 = struct module B3 = struct let id = A.A1.A2.id end end end module B4 = struct include A module B5 = B1.B2 end """); FileBase e = configureCode("C.ml", """ module C1 = B.B4 module C2 = C1.B5.B3 let _ = C2.id """); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); List ems = copyOf(PsiTreeUtil.findChildrenOfType(e, RPsiModule.class)); assertOrderedEquals(data.getValues(ems.get(0)/*C1*/), "B.B4", "A"); assertOrderedEquals(data.getValues(ems.get(1)/*C2*/), "B.B1.B2.B3"); } @Test public void test_alias_of_alias_02() { configureCode("A.ml", """ module A1 = struct module A2 = struct end end """); configureCode("B.ml", """ module B1 = struct include A end """); FileBase e = configureCode("C.ml", """ module C1 = B.B1 module C2 = C1.A1 """); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); List ems = copyOf(PsiTreeUtil.findChildrenOfType(e, RPsiModule.class)); assertOrderedEquals(data.getValues(ems.get(0)/*C1*/), "B.B1", "A"); assertOrderedEquals(data.getValues(ems.get(1)/*C2*/), "A.A1"); } @Test public void test_alias_of_alias_03() { configureCode("A.ml", "module A1 = struct end"); configureCode("B.ml", "include A"); FileBase e = configureCode("C.ml", """ module C1 = B module C2 = C1.A1 """); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); List ems = copyOf(PsiTreeUtil.findChildrenOfType(e, RPsiModule.class)); assertOrderedEquals(data.getValues(ems.get(0)/*C1*/), "B", "A"); assertOrderedEquals(data.getValues(ems.get(1)/*C2*/), "A.A1"); } // https://github.com/giraud/reasonml-idea-plugin/issues/426 @Test public void test_GH_426_aliases_same_file() { FileBase e = configureCode("A.ml", """ module W = struct module X = struct module Y = struct module Z = struct let z = 1 end end end end module C = W.X module D = C.Y.Z """); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); RPsiModule[] ems = PsiTreeUtil.getChildrenOfType(e, RPsiModule.class); assertOrderedEquals(data.getValues(ems[1/*C*/]), "A.W.X"); assertOrderedEquals(data.getValues(ems[2/*D*/]), "A.W.X.Y.Z"); assertEmpty(data.getValues(e/*B*/)); } @Test public void test_include_in_module() { configureCode("A.ml", ""); FileBase e = configureCode("B.ml", """ module B1 = struct include A end """); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); RPsiModule em = PsiTreeUtil.findChildOfType(e, RPsiModule.class); assertOrderedEquals(data.getValues(em/*B.B1*/), "A"); } @Test public void test_same_includes() { FileBase e = configureCode("A.ml", """ module A1 = struct module A2 = struct end end open A1 include A2 module A2 = struct end include A2 """); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); ArrayList eis = new ArrayList<>(PsiTreeUtil.findChildrenOfType(e, RPsiInclude.class)); assertOrderedEquals(data.getValues(eis.get(0)/*A2*/), "A.A1.A2"); assertOrderedEquals(data.getValues(eis.get(1)/*A2*/), "A.A2"); } @Test public void test_functor_in_file() { FileBase e = configureCode("A.ml", """ module type S = sig module P : sig end end module F() : S = struct module P = struct end end module M = F(struct end) """); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); List ems = ORUtil.findImmediateChildrenOfClass(e, RPsiModule.class); assertOrderedEquals(data.getValues(ems.get(1)/*F*/), "A.S"); assertOrderedEquals(data.getValues(ems.get(2)/*M*/), "A.F"); } @Test public void test_functor_result_out_file() { configureCode("A.ml", "module type Result = sig val a: int end"); FileBase e = configureCode("B.ml", """ module T = A module Make(M:Intf) : T.Result = struct let b = 3 end """); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); List ems = ORUtil.findImmediateChildrenOfClass(e, RPsiModule.class); assertOrderedEquals(data.getValues(ems.get(1)/*Make*/), "A.Result"); } @Test public void test_functor_no_signature() { FileBase e = configureCode("A.ml", """ module M() = struct let x = true end module X = M(struct end) """); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); List ems = ORUtil.findImmediateChildrenOfClass(e, RPsiModule.class); assertOrderedEquals(data.getValues(ems.get(1)/*M*/), "A.M"); } @Test public void test_functor_outside() { configureCode("A.ml", """ module type S = sig module P : sig end end module F() : S = struct module P = struct end end """); FileBase e = configureCode("B.ml", "module M = A.F(struct end)"); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); RPsiModule em = ORUtil.findImmediateFirstChildOfClass(e, RPsiModule.class); assertOrderedEquals(data.getValues(em/*M*/), "A.F"); assertEmpty(data.getValues(e)); } @Test public void test_functor_open() { configureCode("A.ml", """ module type Intf = sig val x : bool end module MakeIntf(I:Intf) : Intf = struct let y = 1 end """); FileBase e = configureCode("B.ml", """ open A module Instance = MakeIntf(struct let x = true end) """); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); RPsiModule em = ORUtil.findImmediateFirstChildOfClass(e, RPsiModule.class); assertOrderedEquals(data.getValues(em/*Instance*/), "A.MakeIntf"); assertEmpty(data.getValues(e)); } @Test public void test_functor_instance_same_file() { configureCode("B.ml", "module type Result = sig val a: int end"); FileBase e = configureCode("A.ml", """ module Make(M:Intf) = (struct end : (B.Result with type t := M.t)) module Instance = Make(struct end) """); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); List ems = copyOf(PsiTreeUtil.findChildrenOfType(e, RPsiModule.class)); assertOrderedEquals(data.getValues(ems.get(1)/*Instance*/), "A.Make"); } @Test public void test_file_include_functor() { FileBase e = configureCode("A.ml", """ module Make() = struct let y = 1 end include Make(struct end) """); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); assertOrderedEquals(data.getValues(e), "A.Make"); } @Test public void test_functor_path() { configureCode("D.ml", """ module type D1Intf = sig type t end """); configureCode("C.ml", """ module type C1Intf = sig val make: unit => string end module Make (MX: D.D1Intf): C1Intf = struct let make () = "" end """); configureCode("B.ml", "module B1 = C"); FileBase e = configureCode("A.ml", "module Instance = B.B1.Make(X)"); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); RPsiModule em = PsiTreeUtil.findChildOfType(e, RPsiModule.class); assertOrderedEquals(data.getValues(em/*Instance*/), "C.Make"); } @Test public void test_unpack_local() { FileBase e = configureCode("A.ml", """ module type I = sig val x : int end module Three : I = struct let x = 3 end let three = (module Three : I) module New_three = (val three : I) """); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); RPsiModule em = ORUtil.findImmediateLastChildOfClass(e, RPsiModule.class); assertOrderedEquals(data.getValues(em), "A.I"); } @Test public void test_unpack_open() { configureCode("A.ml", """ module type I = sig val x : int end module Three : I = struct let x = 3 end """); FileBase e = configureCode("B.ml", """ open A let three = (module Three : I) module New_three = (val three : I) """); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); RPsiModule em = ORUtil.findImmediateFirstChildOfClass(e, RPsiModule.class); assertOrderedEquals(data.getValues(em), "A.I"); } @Test public void test_unpack_no_signature() { configureCode("A.ml", """ module type I = sig val x : int end module Three : I = struct let x = 3 end """); FileBase e = configureCode("B.ml", """ open A let three = (module Three : I) module New_three = (val three) """); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); RPsiModule em = ORUtil.findImmediateFirstChildOfClass(e, RPsiModule.class); assertOrderedEquals(data.getValues(em), "A.I"); } @Test public void test_unpack_no_signature_qname() { configureCode("A.ml", """ module A1 = struct module type I = sig val x : int end end module Three : A1.I = struct let x = 3 end """); FileBase e = configureCode("B.ml", """ module B1 = A let three = (module B1.Three : B1.A1.I) module New_three = (val three) """); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); RPsiModule em = ORUtil.findImmediateLastChildOfClass(e, RPsiModule.class); assertOrderedEquals(data.getValues(em), "A.A1.I"); } @Test public void test_unpack_parameter() { FileBase e = configureCode("A.ml", """ module type I = sig val x: int end let x ~p:(p: (module I)) = let module S = (val p) in S.x """); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); RPsiLetBinding el = PsiTreeUtil.getChildOfType(e, RPsiLet.class).getBinding(); RPsiModule em = PsiTreeUtil.findChildOfType(el, RPsiModule.class); assertOrderedEquals(data.getValues(em), "A.I"); } @Test public void test_unpack_parameter_global_module() { configureCode("A.ml", """ module B = struct module type I = sig val fn: int => unit end end """); FileBase e = configureCode("C.ml", "let x ~p:(p:(module A.B.I)) = let module S = (val p) in S.fn"); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); RPsiModule em = PsiTreeUtil.findChildOfType(PsiTreeUtil.getChildOfType(e, RPsiLet.class).getBinding(), RPsiModule.class); assertOrderedEquals(data.getValues(em), "A.B.I"); } } ================================================ FILE: src/test/java/com/reason/ide/search/reference/ORModuleResolutionPsiGist_RES_Test.java ================================================ package com.reason.ide.search.reference; import com.intellij.psi.util.*; import com.reason.ide.*; import com.reason.ide.files.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import org.junit.*; import java.util.*; import static java.util.List.*; @SuppressWarnings("ConstantConditions") public class ORModuleResolutionPsiGist_RES_Test extends ORBasePlatformTestCase { @Test public void test_include_no_resolution() { FileBase e = configureCode("A.res", "module A1 = {}\n include B.B1"); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); RPsiInclude ei = PsiTreeUtil.findChildOfType(e, RPsiInclude.class); assertEmpty(data.getValues(ei)); assertEmpty(data.getValues(e)); } @Test public void test_open_no_resolution() { FileBase e = configureCode("A.res", "module A1 = {}\n open B.B1"); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); RPsiOpen eo = PsiTreeUtil.findChildOfType(e, RPsiOpen.class); assertEmpty(data.getValues(eo)); assertEmpty(data.getValues(e)); } @Test public void test_include_in_file() { FileBase e = configureCode("A.res", "module A1 = { module A2 = {} }\n include A1.A2"); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); RPsiInclude ei = PsiTreeUtil.findChildOfType(e, RPsiInclude.class); assertOrderedEquals(data.getValues(ei/*A1.A2*/), "A.A1.A2"); assertOrderedEquals(data.getValues(e/*A.re*/), "A.A1.A2"); } @Test public void test_open_in_file() { FileBase e = configureCode("A.res", "module A1 = { module A2 = {} }\n open A1.A2"); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); RPsiOpen eo = PsiTreeUtil.findChildOfType(e, RPsiOpen.class); assertOrderedEquals(data.getValues(eo/*A1.A11*/), "A.A1.A2"); assertEmpty(data.getValues(e/*A.re*/)); } @Test public void test_include_in_file_steps() { FileBase e = configureCode("A.res", "module A1 = { module A2 = {} }\n include A1\n include A2"); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); ArrayList eis = new ArrayList<>(PsiTreeUtil.findChildrenOfType(e, RPsiInclude.class)); assertOrderedEquals(data.getValues(eis.get(0/*A1*/)), "A.A1"); assertOrderedEquals(data.getValues(eis.get(1/*A2*/)), "A.A1.A2"); assertOrderedEquals(data.getValues(e/*A.re*/), "A.A1", "A.A1.A2"); } @Test public void test_open_in_file_steps() { FileBase e = configureCode("A.res", "module A1 = { module A2 = {} }\n open A1\n open A2;"); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); ArrayList eos = new ArrayList<>(PsiTreeUtil.findChildrenOfType(e, RPsiOpen.class)); assertOrderedEquals(data.getValues(eos.get(0/*A1*/)), "A.A1"); assertOrderedEquals(data.getValues(eos.get(1/*A2*/)), "A.A1.A2"); assertEmpty(data.getValues(e/*A.re*/)); } @Test public void test_include_in_steps() { configureCode("A.res", "module A1 = { module A2 = {} }"); FileBase e = configureCode("B.res", "include A.A1\n include A2"); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); ArrayList eis = new ArrayList<>(PsiTreeUtil.findChildrenOfType(e, RPsiInclude.class)); assertEmpty(data.getValues(eis.get(0/*A.A1*/))); // Same assertOrderedEquals(data.getValues(eis.get(1/*A2*/)), "A.A1.A2"); assertOrderedEquals(data.getValues(e/*B.re*/), "A.A1", "A.A1.A2"); } @Test public void test_open_in_steps() { configureCode("A.res", "module A1 = { module A2 = {} }"); FileBase e = configureCode("B.res", "open A.A1\n open A2"); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); ArrayList eos = new ArrayList<>(PsiTreeUtil.findChildrenOfType(e, RPsiOpen.class)); assertEmpty(data.getValues(eos.get(0/*A.A1*/))); // Same assertOrderedEquals(data.getValues(eos.get(1/*A2*/)), "A.A1.A2"); assertEmpty(data.getValues(e/*B*/)); } @Test public void test_aliases_01() { FileBase e = configureCode("A.res", """ module A1 = { module A2 = {} } module B1 = A1 include B1 module B2 = A2 include B2 """); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); List eis = new ArrayList<>(PsiTreeUtil.findChildrenOfType(e, RPsiInclude.class)); assertOrderedEquals(data.getValues(eis.get(0/*B1*/)), "A.A1"); assertOrderedEquals(data.getValues(eis.get(1/*B2*/)), "A.A1.A2"); List ems = new ArrayList<>(PsiTreeUtil.findChildrenOfType(e, RPsiModule.class)); assertOrderedEquals(data.getValues(ems.get(2/*B1*/)), "A.A1"); assertOrderedEquals(data.getValues(ems.get(3/*B2*/)), "A.A1.A2"); assertOrderedEquals(data.getValues(e/*A*/), "A.A1", "A.A1.A2"); } @Test public void test_aliases_02() { FileBase e = configureCode("A.res", """ module A1 = { module A2 = {} module B2 = A2 } module B1 = A1 include B1.B2 """); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); RPsiInclude ei = PsiTreeUtil.findChildOfType(e, RPsiInclude.class); assertOrderedEquals(data.getValues(ei/*B1.B2*/), "A.A1.A2"); assertOrderedEquals(data.getValues(e/*A*/), "A.A1.A2"); } @Test public void test_aliases_03() { configureCode("A.res", ""); FileBase e = configureCode("B.res", "module B1 = A;"); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); RPsiModule em = PsiTreeUtil.findChildOfType(e, RPsiModule.class); assertOrderedEquals(data.getValues(em/*B1*/), "A"); assertEmpty(data.getValues(e/*B*/)); } @Test public void test_aliases_04() { configureCode("A.res", "module W = { module X = { module Y = { module Z = { let z = 1 } } } }"); FileBase e = configureCode("B.res", "module C = A.W.X\n module D = C.Y.Z"); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); List ems = copyOf(PsiTreeUtil.findChildrenOfType(e, RPsiModule.class)); assertOrderedEquals(data.getValues(ems.get(0/*C*/)), "A.W.X"); assertOrderedEquals(data.getValues(ems.get(1/*D*/)), "A.W.X.Y.Z"); assertEmpty(data.getValues(e/*B*/)); } @Test public void test_aliases_05() { configureCode("A.res", "module A1 = { module A11 = { type id = string } }"); configureCode("B.res", "module B1 = A.A1"); FileBase e = configureCode("Dummy.res", "module X = B.B1.A11"); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); RPsiModule em = PsiTreeUtil.findChildOfType(e, RPsiModule.class); assertOrderedEquals(data.getValues(em/*X*/), "A.A1.A11"); } @Test public void test_alias_of_alias_01() { configureCode("A.res", """ module A1 = { module A2 = { let id = "_new_" } } """); configureCode("B.res", """ module B1 = { module B2 = { module B3 = { let id = A.A1.A2.id } } } module B4 = { include A module B5 = B1.B2 } """); FileBase e = configureCode("C.res", """ module C1 = B.B4 module C2 = C1.B5.B3 let _ = C2.id """); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); List ems = copyOf(PsiTreeUtil.findChildrenOfType(e, RPsiModule.class)); assertOrderedEquals(data.getValues(ems.get(0)/*C1*/), "B.B4", "A"); assertOrderedEquals(data.getValues(ems.get(1)/*C2*/), "B.B1.B2.B3"); } @Test public void test_alias_of_alias_02() { configureCode("A.res", """ module A1 = { module A2 = { } } """); configureCode("B.res", """ module B1 = { include A } """); FileBase e = configureCode("C.res", """ module C1 = B.B1 module C2 = C1.A1 """); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); List ems = copyOf(PsiTreeUtil.findChildrenOfType(e, RPsiModule.class)); assertOrderedEquals(data.getValues(ems.get(0)/*C1*/), "B.B1", "A"); assertOrderedEquals(data.getValues(ems.get(1)/*C2*/), "A.A1"); } @Test public void test_alias_of_alias_03() { configureCode("A.res", "module A1 = {}"); configureCode("B.res", "include A"); FileBase e = configureCode("C.res", """ module C1 = B module C2 = C1.A1 """); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); List ems = copyOf(PsiTreeUtil.findChildrenOfType(e, RPsiModule.class)); assertOrderedEquals(data.getValues(ems.get(0)/*C1*/), "B", "A"); assertOrderedEquals(data.getValues(ems.get(1)/*C2*/), "A.A1"); } @Test public void test_stack_overflow() { configureCode("B.res", "module B1 = C"); FileBase e = configureCode("C.res", "module C1 = B"); ORModuleResolutionPsiGist.getData(e); // Should not generate a StackOverflow error } // https://github.com/giraud/reasonml-idea-plugin/issues/426 @Test public void test_GH_426_aliases_same_file() { FileBase e = configureCode("A.res", """ module W = { module X = { module Y = { module Z = {} } } } module C = W.X module D = C.Y.Z """); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); RPsiModule[] ems = PsiTreeUtil.getChildrenOfType(e, RPsiModule.class); assertOrderedEquals(data.getValues(ems[1/*C*/]), "A.W.X"); assertOrderedEquals(data.getValues(ems[2/*D*/]), "A.W.X.Y.Z"); assertEmpty(data.getValues(e/*B*/)); } @Test public void test_include_in_module() { configureCode("A.res", ""); FileBase e = configureCode("B.res", "module B1 = { include A }"); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); RPsiModule em = PsiTreeUtil.findChildOfType(e, RPsiModule.class); assertOrderedEquals(data.getValues(em/*B.B1*/), "A"); } @Test public void test_same_includes() { FileBase e = configureCode("A.res", "module A1 = { module A2 = {} }\n open A1\n include A2\n module A2 = {}\n include A2"); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); ArrayList eis = new ArrayList<>(PsiTreeUtil.findChildrenOfType(e, RPsiInclude.class)); assertOrderedEquals(data.getValues(eis.get(0)/*A2*/), "A.A1.A2"); assertOrderedEquals(data.getValues(eis.get(1)/*A2*/), "A.A2"); } @Test public void test_functor_in_file() { FileBase e = configureCode("A.res", """ module type S = { module P: {} } module F = () : S => { module P = {} } module M = F({}) """); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); List ems = ORUtil.findImmediateChildrenOfClass(e, RPsiModule.class); assertOrderedEquals(data.getValues(ems.get(1)/*F*/), "A.S"); assertOrderedEquals(data.getValues(ems.get(2)/*M*/), "A.F"); } @Test public void test_functor_result_out_file() { configureCode("A.res", "module type Result = { let a: int }"); FileBase e = configureCode("B.res", "module T = A\n module Make = (M:Intf): T.Result => { let b = 3 }"); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); List ems = ORUtil.findImmediateChildrenOfClass(e, RPsiModule.class); assertOrderedEquals(data.getValues(ems.get(1)/*Make*/), "A.Result"); } @Test public void test_functor_no_signature() { FileBase e = configureCode("A.res", "module M = () => { let x = true }\n module X = M()"); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); List ems = ORUtil.findImmediateChildrenOfClass(e, RPsiModule.class); assertOrderedEquals(data.getValues(ems.get(1)/*M*/), "A.M"); } @Test public void test_functor_outside() { configureCode("A.res", "module type S = { module P: {} }\n module F = () : S => { module P = {} }"); FileBase e = configureCode("B.res", "module M = A.F({})"); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); RPsiModule em = ORUtil.findImmediateFirstChildOfClass(e, RPsiModule.class); assertOrderedEquals(data.getValues(em/*M*/), "A.F"); assertEmpty(data.getValues(e)); } @Test public void test_functor_open() { configureCode("A.res", "module type Intf = { let x: bool }\n module MakeIntf = (I:Intf) : Intf => { let y = 1 }"); FileBase e = configureCode("B.res", "open A\n module Instance = MakeIntf({let x = true})"); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); RPsiModule em = ORUtil.findImmediateFirstChildOfClass(e, RPsiModule.class); assertOrderedEquals(data.getValues(em/*Instance*/), "A.MakeIntf"); assertEmpty(data.getValues(e)); } @Test public void test_functor_instance_same_file() { configureCode("B.res", "module type Result = { let a: int }"); FileBase e = configureCode("A.res", """ module Make = (M:Intf): (B.Result with type t := M.t) => {} module Instance = Make({}) """); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); List ems = copyOf(PsiTreeUtil.findChildrenOfType(e, RPsiModule.class)); assertOrderedEquals(data.getValues(ems.get(1)/*Instance*/), "A.Make"); } @Test public void test_file_include_functor() { FileBase e = configureCode("A.res", "module Make = () => { let y = 1 }\n include Make()"); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); assertOrderedEquals(data.getValues(e), "A.Make"); } @Test public void test_functor_path() { configureCode("D.res", """ module type D1Intf = { type t } """); configureCode("C.res", """ module type C1Intf = { let make: unit => string } module Make = (MX: D.D1Intf): C1Intf => { let make = () => "" } """); configureCode("B.res", "module B1 = C"); FileBase e = configureCode("A.res", "module Instance = B.B1.Make(X)"); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); RPsiModule em = PsiTreeUtil.findChildOfType(e, RPsiModule.class); assertOrderedEquals(data.getValues(em/*Instance*/), "C.Make"); } @Test public void test_module_type_inside() { FileBase e = configureCode("A.res", """ module type S = {} module M: S = {} """); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); RPsiModuleSignature emt = PsiTreeUtil.findChildOfType(e, RPsiModuleSignature.class); assertOrderedEquals(data.getValues(emt/*S*/), "A.S"); } @Test public void test_tag_inside() { FileBase e = configureCode("A.res", "module X = { @react.component let make = (~value) =>
}\n let _ = "); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); RPsiTag et = copyOf(PsiTreeUtil.findChildrenOfType(e, RPsiTag.class)).get(1); assertOrderedEquals(data.getValues(et.getFirstChild()/*X*/), "A.X"); } @Test public void test_tag_outside() { configureCode("X.res", "@react.component let make = (~value) =>
"); FileBase e = configureCode("A.res", ""); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); RPsiTagStart et = PsiTreeUtil.findChildOfType(e, RPsiTagStart.class); assertOrderedEquals(data.getValues(et/*X*/), "X"); } @Test public void test_tag_open_outside() { configureCode("X.res", "module Y = { @react.component let make = (~value) =>
}"); FileBase e = configureCode("A.res", "open X\n "); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); RPsiTagStart et = PsiTreeUtil.findChildOfType(e, RPsiTagStart.class); assertOrderedEquals(data.getValues(et/*Y*/), "X.Y"); } @Test public void test_unpack_local() { FileBase e = configureCode("A.res", """ module type I = { let x: int } module Three: I = { let x = 3 } let three: module(I) = module(Three) module New_three = unpack(three: I) """); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); RPsiModule em = ORUtil.findImmediateLastChildOfClass(e, RPsiModule.class); assertOrderedEquals(data.getValues(em), "A.I"); } @Test public void test_unpack_open() { configureCode("A.res", """ module type I = { let x: int } module Three: I = { let x = 3 } """); FileBase e = configureCode("B.res", """ open A let three: module(I) = module(Three) module New_three = unpack(three: I) """); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); RPsiModule em = ORUtil.findImmediateLastChildOfClass(e, RPsiModule.class); assertOrderedEquals(data.getValues(em), "A.I"); } @Test public void test_unpack_no_signature() { configureCode("A.res", """ module type I = { let x: int } module Three: I = { let x = 3 } """); FileBase e = configureCode("B.res", """ open A let three: module(I) = module(Three) module New_three = unpack(three) """); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); RPsiModule em = ORUtil.findImmediateFirstChildOfClass(e, RPsiModule.class); assertOrderedEquals(data.getValues(em), "A.I"); } @Test public void test_unpack_no_signature_qname() { configureCode("A.res", """ module A1 = { module type I = { let x: int } } module Three : A1.I = { let x = 3 } """); FileBase e = configureCode("B.res", """ module B1 = A let three: module(B1.A1.I) = module(B1.Three) module New_three = unpack(three) """); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); RPsiModule em = ORUtil.findImmediateLastChildOfClass(e, RPsiModule.class); assertOrderedEquals(data.getValues(em), "A.A1.I"); } @Test public void test_unpack_parameter() { FileBase e = configureCode("A.res", """ module type I = { let x: int } let x = (~p: module(I)) => { module S = unpack(p) } """); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); RPsiModule em = PsiTreeUtil.findChildOfType(PsiTreeUtil.getChildOfType(e, RPsiLet.class).getBinding(), RPsiModule.class); assertOrderedEquals(data.getValues(em), "A.I"); } @Test public void test_unpack_parameter_global_module() { configureCode("A.res", """ module B = { module type I = { let fn: int => unit } } """); FileBase e = configureCode("C.res", """ let x = (~p: module(A.B.I)) => { module S = unpack(p) } """); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); RPsiModule em = PsiTreeUtil.findChildOfType(PsiTreeUtil.getChildOfType(e, RPsiLet.class).getBinding(), RPsiModule.class); assertOrderedEquals(data.getValues(em), "A.B.I"); } } ================================================ FILE: src/test/java/com/reason/ide/search/reference/ORModuleResolutionPsiGist_RML_Test.java ================================================ package com.reason.ide.search.reference; import com.intellij.psi.util.*; import com.reason.ide.*; import com.reason.ide.files.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import org.junit.*; import java.util.*; import static java.util.List.*; @SuppressWarnings("ConstantConditions") public class ORModuleResolutionPsiGist_RML_Test extends ORBasePlatformTestCase { @Test public void test_include_no_resolution() { FileBase e = configureCode("A.re", "module A1 = {}; include B.B1;"); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); RPsiInclude ei = PsiTreeUtil.findChildOfType(e, RPsiInclude.class); assertEmpty(data.getValues(ei)); assertEmpty(data.getValues(e)); } @Test public void test_open_no_resolution() { FileBase e = configureCode("A.re", "module A1 = {}; open B.B1;"); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); RPsiOpen eo = PsiTreeUtil.findChildOfType(e, RPsiOpen.class); assertEmpty(data.getValues(eo)); assertEmpty(data.getValues(e)); } @Test public void test_include_in_file() { FileBase e = configureCode("A.re", "module A1 = { module A2 = {}; }; include A1.A2;"); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); RPsiInclude ei = PsiTreeUtil.findChildOfType(e, RPsiInclude.class); assertOrderedEquals(data.getValues(ei/*A1.A2*/), "A.A1.A2"); assertOrderedEquals(data.getValues(e/*A.re*/), "A.A1.A2"); } @Test public void test_open_in_file() { FileBase e = configureCode("A.re", "module A1 = { module A2 = {}; }; open A1.A2;"); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); RPsiOpen eo = PsiTreeUtil.findChildOfType(e, RPsiOpen.class); assertOrderedEquals(data.getValues(eo/*A1.A11*/), "A.A1.A2"); assertEmpty(data.getValues(e/*A.re*/)); } @Test public void test_include_in_file_steps() { FileBase e = configureCode("A.re", "module A1 = { module A2 = {}; }; include A1; include A2;"); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); ArrayList eis = new ArrayList<>(PsiTreeUtil.findChildrenOfType(e, RPsiInclude.class)); assertOrderedEquals(data.getValues(eis.get(0/*A1*/)), "A.A1"); assertOrderedEquals(data.getValues(eis.get(1/*A2*/)), "A.A1.A2"); assertOrderedEquals(data.getValues(e/*A.re*/), "A.A1", "A.A1.A2"); } @Test public void test_open_in_file_steps() { FileBase e = configureCode("A.re", "module A1 = { module A2 = {}; }; open A1; open A2;"); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); ArrayList eos = new ArrayList<>(PsiTreeUtil.findChildrenOfType(e, RPsiOpen.class)); assertOrderedEquals(data.getValues(eos.get(0/*A1*/)), "A.A1"); assertOrderedEquals(data.getValues(eos.get(1/*A2*/)), "A.A1.A2"); assertEmpty(data.getValues(e/*A.re*/)); } @Test public void test_include_in_steps() { configureCode("A.re", "module A1 = { module A2 = {}; }"); FileBase e = configureCode("B.re", "include A.A1; include A2;"); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); ArrayList eis = new ArrayList<>(PsiTreeUtil.findChildrenOfType(e, RPsiInclude.class)); assertEmpty(data.getValues(eis.get(0/*A.A1*/))); // Same assertOrderedEquals(data.getValues(eis.get(1/*A2*/)), "A.A1.A2"); assertOrderedEquals(data.getValues(e/*B.re*/), "A.A1", "A.A1.A2"); } @Test public void test_open_in_steps() { configureCode("A.re", "module A1 = { module A2 = {}; }"); FileBase e = configureCode("B.re", "open A.A1; open A2;"); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); ArrayList eos = new ArrayList<>(PsiTreeUtil.findChildrenOfType(e, RPsiOpen.class)); assertEmpty(data.getValues(eos.get(0/*A.A1*/))); // Same assertOrderedEquals(data.getValues(eos.get(1/*A2*/)), "A.A1.A2"); assertEmpty(data.getValues(e/*B*/)); } @Test public void test_aliases_01() { FileBase e = configureCode("A.re", """ module A1 = { module A2 = {}; }; module B1 = A1; include B1; module B2 = A2; include B2; """); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); List eis = new ArrayList<>(PsiTreeUtil.findChildrenOfType(e, RPsiInclude.class)); assertOrderedEquals(data.getValues(eis.get(0/*B1*/)), "A.A1"); assertOrderedEquals(data.getValues(eis.get(1/*B2*/)), "A.A1.A2"); List ems = new ArrayList<>(PsiTreeUtil.findChildrenOfType(e, RPsiModule.class)); assertOrderedEquals(data.getValues(ems.get(2/*B1*/)), "A.A1"); assertOrderedEquals(data.getValues(ems.get(3/*B2*/)), "A.A1.A2"); assertOrderedEquals(data.getValues(e/*A*/), "A.A1", "A.A1.A2"); } @Test public void test_aliases_02() { FileBase e = configureCode("A.re", "module A1 = { module A2 = {}; module B2 = A2; };" + "module B1 = A1;" + "include B1.B2;"); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); RPsiInclude ei = PsiTreeUtil.findChildOfType(e, RPsiInclude.class); assertOrderedEquals(data.getValues(ei/*B1.B2*/), "A.A1.A2"); assertOrderedEquals(data.getValues(e/*A*/), "A.A1.A2"); } @Test public void test_aliases_03() { configureCode("A.re", ""); FileBase e = configureCode("B.re", "module B1 = A;"); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); RPsiModule em = PsiTreeUtil.findChildOfType(e, RPsiModule.class); assertOrderedEquals(data.getValues(em/*B1*/), "A"); assertEmpty(data.getValues(e/*B*/)); } @Test public void test_aliases_04() { configureCode("A.re", "module W = { module X = { module Y = { module Z = { let z = 1; }; }; }; };"); FileBase e = configureCode("B.re", "module C = A.W.X; module D = C.Y.Z;"); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); List ems = copyOf(PsiTreeUtil.findChildrenOfType(e, RPsiModule.class)); assertOrderedEquals(data.getValues(ems.get(0/*C*/)), "A.W.X"); assertOrderedEquals(data.getValues(ems.get(1/*D*/)), "A.W.X.Y.Z"); assertEmpty(data.getValues(e/*B*/)); } @Test public void test_aliases_05() { configureCode("A.re", "module A1 = { module A11 = { type id = string; }; };"); configureCode("B.re", "module B1 = A.A1;"); FileBase e = configureCode("Dummy.re", "module X = B.B1.A11;"); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); RPsiModule em = PsiTreeUtil.findChildOfType(e, RPsiModule.class); assertOrderedEquals(data.getValues(em/*ELayout*/), "A.A1.A11"); } @Test public void test_alias_of_alias() { configureCode("A.re", """ module A1 = { module A2 = { let id = "_new_"; }; }; """); configureCode("B.re", """ module B1 = { module B2 = { module B3 = { let id = A.A1.A2.id; }; }; }; module B4 = { include A; module B5 = B1.B2; }; """); FileBase e = configureCode("C.res", """ module C1 = B.B4; module C2 = C1.B5.B3; let _ = C2.id; """); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); List ems = copyOf(PsiTreeUtil.findChildrenOfType(e, RPsiModule.class)); assertOrderedEquals(data.getValues(ems.get(0)/*C1*/), "B.B4", "A"); assertOrderedEquals(data.getValues(ems.get(1)/*C2*/), "B.B1.B2.B3"); } @Test public void test_alias_of_alias_02() { configureCode("A.re", """ module A1 = { module A2 = { }; }; """); configureCode("B.re", """ module B1 = { include A; }; """); FileBase e = configureCode("C.re", """ module C1 = B.B1; module C2 = C1.A1; """); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); List ems = copyOf(PsiTreeUtil.findChildrenOfType(e, RPsiModule.class)); assertOrderedEquals(data.getValues(ems.get(0)/*C1*/), "B.B1", "A"); assertOrderedEquals(data.getValues(ems.get(1)/*C2*/), "A.A1"); } @Test public void test_alias_of_alias_03() { configureCode("A.re", "module A1 = {};"); configureCode("B.re", "include A;"); FileBase e = configureCode("C.re", """ module C1 = B; module C2 = C1.A1; """); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); List ems = copyOf(PsiTreeUtil.findChildrenOfType(e, RPsiModule.class)); assertOrderedEquals(data.getValues(ems.get(0)/*C1*/), "B", "A"); assertOrderedEquals(data.getValues(ems.get(1)/*C2*/), "A.A1"); } // https://github.com/giraud/reasonml-idea-plugin/issues/426 @Test public void test_GH_426_aliases_same_file() { FileBase e = configureCode("A.re", """ module W = { module X = { module Y = { module Z = {}; }; }; }; module C = W.X; module D = C.Y.Z; """); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); RPsiModule[] ems = PsiTreeUtil.getChildrenOfType(e, RPsiModule.class); assertOrderedEquals(data.getValues(ems[1/*C*/]), "A.W.X"); assertOrderedEquals(data.getValues(ems[2/*D*/]), "A.W.X.Y.Z"); assertEmpty(data.getValues(e/*B*/)); } @Test public void test_include_in_module() { configureCode("A.re", ""); FileBase e = configureCode("B.re", "module B1 = { include A; };"); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); RPsiModule em = PsiTreeUtil.findChildOfType(e, RPsiModule.class); assertOrderedEquals(data.getValues(em/*B.B1*/), "A"); } @Test public void test_same_includes() { FileBase e = configureCode("A.re", "module A1 = { module A2 = {}; }; open A1; include A2; module A2 = {}; include A2;"); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); ArrayList eis = new ArrayList<>(PsiTreeUtil.findChildrenOfType(e, RPsiInclude.class)); assertOrderedEquals(data.getValues(eis.get(0)/*A2*/), "A.A1.A2"); assertOrderedEquals(data.getValues(eis.get(1)/*A2*/), "A.A2"); } @Test public void test_functor_in_file() { FileBase e = configureCode("A.re", "module type S = { module P: {}; };" + "module F = () : S => { module P = {}; };" + "module M = F({});"); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); List ems = ORUtil.findImmediateChildrenOfClass(e, RPsiModule.class); assertOrderedEquals(data.getValues(ems.get(1)/*F*/), "A.S"); assertOrderedEquals(data.getValues(ems.get(2)/*M*/), "A.F"); } @Test public void test_functor_result_out_file() { configureCode("A.re", "module type Result = { let a: int; };"); FileBase e = configureCode("B.re", "module T = A; module Make = (M:Intf): T.Result => { let b = 3; };"); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); List ems = ORUtil.findImmediateChildrenOfClass(e, RPsiModule.class); assertOrderedEquals(data.getValues(ems.get(1)/*Make*/), "A.Result"); } @Test public void test_functor_no_signature() { FileBase e = configureCode("A.re", "module M = () => { let x = true; }; module X = M();"); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); List ems = ORUtil.findImmediateChildrenOfClass(e, RPsiModule.class); assertOrderedEquals(data.getValues(ems.get(1)/*M*/), "A.M"); } @Test public void test_functor_outside() { configureCode("A.re", "module type S = { module P: {}; }; module F = () : S => { module P = {}; };"); FileBase e = configureCode("B.re", "module M = A.F({});"); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); RPsiModule em = ORUtil.findImmediateFirstChildOfClass(e, RPsiModule.class); assertOrderedEquals(data.getValues(em/*M*/), "A.F"); assertEmpty(data.getValues(e)); } @Test public void test_functor_open() { configureCode("A.re", "module type Intf = { let x: bool; }; module MakeIntf = (I:Intf) : Intf => { let y = 1; };"); FileBase e = configureCode("B.re", "open A; module Instance = MakeIntf({let x = true});"); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); RPsiModule em = ORUtil.findImmediateFirstChildOfClass(e, RPsiModule.class); assertOrderedEquals(data.getValues(em/*Instance*/), "A.MakeIntf"); assertEmpty(data.getValues(e)); } @Test public void test_functor_instance_same_file() { configureCode("B.re", "module type Result = { let a: int; };"); FileBase e = configureCode("A.re", """ module Make = (M:Intf): (B.Result with type t := M.t) => {}; module Instance = Make({}); """); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); List ems = copyOf(PsiTreeUtil.findChildrenOfType(e, RPsiModule.class)); assertOrderedEquals(data.getValues(ems.get(1)/*Instance*/), "A.Make"); } @Test public void test_file_include_functor() { FileBase e = configureCode("A.re", "module Make = () => { let y = 1; }; include Make();"); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); assertOrderedEquals(data.getValues(e), "A.Make"); } @Test public void test_functor_path() { configureCode("D.re", """ module type D1Intf = { type t; }; """); configureCode("C.re", """ module type C1Intf = { let make: unit => string; }; module Make = (MX: D.D1Intf): C1Intf => { let make = () => ""; }; """); configureCode("B.re", "module B1 = C;"); FileBase e = configureCode("A.re", "module Instance = B.B1.Make(X);"); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); RPsiModule em = PsiTreeUtil.findChildOfType(e, RPsiModule.class); assertOrderedEquals(data.getValues(em/*Instance*/), "C.Make"); } @Test public void test_module_type_inside() { FileBase e = configureCode("A.re", """ module type S = {}; module M : S = {}; """); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); RPsiModuleSignature emt = PsiTreeUtil.findChildOfType(e, RPsiModuleSignature.class); assertOrderedEquals(data.getValues(emt/*S*/), "A.S"); } @Test public void test_module_type_open_alias() { FileBase e = configureCode("A.re", """ module A1 = { module type S = {}; }; module X = A1; open X; module M: S = {}; """); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); RPsiModuleSignature emt = PsiTreeUtil.findChildOfType(e, RPsiModuleSignature.class); assertOrderedEquals(data.getValues(emt/*S*/), "A.A1.S"); } @Test public void test_module_type_outside() { configureCode("A.re", "module type S = {};"); FileBase e = configureCode("B.re", "module M: A.S = {};"); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); RPsiModuleSignature emt = PsiTreeUtil.findChildOfType(e, RPsiModuleSignature.class); assertOrderedEquals(data.getValues(emt/*S*/), "A.S"); } @Test public void test_module_type_deep() { FileBase e = configureCode("A.re", """ module B = { module type Intf = {}; }; module Impl : B.Intf = {}; """); RPsiModuleSignature es = PsiTreeUtil.findChildOfType(e, RPsiModuleSignature.class); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); assertOrderedEquals(data.getValues(es/*B.Intf*/), "A.B.Intf"); } @Test public void test_tag_inside() { FileBase e = configureCode("A.re", "module X = { [@react.component] let make = (~value) =>
; }; ;"); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); RPsiTag et = ORUtil.findImmediateFirstChildOfClass(e, RPsiTag.class); assertOrderedEquals(data.getValues(et.getFirstChild()/*X*/), "A.X"); } @Test public void test_tag_outside() { configureCode("X.re", "[@react.component] let make = (~value) =>
;"); FileBase e = configureCode("A.re", ";"); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); RPsiTagStart et = PsiTreeUtil.findChildOfType(e, RPsiTagStart.class); assertOrderedEquals(data.getValues(et/*X*/), "X"); } @Test public void test_tag_open_outside() { configureCode("X.re", "module Y = { [@react.component] let make = (~value) =>
; }"); FileBase e = configureCode("A.re", "open X; ;"); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); RPsiTagStart et = PsiTreeUtil.findChildOfType(e, RPsiTagStart.class); assertOrderedEquals(data.getValues(et/*Y*/), "X.Y"); } @Test public void test_unpack_local() { FileBase e = configureCode("A.re", """ module type I = { let x: int; }; module Three: I = { let x = 3; }; let three: module I = (module Three); module New_three = (val three: I); """); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); RPsiModule em = ORUtil.findImmediateLastChildOfClass(e, RPsiModule.class); assertOrderedEquals(data.getValues(em), "A.I"); } @Test public void test_unpack_open() { configureCode("A.re", """ module type I = { let x: int; }; module Three: I = { let x = 3; }; """); FileBase e = configureCode("B.re", """ open A; let three: module I = (module Three); module New_three = (val three: I); """); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); RPsiModule em = ORUtil.findImmediateLastChildOfClass(e, RPsiModule.class); assertOrderedEquals(data.getValues(em), "A.I"); } @Test public void test_unpack_no_signature() { configureCode("A.re", """ module type I = { let x: int; }; module Three: I = { let x = 3; }; """); FileBase e = configureCode("B.re", """ open A; let three: module I = (module Three); module New_three = (val three); """); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); RPsiModule em = ORUtil.findImmediateFirstChildOfClass(e, RPsiModule.class); assertOrderedEquals(data.getValues(em), "A.I"); } @Test public void test_unpack_no_signature_qname() { configureCode("A.re", """ module A1 = { module type I = { let x: int; }; }; module Three : A1.I = { let x = 3 }; """); FileBase e = configureCode("B.re", """ module B1 = A; let three: module B1.A1.I = (module B1.Three); module New_three = (val three); """); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); RPsiModule em = ORUtil.findImmediateLastChildOfClass(e, RPsiModule.class); assertOrderedEquals(data.getValues(em), "A.A1.I"); } @Test public void test_unpack_parameter() { FileBase e = configureCode("A.re", """ module type I = { let x: int; }; let x = (~p: (module I)) => { module S = (val p); }; """); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); RPsiModule em = PsiTreeUtil.findChildOfType(PsiTreeUtil.getChildOfType(e, RPsiLet.class).getBinding(), RPsiModule.class); assertOrderedEquals(data.getValues(em), "A.I"); } @Test public void test_unpack_parameter_global_module() { configureCode("A.re", """ module B = { module type I = { let fn: int => unit; }; }; """); FileBase e = configureCode("C.re", "let x = (~p: (module A.B.I)) => { module S = (val p); };"); ORModuleResolutionPsiGist.Data data = ORModuleResolutionPsiGist.getData(e); RPsiModule em = PsiTreeUtil.findChildOfType(PsiTreeUtil.getChildOfType(e, RPsiLet.class).getBinding(), RPsiModule.class); assertOrderedEquals(data.getValues(em), "A.B.I"); } } ================================================ FILE: src/test/java/com/reason/ide/search/reference/ResolveJsxPropertyElementRESTest.java ================================================ package com.reason.ide.search.reference; import com.intellij.psi.*; import com.reason.ide.*; import org.junit.*; import org.junit.runner.*; import org.junit.runners.*; @RunWith(JUnit4.class) public class ResolveJsxPropertyElementRESTest extends ORBasePlatformTestCase { @Test public void test_basic() { configureCode("X.res", "@react.component let make = (~value) =>
"); configureCode("A.res", "=1>"); PsiElement e = myFixture.getElementAtCaret(); assertEquals("X.make[value]", ((PsiQualifiedNamedElement) e).getQualifiedName()); } @Test public void test_basic_multiple_props() { configureCode("X.res", "@react.component let make = (~propA, ~propB, ~propC) =>
"); configureCode("A.res", " />"); PsiElement e = myFixture.getElementAtCaret(); assertEquals("X.make[propC]", ((PsiQualifiedNamedElement) e).getQualifiedName()); } @Test public void test_basic_nested() { configureCode("A.res", "module X = {\n @react.component\n let make = (~value) =>
\n }\n let _ = =1>"); PsiElement e = myFixture.getElementAtCaret(); assertEquals("A.X.make[value]", ((PsiQualifiedNamedElement) e).getQualifiedName()); } @Test public void test_autoclose() { configureCode("X.res", "@react.component let make = (~value) =>
"); configureCode("A.res", "=1/>"); PsiElement e = myFixture.getElementAtCaret(); assertEquals("X.make[value]", ((PsiQualifiedNamedElement) e).getQualifiedName()); } @Test public void test_open() { configureCode("A.res", "module X = { @react.component let make = (~value) =>
; }"); configureCode("B.res", "open A; =1"); PsiElement e = myFixture.getElementAtCaret(); assertEquals("A.X.make[value]", ((PsiQualifiedNamedElement) e).getQualifiedName()); } @Test public void test_open_multiple() { configureCode("A.res", "module X = { @react.component let make = (~value) =>
}\n module Y = { @react.component let make = (~value) =>
}"); configureCode("B.res", "open A; =1"); PsiElement e = myFixture.getElementAtCaret(); assertEquals("A.X.make[value]", ((PsiQualifiedNamedElement) e).getQualifiedName()); } @Test public void test_make_make() { configureCode("A.res", "module X = { @react.component let make = (~value) =>
}\n let make = (~value) => =1>"); PsiElement e = myFixture.getElementAtCaret(); assertEquals("A.X.make[value]", ((PsiQualifiedNamedElement) e).getQualifiedName()); } } ================================================ FILE: src/test/java/com/reason/ide/search/reference/ResolveJsxPropertyElementRMLTest.java ================================================ package com.reason.ide.search.reference; import com.intellij.psi.*; import com.reason.ide.*; import org.junit.*; import org.junit.runner.*; import org.junit.runners.*; @RunWith(JUnit4.class) public class ResolveJsxPropertyElementRMLTest extends ORBasePlatformTestCase { @Test public void test_basic() { configureCode("X.re", "[@react.component] let make = (~value) =>
;"); configureCode("A.re", "=1>;"); PsiElement e = myFixture.getElementAtCaret(); assertEquals("X.make[value]", ((PsiQualifiedNamedElement) e).getQualifiedName()); } @Test public void test_basic_multiple_props() { configureCode("X.re", "[@react.component] let make = (~propA, ~propB, ~propC) =>
;"); configureCode("A.re", " />;"); PsiElement e = myFixture.getElementAtCaret(); assertEquals("X.make[propC]", ((PsiQualifiedNamedElement) e).getQualifiedName()); } @Test public void test_basic_nested() { configureCode("A.re", "module X = { [@react.component] let make = (~value) =>
; }; =1>;"); PsiElement e = myFixture.getElementAtCaret(); assertEquals("A.X.make[value]", ((PsiQualifiedNamedElement) e).getQualifiedName()); } @Test public void test_autoclose() { configureCode("X.re", "[@react.component] let make = (~value) =>
;"); configureCode("A.re", "=1/>;"); PsiElement e = myFixture.getElementAtCaret(); assertEquals("X.make[value]", ((PsiQualifiedNamedElement) e).getQualifiedName()); } @Test public void test_open() { configureCode("A.re", "module X = { [@react.component] let make = (~value) =>
; };"); configureCode("B.re", "open A; =1;"); PsiElement e = myFixture.getElementAtCaret(); assertEquals("A.X.make[value]", ((PsiQualifiedNamedElement) e).getQualifiedName()); } @Test public void test_open_multiple() { configureCode("A.re", "module X = { [@react.component] let make = (~value) =>
; }; module Y = { [@react.component] let make = (~value) =>
; }; "); configureCode("B.re", "open A; =1;"); PsiElement e = myFixture.getElementAtCaret(); assertEquals("A.X.make[value]", ((PsiQualifiedNamedElement) e).getQualifiedName()); } @Test public void test_make_make() { configureCode("A.re", "module X = { [@react.component] let make = (~value) =>
; }; [@react.component] let make = (~value) => = 1>; "); PsiElement e = myFixture.getElementAtCaret(); assertEquals("A.X.make[value]", ((PsiQualifiedNamedElement) e).getQualifiedName()); } } ================================================ FILE: src/test/java/com/reason/ide/search/reference/ResolveJsxTagElementRESTest.java ================================================ package com.reason.ide.search.reference; import com.intellij.psi.*; import com.reason.ide.*; import com.reason.lang.core.psi.*; import org.junit.*; import org.junit.runner.*; import org.junit.runners.*; @RunWith(JUnit4.class) public class ResolveJsxTagElementRESTest extends ORBasePlatformTestCase { @Test public void test_basic_let() { configureCode("X.res", "@react.component\n let make = () =>
;"); configureCode("A.res", " >"); RPsiLet e = (RPsiLet) getNavigationElementAtCaret(); assertEquals("X.make", e.getQualifiedName()); } @Test public void test_basic_external() { configureCode("X.res", "@react.component\n external make : (~value:string) => React.element = \"Xx\""); configureCode("A.res", " >;"); RPsiExternal e = (RPsiExternal) getNavigationElementAtCaret(); assertEquals("X.make", e.getQualifiedName()); } @Test public void test_not_a_component() { configureCode("X.res", "let make = (~value) =>
"); configureCode("A.res", " >"); PsiQualifiedNamedElement e = (PsiQualifiedNamedElement) getNavigationElementAtCaret(); assertEquals("X", e.getQualifiedName()); } @Test public void test_nested_let() { configureCode("A.res", "module X = { module Y = { @react.component\n let make = (~value) =>
} }\n >"); RPsiLet e = (RPsiLet) getNavigationElementAtCaret(); assertEquals("A.X.Y.make", e.getQualifiedName()); } @Test public void test_nested_external() { configureCode("A.res", "module X = { module Y = { @react.component\n external make : (~value:string) => React.element = \"XY\" } }\n >;"); RPsiExternal e = (RPsiExternal) getNavigationElementAtCaret(); assertEquals("A.X.Y.make", e.getQualifiedName()); } @Test public void test_autoclose() { configureCode("X.res", "@react.component\n let make = (~value) =>
"); configureCode("A.res", " />"); RPsiLet e = (RPsiLet) getNavigationElementAtCaret(); assertEquals("X.make", e.getQualifiedName()); } @Test public void test_open() { configureCode("A.res", "module X = { @react.component\n let make = (~value) =>
}"); configureCode("B.res", """ open A """); RPsiLet e = (RPsiLet) getNavigationElementAtCaret(); assertEquals("A.X.make", e.getQualifiedName()); } } ================================================ FILE: src/test/java/com/reason/ide/search/reference/ResolveJsxTagElementRMLTest.java ================================================ package com.reason.ide.search.reference; import com.intellij.psi.*; import com.reason.ide.*; import com.reason.lang.core.psi.*; import org.junit.*; import org.junit.runner.*; import org.junit.runners.*; @RunWith(JUnit4.class) public class ResolveJsxTagElementRMLTest extends ORBasePlatformTestCase { @Test public void test_basic_let() { configureCode("X.re", "[@react.component] let make = (~value) =>
;"); configureCode("A.re", " >;"); RPsiLet e = (RPsiLet) getNavigationElementAtCaret(); assertEquals("X.make", e.getQualifiedName()); } @Test public void test_basic_external() { configureCode("X.re", "[@react.component] external make : (~value:string) => React.element = \"Xx\";"); configureCode("A.re", " >;"); RPsiExternal e = (RPsiExternal) getNavigationElementAtCaret(); assertEquals("X.make", e.getQualifiedName()); } @Test public void test_not_a_component() { configureCode("X.re", "let make = (~value) =>
;"); configureCode("A.re", " >;"); PsiQualifiedNamedElement e = (PsiQualifiedNamedElement) getNavigationElementAtCaret(); assertEquals("X", e.getQualifiedName()); } @Test public void test_nested_let() { configureCode("A.re", "module X = { module Y = { [@react.component] let make = (~value) =>
; }; }; >;"); RPsiLet e = (RPsiLet) getNavigationElementAtCaret(); assertEquals("A.X.Y.make", e.getQualifiedName()); } @Test public void test_nested_external() { configureCode("A.re", "module X = { module Y = { [@react.component] external make : (~value:string) => React.element = \"XY\"; }; }; >;"); RPsiExternal e = (RPsiExternal) getNavigationElementAtCaret(); assertEquals("A.X.Y.make", e.getQualifiedName()); } @Test public void test_autoclose() { configureCode("X.re", "[@react.component] let make = (~value) =>
;"); configureCode("A.re", " />;"); RPsiLet e = (RPsiLet) getNavigationElementAtCaret(); assertEquals("X.make", e.getQualifiedName()); } @Test public void test_open() { configureCode("A.re", "module X = { [@react.component] let make = (~value) =>
; };"); configureCode("B.re", "open A; "); RPsiLet e = (RPsiLet) getNavigationElementAtCaret(); assertEquals("A.X.make", e.getQualifiedName()); } } ================================================ FILE: src/test/java/com/reason/ide/search/reference/ResolveLowerElement_OCL_Test.java ================================================ package com.reason.ide.search.reference; import com.intellij.psi.*; import com.reason.ide.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import org.junit.*; public class ResolveLowerElement_OCL_Test extends ORBasePlatformTestCase { @Test public void test_let_basic() { configureCode("A.ml", "let x = 1\n let z = x + 1"); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("A.x", e.getQualifiedName()); } @Test public void test_call_function_with_module() { configureCode("A.ml", "let fn () = 1"); configureCode("B.ml", "let x = 2"); configureCode("C.ml", "let _ = A.fn()\n let _ = B.x"); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("B.x", e.getQualifiedName()); } @Test public void test_let_in_module_binding() { configureCode("A.ml", "let foo = 2 module X = struct let foo = 1 let z = foo end"); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("A.X.foo", e.getQualifiedName()); } @Test public void test_let_inner_scope() { configureCode("A.ml", "let x = 1\n let a = let x = 2 in x + 10"); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("A.a.x", e.getQualifiedName()); } @Test public void test_inner_scope_in_function() { configureCode("A.ml", "let x = 1\n let fn = let x = 2 in fn1 x"); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("A.fn.x", e.getQualifiedName()); } @Test public void test_inner_scope_in_impl() { configureCode("A.mli", "val x:int"); configureCode("A.ml", "let x = 1\n let fn = let foo = 2 in fn1 foo"); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("A.fn.foo", e.getQualifiedName()); assertEquals("A.ml", e.getContainingFile().getName()); } @Test public void test_let_local_module_alias() { configureCode("A.mli", "val x:int"); configureCode("B.ml", "let x = 1\n module X = A\n let _ = X.x"); RPsiVal e = (RPsiVal) myFixture.getElementAtCaret(); assertEquals("A.x", e.getQualifiedName()); } @Test public void test_alias_path() { configureCode("A.ml", "module X = struct module Y = struct let z = 1 end end"); configureCode("B.ml", "module C = A.X.Y\n let _ = C.z"); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("A.X.Y.z", e.getQualifiedName()); } @Test public void test_alias_01() { configureCode("A.ml", "module Mode = struct type t end"); configureCode("B.ml", "module B1 = struct module Mode = A.Mode end"); configureCode("C.ml", "type t = B.B1.Mode.t"); // B.B1.Mode.t -> A.Mode.t RPsiType e = (RPsiType) myFixture.getElementAtCaret(); assertEquals("A.Mode.t", e.getQualifiedName()); } @Test public void test_alias_02() { configureCode("A.ml", "module A1 = struct module A11 = struct type t = string end end"); configureCode("B.ml", "module B1 = A.A1"); configureCode("C.ml", """ module C1 = B.B1.A11 type t = C1.t """); RPsiQualifiedPathElement e = (RPsiQualifiedPathElement) myFixture.getElementAtCaret(); assertEquals("A.A1.A11.t", e.getQualifiedName()); } @Test public void test_open() { configureCode("B.ml", "let x = 1"); configureCode("A.ml", "let x = 2 open B let _ = x"); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("B.x", e.getQualifiedName()); } @Test public void test_alias_open() { configureCode("B.ml", "let x = 1"); configureCode("A.ml", "let x = 2 module C = B open C let _ = x"); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("B.x", e.getQualifiedName()); } @Test public void test_let_local_open_parens() { configureCode("A.ml", "module A1 = struct let a = 1 end"); configureCode("B.ml", "let a = 2 let b = A.(A1.a)"); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("A.A1.a", e.getQualifiedName()); } @Test public void test_let_local_open_parens_2() { configureCode("A.ml", "module A1 = struct let a = 3 end"); configureCode("B.ml", "let a = A.A1.(a)"); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("A.A1.a", e.getQualifiedName()); } @Test public void test_type() { configureCode("A.ml", "type t type t' = t"); RPsiType e = (RPsiType) myFixture.getElementAtCaret(); assertEquals("A.t", e.getQualifiedName()); assertTrue(e.isAbstract()); } @Test public void test_type_with_path() { configureCode("A.ml", "type t"); configureCode("B.ml", "type t = A.t"); RPsiType e = (RPsiType) myFixture.getElementAtCaret(); assertEquals("A.t", e.getQualifiedName()); } @Test public void test_type_with_path_2() { configureCode("A.ml", "type t\n type y = X.Y.t"); assertThrows(AssertionError.class, "element not found in file A.ml", () -> myFixture.getElementAtCaret()); } @Test public void test_external() { configureCode("A.ml", "external e : string -> int = \"name\"\n let x = e"); RPsiExternal e = (RPsiExternal) myFixture.getElementAtCaret(); assertEquals("A.e", e.getQualifiedName()); } @Test public void test_function() { configureCode("A.ml", "module B = struct let bb = 1 end\n module C = struct let cc x = x end let z = C.cc(B.bb)"); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("A.B.bb", e.getQualifiedName()); } @Test public void test_function_open() { configureCode("B.ml", "module C = struct let make x = x\n let convert x = x end"); configureCode("A.ml", "open B\n let _ = C.make([| C.convert |])"); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("B.C.convert", e.getQualifiedName()); } @Test public void test_param_parenLess() { configureCode("A.ml", "let add10 x = x + 10;"); RPsiParameterDeclaration e = (RPsiParameterDeclaration) myFixture.getElementAtCaret(); assertEquals("A.add10[x]", e.getQualifiedName()); } @Test public void test_local_open() { configureCode("A.ml", "module A1 = struct external a : int = \"\" end"); configureCode("B.ml", "let _ = let open A.A1 in a"); RPsiExternal e = (RPsiExternal) myFixture.getElementAtCaret(); assertEquals("A.A1.a", e.getQualifiedName()); } @Test public void test_local_open_2() { configureCode("A.ml", """ module A1 = struct type t = | Variant let toString = x => x end """); configureCode("B.ml", "let _ = let open A.A1 in Variant |. toString"); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("A.A1.toString", e.getQualifiedName()); } @Test public void test_include() { configureCode("A.ml", "module B = struct type t end\n module C = B\n include C\n type x = t"); RPsiType e = (RPsiType) myFixture.getElementAtCaret(); assertEquals("A.B.t", e.getQualifiedName()); } @Test public void test_include_alias() { configureCode("A.ml", """ module B = struct type t end module C = B include C type x = t """); RPsiType e = (RPsiType) myFixture.getElementAtCaret(); assertEquals("A.B.t", e.getQualifiedName()); } @Test public void test_include_2() { configureCode("Css_AtomicTypes.mli", "module Visibility: sig\n type t = [ | `visible | `hidden | `collapse]\n end"); configureCode("Css_Legacy_Core.ml", "module Types = Css_AtomicTypes"); configureCode("Css.ml", "include Css_Legacy_Core"); configureCode("A.ml", "type layoutRule\n let visibility: [< Css.Types.Length.t | Css.Types.Visibility.t ] -> layoutRule"); RPsiType e = (RPsiType) myFixture.getElementAtCaret(); assertEquals("Css_AtomicTypes.Visibility.t", e.getQualifiedName()); } @Test public void test_include_qualified() { configureCode("A.ml", """ module B = struct module C = struct type t end end module D = B include D.C """); configureCode("C.ml", "type t = A.t"); RPsiType e = (RPsiType) myFixture.getElementAtCaret(); assertEquals("A.B.C.t", e.getQualifiedName()); } @Test public void test_module_signature() { configureCode("A.ml", """ module B = sig type t let toString: t -> string end module C = sig type t let toString: t -> string end """); RPsiType e = (RPsiType) myFixture.getElementAtCaret(); assertEquals("A.C.t", e.getQualifiedName()); } @Test public void test_let_Local_open_pipe_first() { configureCode("A.ml", "module A1 = struct let add x = x + 3 end"); configureCode("B.ml", "let x = let open A.A1 in x |. add"); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("A.A1.add", e.getQualifiedName()); } @Test public void test_external_local_open_pipe_first() { configureCode("A.ml", "module A1 = struct external add : int -> int = \"\" end"); configureCode("B.ml", "let x = let open A.A1 in x |. add);"); RPsiExternal e = (RPsiExternal) myFixture.getElementAtCaret(); assertEquals("A.A1.add", e.getQualifiedName()); } @Test public void test_pipe_first() { configureCode("Css.mli", "val px: int -> string"); configureCode("A.ml", "let _ = Dimensions.spacing.small |. Css.px"); RPsiVal e = (RPsiVal) myFixture.getElementAtCaret(); assertEquals("Css.px", e.getQualifiedName()); } @Test public void test_pipe_first_open() { configureCode("Css.mli", "val px: int -> string"); configureCode("A.ml", "let make () = let open Css in Dimensions.Spacing.small |. px"); RPsiVal e = (RPsiVal) myFixture.getElementAtCaret(); assertEquals("Css.px", e.getQualifiedName()); } @Test public void test_pipe_first_open_2() { configureCode("Core.ml", """ module Async = struct let get x = x end """); configureCode("A.ml", """ open Core.Async let _ = request |. get "windows/settings" """); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("Core.Async.get", e.getQualifiedName()); } @Test public void test_pipe_first_open_with_path() { configureCode("Css.mli", "module Rule = struct val px: int -> string end"); configureCode("A.ml", "let make () = let open Css in Dimensions.spacing.small |. Rule.px"); RPsiVal e = (RPsiVal) myFixture.getElementAtCaret(); assertEquals("Css.Rule.px", e.getQualifiedName()); } @Test public void test_multiple_module() { configureCode("Command.ml", "module Settings = struct module Action = struct let convert x = x end end"); configureCode("A.ml", "module C = Y\n open Command\n Settings.Action.convert"); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("Command.Settings.Action.convert", e.getQualifiedName()); } //region Variants @Test public void test_variant_constructor() { configureCode("B.ml", "let convert x = x"); configureCode("A.ml", "let _ = X.Variant(B.convert 1)"); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("B.convert", e.getQualifiedName()); } @Test public void test_variant_constructor_tuple() { configureCode("B.ml", "type ('a, 'b) t = | Variant of 'a * 'b"); configureCode("A.ml", "let x = 1\n let _ = B.Variant(X.Y, x)"); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("A.x", e.getQualifiedName()); } //endregion //region Poly-variants @Test public void test_local_poly_variant() { configureCode("A.ml", "type a = [ | `variant ]\n let _ = `variant"); PsiElement e = myFixture.getElementAtCaret(); assertEquals("A.#variant", ((RPsiVariantDeclaration) e).getQualifiedName()); } @Test public void test_poly_variant_with_path() { configureCode("A.ml", "type a = [ | `variant ]"); configureCode("B.ml", "type b = [ | `variant ]"); configureCode("C.ml", "A.`variant"); RPsiVariantDeclaration e = (RPsiVariantDeclaration) myFixture.getElementAtCaret(); assertEquals("A.#variant", e.getQualifiedName()); } @Test public void test_poly_variant_module_alias() { configureCode("Aaa.ml", "type t = [ | `test ]"); configureCode("Bbb.ml", "module A = Aaa\nlet _ = A.`test"); RPsiVariantDeclaration e = (RPsiVariantDeclaration) myFixture.getElementAtCaret(); assertEquals("Aaa.#test", e.getQualifiedName()); } @Test public void test_poly_variant_module_alias_inner() { configureCode("Aaa.ml", "module Option = struct type t = [ | `test ] end"); configureCode("Bbb.ml", "module A = Aaa\nlet _ = A.Option.`test"); RPsiVariantDeclaration e = (RPsiVariantDeclaration) myFixture.getElementAtCaret(); assertEquals("Aaa.Option.#test", e.getQualifiedName()); } @Test public void test_poly_variant_constructor() { configureCode("A.ml", "type a = | `variant of int"); configureCode("B.ml", "let _ = A.`variant 1"); RPsiVariantDeclaration e = (RPsiVariantDeclaration) myFixture.getElementAtCaret(); assertEquals("A.#variant", e.getQualifiedName()); } //endregion @Test public void test_open_include() { configureCode("Css_Core.ml", "let fontStyle x = x"); configureCode("Css.ml", "include Css_Core"); configureCode("A.ml", "open Css\n let _ = fontStyle"); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("Css_Core.fontStyle", e.getQualifiedName()); } @Test public void test_open_include_deep() { configureCode("Css_Rule.ml", "let fontStyle x = x"); configureCode("Css_Core.ml", "module Rules = struct include Css_Rule end"); configureCode("Css.ml", "include Css_Core"); configureCode("A.ml", "open Css.Rules\n let _ = fontStyle"); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("Css_Rule.fontStyle", e.getQualifiedName()); } @Test public void test_resolution_1() { configureCode("Belt_MapString.mli", "val get: 'v t -> key -> 'v option"); configureCode("Belt_Map.ml", "module String = Belt_MapString"); configureCode("Belt_Option.mli", "val flatMap : 'a option -> ('a -> 'b option) -> 'b option"); configureCode("Belt.ml", "module Option = Belt_Option\n module Map = Belt_Map"); configureCode("A.ml", "let fn dict locale = (locale |. Belt.Option.flatMap) (dict |. Belt.Map.String.get)"); RPsiVal e = (RPsiVal) myFixture.getElementAtCaret(); assertEquals("Belt_Option.flatMap", e.getQualifiedName()); } @Test public void test_resolution_2() { configureCode("Belt_MapString.mli", "val get: 'v t -> key -> 'v option"); configureCode("Belt_Map.ml", "module String = Belt_MapString"); configureCode("Belt_Option.mli", "val flatMap : 'a option -> ('a -> 'b option) -> 'b option"); configureCode("Belt.ml", "module Option = Belt_Option\n module Map = Belt_Map"); configureCode("A.ml", "let x dict locale = (locale |. Belt.Option.flatMap) (dict |. Belt.Map.String.get)"); RPsiVal e = (RPsiVal) myFixture.getElementAtCaret(); assertEquals("Belt_MapString.get", e.getQualifiedName()); } @Test public void test_functor_body() { configureCode("A.ml", "module Make(M:I) = struct let a = 3 end"); configureCode("B.ml", "module Instance = A.Make(struct end)\n let b = Instance.a"); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("A.Make.a", e.getQualifiedName()); } @Test public void test_file_include_functor() { configureCode("A.ml", """ module Make (M:I) = struct let a = 3 end include Make(struct end) """); configureCode("B.ml", "let b = A.a;"); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("A.Make.a", e.getQualifiedName()); } @Test public void test_functor_result_with_alias() { configureCode("A.ml", "module type Result = sig let a: int end"); configureCode("B.ml", "module T = A\n module Make(M:Intf): T.Result = struct let b = 3 end"); configureCode("C.ml", "module Instance = B.Make(struct end)\n let c = Instance.a"); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("A.Result.a", e.getQualifiedName()); } @Test public void test_path_functor() { configureCode("pervasives.mli", "external compare : 'a -> 'a -> int = \"%compare\""); configureCode("A.ml", "module B = X.Functor(struct let cmp = Pervasives.compare end)"); RPsiExternal e = (RPsiExternal) myFixture.getElementAtCaret(); assertEquals("Pervasives.compare", e.getQualifiedName()); } @Test public void test_path_functor_1() { configureCode("E.ml", """ module type E1Intf = sig type t end """); configureCode("D.ml", """ module type D1Intf = sig val make: unit -> unit end module Make(M: E.E1Intf): D1Intf = struct let make () = () end """); configureCode("C.ml", "module C1 = D"); configureCode("B.ml", "module Instance = C.C1.Make(X)"); configureCode("A.ml", "let _ = B.Instance.make"); PsiElement e = myFixture.getElementAtCaret(); assertEquals("D.D1Intf.make", ((RPsiQualifiedPathElement) e).getQualifiedName()); } @Test public void test_parameter_signature() { configureCode("A.ml", """ module A1 = struct type t = { a: int; b: int } end let x (p0: A1.t) = p0.a """); PsiElement e = myFixture.getElementAtCaret(); assertEquals("A.A1.t.a", ((RPsiQualifiedPathElement) e).getQualifiedName()); } //region record @Test public void test_record_type() { configureCode("A.ml", """ type t = { f1: bool; f2: int; } let x = { f1 = true; f2 = 421 } """); RPsiRecordField e = (RPsiRecordField) myFixture.getElementAtCaret(); assertEquals("A.t.f2", e.getQualifiedName()); } @Test public void test_record() { configureCode("B.ml", """ let b = { a = 1; b = 2; } let _ = b """); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("B.b", e.getQualifiedName()); } @Test public void test_record_l1() { configureCode("B.ml", """ let b = { a = 1; b = 2; } let _ = b.b """); RPsiRecordField e = (RPsiRecordField) myFixture.getElementAtCaret(); assertEquals("B.b.b", e.getQualifiedName()); } @Test public void test_record_l3() { configureCode("A.ml", """ let a = { b = { c = { d = 1 } } } let _ = a.b.c.d """); RPsiRecordField e = (RPsiRecordField) myFixture.getElementAtCaret(); assertEquals("A.a.b.c.d", e.getQualifiedName()); } //endregion @Test public void test_pervasives() { configureCode("JsxDOMC.ml", "type style"); configureCode("pervasives.ml", "module JsxDOM = JsxDOMC"); configureCode("A.ml", "module A1 = JsxDOM.style"); PsiElement e = myFixture.getElementAtCaret(); assertEquals("JsxDOMC.style", ((RPsiType) e).getQualifiedName()); } // https://github.com/giraud/reasonml-idea-plugin/issues/452 @Test public void test_GH_452_resolve_unpacked_module() { configureCode("A.ml", """ module type I = sig val x: int end let x ~p:(p: (module I)) = let module S = (val p) in S.x """); RPsiVal e = (RPsiVal) myFixture.getElementAtCaret(); assertEquals("A.I.x", e.getQualifiedName()); } // https://github.com/giraud/reasonml-idea-plugin/issues/452 @Test public void test_GH_452_resolve_unpacked_module_inner_module() { configureCode("A.ml", """ module B = struct module type I = sig val fn: int -> unit end end """); configureCode("C.ml", """ let x ~p:(p:(module A.B.I)) = let module S = (val p) in S.fn(1) }; """); RPsiVal e = (RPsiVal) myFixture.getElementAtCaret(); assertEquals("A.B.I.fn", e.getQualifiedName()); } @Test public void test_GH_167_deconstruction_first() { configureCode("A.ml", """ let (count, setCount) = React.useState(fun () -> 0) let _ = count 1 """); PsiElement elementAtCaret = myFixture.getElementAtCaret(); assertEquals(5, elementAtCaret.getTextOffset()); } @Test public void test_GH_167_deconstruction_second() { configureCode("A.ml", """ let (count, setCount) = React.useState(fun () -> 0) let _ = setCount 1 """); PsiElement elementAtCaret = myFixture.getElementAtCaret(); assertEquals(12, elementAtCaret.getTextOffset()); } @Test public void test_GH_303() { configureCode("B.ml", "type t1 = {bar: string}"); configureCode("A.ml", "type t = {bar: string} let bar item = item.bar"); RPsiRecordField e = (RPsiRecordField) myFixture.getElementAtCaret(); assertEquals("A.t.bar", e.getQualifiedName()); } @Test public void test_GH_303_2() { configureCode("B.ml", "type t1 = {bar:string}"); configureCode("A.ml", "type t = {bar: string} let bar item = item.bar"); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("A.bar", e.getQualifiedName()); } // https://github.com/giraud/reasonml-idea-plugin/issues/358 @Test public void test_GH_358() { configureCode("A.ml", """ let clearPath () = () module Xxx = struct type t = | ClearPath let clearPath () = () end let reducer = function | Xxx.ClearPath -> clearPath() """); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("A.clearPath", e.getQualifiedName()); } // https://github.com/giraud/reasonml-idea-plugin/issues/461 @Test public void test_GH_461_parameter_type() { configureCode("A.ml", """ type store = {x: int} let fn (store: store) = store.x """); PsiElement e = myFixture.getElementAtCaret(); assertEquals("A.store", ((RPsiQualifiedPathElement) e).getQualifiedName()); } // https://github.com/giraud/reasonml-idea-plugin/issues/476 @Test public void test_GH_476_and_let() { configureCode("A.ml", """ let rec x () = y () (* comment *) and z () = x () and y () = x () """); PsiElement e = myFixture.getElementAtCaret(); assertEquals("A.y", ((RPsiLet) e).getQualifiedName()); } // https://github.com/giraud/reasonml-idea-plugin/issues/476 @Test public void test_GH_476_and_type() { configureCode("A.ml", """ type x = y (* comment *) and y = string """); PsiElement e = myFixture.getElementAtCaret(); assertEquals("A.y", ((RPsiType) e).getQualifiedName()); } } ================================================ FILE: src/test/java/com/reason/ide/search/reference/ResolveLowerElement_RES_Test.java ================================================ package com.reason.ide.search.reference; import com.intellij.psi.*; import com.reason.ide.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import org.junit.*; public class ResolveLowerElement_RES_Test extends ORBasePlatformTestCase { @Test public void test_let_basic() { configureCode("A.res", "let x = 1\n let z = x + 1"); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("A.x", e.getQualifiedName()); } @Test public void test_call_function_with_module() { configureCode("A.res", "let fn = () => 1"); configureCode("B.res", "let x = 2"); configureCode("C.res", "A.fn()\n B.x"); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("B.x", e.getQualifiedName()); } @Test public void test_let_in_module_binding() { configureCode("A.res", "let foo = 2\n module X = { let foo = 1\n let z = foo }"); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("A.X.foo", e.getQualifiedName()); } @Test public void test_let_inner_scope() { configureCode("A.res", """ let x = 1 let a = { let x = 2 x + 10 } """); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("A.a.x", e.getQualifiedName()); } @Test public void test_inner_scope_in_function() { configureCode("A.res", "let x = 1\n let fn = { let x = 2\n fn1(x)\n }"); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("A.fn.x", e.getQualifiedName()); } @Test public void test_inner_scope_in_impl() { configureCode("A.rei", "let x:int"); configureCode("A.res", "let x = 1\n let fn = { let foo = 2\n fn1(foo) }"); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("A.fn.foo", e.getQualifiedName()); assertEquals("A.res", e.getContainingFile().getName()); } @Test public void test_let_local_module_alias() { configureCode("A.rei", "let x:int"); configureCode("B.res", "let x = 1\n module X = A\n X.x"); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("A.x", e.getQualifiedName()); } @Test public void test_alias_path() { configureCode("A.res", "module W = { module X = { module Y = { module Z = { let z = 1 } } } }"); configureCode("B.res", "module C = A.W.X\n module D = C.Y.Z\n D.z"); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("A.W.X.Y.Z.z", e.getQualifiedName()); } @Test public void test_alias_01() { configureCode("A.res", "module Mode = { type t }"); configureCode("B.res", "module B1 = { module Mode = A.Mode }"); configureCode("C.res", "B.B1.Mode.t"); // B.B1.Mode.t -> A.Mode.t RPsiType e = (RPsiType) myFixture.getElementAtCaret(); assertEquals("A.Mode.t", e.getQualifiedName()); } @Test public void test_alias_02() { configureCode("A.res", "module A1 = { module A11 = { type id = string } }"); configureCode("B.res", "module B1 = A.A1"); configureCode("C.res", """ module C1 = B.B1.A11 type t = C1.id """); RPsiQualifiedPathElement e = (RPsiQualifiedPathElement) myFixture.getElementAtCaret(); assertEquals("A.A1.A11.id", e.getQualifiedName()); } @Test public void test_open() { configureCode("B.res", "let x = 1"); configureCode("A.res", "let x = 2\n open B\n x"); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("B.x", e.getQualifiedName()); } @Test public void test_alias_open() { configureCode("B.res", "let x = 1"); configureCode("A.res", "let x = 2\n module C = B\n open C\n x"); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("B.x", e.getQualifiedName()); } @Test public void test_type() { configureCode("A.res", "type t\n type t' = t"); RPsiType e = (RPsiType) myFixture.getElementAtCaret(); assertEquals("A.t", e.getQualifiedName()); assertTrue(e.isAbstract()); } @Test public void test_type_with_path() { configureCode("A.res", "type t"); configureCode("B.res", "type t = A.t"); RPsiType e = (RPsiType) myFixture.getElementAtCaret(); assertEquals("A.t", e.getQualifiedName()); } @Test public void test_type_with_path_2() { configureCode("A.res", "type t\n type y = X.Y.t"); assertThrows(AssertionError.class, "element not found in file A.res", () -> myFixture.getElementAtCaret()); } @Test public void test_external() { configureCode("A.res", "external e : string -> int = \"name\"\n let x = e"); RPsiExternal e = (RPsiExternal) myFixture.getElementAtCaret(); assertEquals("A.e", e.getQualifiedName()); } @Test public void test_record_field() { configureCode("A.res", """ type t = { f1: bool, f2: int } let x = { f1: true, f2: 421 } """); RPsiRecordField e = (RPsiRecordField) myFixture.getElementAtCaret(); assertEquals("A.t.f2", e.getQualifiedName()); } @Test public void test_function() { configureCode("A.res", "module B = { let bb = 1; }\n module C = { let cc = x => x }\n let z = C.cc(B.bb)"); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("A.B.bb", e.getQualifiedName()); } @Test public void test_function_open() { configureCode("B.res", "module C = { let make = x => x\n let convert = x => x }"); configureCode("A.res", "open B\n C.make([| C.convert |])"); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("B.C.convert", e.getQualifiedName()); } @Test public void test_param_parenLess() { configureCode("A.res", "let add10 = x => x + 10"); RPsiParameterDeclaration e = (RPsiParameterDeclaration) myFixture.getElementAtCaret(); assertEquals("A.add10[x]", e.getQualifiedName()); } @Test public void test_include() { configureCode("A.res", "module B = { type t; }\n module C = B\n include C\n type x = t"); RPsiType e = (RPsiType) myFixture.getElementAtCaret(); assertEquals("A.B.t", e.getQualifiedName()); } @Test public void test_include_alias() { configureCode("A.res", "module B = { type t }\n module C = B\n include C\n type x = t"); RPsiType e = (RPsiType) myFixture.getElementAtCaret(); assertEquals("A.B.t", e.getQualifiedName()); } @Test public void test_include_2() { configureCode("Css_AtomicTypes.resi", "module Visibility: { type t = [ #visible | #hidden | #collapse ] }"); configureCode("Css_Legacy_Core.res", "module Types = Css_AtomicTypes"); configureCode("Css.res", "include Css_Legacy_Core"); configureCode("A.res", "type layoutRule\n let visibility: [< Css.Types.Length.t | Css.Types.Visibility.t ] => layoutRule"); RPsiType e = (RPsiType) myFixture.getElementAtCaret(); assertEquals("Css_AtomicTypes.Visibility.t", e.getQualifiedName()); } @Test public void test_include_qualified() { configureCode("A.res", "module B = { module C = { type t } }\n module D = B\n include D.C"); configureCode("C.res", "type t = A.t"); RPsiType e = (RPsiType) myFixture.getElementAtCaret(); assertEquals("A.B.C.t", e.getQualifiedName()); } @Test public void test_module_signature() { configureCode("A.res", "module B: { type t\n let toString: t => string }\n module C: { type t\n let toString: t => string }"); RPsiType e = (RPsiType) myFixture.getElementAtCaret(); assertEquals("A.C.t", e.getQualifiedName()); } @Test public void test_pipe_first() { configureCode("Css.mli", "val px: int -> string"); configureCode("A.res", "Dimensions.spacing.small->Css.px"); RPsiVal e = (RPsiVal) myFixture.getElementAtCaret(); assertEquals("Css.px", e.getQualifiedName()); } @Test public void test_pipe_first_open() { configureCode("Css.mli", "val px: int -> string"); configureCode("A.res", "let make = () => { open Css; Dimensions.spacing.small->px }"); RPsiVal e = (RPsiVal) myFixture.getElementAtCaret(); assertEquals("Css.px", e.getQualifiedName()); } @Test public void test_pipe_first_open_2() { configureCode("Core.res", "module Async = { let get = x => x }"); configureCode("A.res", "open Core.Async\n request->get(\"windows/settings\")"); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("Core.Async.get", e.getQualifiedName()); } @Test public void test_pipe_first_open_with_path() { configureCode("Css.mli", "module Rule = { val px: int => string }"); configureCode("A.res", "let make = () => { open Css\n Dimensions.spacing.small->Rule.px }"); RPsiVal e = (RPsiVal) myFixture.getElementAtCaret(); assertEquals("Css.Rule.px", e.getQualifiedName()); } @Test public void test_multiple_module() { configureCode("Command.res", "module Settings = { module Action = { let convert = x => x } }"); configureCode("A.res", "module C = Y\n open Command\n Settings.Action.convert"); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("Command.Settings.Action.convert", e.getQualifiedName()); } //region Variants @Test public void test_variant_constructor() { configureCode("B.res", "let convert = x => x"); configureCode("A.res", "X.Variant(B.convert())"); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("B.convert", e.getQualifiedName()); } @Test public void test_variant_constructor_tuple() { configureCode("B.res", "type t('a) = | Variant('a, 'b)"); configureCode("A.res", "let x = 1\n B.Variant(X.Y, x)"); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("A.x", e.getQualifiedName()); } //endregion //region Poly-variants @Test public void test_local_poly_variant() { configureCode("A.res", "type a = [ | #variant ]\n let _ = #variant"); PsiElement e = myFixture.getElementAtCaret(); assertEquals("A.#variant", ((RPsiVariantDeclaration) e).getQualifiedName()); } @Test public void test_poly_variant_with_path() { configureCode("A.res", "type a = [ | #variant ]"); configureCode("B.res", "type b = [ | #variant ]"); configureCode("C.res", "A.#variant"); RPsiVariantDeclaration e = (RPsiVariantDeclaration) myFixture.getElementAtCaret(); assertEquals("A.#variant", e.getQualifiedName()); } @Test public void test_poly_variant_module_alias() { configureCode("Aaa.res", "type t = [ | #test ]"); configureCode("Bbb.res", "module A = Aaa\n A.#test"); RPsiVariantDeclaration e = (RPsiVariantDeclaration) myFixture.getElementAtCaret(); assertEquals("Aaa.#test", e.getQualifiedName()); } @Test public void test_poly_variant_module_alias_inner() { configureCode("Aaa.res", "module Option = { type t = [ | #test ] }"); configureCode("Bbb.res", "module A = Aaa\n A.Option.#test"); RPsiVariantDeclaration e = (RPsiVariantDeclaration) myFixture.getElementAtCaret(); assertEquals("Aaa.Option.#test", e.getQualifiedName()); } @Test public void test_poly_variant_constructor() { configureCode("A.res", "type a = [ | #variant(int) ]"); configureCode("B.res", "let _ = A.#variant(1)"); RPsiVariantDeclaration e = (RPsiVariantDeclaration) myFixture.getElementAtCaret(); assertEquals("A.#variant", e.getQualifiedName()); } //endregion @Test public void test_open_include() { configureCode("Css_Core.res", "let fontStyle = x => x"); configureCode("Css.res", "include Css_Core"); configureCode("A.res", "open Css\n fontStyle"); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("Css_Core.fontStyle", e.getQualifiedName()); } @Test public void test_open_include_deep() { configureCode("Css_Rule.res", "let fontStyle = x => x"); configureCode("Css_Core.res", "module Rules = { include Css_Rule }"); configureCode("Css.res", "include Css_Core"); configureCode("A.res", "open Css.Rules\n fontStyle"); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("Css_Rule.fontStyle", e.getQualifiedName()); } @Test public void test_function_call_1() { configureCode("Belt_MapString.mli", "val get: 'v t -> key -> 'v option"); configureCode("Belt_Map.ml", "module String = Belt_MapString"); configureCode("Belt_Option.mli", "val flatMap : 'a option -> ('a -> 'b option) -> 'b option"); configureCode("Belt.res", "module Option = Belt_Option\n module Map = Belt_Map"); configureCode("A.res", "let x = (dict, locale) => locale->Belt.Option.flatMap(dict->Belt.Map.String.get)"); RPsiVal e = (RPsiVal) myFixture.getElementAtCaret(); assertEquals("Belt_Option.flatMap", e.getQualifiedName()); } @Test public void test_function_call_2() { configureCode("Belt_MapString.mli", "val get: 'v t -> key -> 'v option"); configureCode("Belt_Map.ml", "module String = Belt_MapString"); configureCode("Belt_Option.mli", "val flatMap : 'a option -> ('a -> 'b option) -> 'b option"); configureCode("Belt.res", "module Option = Belt_Option\n module Map = Belt_Map;"); configureCode("A.res", "let x = (dict, locale) => locale->Belt.Option.flatMap(dict->Belt.Map.String.get)"); RPsiVal e = (RPsiVal) myFixture.getElementAtCaret(); assertEquals("Belt_MapString.get", e.getQualifiedName()); } @Test public void test_function_call_3() { configureCode("Storybook.res", "external action: string => unit => unit"); configureCode("A.res", "let _ = Storybook.action(\"Cancel\")()"); PsiElement e = myFixture.getElementAtCaret(); assertEquals("Storybook.action", ((RPsiQualifiedPathElement) e).getQualifiedName()); } @Test public void test_functor_body() { configureCode("A.res", "module Make = (M:I) => { let a = 3 }"); configureCode("B.res", "module Instance = A.Make({})\n let b = Instance.a"); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("A.Make.a", e.getQualifiedName()); } @Test public void test_file_include_functor() { configureCode("A.res", "module Make = (M:I) => { let a = 3 }\n include Make({})"); configureCode("B.res", "let b = A.a"); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("A.Make.a", e.getQualifiedName()); } @Test public void test_functor_result_with_alias() { configureCode("A.res", "module type Result = { let a: int }"); configureCode("B.res", "module T = A\n module Make = (M:Intf): T.Result => { let b = 3 }"); configureCode("C.res", "module Instance = B.Make({})\n let c = Instance.a;"); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("A.Result.a", e.getQualifiedName()); } @Test public void test_path_functor() { configureCode("pervasives.mli", "external compare : 'a -> 'a -> int = \"%compare\""); configureCode("A.res", "module B = X.Functor({ let cmp = Pervasives.compare })"); RPsiExternal e = (RPsiExternal) myFixture.getElementAtCaret(); assertEquals("Pervasives.compare", e.getQualifiedName()); } @Test public void test_path_functor_1() { configureCode("E.res", """ module type E1Intf = { type t } """); configureCode("D.res", """ module type D1Intf = { let make: unit => unit } module Make = (M: E.E1Intf): D1Intf => { let make = () => () } """); configureCode("C.res", "module C1 = D"); configureCode("B.res", "module Instance = C.C1.Make(X)"); configureCode("A.res", "let _ = B.Instance.make"); PsiElement e = myFixture.getElementAtCaret(); assertEquals("D.D1Intf.make", ((RPsiQualifiedPathElement) e).getQualifiedName()); } @Test public void test_global_local() { configureCode("Styles.res", ""); configureCode("B.res", ""); configureCode("A.res", """ open B module Styles = { let x = 1 } let x = Styles.x """); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("A.Styles.x", e.getQualifiedName()); } //region record @Test public void test_record_type() { configureCode("A.res", """ type t = { f1: bool, f2: int } let x = { f1: true, f2: 421 } """); RPsiRecordField e = (RPsiRecordField) myFixture.getElementAtCaret(); assertEquals("A.t.f2", e.getQualifiedName()); } @Test public void test_record() { configureCode("B.res", """ let b = { a: 1, b: 2 } b """); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("B.b", e.getQualifiedName()); } @Test public void test_record_l1() { configureCode("B.res", "let b = { a: 1, b: 2 }\n b.b"); RPsiRecordField e = (RPsiRecordField) myFixture.getElementAtCaret(); assertEquals("B.b.b", e.getQualifiedName()); } @Test public void test_record_l3() { configureCode("A.res", """ let a = { b: { c: { d: 1 } } } a.b.c.d """); RPsiRecordField e = (RPsiRecordField) myFixture.getElementAtCaret(); assertEquals("A.a.b.c.d", e.getQualifiedName()); } //endregion //region object @Test public void test_object_1() { configureCode("A.res", """ let a = { "b": 1, "c": 2 } a["b"] """); RPsiObjectField e = (RPsiObjectField) myFixture.getElementAtCaret(); assertEquals("A.a.b", e.getQualifiedName()); } @Test public void test_object_2() { configureCode("A.res", """ let a = { "b": 1, "c": 2 } let _ = a["b"]["c"] """); RPsiObjectField e = (RPsiObjectField) myFixture.getElementAtCaret(); assertEquals("A.a.b", e.getQualifiedName()); } @Test public void test_object_3() { configureCode("A.res", """ let a = { "b": { "c": { "d": 1 } } } a["b"]["c"]["d"] """); RPsiObjectField e = (RPsiObjectField) myFixture.getElementAtCaret(); assertEquals("A.a.b.c.d", e.getQualifiedName()); } @Test public void test_object_4() { configureCode("B.res", """ type t = { "x": { "y": string } } """); configureCode("A.res", """ let _ = (p0: B.t) => p0["x"]["y"] """); PsiElement e = myFixture.getElementAtCaret(); assertEquals("B.t.x.y", ((RPsiObjectField) e).getQualifiedName()); } @Test public void test_deep_open() { configureCode("A.res", """ let oo = {"first": {"deep": true}, "deep": {"other": {"asd": 1} } } """); configureCode("B.res", """ open A oo["deep"]["other"] """); RPsiObjectField e = (RPsiObjectField) myFixture.getElementAtCaret(); assertEquals("A.oo.deep.other", e.getQualifiedName()); } //endregion @Test public void test_alias_of_alias() { configureCode("A.res", """ module A1 = { module A2 = { let id = "_new_" } } """); configureCode("B.res", """ module B1 = { module B2 = { module B3 = { let id = A.A1.A2.id } } } module B4 = { include A module B5 = B1.B2 } """); configureCode("C.res", """ module C1 = B.B4 module C2 = C1.B5.B3 let _ = C2.id """); PsiElement e = myFixture.getElementAtCaret(); assertEquals("B.B1.B2.B3.id", ((RPsiQualifiedPathElement) e).getQualifiedName()); } @Test public void test_parameter_signature() { configureCode("A.res", """ module A1 = { module A2 = { type t = { a: int, b: int } } } let x = (p0: A1.A2.t) => { p0.a } """); PsiElement e = myFixture.getElementAtCaret(); assertEquals("A.A1.A2.t.a", ((RPsiQualifiedPathElement) e).getQualifiedName()); } @Test public void test_pervasives() { configureCode("JsxDOMC.res", "type style"); configureCode("pervasives.res", "module JsxDOM = JsxDOMC"); configureCode("A.res", "module A1 = JsxDOM.style"); PsiElement e = myFixture.getElementAtCaret(); assertEquals("JsxDOMC.style", ((RPsiType) e).getQualifiedName()); } @Test public void test_typed_parameter() { configureCode("A.res", """ let update = (x) => x let reducers = (x: store) => update(x) """); PsiElement e = myFixture.getElementAtCaret(); assertEquals("A.update", ((RPsiLet) e).getQualifiedName()); } // https://github.com/giraud/reasonml-idea-plugin/issues/452 @Test public void test_GH_452_resolve_unpacked_module() { configureCode("A.res", """ module type I = { let x: int } let x = (~p: module(I)) => { module S = unpack(p) S.x }; """); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("A.I.x", e.getQualifiedName()); } // https://github.com/giraud/reasonml-idea-plugin/issues/452 @Test public void test_GH_452_resolve_unpacked_module_inner_module() { configureCode("A.res", """ module B = { module type I = { let fn: int => unit } } """); configureCode("C.res", """ let x = (~p: module(A.B.I)) => { module S = unpack(p) S.fn(1) }; """); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("A.B.I.fn", e.getQualifiedName()); } @Test public void test_GH_167_deconstruction() { configureCode("A.res", """ let (count, setCount) = React.useState(() => 0) setCount(1) """); PsiElement elementAtCaret = myFixture.getElementAtCaret(); assertEquals(12, elementAtCaret.getTextOffset()); assertInstanceOf(elementAtCaret, RPsiLowerName.class); } @Test public void test_GH_303() { configureCode("B.res", "type t1 = {bar: string}"); configureCode("A.res", "type t = {bar: string}\n let bar = item => item.bar"); RPsiRecordField e = (RPsiRecordField) myFixture.getElementAtCaret(); assertEquals("A.t.bar", e.getQualifiedName()); } @Test public void test_GH_303_2() { configureCode("B.res", "type t1 = {bar:string}"); configureCode("A.res", "type t = {bar: string}\n let bar = item => item.bar"); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("A.bar", e.getQualifiedName()); } // https://github.com/giraud/reasonml-idea-plugin/issues/358 @Test public void test_GH_358() { configureCode("A.res", """ let clearPath = () => () module Xxx = { type t = | ClearPath let clearPath = () => () } let reducer = x => switch x { | Xxx.ClearPath => clearPath() } """); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("A.clearPath", e.getQualifiedName()); } // https://github.com/giraud/reasonml-idea-plugin/issues/461 @Test public void test_GH_461_parameter_type() { configureCode("A.res", """ type store = {x: int} let fn = (store: store) => store.x """); PsiElement e = myFixture.getElementAtCaret(); assertEquals("A.store", ((RPsiQualifiedPathElement) e).getQualifiedName()); } // https://github.com/giraud/reasonml-idea-plugin/issues/475 @Test public void test_GH_475_stack_overflow() { configureCode("A.res", """ type t = { id: string, name: string } let isSame = (item:t, item':t) => { let sameId = item.id == item'.id let sameName = item.name == item'.name sameId && sameName } """); PsiElement e = myFixture.getElementAtCaret(); // must not throw StackOverflowError assertEquals("A.t.name", ((RPsiQualifiedPathElement) e).getQualifiedName()); } // https://github.com/giraud/reasonml-idea-plugin/issues/476 @Test public void test_GH_476_and_let() { configureCode("A.res", """ let rec x = () => y() /* comment */ and z = () => x() and y = () => x() """); PsiElement e = myFixture.getElementAtCaret(); assertEquals("A.y", ((RPsiLet) e).getQualifiedName()); } // https://github.com/giraud/reasonml-idea-plugin/issues/476 @Test public void test_GH_476_and_type() { configureCode("A.res", """ type rec x = y /* comment */ and y = string """); PsiElement e = myFixture.getElementAtCaret(); assertEquals("A.y", ((RPsiType) e).getQualifiedName()); } // https://github.com/giraud/reasonml-idea-plugin/issues/511 @Test public void test_GH_511_infinite_resolution() { configureCode("A.res", """ let nothing = () => () let make = (~p1: t) => { let x = 1 let y = p2 => nothing() } """); PsiElement e = myFixture.getElementAtCaret(); assertEquals("A.nothing", ((RPsiQualifiedPathElement) e).getQualifiedName()); } } ================================================ FILE: src/test/java/com/reason/ide/search/reference/ResolveLowerElement_RML_Test.java ================================================ package com.reason.ide.search.reference; import com.intellij.psi.*; import com.reason.ide.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import org.junit.*; public class ResolveLowerElement_RML_Test extends ORBasePlatformTestCase { @Test public void test_let_basic() { configureCode("A.re", "let x = 1; let z = x + 1;"); PsiElement e = myFixture.getElementAtCaret(); assertEquals("A.x", ((RPsiLet) e).getQualifiedName()); } @Test public void test_call_function_with_module() { configureCode("A.re", "let fn=()=>1;"); configureCode("B.re", "let x=2;"); configureCode("C.re", "A.fn(); B.x"); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("B.x", e.getQualifiedName()); } @Test public void test_let_in_module_binding() { configureCode("A.re", "let foo = 2; module X = { let foo = 1; let z = foo; };"); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("A.X.foo", e.getQualifiedName()); } @Test public void test_let_inner_scope() { configureCode("A.re", "let x = 1; let a = { let x = 2; x + 10 };"); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("A.a.x", e.getQualifiedName()); } @Test public void test_inner_scope_in_function() { configureCode("A.re", "let x = 1; let fn = { let x = 2; fn1(x); };"); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("A.fn.x", e.getQualifiedName()); } @Test public void test_inner_scope_in_impl() { configureCode("A.rei", "let x:int;"); configureCode("A.re", "let x = 1; let fn = { let foo = 2; fn1(foo); };"); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("A.fn.foo", e.getQualifiedName()); assertEquals("A.re", e.getContainingFile().getName()); } @Test public void test_let_local_module_alias() { configureCode("A.rei", "let x:int;"); configureCode("B.re", "let x = 1; module X = A; X.x"); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("A.x", e.getQualifiedName()); } @Test public void test_alias_path() { configureCode("A.re", "module W = { module X = { module Y = { module Z = { let z = 1; }; }; }; };"); configureCode("B.re", "module C = A.W.X; module D = C.Y.Z; D.z"); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("A.W.X.Y.Z.z", e.getQualifiedName()); } @Test public void test_alias_01() { configureCode("A.re", "module Mode = { type t; };"); configureCode("B.re", "module B1 = { module Mode = A.Mode; };"); configureCode("C.re", "B.B1.Mode.t"); RPsiType e = (RPsiType) myFixture.getElementAtCaret(); assertEquals("A.Mode.t", e.getQualifiedName()); } @Test public void test_alias_02() { configureCode("A.re", "module A1 = { module A11 = { type id = string; }; };"); configureCode("B.re", "module B1 = A.A1;"); configureCode("C.re", """ module C1 = B.B1.A11; type t = C1.id """); RPsiQualifiedPathElement e = (RPsiQualifiedPathElement) myFixture.getElementAtCaret(); assertEquals("A.A1.A11.id", e.getQualifiedName()); } @Test public void test_open() { configureCode("B.re", "let x = 1;"); configureCode("A.re", "let x = 2; open B; x"); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("B.x", e.getQualifiedName()); } @Test public void test_alias_open() { configureCode("B.re", "let x = 1;"); configureCode("A.re", "let x = 2; module C = B; open C; x"); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("B.x", e.getQualifiedName()); } @Test public void test_alias_of_alias() { configureCode("A.re", """ module A1 = { module A2 = { let id = "_new_"; }; }; """); configureCode("B.re", """ module B1 = { module B2 = { module B3 = { let id = A.A1.A2.id; }; }; }; module B4 = { include A; module B5 = B1.B2; }; """); configureCode("C.re", """ module C1 = B.B4; module C2 = C1.B5.B3; let _ = C2.id; """); PsiElement e = myFixture.getElementAtCaret(); assertEquals("B.B1.B2.B3.id", ((RPsiQualifiedPathElement) e).getQualifiedName()); } @Test public void test_let_local_open_parens() { configureCode("A.re", "module A1 = { let a = 1; };"); configureCode("B.re", "let a = 2; let b = A.(A1.a);"); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("A.A1.a", e.getQualifiedName()); } @Test public void test_let_local_open_parens_2() { configureCode("A.re", "module A1 = { let a = 3; };"); configureCode("B.re", "let a = A.A1.(a);"); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("A.A1.a", e.getQualifiedName()); } @Test public void test_type() { configureCode("A.re", "type t; type t' = t;"); RPsiType e = (RPsiType) myFixture.getElementAtCaret(); assertEquals("A.t", e.getQualifiedName()); assertTrue(e.isAbstract()); } @Test public void test_type_with_path() { configureCode("A.re", "type t;"); configureCode("B.re", "type t = A.t;"); RPsiType e = (RPsiType) myFixture.getElementAtCaret(); assertEquals("A.t", e.getQualifiedName()); } @Test public void test_type_with_path_2() { configureCode("A.re", "type t; type y = X.Y.t"); assertThrows(AssertionError.class, "element not found in file A.re", () -> { @SuppressWarnings("unused") PsiElement e = myFixture.getElementAtCaret(); }); } @Test public void test_external() { configureCode("A.re", "external e : string -> int = \"name\"; let x = e"); RPsiExternal e = (RPsiExternal) myFixture.getElementAtCaret(); assertEquals("A.e", e.getQualifiedName()); } @Test public void test_record_field() { configureCode("A.re", """ type t = { f1: bool, f2: int }; let x = { f1: true, f2: 421 }; """); RPsiRecordField e = (RPsiRecordField) myFixture.getElementAtCaret(); assertEquals("A.t.f2", e.getQualifiedName()); } @Test public void test_function() { configureCode("A.re", "module B = { let bb = 1; }; module C = { let cc = x => x; }; let z = C.cc(B.bb);"); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("A.B.bb", e.getQualifiedName()); } @Test public void test_function_open() { configureCode("B.re", "module C = { let make = x => x; let convert = x => x; }"); configureCode("A.re", "open B; C.make([| C.convert |]);"); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("B.C.convert", e.getQualifiedName()); } @Test public void test_param_parenLess() { configureCode("A.re", "let add10 = x => x + 10;"); RPsiParameterDeclaration e = (RPsiParameterDeclaration) myFixture.getElementAtCaret(); assertEquals("A.add10[x]", e.getQualifiedName()); } @Test public void test_local_open_parens() { configureCode("A.re", "module A1 = { external a : int = \"\"; };"); configureCode("B.re", "let b = A.(A1.a);"); RPsiExternal e = (RPsiExternal) myFixture.getElementAtCaret(); assertEquals("A.A1.a", e.getQualifiedName()); } @Test public void test_local_open_parens_2() { configureCode("A.re", "module A1 = { external a : int = \"\"; };"); configureCode("B.re", "let a = A.A1.(a);"); RPsiExternal e = (RPsiExternal) myFixture.getElementAtCaret(); assertEquals("A.A1.a", e.getQualifiedName()); } @Test public void test_local_open_parens_3() { configureCode("A.re", "module A1 = { type t = | Variant; let toString = x => x; };"); configureCode("B.re", "A.A1.(Variant->toString);"); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("A.A1.toString", e.getQualifiedName()); } @Test public void test_include() { configureCode("A.re", "module B = { type t; }; include B; type x = t;"); RPsiType e = (RPsiType) myFixture.getElementAtCaret(); assertEquals("A.B.t", e.getQualifiedName()); } @Test public void test_include_alias() { configureCode("A.re", "module B = { type t; }; module C = B; include C; type x = t;"); RPsiType e = (RPsiType) myFixture.getElementAtCaret(); assertEquals("A.B.t", e.getQualifiedName()); } @Test public void test_include_2() { configureCode("Css_AtomicTypes.rei", "module Visibility: { type t = [ | `visible | `hidden | `collapse]; };"); configureCode("Css_Legacy_Core.re", "module Types = Css_AtomicTypes;"); configureCode("Css.re", "include Css_Legacy_Core;"); configureCode("A.re", "type layoutRule; let visibility: [< Css.Types.Length.t | Css.Types.Visibility.t ] => layoutRule;"); RPsiType e = (RPsiType) myFixture.getElementAtCaret(); assertEquals("Css_AtomicTypes.Visibility.t", e.getQualifiedName()); } @Test public void test_include_qualified() { configureCode("A.re", "module B = { module C = { type t; }; }; module D = B; include D.C;"); configureCode("C.re", "type t = A.t;"); RPsiType e = (RPsiType) myFixture.getElementAtCaret(); assertEquals("A.B.C.t", e.getQualifiedName()); } @Test public void test_module_signature() { configureCode("A.re", """ module B: { type t; let toString: t => string; }; module C: { type t; let toString: t => string; }; """); RPsiType e = (RPsiType) myFixture.getElementAtCaret(); assertEquals("A.C.t", e.getQualifiedName()); } @Test public void test_let_Local_open_pipe_first() { configureCode("A.re", "module A1 = { let add = x => x + 3; };"); configureCode("B.re", "let x = A.A1.(x->add);"); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("A.A1.add", e.getQualifiedName()); } @Test public void test_external_local_open_pipe_first() { configureCode("A.re", "module A1 = { external add : int => int = \"\"; };"); configureCode("B.re", "let x = A.A1.(x->add);"); RPsiExternal e = (RPsiExternal) myFixture.getElementAtCaret(); assertEquals("A.A1.add", e.getQualifiedName()); } @Test public void test_pipe_first() { configureCode("Css.mli", "val px: int -> string"); configureCode("A.re", "Dimensions.spacing.small->Css.px"); RPsiVal e = (RPsiVal) myFixture.getElementAtCaret(); assertEquals("Css.px", e.getQualifiedName()); } @Test public void test_pipe_first_open() { configureCode("Css.mli", "val px: int -> string"); configureCode("A.re", "let make = () => { open Css; Dimensions.Spacing.small->px; }"); RPsiVal e = (RPsiVal) myFixture.getElementAtCaret(); assertEquals("Css.px", e.getQualifiedName()); } @Test public void test_pipe_first_open_2() { configureCode("Core.re", "module Async = { let get = x => x; };"); configureCode("A.re", "open Core.Async; request->get(\"windows/settings\")"); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("Core.Async.get", e.getQualifiedName()); } @Test public void test_pipe_first_open_with_path() { configureCode("Css.mli", "module Rule = { val px: int => string; };"); configureCode("A.re", "let make = () => { open Css; Dimensions.spacing.small->Rule.px; }"); RPsiVal e = (RPsiVal) myFixture.getElementAtCaret(); assertEquals("Css.Rule.px", e.getQualifiedName()); } @Test public void test_multiple_module() { configureCode("Command.re", "module Settings = { module Action = { let convert = x => x; }; };"); configureCode("A.re", "module C = Y; open Command; Settings.Action.convert"); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("Command.Settings.Action.convert", e.getQualifiedName()); } //region Variants @Test public void test_variant_constructor() { configureCode("B.re", "let convert = x => x;"); configureCode("A.re", "X.Variant(B.convert())"); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("B.convert", e.getQualifiedName()); } @Test public void test_variant_constructor_tuple() { configureCode("B.re", "type t('a) = | Variant('a, 'b);"); configureCode("A.re", "let x = 1; B.Variant(X.Y, x)"); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("A.x", e.getQualifiedName()); } //endregion //region Poly-variants @Test public void test_local_poly_variant() { configureCode("A.re", "type a = [ | `variant ]; let _ = `variant"); PsiElement e = myFixture.getElementAtCaret(); assertEquals("A.#variant", ((RPsiVariantDeclaration) e).getQualifiedName()); } @Test public void test_poly_variant_with_path() { configureCode("A.re", "type a = [ | `variant ];"); configureCode("B.re", "type b = [ | `variant ];"); configureCode("C.re", "A.`variant"); RPsiVariantDeclaration e = (RPsiVariantDeclaration) myFixture.getElementAtCaret(); assertEquals("A.#variant", e.getQualifiedName()); } @Test public void test_poly_variant_module_alias() { configureCode("Aaa.re", "type t = [ | `test ];"); configureCode("Bbb.re", "module A = Aaa; A.`test"); RPsiVariantDeclaration e = (RPsiVariantDeclaration) myFixture.getElementAtCaret(); assertEquals("Aaa.#test", e.getQualifiedName()); } @Test public void test_poly_variant_module_alias_inner() { configureCode("Aaa.re", "module Option = { type t = [ | `test ]; };"); configureCode("Bbb.re", "module A = Aaa; A.Option.`test"); RPsiVariantDeclaration e = (RPsiVariantDeclaration) myFixture.getElementAtCaret(); assertEquals("Aaa.Option.#test", e.getQualifiedName()); } @Test public void test_poly_variant_constructor() { configureCode("A.re", "type a = | `variant(int);"); configureCode("B.re", "let _ = A.`variant(1);"); RPsiVariantDeclaration e = (RPsiVariantDeclaration) myFixture.getElementAtCaret(); assertEquals("A.#variant", e.getQualifiedName()); } //endregion @Test public void test_open_include() { configureCode("Css_Core.re", "let fontStyle = x => x;"); configureCode("Css.re", "include Css_Core;"); configureCode("A.re", "open Css; fontStyle"); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("Css_Core.fontStyle", e.getQualifiedName()); } @Test public void test_open_include_deep() { configureCode("Css_Rule.re", "let fontStyle = x => x;"); configureCode("Css_Core.re", "module Rules = { include Css_Rule; }"); configureCode("Css.re", "include Css_Core;"); configureCode("A.re", "open Css.Rules; fontStyle"); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("Css_Rule.fontStyle", e.getQualifiedName()); } @Test public void test_resolution_1() { configureCode("Belt_MapString.mli", "val get: 'v t -> key -> 'v option"); configureCode("Belt_Map.ml", "module String = Belt_MapString"); configureCode("Belt_Option.mli", "val flatMap : 'a option -> ('a -> 'b option) -> 'b option"); configureCode("Belt.re", "module Option = Belt_Option; module Map = Belt_Map;"); configureCode("A.re", "let fn = (dict, locale) => locale->Belt.Option.flatMap(dict->Belt.Map.String.get);"); RPsiVal e = (RPsiVal) myFixture.getElementAtCaret(); assertEquals("Belt_Option.flatMap", e.getQualifiedName()); } @Test public void test_resolution_2() { configureCode("Belt_MapString.mli", "val get: 'v t -> key -> 'v option"); configureCode("Belt_Map.ml", "module String = Belt_MapString;"); configureCode("Belt_Option.mli", "val flatMap : 'a option -> ('a -> 'b option) -> 'b option"); configureCode("Belt.re", "module Option = Belt_Option; module Map = Belt_Map;"); configureCode("A.re", "let x = (dict, locale) => locale->Belt.Option.flatMap(dict->Belt.Map.String.get);"); RPsiVal e = (RPsiVal) myFixture.getElementAtCaret(); assertEquals("Belt_MapString.get", e.getQualifiedName()); } @Test public void test_functor_body() { configureCode("A.re", "module Make = (M:I) => { let a = 3; };"); configureCode("B.re", "module Instance = A.Make({}); let b = Instance.a;"); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("A.Make.a", e.getQualifiedName()); } @Test public void test_file_include_functor() { configureCode("A.re", "module Make = (M:I) => { let a = 3; }; include Make({})"); configureCode("B.re", "let b = A.a;"); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("A.Make.a", e.getQualifiedName()); } @Test public void test_functor_result_with_alias() { configureCode("A.re", "module type Result = { let a: int; };"); configureCode("B.re", "module T = A; module Make = (M:Intf): T.Result => { let b = 3; };"); configureCode("C.re", "module Instance = B.Make({}); let c = Instance.a;"); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("A.Result.a", e.getQualifiedName()); } @Test public void test_path_functor() { configureCode("pervasives.mli", "external compare : 'a -> 'a -> int = \"%compare\""); configureCode("A.re", "module B = X.Functor({ let cmp = Pervasives.compare; })"); RPsiExternal e = (RPsiExternal) myFixture.getElementAtCaret(); assertEquals("Pervasives.compare", e.getQualifiedName()); } @Test public void test_path_functor_1() { configureCode("E.re", """ module type E1Intf = { type t; }; """); configureCode("D.re", """ module type D1Intf = { let make: unit => unit; }; module Make = (M: E.E1Intf): D1Intf => { let make = () => (); }; """); configureCode("C.re", "module C1 = D;"); configureCode("B.re", "module Instance = C.C1.Make(X);"); configureCode("A.re", "let _ = B.Instance.make;"); PsiElement e = myFixture.getElementAtCaret(); assertEquals("D.D1Intf.make", ((RPsiQualifiedPathElement) e).getQualifiedName()); } @Test public void test_global_local() { configureCode("Styles.re", ""); configureCode("B.re", ""); configureCode("A.re", """ open B; module Styles = { let x = 1; }; let x = Styles.x; """); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("A.Styles.x", e.getQualifiedName()); } @Test public void test_parameter_signature() { configureCode("A.re", """ module A1 = { type t = { a: int, b: int }; }; let x = (p0: A1.t) => { p0.a }; """); PsiElement e = myFixture.getElementAtCaret(); assertEquals("A.A1.t.a", ((RPsiQualifiedPathElement) e).getQualifiedName()); } //region record @Test public void test_record_type() { configureCode("A.re", """ type t = { f1: bool, f2: int }; let x = { f1: true, f2: 421 }; """); RPsiRecordField e = (RPsiRecordField) myFixture.getElementAtCaret(); assertEquals("A.t.f2", e.getQualifiedName()); } @Test public void test_record() { configureCode("B.re", "let b = { a: 1, b: 2 }; b"); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("B.b", e.getQualifiedName()); } @Test public void test_record_l1() { configureCode("B.re", "let b = { a: 1, b: 2 }; b.b"); RPsiRecordField e = (RPsiRecordField) myFixture.getElementAtCaret(); assertEquals("B.b.b", e.getQualifiedName()); } @Test public void test_record_l3() { configureCode("A.re", "let a = { b: { c: { d: 1 } } }; a.b.c.d"); RPsiRecordField e = (RPsiRecordField) myFixture.getElementAtCaret(); assertEquals("A.a.b.c.d", e.getQualifiedName()); } //endregion //region object @Test public void test_object_1() { configureCode("A.re", """ let a = { "b": 1, "c": 2 }; a##b """); RPsiObjectField e = (RPsiObjectField) myFixture.getElementAtCaret(); assertEquals("A.a.b", e.getQualifiedName()); } @Test public void test_object_2() { configureCode("A.re", """ let a = { "b": 1, "c": 2 }; let _ = a##b##c """); RPsiObjectField e = (RPsiObjectField) myFixture.getElementAtCaret(); assertEquals("A.a.b", e.getQualifiedName()); } @Test public void test_object_3() { configureCode("A.re", """ let a = { "b": { "c": { "d": 1 } } }; a##b##c##d """); RPsiObjectField e = (RPsiObjectField) myFixture.getElementAtCaret(); assertEquals("A.a.b.c.d", e.getQualifiedName()); } @Test public void test_object_4() { configureCode("B.re", """ type t = {. "x": {. "y": string } }; """); configureCode("A.re", """ let _ = (p0: B.t) => p0##x##y """); PsiElement e = myFixture.getElementAtCaret(); assertEquals("B.t.x.y", ((RPsiObjectField) e).getQualifiedName()); } @Test public void test_deep_open() { configureCode("A.re", "let oo = {\"first\": {\"deep\": true}, \"deep\": {\"other\": {\"asd\": 1} } }"); configureCode("B.re", "open A; oo##deep##other"); RPsiObjectField e = (RPsiObjectField) myFixture.getElementAtCaret(); assertEquals("A.oo.deep.other", e.getQualifiedName()); } //endregion @Test public void test_pervasives() { configureCode("JsxDOMC.re", "type style;"); configureCode("pervasives.re", "module JsxDOM = JsxDOMC;"); configureCode("A.re", "module A1 = JsxDOM.style"); PsiElement e = myFixture.getElementAtCaret(); assertEquals("JsxDOMC.style", ((RPsiType) e).getQualifiedName()); } @Test public void test_typed_parameter() { configureCode("A.re", """ let update = (x) => x; let reducers = (x: store) => update(x); """); PsiElement e = myFixture.getElementAtCaret(); assertEquals("A.update", ((RPsiLet) e).getQualifiedName()); } // https://github.com/giraud/reasonml-idea-plugin/issues/452 @Test public void test_GH_452_resolve_unpacked_module() { configureCode("A.re", """ module type I = { let x: int; }; let x = (~p: (module I)) => { module S = (val p); S.x }; """); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("A.I.x", e.getQualifiedName()); } // https://github.com/giraud/reasonml-idea-plugin/issues/452 @Test public void test_GH_452_resolve_unpacked_module_inner_module() { configureCode("A.re", """ module B = { module type I = { let fn: int => unit; }; }; """); configureCode("C.re", """ let x = (~p: (module A.B.I)) => { module S = (val p); S.fn(1) }; """); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("A.B.I.fn", e.getQualifiedName()); } @Test public void test_GH_167_deconstruction_first() { configureCode("A.re", "let (count, setCount) = React.useState(() => 0); count(1);"); PsiElement elementAtCaret = myFixture.getElementAtCaret(); assertEquals(5, elementAtCaret.getTextOffset()); } @Test public void test_GH_167_deconstruction_second() { configureCode("A.re", "let (count, setCount) = React.useState(() => 0); setCount(1);"); PsiElement elementAtCaret = myFixture.getElementAtCaret(); assertEquals(12, elementAtCaret.getTextOffset()); } @Test public void test_GH_303() { configureCode("B.re", "type t1 = {bar: string};"); configureCode("A.re", "type t = {bar: string}; let bar = item => item.bar;"); RPsiRecordField e = (RPsiRecordField) myFixture.getElementAtCaret(); assertEquals("A.t.bar", e.getQualifiedName()); } @Test public void test_GH_303_2() { configureCode("B.re", "type t1 = {bar:string};"); configureCode("A.re", "type t = {bar: string}; let bar = item => item.bar;"); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("A.bar", e.getQualifiedName()); } // https://github.com/giraud/reasonml-idea-plugin/issues/358 @Test public void test_GH_358() { configureCode("A.re", """ let clearPath = () => (); module Xxx = { type t = | ClearPath; let clearPath = () => (); }; let reducer = fun | Xxx.ClearPath => clearPath(); """); RPsiLet e = (RPsiLet) myFixture.getElementAtCaret(); assertEquals("A.clearPath", e.getQualifiedName()); } // https://github.com/giraud/reasonml-idea-plugin/issues/461 @Test public void test_GH_461_parameter_type() { configureCode("A.re", """ type store = {x: int}; let fn = (store: store) => store.x; """); PsiElement e = myFixture.getElementAtCaret(); assertEquals("A.store", ((RPsiQualifiedPathElement) e).getQualifiedName()); } // https://github.com/giraud/reasonml-idea-plugin/issues/476 @Test public void test_GH_476_and_let() { configureCode("A.re", """ let rec x = () => y() /* comment */ and z = () => x() and y = () => x(); """); PsiElement e = myFixture.getElementAtCaret(); assertEquals("A.y", ((RPsiLet) e).getQualifiedName()); } // https://github.com/giraud/reasonml-idea-plugin/issues/476 @Test public void test_GH_476_and_type() { configureCode("A.re", """ type x = y /* comment */ and y = string; """); PsiElement e = myFixture.getElementAtCaret(); assertEquals("A.y", ((RPsiType) e).getQualifiedName()); } } ================================================ FILE: src/test/java/com/reason/ide/search/reference/ResolveUpperElement_OCL_Test.java ================================================ package com.reason.ide.search.reference; import com.intellij.psi.*; import com.reason.ide.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import org.junit.*; public class ResolveUpperElement_OCL_Test extends ORBasePlatformTestCase { @Test public void test_basic_file() { configureCode("Dimensions.ml", "let space = 5"); configureCode("Comp.ml", "Dimensions"); PsiQualifiedNamedElement e = (PsiQualifiedNamedElement) myFixture.getElementAtCaret(); assertEquals("Dimensions.ml", e.getName()); } @Test public void test_interface_implementation() { configureCode("A.mli", "type t"); configureCode("A.ml", "type t"); configureCode("B.ml", "A"); PsiNamedElement e = (PsiNamedElement) myFixture.getElementAtCaret(); assertEquals("A.mli", e.getName()); } @Test public void test_let_alias() { configureCode("Dimensions.ml", "let space = 5"); configureCode("Comp.ml", "let s = Dimensions.space"); PsiQualifiedNamedElement e = (PsiQualifiedNamedElement) myFixture.getElementAtCaret(); assertEquals("Dimensions.ml", e.getName()); } @Test public void test_alias() { configureCode("A1.ml", "module A11 = struct end"); configureCode("A.ml", "module A1 = struct end"); configureCode("B.ml", "module X = A\n let _ = X.A1"); RPsiModule e = (RPsiModule) myFixture.getElementAtCaret(); assertEquals("A.A1", e.getQualifiedName()); } @Test public void test_alias_path_no_resolution() { configureCode("A.ml", "module X = struct module Y = struct let z = 1 end end"); configureCode("B.ml", "module C = A.X\n let _ = C.Y"); RPsiModule e = (RPsiModule) myFixture.getElementAtCaret(); assertEquals("B.C", e.getQualifiedName()); } @Test public void test_alias_path_resolution() { configureCode("A.ml", "module X = struct module Y = struct let z = 1 end end"); configureCode("B.ml", "module C = A.X\n let _ = C.Y"); RPsiModule e = (RPsiModule) myFixture.getElementAtCaret(); assertEquals("A.X.Y", e.getQualifiedName()); } @Test public void test_local_alias() { configureCode("Belt.ml", "let x = 1;"); configureCode("A.ml", "module B = Belt\n let _ = B"); RPsiModule e = (RPsiModule) myFixture.getElementAtCaret(); assertEquals("A.B", e.getQualifiedName()); } @Test public void test_alias_of_alias() { configureCode("A.ml", """ module A1 = struct module A2 = struct let id = "_new_" end end """); configureCode("B.ml", """ module B1 = struct module B2 = struct module B3 = struct let id = A.A1.A2.id end end end module B4 = struct include A module B5 = B1.B2 end """); configureCode("C.ml", """ module C1 = B.B4 module C2 = C1.B5.B3 let _ = C2.id """); PsiElement e = myFixture.getElementAtCaret(); assertEquals("B.B1.B2.B3.id", ((RPsiQualifiedPathElement) e).getQualifiedName()); } @Test public void test_alias_interface() { configureCode("C.mli", "module A1 = struct end"); configureCode("D.ml", "module X = C\n let _ = X.A1"); RPsiModule e = (RPsiModule) myFixture.getElementAtCaret(); assertEquals("C.A1", e.getQualifiedName()); } @Test public void test_alias_same() { configureCode("A.ml", ""); configureCode("B.ml", "module A = A"); RPsiModule e = (RPsiModule) myFixture.getElementAtCaret(); assertEquals("A", e.getQualifiedName()); assertEquals("A.ml", e.getContainingFile().getName()); } @Test public void test_function_call() { configureCode("AsyncHooks.ml", "module XhrAsync = struct let make = () => () end"); configureCode("A.ml", "let _ = AsyncHooks.useCancellableRequest AsyncHooks.XhrAsync.make"); PsiElement e = myFixture.getElementAtCaret(); assertEquals("AsyncHooks", ((RPsiQualifiedPathElement) e).getQualifiedName()); } @Test public void test_open() { configureCode("Belt.ml", "module Option = struct end"); configureCode("Dummy.ml", "open Belt.Option"); RPsiModule e = (RPsiModule) myFixture.getElementAtCaret(); assertEquals("Belt.Option", e.getQualifiedName()); assertEquals("Belt.ml", e.getContainingFile().getName()); } @Test public void test_include_path() { configureCode("Css_Core.mli", "let display: string -> rule"); configureCode("Css.ml", "include Css_Core\n include Css_Core.Make({})"); PsiQualifiedNamedElement e = (PsiQualifiedNamedElement) myFixture.getElementAtCaret(); assertEquals("Css_Core", e.getQualifiedName()); } @Test public void test_include_alias() { configureCode("Css_AtomicTypes.mli", "module Color = struct type t end"); configureCode("Css_Core.mli", "module Types = Css_AtomicTypes"); configureCode("Css.ml", "include Css_Core"); configureCode("A.ml", "Css.Types.Color"); RPsiModule e = (RPsiModule) myFixture.getElementAtCaret(); assertEquals("Css_AtomicTypes.Color", e.getQualifiedName()); } //region Variants @Test public void test_local_variant() { configureCode("A.ml", "type a = | Variant\n let _ = Variant"); PsiElement e = myFixture.getElementAtCaret(); assertEquals("A.Variant", ((RPsiVariantDeclaration) e).getQualifiedName()); } @Test public void test_variant_with_path() { configureCode("A.ml", "type a = | Variant"); configureCode("B.ml", "type b = | Variant"); configureCode("C.ml", "A.Variant"); RPsiVariantDeclaration e = (RPsiVariantDeclaration) myFixture.getElementAtCaret(); assertEquals("A.Variant", e.getQualifiedName()); } @Test public void test_variant_module_alias() { configureCode("Aaa.ml", "type t = | Test"); configureCode("Bbb.ml", "module A = Aaa\n let _ = A.Test"); RPsiVariantDeclaration e = (RPsiVariantDeclaration) myFixture.getElementAtCaret(); assertEquals("Aaa.Test", e.getQualifiedName()); } @Test public void test_variant_module_alias_inner() { configureCode("Aaa.ml", "module Option = struct type t = | Test end"); configureCode("Bbb.ml", "module A = Aaa\n let _ = A.Option.Test"); RPsiVariantDeclaration e = (RPsiVariantDeclaration) myFixture.getElementAtCaret(); assertEquals("Aaa.Option.Test", e.getQualifiedName()); } @Test public void test_variant_constructor() { configureCode("A.ml", "type a = | Variant(int)"); configureCode("B.ml", "let _ = A.Variant(1)"); RPsiVariantDeclaration e = (RPsiVariantDeclaration) myFixture.getElementAtCaret(); assertEquals("A.Variant", e.getQualifiedName()); } //endregion //region Poly-variants @Test public void test_local_poly_variant() { configureCode("A.ml", "type a = [ | `Variant ]\n let _ = `Variant"); PsiElement e = myFixture.getElementAtCaret(); assertEquals("A.#Variant", ((RPsiVariantDeclaration) e).getQualifiedName()); } @Test public void test_poly_variant_with_path() { configureCode("A.ml", "type a = [ | `Variant ]"); configureCode("B.ml", "type b = [ | `Variant ]"); configureCode("C.ml", "A.`Variant"); RPsiVariantDeclaration e = (RPsiVariantDeclaration) myFixture.getElementAtCaret(); assertEquals("A.#Variant", e.getQualifiedName()); } @Test public void test_poly_variant_module_alias() { configureCode("Aaa.ml", "type t = [ | `Test ]"); configureCode("Bbb.ml", "module A = Aaa\nlet _ = A.`Test"); RPsiVariantDeclaration e = (RPsiVariantDeclaration) myFixture.getElementAtCaret(); assertEquals("Aaa.#Test", e.getQualifiedName()); } @Test public void test_poly_variant_module_alias_inner() { configureCode("Aaa.ml", "module Option = struct type t = [ | `Test ] end"); configureCode("Bbb.ml", "module A = Aaa\nlet _ = A.Option.`Test"); RPsiVariantDeclaration e = (RPsiVariantDeclaration) myFixture.getElementAtCaret(); assertEquals("Aaa.Option.#Test", e.getQualifiedName()); } @Test public void test_poly_variant_constructor() { configureCode("A.ml", "type a = | `Variant of int"); configureCode("B.ml", "let _ = A.`Variant 1"); RPsiVariantDeclaration e = (RPsiVariantDeclaration) myFixture.getElementAtCaret(); assertEquals("A.#Variant", e.getQualifiedName()); } //endregion @Test public void test_exception() { myFixture.configureByText("A.ml", "exception ExceptionName\n let _ = raise ExceptionName"); RPsiException e = (RPsiException) myFixture.getElementAtCaret(); assertEquals("A.ExceptionName", e.getQualifiedName()); } @Test public void test_belt_alias() { configureCode("String.ml", "type t"); configureCode("Belt.ml", "module Map = Belt_Map"); configureCode("Belt_Map.ml", "module String = Belt_MapString"); configureCode("Belt_MapString.ml", "type t"); configureCode("A.ml", "Belt.Map.String"); RPsiModule e = (RPsiModule) myFixture.getElementAtCaret(); assertEquals("Belt_Map.String", e.getQualifiedName()); } @Test public void test_functor_inside() { configureCode("F.ml", """ module type S = sig module X : sig end end module M() : S = struct module X = struct end end\s module A = M(struct end) module X2 = struct module X1 = struct module X = struct end end end module V = A.X"""); RPsiModule e = (RPsiModule) myFixture.getElementAtCaret(); assertEquals("F.M.X", e.getQualifiedName()); } @Test public void test_functor_outside() { configureCode("F.ml", """ module type S = sig module X : sig end end module M() : S = struct module X = struct end end module A = M(struct end)"""); configureCode("B.ml", "module X2 = struct module X1 = struct module X = struct end end end\n" + "module V = F.A.X"); RPsiModule e = (RPsiModule) myFixture.getElementAtCaret(); assertEquals("F.M.X", e.getQualifiedName()); } @Test public void test_module_signature() { configureCode("A.ml", """ module B = struct module C = struct module type S = sig end end module D = C end module M : B.D.S = struct end """); RPsiModule e = (RPsiModule) myFixture.getElementAtCaret(); assertEquals("A.B.C.S", e.getQualifiedName()); } @Test public void test_not_resolve_alternate_name() { configureCode("A.ml", ""); configureCode("B.ml", """ module B1 = struct include A end """); configureCode("C.ml", """ module C1 = B.B1 """); RPsiModule e = (RPsiModule) myFixture.getElementAtCaret(); assertEquals("B.B1", e.getQualifiedName()); } @Test public void test_with_include() { configureCode("A.ml", "module A1 = struct module A2 = struct module A3 = struct end end end"); configureCode("B.ml", """ module B1 = struct include A end """); configureCode("C.ml", """ module C1 = B.B1 module C2 = C1.A1.A2 module M = C2.A3 """); RPsiModule e = (RPsiModule) myFixture.getElementAtCaret(); assertEquals("A.A1.A2.A3", e.getQualifiedName()); } @Test public void test_pervasives_modules() { configureCode("JsxDOMC.ml", "type style"); configureCode("pervasives.ml", "module JsxDOM = JsxDOMC"); configureCode("A.ml", "module A1 = JsxDOM"); PsiElement e = myFixture.getElementAtCaret(); assertEquals("Pervasives.JsxDOM", ((RPsiModule) e).getQualifiedName()); } // https://github.com/giraud/reasonml-idea-plugin/issues/426 @Test public void test_alias_resolution_same_file() { configureCode("Dummy.ml", """ module A = struct module B = struct module C = struct module D = struct end end end end module Bbb = A.B module Ddd = Bbb.C.D """); PsiElement e = myFixture.getElementAtCaret(); assertEquals("Dummy.A.B.C", ((RPsiModule) e).getQualifiedName()); } @Test public void test_typed_parameter() { configureCode("A.ml", """ let update x = x let reducers (x : store) = update x """); PsiElement e = myFixture.getElementAtCaret(); assertEquals("A.update", ((RPsiLet) e).getQualifiedName()); } // https://github.com/giraud/reasonml-idea-plugin/issues/476 @Test public void test_GH_476_and_module() { configureCode("Dummy.ml", """ module rec A:sig end = struct type t = B.b end and B : sig type b end= struct type b end """); PsiElement e = myFixture.getElementAtCaret(); assertEquals("Dummy.B", ((RPsiModule) e).getQualifiedName()); } } ================================================ FILE: src/test/java/com/reason/ide/search/reference/ResolveUpperElement_RES_Test.java ================================================ package com.reason.ide.search.reference; import com.intellij.psi.*; import com.reason.comp.*; import com.reason.ide.*; import com.reason.ide.files.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import org.junit.*; public class ResolveUpperElement_RES_Test extends ORBasePlatformTestCase { @Test public void test_basic_file() { configureCode("Dimensions.res", "let space = 5;"); configureCode("Comp.res", "Dimensions"); ResFile e = (ResFile) myFixture.getElementAtCaret(); assertEquals("Dimensions.res", e.getName()); } @Test public void test_interface_implementation() { configureCode("A.resi", "type t"); configureCode("A.res", "type t"); configureCode("B.res", "A"); PsiNamedElement e = (PsiNamedElement) myFixture.getElementAtCaret(); assertEquals("A.resi", e.getName()); } @Test public void test_let_alias() { configureCode("Dimensions.res", "let space = 5"); configureCode("Comp.res", "let s = Dimensions.space"); ResFile e = (ResFile) myFixture.getElementAtCaret(); assertEquals("Dimensions.res", e.getName()); } @Test public void test_alias() { configureCode("A1.res", "module A11 = {}"); configureCode("A.res", "module A1 = {}"); configureCode("B.res", "module X = A\n X.A1"); RPsiModule e = (RPsiModule) myFixture.getElementAtCaret(); assertEquals("A.A1", e.getQualifiedName()); } @Test public void test_alias_path_no_resolution() { configureCode("A.res", "module X = { module Y = { let z = 1 } }"); configureCode("B.res", "module C = A.X\n C.Y"); RPsiModule e = (RPsiModule) myFixture.getElementAtCaret(); assertEquals("B.C", e.getQualifiedName()); } @Test public void test_alias_path_resolution() { configureCode("A.res", "module X = { module Y = { let z = 1 } }"); configureCode("B.res", "module C = A.X\n C.Y"); RPsiModule e = (RPsiModule) myFixture.getElementAtCaret(); assertEquals("A.X.Y", e.getQualifiedName()); } @Test public void test_local_alias() { configureCode("Belt.res", "let x = 1"); configureCode("A.res", "module B = Belt\n B"); RPsiModule e = (RPsiModule) myFixture.getElementAtCaret(); assertEquals("A.B", e.getQualifiedName()); } @Test public void test_alias_interface() { configureCode("C.resi", "module A1 = {}"); configureCode("D.res", "module X = C\n X.A1;"); RPsiModule e = (RPsiModule) myFixture.getElementAtCaret(); assertEquals("C.A1", e.getQualifiedName()); } @Test public void test_alias_same() { configureCode("A.res", ""); configureCode("B.res", "module A = A"); RPsiModule e = (RPsiModule) myFixture.getElementAtCaret(); assertEquals("A", e.getQualifiedName()); assertEquals("A.res", e.getContainingFile().getName()); } @Test public void test_include_alias() { configureCode("Css_AtomicTypes.resi", "module Color = { type t }"); configureCode("Css_Core.resi", "module Types = Css_AtomicTypes"); configureCode("Css.res", "include Css_Core"); configureCode("A.res", "let t = Css.Types.Color.t"); RPsiModule e = (RPsiModule) myFixture.getElementAtCaret(); assertEquals("Css_AtomicTypes.Color", e.getQualifiedName()); } @Test public void test_function_call() { configureCode("AsyncHooks.res", "module XhrAsync = { let make = () => () }"); configureCode("A.res", "let _ = AsyncHooks.useCancellableRequest(AsyncHooks.XhrAsync.make)"); PsiElement e = myFixture.getElementAtCaret(); assertEquals("AsyncHooks", ((RPsiQualifiedPathElement) e).getQualifiedName()); } @Test public void test_uncurried_function_call() { configureCode("A.res", "type t = | Variant"); configureCode("B.res", "let _ = fn(. A.Variant)"); PsiElement e = myFixture.getElementAtCaret(); assertEquals("A.Variant", ((RPsiQualifiedPathElement) e).getQualifiedName()); } @Test public void test_open() { configureCode("Belt.res", "module Option = {}"); configureCode("Dummy.res", "open Belt.Option"); RPsiModule e = (RPsiModule) myFixture.getElementAtCaret(); assertEquals("Belt.Option", e.getQualifiedName()); assertEquals("Belt.res", e.getContainingFile().getName()); } @Test public void test_include_path() { configureCode("Css_Core.resi", "let display: string => rule"); configureCode("Css.res", """ include Css_Core include Css_Core.Make({}) """); PsiElement e = myFixture.getElementAtCaret(); assertEquals("Css_Core", ((ResInterfaceFile) e).getQualifiedName()); } //region Variants @Test public void test_local_variant() { configureCode("A.res", "type a = | Variant\n let _ = Variant"); PsiElement e = myFixture.getElementAtCaret(); assertEquals("A.Variant", ((RPsiVariantDeclaration) e).getQualifiedName()); } @Test public void test_variant_with_path() { configureCode("A.res", "type a = | Variant"); configureCode("B.res", "type b = | Variant"); configureCode("C.res", "A.Variant"); RPsiVariantDeclaration e = (RPsiVariantDeclaration) myFixture.getElementAtCaret(); assertEquals("A.Variant", e.getQualifiedName()); } @Test public void test_variant_module_alias() { configureCode("Aaa.res", "type t = | Test"); configureCode("Bbb.res", "module A = Aaa\n A.Test"); RPsiVariantDeclaration e = (RPsiVariantDeclaration) myFixture.getElementAtCaret(); assertEquals("Aaa.Test", e.getQualifiedName()); } @Test public void test_variant_module_alias_inner() { configureCode("Aaa.res", "module Option = { type t = | Test }"); configureCode("Bbb.res", "module A = Aaa\n A.Option.Test"); RPsiVariantDeclaration e = (RPsiVariantDeclaration) myFixture.getElementAtCaret(); assertEquals("Aaa.Option.Test", e.getQualifiedName()); } @Test public void test_variant_constructor() { configureCode("A.res", "type a = | Variant(int)"); configureCode("B.res", "let _ = A.Variant(1)"); RPsiVariantDeclaration e = (RPsiVariantDeclaration) myFixture.getElementAtCaret(); assertEquals("A.Variant", e.getQualifiedName()); } //endregion //region Poly-variants @Test public void test_local_poly_variant() { configureCode("A.res", "type a = [ | #Variant ]\n let _ = #Variant"); PsiElement e = myFixture.getElementAtCaret(); assertEquals("A.#Variant", ((RPsiVariantDeclaration) e).getQualifiedName()); } @Test public void test_poly_variant_with_path() { configureCode("A.res", "type a = [ | #Variant ]"); configureCode("B.res", "type b = [ | #Variant ]"); configureCode("C.res", "A.#Variant"); RPsiVariantDeclaration e = (RPsiVariantDeclaration) myFixture.getElementAtCaret(); assertEquals("A.#Variant", e.getQualifiedName()); } @Test public void test_poly_variant_module_alias() { configureCode("Aaa.res", "type t = [ | #Test ]"); configureCode("Bbb.res", "module A = Aaa\n A.#Test"); RPsiVariantDeclaration e = (RPsiVariantDeclaration) myFixture.getElementAtCaret(); assertEquals("Aaa.#Test", e.getQualifiedName()); } @Test public void test_poly_variant_module_alias_inner() { configureCode("Aaa.res", "module Option = { type t = [ | #Test ] }"); configureCode("Bbb.res", "module A = Aaa\n A.Option.#Test"); RPsiVariantDeclaration e = (RPsiVariantDeclaration) myFixture.getElementAtCaret(); assertEquals("Aaa.Option.#Test", e.getQualifiedName()); } @Test public void test_poly_variant_constructor() { configureCode("A.res", "type a = [ | #Variant(int) ]"); configureCode("B.res", "let _ = A.#Variant(1)"); RPsiVariantDeclaration e = (RPsiVariantDeclaration) myFixture.getElementAtCaret(); assertEquals("A.#Variant", e.getQualifiedName()); } //endregion @Test public void test_exception() { myFixture.configureByText("A.res", "exception ExceptionName\n raise(ExceptionName)"); RPsiException e = (RPsiException) myFixture.getElementAtCaret(); assertEquals("A.ExceptionName", e.getQualifiedName()); } @Test public void test_belt_alias() { configureCode("String.res", "type t"); configureCode("Belt_MapString.res", "type t"); configureCode("Belt_Map.res", "module String = Belt_MapString"); configureCode("Belt.res", "module Map = Belt_Map"); configureCode("A.res", "Belt.Map.String"); RPsiModule e = (RPsiModule) myFixture.getElementAtCaret(); assertEquals("Belt_Map.String", e.getQualifiedName()); } @Test public void test_open_belt_alias() { configureCode("String.res", "type t"); configureCode("Belt_MapString.res", "type t"); configureCode("Belt_Map.res", "module String = Belt_MapString"); configureCode("Belt.res", "module Map = Belt_Map"); configureCode("A.res", "open Belt\n open Map\n String"); RPsiModule e = (RPsiModule) myFixture.getElementAtCaret(); assertEquals("Belt_Map.String", e.getQualifiedName()); } @Test public void test_pipe_first() { configureCode("X.res", "let fn = () => ()"); configureCode("B.res", "module C = { module X = { let fn = () => () } }"); configureCode("D.res", "B.C.X.fn()->X.fn"); ResFile e = (ResFile) myFixture.getElementAtCaret(); assertEquals("X", e.getQualifiedName()); } @Test public void test_pipe_last() { configureCode("A.res", "module X = {}"); configureCode("B.res", "module C = { module X = { let fn = () => () } }"); configureCode("D.res", "B.C.X.fn() |> A.X.fn"); RPsiModule e = (RPsiModule) myFixture.getElementAtCaret(); assertEquals("A.X", e.getQualifiedName()); } @Test public void test_no_resolution_1() { configureCode("Belt_MapString.mli", "val get: 'v t -> key -> 'v option"); configureCode("Belt_Map.ml", "module String = Belt_MapString"); configureCode("Belt_Option.mli", "val flatMap : 'a option -> ('a -> 'b option) -> 'b option"); configureCode("Belt.res", "module Option = Belt_Option\n module Map = Belt_Map;"); configureCode("A.res", "let x = (dict, locale) => locale->Belt.Option.flatMap(dict->Belt.Map.String.get);"); RPsiModule e = (RPsiModule) myFixture.getElementAtCaret(); assertEquals("Belt.Option", e.getQualifiedName()); } @Test public void test_no_resolution_2() { configureCode("Belt_MapString.mli", "val get: 'v t -> key -> 'v option"); configureCode("Belt_Map.ml", "module String = Belt_MapString"); configureCode("Belt_Option.mli", "val flatMap : 'a option -> ('a -> 'b option) -> 'b option"); configureCode("Belt.res", "module Option = Belt_Option\n module Map = Belt_Map;"); configureCode("A.res", "let x = (dict, locale) => locale->Belt.Option.flatMap(dict->Belt.Map.String.get);"); RPsiModule e = (RPsiModule) myFixture.getElementAtCaret(); assertEquals("Belt.Map", e.getQualifiedName()); } @Test public void test_functor_inside() { configureCode("F.res", """ module type S = { module X: {} } module M = () : S => { module X = {} } module A = M({}) module X2 = { module X1 = { module X = {} } } module V = A.X """); RPsiModule e = (RPsiModule) myFixture.getElementAtCaret(); assertEquals("F.M.X", e.getQualifiedName()); } @Test public void test_functor_outside() { configureCode("F.res", """ module type S = { module X: {} } module M = () : S => { module X = {} } module A = M({}) """); configureCode("B.res", "module X2 = { module X1 = { module X = {} } }\n module V = F.A.X"); RPsiModule e = (RPsiModule) myFixture.getElementAtCaret(); assertEquals("F.M.X", e.getQualifiedName()); } @Test public void test_with_tag() { configureCode("Form.res", "module Styles = {}"); configureCode("A.res", "module Styles = {}\n let _ =
/>"); RPsiModule e = (RPsiModule) myFixture.getElementAtCaret(); assertEquals("A.Styles", e.getQualifiedName()); } @Test public void test_with_tag_1() { configureCode("Form.res", "module Styles = {}"); configureCode("A.res", "module Styles = {}\n let _ =
/>"); RPsiModule e = (RPsiModule) myFixture.getElementAtCaret(); assertEquals("A.Styles", e.getQualifiedName()); } @Test public void test_module_signature() { configureCode("A.res", """ module B = { module C = { module type S = {} }; module D = C }; module M: B.D.S = {} """); RPsiModule e = (RPsiModule) myFixture.getElementAtCaret(); assertEquals("A.B.C.S", e.getQualifiedName()); } @Test public void test_module_in_between() { configureCode("Styles.res", "let myDiv = 1"); configureCode("A.res", """ module Styles = { let myDiv = CssJs.style(. []) } module Layouts = {} @react.component let make = () => {
es.myDiv /> } """); RPsiModule e = (RPsiModule) myFixture.getElementAtCaret(); assertEquals("A.Styles", e.getQualifiedName()); } @Test public void test_not_resolve_alternate_name() { configureCode("A.res", ""); configureCode("B.res", """ module B1 = { include A } """); configureCode("C.res", """ module C1 = B.B1 """); RPsiModule e = (RPsiModule) myFixture.getElementAtCaret(); assertEquals("B.B1", e.getQualifiedName()); } @Test public void test_with_include() { configureCode("A.res", "module A1 = { module A2 = { module A3 = {} } }"); configureCode("B.res", """ module B1 = { include A } """); configureCode("C.res", """ module C1 = B.B1 module C2 = C1.A1.A2 module M = C2.A3 """); RPsiModule e = (RPsiModule) myFixture.getElementAtCaret(); assertEquals("A.A1.A2.A3", e.getQualifiedName()); } // https://github.com/giraud/reasonml-idea-plugin/issues/418 @Test public void test_with_globally_opened_module() { myFixture.configureByText(ORConstants.BS_CONFIG_FILENAME, toJson("{ 'name': 'foo', 'bsc-flags': ['-open Core'] }")); configureCode("Core.res", "module Console = { }"); configureCode("A.res", "Console.log()"); PsiElement e = myFixture.getElementAtCaret(); assertEquals("Core.Console", ((RPsiModule) e).getQualifiedName()); } // https://github.com/giraud/reasonml-idea-plugin/issues/426 @Test public void test_alias_resolution_same_file() { configureCode("Dummy.res", """ module A = { module B = { module C = { module D = { } } } } module Bbb = A.B module Ddd = Bbb.C.D """); PsiElement e = myFixture.getElementAtCaret(); assertEquals("Dummy.A.B.C", ((RPsiModule) e).getQualifiedName()); } @Test public void test_pervasives_modules() { configureCode("JsxDOMC.res", "type style"); configureCode("pervasives.res", "module JsxDOM = JsxDOMC"); configureCode("A.res", "module A1 = JsxDOM"); PsiElement e = myFixture.getElementAtCaret(); assertEquals("Pervasives.JsxDOM", ((RPsiModule) e).getQualifiedName()); } // https://github.com/giraud/reasonml-idea-plugin/issues/476 @Test public void test_GH_476_and_module() { configureCode("Dummy.res", """ module rec A: {} = { type t = B.b } and B: {type b} = { type b } """); PsiElement e = myFixture.getElementAtCaret(); assertEquals("Dummy.B", ((RPsiModule) e).getQualifiedName()); } } ================================================ FILE: src/test/java/com/reason/ide/search/reference/ResolveUpperElement_RML_Test.java ================================================ package com.reason.ide.search.reference; import com.intellij.psi.*; import com.reason.comp.*; import com.reason.ide.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import org.junit.*; public class ResolveUpperElement_RML_Test extends ORBasePlatformTestCase { @Test public void test_basic_file() { configureCode("Dimensions.re", "let space = 5;"); configureCode("Comp.re", """ Dimensions module Dimensions = {}; """); PsiElement e = myFixture.getElementAtCaret(); assertEquals("Dimensions.re", ((PsiQualifiedNamedElement) e).getName()); } @Test public void test_inner_module() { configureCode("A.re", """ module Dimensions = {}; Dimensions """); PsiQualifiedNamedElement e = (PsiQualifiedNamedElement) myFixture.getElementAtCaret(); assertEquals("A.Dimensions", e.getQualifiedName()); } @Test public void test_interface_implementation() { configureCode("A.rei", "type t;"); configureCode("A.re", "type t;"); configureCode("B.re", "A"); PsiNamedElement e = (PsiNamedElement) myFixture.getElementAtCaret(); assertEquals("A.rei", e.getName()); } @Test public void test_let_alias() { configureCode("Dimensions.re", "let space = 5;"); configureCode("Comp.re", "let s = Dimensions.space"); PsiNamedElement e = (PsiNamedElement) myFixture.getElementAtCaret(); assertEquals("Dimensions.re", e.getName()); } @Test public void test_alias() { configureCode("A1.re", "module A11 = {};"); configureCode("A2.re", "module A21 = {};"); configureCode("A.re", "module A1 = A2;"); configureCode("B.re", "module X = A; X.A1;"); PsiElement e = myFixture.getElementAtCaret(); assertEquals("A.A1", ((RPsiModule) e).getQualifiedName()); } @Test public void test_alias_path_no_resolution() { configureCode("A.re", "module X = { module Y = { let z = 1; }; };"); configureCode("B.re", "module C = A.X; C.Y"); RPsiModule e = (RPsiModule) myFixture.getElementAtCaret(); assertEquals("B.C", e.getQualifiedName()); } @Test public void test_alias_path_resolution() { configureCode("A.re", "module X = { module Y = { let z = 1; }; };"); configureCode("B.re", "module C = A.X; C.Y"); RPsiModule e = (RPsiModule) myFixture.getElementAtCaret(); assertEquals("A.X.Y", e.getQualifiedName()); } @Test public void test_local_alias() { configureCode("Belt.re", "let x = 1;"); configureCode("A.re", "module B = Belt; B"); RPsiModule e = (RPsiModule) myFixture.getElementAtCaret(); assertEquals("A.B", e.getQualifiedName()); } @Test public void test_alias_interface() { configureCode("C.rei", "module A1 = {};"); configureCode("D.re", "module X = C; X.A1;"); RPsiModule e = (RPsiModule) myFixture.getElementAtCaret(); assertEquals("C.A1", e.getQualifiedName()); } @Test public void test_alias_same() { configureCode("A.re", ""); configureCode("B.re", "module A = A"); RPsiModule e = (RPsiModule) myFixture.getElementAtCaret(); assertEquals("A", e.getQualifiedName()); assertEquals("A.re", e.getContainingFile().getName()); } @Test public void test_include_alias() { configureCode("Css_AtomicTypes.rei", "module Color = { type t; };"); configureCode("Css_Core.rei", "module Types = Css_AtomicTypes;"); configureCode("Css.re", "include Css_Core;"); configureCode("A.re", "let t = Css.Types.Color.t"); RPsiModule e = (RPsiModule) myFixture.getElementAtCaret(); assertEquals("Css_AtomicTypes.Color", e.getQualifiedName()); } @Test public void test_function_call() { configureCode("AsyncHooks.re", "module XhrAsync = { let make = () => (); };"); configureCode("A.re", "let _ = AsyncHooks.useCancellableRequest(AsyncHooks.XhrAsync.make);"); PsiElement e = myFixture.getElementAtCaret(); assertEquals("AsyncHooks", ((RPsiQualifiedPathElement) e).getQualifiedName()); } @Test public void test_open() { configureCode("Belt.re", "module Option = {}"); configureCode("Dummy.re", "open Belt.Option;"); PsiElement e = myFixture.getElementAtCaret(); assertEquals("Belt.Option", ((RPsiModule) e).getQualifiedName()); assertEquals("Belt.re", e.getContainingFile().getName()); } @Test public void test_path_from_include() { configureCode("Css_Core.rei", "let display: string => rule"); configureCode("Css.re", "include Css_Core; include Css_Core.Make({})"); PsiQualifiedNamedElement e = (PsiQualifiedNamedElement) myFixture.getElementAtCaret(); assertEquals("Css_Core", e.getQualifiedName()); } //region Variants @Test public void test_local_variant() { configureCode("A.re", "type a = | Variant; let _ = Variant"); PsiElement e = myFixture.getElementAtCaret(); assertEquals("A.Variant", ((RPsiVariantDeclaration) e).getQualifiedName()); } @Test public void test_variant_with_path() { configureCode("A.re", "type a = | Variant;"); configureCode("B.re", "type b = | Variant;"); configureCode("C.re", "A.Variant"); RPsiVariantDeclaration e = (RPsiVariantDeclaration) myFixture.getElementAtCaret(); assertEquals("A.Variant", e.getQualifiedName()); } @Test public void test_variant_module_alias() { configureCode("Aaa.re", "type t = | Test;"); configureCode("Bbb.re", "module A = Aaa; A.Test"); RPsiVariantDeclaration e = (RPsiVariantDeclaration) myFixture.getElementAtCaret(); assertEquals("Aaa.Test", e.getQualifiedName()); } @Test public void test_variant_module_alias_inner() { configureCode("Aaa.re", "module Option = { type t = | Test; }"); configureCode("Bbb.re", "module A = Aaa; A.Option.Test"); RPsiVariantDeclaration e = (RPsiVariantDeclaration) myFixture.getElementAtCaret(); assertEquals("Aaa.Option.Test", e.getQualifiedName()); } @Test public void test_variant_constructor() { configureCode("A.re", "type a = | Variant(int);"); configureCode("B.re", "let _ = A.Variant(1)"); RPsiVariantDeclaration e = (RPsiVariantDeclaration) myFixture.getElementAtCaret(); assertEquals("A.Variant", e.getQualifiedName()); } //endregion //region Poly-variants @Test public void test_local_poly_variant() { configureCode("A.re", "type a = [ | `Variant ]; let _ = `Variant"); PsiElement e = myFixture.getElementAtCaret(); assertEquals("A.#Variant", ((RPsiVariantDeclaration) e).getQualifiedName()); } @Test public void test_poly_variant_with_path() { configureCode("A.re", "type a = [ | `Variant ];"); configureCode("B.re", "type b = [ | `Variant ];"); configureCode("C.re", "A.`Variant"); RPsiVariantDeclaration e = (RPsiVariantDeclaration) myFixture.getElementAtCaret(); assertEquals("A.#Variant", e.getQualifiedName()); } @Test public void test_poly_variant_module_alias() { configureCode("Aaa.re", "type t = [ | `Test ];"); configureCode("Bbb.re", "module A = Aaa; A.`Test"); RPsiVariantDeclaration e = (RPsiVariantDeclaration) myFixture.getElementAtCaret(); assertEquals("Aaa.#Test", e.getQualifiedName()); } @Test public void test_poly_variant_module_alias_inner() { configureCode("Aaa.re", "module Option = { type t = [ | `Test ]; };"); configureCode("Bbb.re", "module A = Aaa; A.Option.`Test"); RPsiVariantDeclaration e = (RPsiVariantDeclaration) myFixture.getElementAtCaret(); assertEquals("Aaa.Option.#Test", e.getQualifiedName()); } @Test public void test_poly_variant_constructor() { configureCode("A.re", "type a = | `Variant(int);"); configureCode("B.re", "let _ = A.`Variant(1);"); RPsiVariantDeclaration e = (RPsiVariantDeclaration) myFixture.getElementAtCaret(); assertEquals("A.#Variant", e.getQualifiedName()); } //endregion @Test public void test_exception() { myFixture.configureByText("A.re", "exception ExceptionName; raise(ExceptionName);"); RPsiException e = (RPsiException) myFixture.getElementAtCaret(); assertEquals("A.ExceptionName", e.getQualifiedName()); } @Test public void test_exception_with_path() { myFixture.configureByText("A.re", "exception ExceptionName;"); myFixture.configureByText("B.re", "exception ExceptionName; raise(A.ExceptionName);"); RPsiException e = (RPsiException) myFixture.getElementAtCaret(); assertEquals("A.ExceptionName", e.getQualifiedName()); } @Test public void test_belt_alias() { configureCode("String.re", "type t;"); configureCode("Belt_MapString.re", "type t;"); configureCode("Belt_Map.re", "module String = Belt_MapString;"); configureCode("Belt.re", "module Map = Belt_Map;"); configureCode("A.re", "Belt.Map.String"); RPsiModule e = (RPsiModule) myFixture.getElementAtCaret(); assertEquals("Belt_Map.String", e.getQualifiedName()); } @Test public void test_open_belt_alias() { configureCode("String.re", "type t;"); configureCode("Belt_MapString.re", "type t;"); configureCode("Belt_Map.re", "module String = Belt_MapString;"); configureCode("Belt.re", "module Map = Belt_Map;"); configureCode("A.re", "open Belt; open Map; String"); RPsiQualifiedPathElement e = (RPsiQualifiedPathElement) myFixture.getElementAtCaret(); assertEquals("Belt_Map.String", e.getQualifiedName()); } @Test public void test_pipe_first() { configureCode("X.re", "let fn = () => ();"); configureCode("B.re", "module C = { module X = { let fn = () => (); }; };"); configureCode("D.re", "B.C.X.fn()->X.fn"); PsiQualifiedNamedElement e = (PsiQualifiedNamedElement) myFixture.getElementAtCaret(); assertEquals("X", e.getQualifiedName()); } @Test public void test_pipe_last() { configureCode("A.re", "module X = {};"); configureCode("B.re", "module C = { module X = { let fn = () => (); }; };"); configureCode("D.re", "B.C.X.fn() |> A.X.fn"); PsiQualifiedNamedElement e = (PsiQualifiedNamedElement) myFixture.getElementAtCaret(); assertEquals("A.X", e.getQualifiedName()); } @Test public void test_no_resolution_1() { configureCode("Belt_MapString.mli", "val get: 'v t -> key -> 'v option"); configureCode("Belt_Map.ml", "module String = Belt_MapString;"); configureCode("Belt_Option.mli", "val flatMap : 'a option -> ('a -> 'b option) -> 'b option"); configureCode("Belt.re", "module Option = Belt_Option; module Map = Belt_Map;"); configureCode("A.re", "let x = (dict, locale) => locale->Belt.Option.flatMap(dict->Belt.Map.String.get);"); RPsiModule e = (RPsiModule) myFixture.getElementAtCaret(); assertEquals("Belt.Option", e.getQualifiedName()); } @Test public void test_no_resolution_2() { configureCode("Belt_MapString.mli", "val get: 'v t -> key -> 'v option"); configureCode("Belt_Map.ml", "module String = Belt_MapString;"); configureCode("Belt_Option.mli", "val flatMap : 'a option -> ('a -> 'b option) -> 'b option"); configureCode("Belt.re", "module Option = Belt_Option; module Map = Belt_Map;"); configureCode("A.re", "let x = (dict, locale) => locale->Belt.Option.flatMap(dict->Belt.Map.String.get);"); RPsiModule e = (RPsiModule) myFixture.getElementAtCaret(); assertEquals("Belt.Map", e.getQualifiedName()); } @Test public void test_functor_inside() { configureCode("F.re", """ module type S = {module X: {};}; module M = () : S => { module X = {}; }; module A = M({}); module X2 = { module X1 = { module X = {}; }; }; module V = A.X """); RPsiModule e = (RPsiModule) myFixture.getElementAtCaret(); assertEquals("F.M.X", e.getQualifiedName()); } @Test public void test_functor_outside() { configureCode("F.re", """ module type S = {module X: {};}; module M = () : S => { module X = {}; }; module A = M({}); """); configureCode("B.re", "module X2 = { module X1 = { module X = {}; }; }; module V = F.A.X"); RPsiModule e = (RPsiModule) myFixture.getElementAtCaret(); assertEquals("F.M.X", e.getQualifiedName()); } @Test public void test_with_tag() { configureCode("Form.re", "module Styles = {};"); configureCode("A.re", "module Styles = {}; let _ =
/>"); RPsiModule e = (RPsiModule) myFixture.getElementAtCaret(); assertEquals("A.Styles", e.getQualifiedName()); } @Test public void test_with_tag_1() { configureCode("Form.re", "module Styles = {};"); configureCode("A.re", "module Styles = {}; let _ =
/>"); RPsiModule e = (RPsiModule) myFixture.getElementAtCaret(); assertEquals("A.Styles", e.getQualifiedName()); } @Test public void test_module_signature() { configureCode("A.re", """ module B = { module C = { module type S = {}; }; module D = C; }; module M: B.D.S = {}; """); RPsiModule e = (RPsiModule) myFixture.getElementAtCaret(); assertEquals("A.B.C.S", e.getQualifiedName()); } @Test public void test_module_signature_with_open() { configureCode("A.re", """ module B = { module C = { module type S = {}; }; module D = C; }; open B; open D; module M: S = {}; """); RPsiQualifiedPathElement e = (RPsiQualifiedPathElement) myFixture.getElementAtCaret(); assertEquals("A.B.C.S", e.getQualifiedName()); } @Test(expected = AssertionError.class) public void test_module_signature_incorrect() { configureCode("A.re", """ module B = { module type Intf = {}; }; module IncorrectImpl : Intf = {}; """); myFixture.getElementAtCaret(); // not found -> AssertionError } @Test public void test_local_open() { configureCode("AsyncHooks.re", "module XhrAsync = {};"); configureCode("DashboardReducers.re", "type t = | IncrementVersion;"); configureCode("A.re", "let _ = AsyncHooks.XhrAsync.(dispatch(. DashboardReducers.IncrementVersion));"); PsiElement e = myFixture.getElementAtCaret(); assertEquals("DashboardReducers", ((RPsiQualifiedPathElement) e).getQualifiedName()); } @Test public void test_not_resolve_alternate_name() { configureCode("A.re", ""); configureCode("B.re", """ module B1 = { include A; }; """); configureCode("C.re", """ module C1 = B.B1 """); RPsiModule e = (RPsiModule) myFixture.getElementAtCaret(); assertEquals("B.B1", e.getQualifiedName()); } @Test public void test_with_include() { configureCode("A.re", "module A1 = { module A2 = { module A3 = {}; }; };"); configureCode("B.re", """ module B1 = { include A; }; """); configureCode("C.re", """ module C1 = B.B1; module C2 = C1.A1.A2; module M = C2.A3 """); RPsiModule e = (RPsiModule) myFixture.getElementAtCaret(); assertEquals("A.A1.A2.A3", e.getQualifiedName()); } // https://github.com/giraud/reasonml-idea-plugin/issues/418 @Test public void test_with_globally_opened_module() { myFixture.configureByText(ORConstants.BS_CONFIG_FILENAME, toJson("{ 'name': 'foo', 'bsc-flags': ['-open Core'] }")); configureCode("Core.re", "module Console = { };"); configureCode("A.re", "Console.log()"); PsiElement e = myFixture.getElementAtCaret(); assertEquals("Core.Console", ((RPsiModule) e).getQualifiedName()); } // https://github.com/giraud/reasonml-idea-plugin/issues/426 @Test public void test_alias_resolution_same_file() { configureCode("Dummy.re", """ module A = { module B = { module C = { module D = {}; }; }; }; module Bbb = A.B; module Ddd = Bbb.C.D; """); PsiElement e = myFixture.getElementAtCaret(); assertEquals("Dummy.A.B.C", ((RPsiModule) e).getQualifiedName()); } @Test public void test_pervasives_modules() { configureCode("JsxDOMC.re", "type style;"); configureCode("pervasives.re", "module JsxDOM = JsxDOMC;"); configureCode("A.re", "module A1 = JsxDOM"); PsiElement e = myFixture.getElementAtCaret(); assertEquals("Pervasives.JsxDOM", ((RPsiModule) e).getQualifiedName()); } // https://github.com/giraud/reasonml-idea-plugin/issues/476 @Test public void test_GH_476_and_module() { configureCode("Dummy.re", """ module rec A: {} = { type t = B.b; } and B: {type b;} = { type b; }; """); PsiElement e = myFixture.getElementAtCaret(); assertEquals("Dummy.B", ((RPsiModule) e).getQualifiedName()); } } ================================================ FILE: src/test/java/com/reason/ide/structure/ProjectStructure_DUNE_Test.java ================================================ package com.reason.ide.structure; import com.intellij.ide.structureView.*; import com.intellij.ide.util.treeView.smartTree.*; import com.reason.ide.*; import com.reason.ide.files.*; import org.junit.*; public class ProjectStructure_DUNE_Test extends ORBasePlatformTestCase { @Test public void test_stanza() { DuneFile a = (DuneFile) myFixture.configureByText("dune-project", "(lang dune 2.9) (licence MIT)"); StructureViewModel model = new ORStructureViewModel(a); TreeElement[] children = model.getRoot().getChildren(); assertEquals("lang", children[0].getPresentation().getPresentableText()); assertEquals("licence", children[1].getPresentation().getPresentableText()); assertSize(2, children); } @Test public void test_fields() { DuneFile a = (DuneFile) myFixture.configureByText("dune-project", "(package (name xxx) (github aaa/bbb))"); StructureViewModel model = new ORStructureViewModel(a); TreeElement pkg = model.getRoot().getChildren()[0]; TreeElement[] children = pkg.getChildren(); assertEquals("name", children[0].getPresentation().getPresentableText()); assertEquals("github", children[1].getPresentation().getPresentableText()); assertSize(2, children); } } ================================================ FILE: src/test/java/com/reason/ide/structure/ProjectStructure_OCL_Test.java ================================================ package com.reason.ide.structure; import com.intellij.ide.structureView.*; import com.intellij.ide.util.treeView.smartTree.*; import com.intellij.navigation.*; import com.reason.ide.*; import com.reason.ide.files.*; import org.jetbrains.annotations.*; import org.junit.*; import javax.swing.*; public class ProjectStructure_OCL_Test extends ORBasePlatformTestCase { @Test public void test_let() { FileBase a = configureCode("A.ml", "let x = 1"); StructureViewModel model = new ORStructureViewModel(a); TreeElement x = model.getRoot().getChildren()[0]; ItemPresentation pres = x.getPresentation(); assertEquals("x", pres.getPresentableText()); } // https://github.com/giraud/reasonml-idea-plugin/issues/439 @Test public void test_let_underscore() { FileBase a = configureCode("A.ml", "let _ = ()"); StructureViewModel model = new ORStructureViewModel(a); assertEmpty(model.getRoot().getChildren()); } @Test public void test_deconstruction() { FileBase a = configureCode("A.ml", "let (a, b) = x"); StructureViewModel model = new ORStructureViewModel(a); TreeElement[] children = model.getRoot().getChildren(); assertSize(2, children); assertPresentation("a", "", ORIcons.LET, children[0].getPresentation()); assertPresentation("b", "", ORIcons.LET, children[1].getPresentation()); } @Test public void test_val() { FileBase a = configureCode("A.mli", "val x: int"); StructureViewModel model = new ORStructureViewModel(a); TreeElement x = model.getRoot().getChildren()[0]; assertPresentation("x", "int", ORIcons.VAL, x.getPresentation()); } @Test public void test_type_record() { FileBase a = configureCode("A.ml", "type x = { a: int; mutable b: string list }"); StructureViewModel model = new ORStructureViewModel(a); TreeElement e = model.getRoot().getChildren()[0]; assertPresentation("x", null, ORIcons.TYPE, e.getPresentation()); TreeElement c1 = e.getChildren()[0]; assertPresentation("a", "int", ORIcons.VAL, c1.getPresentation()); TreeElement c2 = e.getChildren()[1]; assertPresentation("b", "string list", ORIcons.VAL, c2.getPresentation()); } @Test public void test_module_type() { FileBase a = configureCode("A.ml", "module type I = sig val x : bool end"); StructureViewModel model = new ORStructureViewModel(a); TreeElement i = model.getRoot().getChildren()[0]; assertPresentation("I", "A.I", ORIcons.MODULE_TYPE, i.getPresentation()); TreeElement x = i.getChildren()[0]; assertPresentation("x", "bool", ORIcons.VAL, x.getPresentation()); } @Test public void test_module_type_extraction() { configureCode("A.mli", "module type S = sig\n end"); FileBase b = configureCode("B.ml", "module X : module type of A.S"); StructureViewModel model = new ORStructureViewModel(b); TreeElement e = model.getRoot().getChildren()[0]; assertPresentation("X", "B.X", ORIcons.INNER_MODULE, e.getPresentation()); TreeElement ee = e.getChildren()[0]; assertPresentation("S", "", ORIcons.MODULE_TYPE, ee.getPresentation()); } @Test public void test_module_type_extraction_functor() { configureCode("A.mli", "module type S = sig\n module Branch : sig type t end\n end\n module Make() : S\n module Vcs = Make()"); FileBase b = configureCode("B.ml", "module X : module type of A.Vcs.Branch"); StructureViewModel model = new ORStructureViewModel(b); TreeElement e = model.getRoot().getChildren()[0]; assertPresentation("X", "B.X", ORIcons.INNER_MODULE, e.getPresentation()); TreeElement ee = e.getChildren()[0]; assertPresentation("Branch", "", ORIcons.MODULE_TYPE, ee.getPresentation()); } // https://github.com/giraud/reasonml-idea-plugin/issues/274 // omit () in structure panel @Test public void test_GH_274() { FileBase a = configureCode("A.ml", "let () = 1 + 2"); StructureViewModel model = new ORStructureViewModel(a); assertEmpty(model.getRoot().getChildren()); } // https://github.com/giraud/reasonml-idea-plugin/issues/190 // nested functions @Test public void test_GH_190() { FileBase e = configureCode("A.ml", "let fn a b = let open Pp in let fn1 = 1 in let fn2 = 2"); StructureViewModel model = new ORStructureViewModel(e); TreeElement fn = model.getRoot().getChildren()[0]; assertPresentation("fn", null, ORIcons.FUNCTION, fn.getPresentation()); TreeElement fn1 = fn.getChildren()[0]; assertPresentation("fn1", null, ORIcons.LET, fn1.getPresentation()); TreeElement fn2 = fn.getChildren()[1]; assertPresentation("fn2", null, ORIcons.LET, fn2.getPresentation()); } // https://github.com/giraud/reasonml-idea-plugin/issues/407 @Test public void test_GH_407() { FileBase e = configureCode("A.ml", "let (!!) r = !r"); StructureViewModel model = new ORStructureViewModel(e); TreeElement fn = model.getRoot().getChildren()[0]; assertPresentation("(!!)", null, ORIcons.LET, fn.getPresentation()); } // https://github.com/giraud/reasonml-idea-plugin/issues/408 @Test public void test_GH_408() { FileBase e = configureCode("A.ml", """ let a = let x = 0 in x """); StructureViewModel model = new ORStructureViewModel(e); TreeElement fn = model.getRoot().getChildren()[0]; assertPresentation("a", null, ORIcons.LET, fn.getPresentation()); TreeElement fn1 = fn.getChildren()[0]; assertPresentation("x", null, ORIcons.LET, fn1.getPresentation()); } // https://github.com/giraud/reasonml-idea-plugin/issues/429 @Test public void test_GH_429() { FileBase e = configureCode("A.mli", """ module X : sig type t end """); StructureViewModel model = new ORStructureViewModel(e); TreeElement x = model.getRoot().getChildren()[0]; assertPresentation("X", "A.X", ORIcons.INNER_MODULE_INTF, x.getPresentation()); TreeElement xt = x.getChildren()[0]; assertPresentation("t", null, ORIcons.TYPE, xt.getPresentation()); } // https://github.com/giraud/reasonml-idea-plugin/issues/490 @Test public void test_GH_490() { FileBase e = configureCode("A.ml", """ let o = object(self) val mutable v1 = 1 method m1 = () => () end """); StructureViewModel model = new ORStructureViewModel(e); StructureViewElement.StructureObjectView o = (StructureViewElement.StructureObjectView) model.getRoot().getChildren()[0]; assertPresentation("o", null, ORIcons.CLASS, o.getPresentation()); } // https://github.com/giraud/reasonml-idea-plugin/issues/490 @Test public void test_GH_490_factory() { FileBase e = configureCode("A.ml", """ let segment_model (doc : sentence Doc.document) : Wg_Segment.model = object (self) val mutable cbs = [] end """); StructureViewModel model = new ORStructureViewModel(e); StructureViewElement.StructureObjectView o = (StructureViewElement.StructureObjectView) model.getRoot().getChildren()[0]; assertPresentation("segment_model", null, ORIcons.CLASS, o.getPresentation()); } private void assertPresentation(String name, String location, @Nullable Icon icon, @NotNull ItemPresentation pres) { assertEquals("Incorrect name", name, pres.getPresentableText()); assertEquals("Incorrect location", location, pres.getLocationString()); assertEquals("Incorrect icon", icon, pres.getIcon(false)); } } ================================================ FILE: src/test/java/com/reason/ide/structure/ValPresentation_OCL_Test.java ================================================ package com.reason.ide.structure; import com.reason.ide.*; import com.reason.lang.core.psi.*; import org.junit.*; import org.junit.runner.*; import org.junit.runners.*; @SuppressWarnings("ConstantConditions") @RunWith(JUnit4.class) public class ValPresentation_OCL_Test extends ORBasePlatformTestCase { @Test public void test_noSig() { RPsiVal e = configureCode("A.ml", "val x = 1").getQualifiedExpressions("A.x", RPsiVal.class).get(0); assertEquals("x", e.getPresentation().getPresentableText()); assertNull(e.getPresentation().getLocationString()); } @Test public void test_sig() { RPsiVal e = configureCode("A.mli", "val x : 'a -> 'a t").getQualifiedExpressions("A.x", RPsiVal.class).get(0); assertEquals("x", e.getPresentation().getPresentableText()); assertEquals("'a -> 'a t", e.getPresentation().getLocationString()); } } ================================================ FILE: src/test/java/com/reason/lang/BaseParsingTestCase.java ================================================ package com.reason.lang; import com.intellij.lang.*; import com.intellij.psi.*; import com.intellij.psi.impl.*; import com.intellij.psi.tree.*; import com.intellij.psi.util.*; import com.intellij.testFramework.*; import com.reason.ide.files.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import org.jetbrains.annotations.*; import org.junit.runner.*; import org.junit.runners.*; import java.io.*; import java.util.*; import java.util.stream.*; import static com.intellij.psi.util.PsiTreeUtil.*; @RunWith(JUnit4.class) public abstract class BaseParsingTestCase extends ParsingTestCase { protected BaseParsingTestCase(@NotNull String dataPath, @NotNull String fileExt, @NotNull ParserDefinition... definitions) { super(dataPath, fileExt, definitions); } @Override protected @NotNull String getTestDataPath() { return "testData"; } @NotNull protected List expressions(@NotNull PsiFile file) { return ORUtil.findImmediateChildrenOfClass(file, PsiNamedElement.class); } @NotNull protected List typeExpressions(@NotNull PsiFile file) { return new ArrayList<>(PsiTreeUtil.findChildrenOfType(file, RPsiType.class)); } @NotNull protected List moduleExpressions(@NotNull PsiFile file) { return getStubChildrenOfTypeAsList(file, RPsiInnerModule.class); } @NotNull protected Collection functorExpressions(@NotNull PsiFile file) { return getStubChildrenOfTypeAsList(file, RPsiFunctor.class); } @NotNull protected List letAllExpressions(@NotNull PsiFile file) { return new ArrayList<>(PsiTreeUtil.findChildrenOfType(file, RPsiLet.class)); } public static int childrenCount(@NotNull FileBase file) { return file.getChildren().length; } public static T first(@NotNull Collection collection) { return collection.iterator().next(); } protected T second(@NotNull Collection collection) { Iterator iterator = collection.iterator(); iterator.next(); return iterator.next(); } protected T firstOfType(PsiElement element, @NotNull Class aClass) { return first(findChildrenOfType(element, aClass)); } protected List childrenOfType(PsiElement element, @NotNull Class aClass) { return new ArrayList<>(findChildrenOfType(element, aClass)); } @NotNull protected List extractUpperSymbolTypes(PsiElement e) { Collection symbols = findChildrenOfType(e, RPsiUpperSymbol.class); return symbols .stream() .map(psi -> psi.getNode().getElementType()) .collect(Collectors.toList()); } protected void assertNoParserError(PsiElement e) { assertNull(PsiTreeUtil.findChildOfType(e, PsiErrorElement.class)); } @NotNull protected PsiFile parseFile(String name) throws IOException { String text = loadFile(name + "." + myFileExt); return parseRawCode(text); } @NotNull protected FileBase parseCode(@NotNull String code) { parseRawCode(code); assertNoParserError(myFile); return (FileBase) myFile; } protected PsiFile parseRawCode(@NotNull String code) { myFile = createPsiFile("dummy", code); System.out.println("» " + this.getClass()); System.out.println(DebugUtil.psiToString(myFile, false, true)); return myFile; } @NotNull protected DuneFile parseDuneCode(@NotNull String code) { myFile = createFile("jbuild", code); System.out.println("» " + this.getClass()); System.out.println(DebugUtil.psiToString(myFile, false, true)); assertNoParserError(myFile); return (DuneFile) myFile; } @SuppressWarnings("unused") void debugPsiAst(@NotNull PsiElement element) { System.out.println(DebugUtil.psiToString(element, false, true)); } } ================================================ FILE: src/test/java/com/reason/lang/core/FileBaseTest.java ================================================ package com.reason.lang.core; import com.reason.ide.ORBasePlatformTestCase; import com.reason.ide.files.FileBase; import com.reason.lang.core.psi.RPsiLet; import org.junit.*; import java.util.*; public class FileBaseTest extends ORBasePlatformTestCase { @Test public void test_Rml_getQNameExpression() { FileBase f = configureCode("A.re", "module B = { let x = 1; }; let x = 2;"); List e = f.getQualifiedExpressions("A.B.x", RPsiLet.class); assertSize(1, e); assertInstanceOf(e.iterator().next(), RPsiLet.class); } } ================================================ FILE: src/test/java/com/reason/lang/core/ORSignatureTest.java ================================================ package com.reason.lang.core; import com.intellij.lang.*; import com.intellij.psi.*; import com.intellij.psi.impl.*; import com.intellij.psi.util.*; import com.intellij.testFramework.*; import com.reason.lang.core.psi.*; import com.reason.lang.ocaml.*; import com.reason.lang.reason.*; import com.reason.lang.rescript.*; import org.jetbrains.annotations.*; import org.junit.*; import org.junit.runner.*; import org.junit.runners.*; @RunWith(JUnit4.class) public class ORSignatureTest extends LightJavaCodeInsightTestCase { private static final ResLanguage NS = ResLanguage.INSTANCE; private static final RmlLanguage RML = RmlLanguage.INSTANCE; private static final OclLanguage OCL = OclLanguage.INSTANCE; @Test public void testReasonSingleFun() { RPsiSignature sig = makeSignature(RML, "unit=>unit"); assertEquals("unit=>unit", sig.asText(RML)); assertEquals("unit -> unit", sig.asText(OCL)); } @Test public void testOCamlSingleFun() { RPsiSignature sig = makeSignature(OCL, "unit->unit"); assertEquals("unit => unit", sig.asText(RML)); assertEquals("unit->unit", sig.asText(OCL)); } @Test public void testReasonSingle() { RPsiSignature sig = makeSignature(RML, "unit"); assertEquals("unit", sig.asText(RML)); assertEquals("unit", sig.asText(OCL)); } @Test public void testOCamlSingle() { RPsiSignature sig = makeSignature(OCL, "unit"); assertEquals("unit", sig.asText(RML)); assertEquals("unit", sig.asText(OCL)); } @Test public void testReasonMultiFun() { RPsiSignature sig = makeSignature(RML, "unit => string => float => unit"); assertEquals("unit => string => float => unit", sig.asText(RML)); assertEquals("unit -> string -> float -> unit", sig.asText(OCL)); } @Test public void testOcamlMultiFun() { RPsiSignature sig = makeSignature(OCL, "unit -> string -> float -> unit"); assertEquals("(unit, string, float) => unit", sig.asText(RML)); assertEquals("unit -> string -> float -> unit", sig.asText(OCL)); } @Test public void testOCamlObject() { RPsiSignature sig = makeSignature(OCL, " -> string"); assertEquals(" -> string", sig.asText(OCL)); assertEquals("{. a:string } => string", sig.asText(RML)); } @Test public void testReasonJsObject() { RPsiSignature sig = makeSignature(RML, "{. a:string, b:int } => string"); assertEquals(" Js.t -> string", sig.asText(OCL)); assertEquals("{. a:string, b:int } => string", sig.asText(RML)); //assertEquals("{. a:string, b:int } => string", sig.asText(NS)); } @Test public void testOCamlJsObject() { RPsiSignature sig = makeSignature(OCL, " Js.t -> string"); assertEquals(" Js.t -> string", sig.asText(OCL)); assertEquals("{. a:string, b:int } => string", sig.asText(RML)); } @Test public void testOcamJsJsObject() { RPsiSignature sig = makeSignature(OCL, "string -> < a : string; b : < b1 : string; b2 : string > Js.t > Js.t"); assertEquals("string -> < a : string; b : < b1 : string; b2 : string > Js.t > Js.t", sig.asText(OCL)); assertEquals("string => {. a:string, b:{. b1:string, b2:string } }", sig.asText(RML)); } @SuppressWarnings({"SameParameterValue", "ConstantConditions"}) private @NotNull RPsiSignature makeSignature(@NotNull Language lang, String sig) { PsiFileFactory instance = PsiFileFactory.getInstance(getProject()); PsiFile psiFile = instance.createFileFromText("Dummy." + lang.getAssociatedFileType().getDefaultExtension(), lang, "let x:" + sig); System.out.println(DebugUtil.psiToString(psiFile, false, true)); return PsiTreeUtil.findChildOfType(psiFile, RPsiSignature.class); } } ================================================ FILE: src/test/java/com/reason/lang/core/ORUtilTest.java ================================================ package com.reason.lang.core; import com.google.common.collect.*; import com.intellij.psi.util.*; import com.reason.ide.*; import com.reason.ide.files.*; import com.reason.lang.core.psi.*; import jpsplugin.com.reason.*; import org.junit.*; @SuppressWarnings("ConstantConditions") public class ORUtilTest extends ORBasePlatformTestCase { @Test public void testModuleNameToFileNameWhenEmpty() { assertEquals("", ORUtil.moduleNameToFileName("")); } @Test public void testModuleNameToFileName() { assertEquals("testLower", ORUtil.moduleNameToFileName("TestLower")); } @Test public void testFileNameToModuleNameWhenEmpty() { assertEquals("", ORUtil.fileNameToModuleName("")); assertEquals("", ORUtil.fileNameToModuleName(".ml")); } @Test public void testFileNameToModuleName() { assertEquals("Lower", ORUtil.fileNameToModuleName("lower.ml")); assertEquals("Upper", ORUtil.fileNameToModuleName("Upper.ml")); } @Test public void test_Rml_letQualifiedPath() { FileBase f = configureCode("A.re", "let make = () => { let x = 1; }"); RPsiLet e = ImmutableList.copyOf(PsiTreeUtil.findChildrenOfType(f, RPsiLet.class)).get(1); String qPath = Joiner.join(".", ORUtil.getQualifiedPath(e)); assertEquals("A.make", qPath); } @Test public void test_Rml_letDestructuredQualifiedPath() { FileBase f = configureCode("A.re", "module M = { let make = () => { let (x, y) = other; }; }"); RPsiLet letExpression = ImmutableList.copyOf(PsiTreeUtil.findChildrenOfType(f, RPsiLet.class)).get(1); String qualifiedPath = Joiner.join(".", ORUtil.getQualifiedPath(letExpression)); assertEquals("A.M.make", qualifiedPath); } @Test public void test_in_interface_file() { FileBase intf = configureCode("A.resi", "module M = { let x: int }"); FileBase impl = configureCode("A.res", "module M = { let x = 1 }"); assertTrue(ORUtil.inInterface(PsiTreeUtil.findChildOfType(intf, RPsiLet.class))); assertFalse(ORUtil.inInterface(PsiTreeUtil.findChildOfType(impl, RPsiLet.class))); } @Test public void test_in_interface_module_type() { FileBase f = configureCode("A.res", "module type M = { let x: 1 }"); assertTrue(ORUtil.inInterface(PsiTreeUtil.findChildOfType(f, RPsiLet.class))); } @Test public void test_in_interface_anonymous_module_type() { FileBase f = configureCode("A.res", "module M: { let x: int } = { let x = 1 }"); ImmutableList es = ImmutableList.copyOf(PsiTreeUtil.findChildrenOfType(f, RPsiLet.class)); assertTrue(ORUtil.inInterface(es.get(0))); assertFalse(ORUtil.inInterface(es.get(1))); } } ================================================ FILE: src/test/java/com/reason/lang/doc/ocaml/OclDocConverterTest.java ================================================ package com.reason.lang.doc.ocaml; import com.reason.ide.*; import org.junit.*; import static com.reason.ide.docs.ORDocumentationProvider.*; public class OclDocConverterTest extends ORBasePlatformTestCase { @Test public void test_detection() { assertTrue(isSpecialComment(configureCode("A.ml", "(** ok *)").getFirstChild())); assertTrue(isSpecialComment(configureCode("A.ml", "(**\n ok *)").getFirstChild())); assertFalse(isSpecialComment(configureCode("A.ml", "(**********)").getFirstChild())); } @Test public void test_basic() { String comment = "(** Hello doc*)"; OclDocConverter oDoc = new OclDocConverter(); assertEquals("

Hello doc

", oDoc.convert(null, comment).toString()); } @Test public void test_paragraphs() { String comment = "(** 1st paragraph\n multiline\n \n Another paragraph *)"; OclDocConverter oDoc = new OclDocConverter(); assertEquals("

1st paragraph multiline

Another paragraph

", oDoc.convert(null, comment).toString()); } @Test public void test_code() { String comment = "(** [% [flags] [width] [.precision] type] *)"; OclDocConverter oDoc = new OclDocConverter(); assertEquals("

% [flags] [width] [.precision] type

", oDoc.convert(null, comment).toString()); } @Test public void test_bold() { String comment = "(** {b See} other *)"; OclDocConverter oDoc = new OclDocConverter(); assertEquals("

See other

", oDoc.convert(null, comment).toString()); } @Test public void test_italic() { String comment = "(** {i See} other *)"; OclDocConverter oDoc = new OclDocConverter(); assertEquals("

See other

", oDoc.convert(null, comment).toString()); } @Test public void test_emphasis() { String comment = "(** {e See} other *)"; OclDocConverter oDoc = new OclDocConverter(); assertEquals("

See other

", oDoc.convert(null, comment).toString()); } @Test public void test_oList() { String comment = "(** {ol {- l1 {b l11}} {-l2}} *)"; OclDocConverter oDoc = new OclDocConverter(); assertEquals("

  1. l1 l11
  2. l2

", oDoc.convert(null, comment).toString()); } @Test public void test_uList() { String comment = "(** {ul {- l1} {-l2}} *)"; OclDocConverter oDoc = new OclDocConverter(); assertEquals("

  • l1
  • l2

", oDoc.convert(null, comment).toString()); } @Test public void test_section() { String comment = "(** \n" + " a paragraph\n" + " {3 Title}\n" + " another paragraph\n" + " *)"; OclDocConverter oDoc = new OclDocConverter(); assertEquals("

a paragraph

Title

another paragraph

", oDoc.convert(null, comment).toString()); } @Test public void test_pre() { String comment = "(** {[\n Test\n ]} *)"; OclDocConverter oDoc = new OclDocConverter(); assertEquals("

\n Test\n  

", oDoc.convert(null, comment).toString()); } @Test public void test_link() { String comment = "(** {{:http://unicode.org/glossary/#unicode_scalar_value}scalar\n value} other text *)"; OclDocConverter oDoc = new OclDocConverter(); assertEquals("

scalar value other text

", oDoc.convert(null, comment).toString()); } @Test public void test_tag() { String comment = "(** @author author1 author2 & author3 *)"; OclDocConverter oDoc = new OclDocConverter(); assertEquals("

Author:

author1 author2 & author3

", oDoc.convert(null, comment).toString()); } @Test public void test_tags() { String comment = "(**\n" + " @author author1 author2 & author3\n" + " @version 1\n" + " *)"; OclDocConverter oDoc = new OclDocConverter(); assertEquals("" + "" + "" + "

Author:

author1 author2 & author3

Version:

1

", oDoc.convert(null, comment).toString()); } @Test public void test_tag_pre() { String comment = "(**\n" + " @example {[\n" + " forEach Some (* comment *)\n" + " forEach None\n" + " ]}" + " *)"; OclDocConverter oDoc = new OclDocConverter(); assertEquals("" + "" + "" + "

Example:

\n    forEach Some (* comment *)\n    forEach None\n  

", oDoc.convert(null, comment).toString()); } } ================================================ FILE: src/test/java/com/reason/lang/doc/reason/RmlDocConverterTest.java ================================================ package com.reason.lang.doc.reason; import com.reason.ide.*; import org.junit.*; import static com.reason.ide.docs.ORDocumentationProvider.*; public class RmlDocConverterTest extends ORBasePlatformTestCase { @Test public void test_detection() { assertTrue(isSpecialComment(configureCode("A.re", "/** ok */").getFirstChild())); assertTrue(isSpecialComment(configureCode("A.re", "/**\n ok */").getFirstChild())); assertFalse(isSpecialComment(configureCode("A.re", "/**********/").getFirstChild())); } @Test public void test_basic() { String comment = "/** Hello doc*/"; RmlDocConverter rDoc = new RmlDocConverter(); assertEquals("

Hello doc

", rDoc.convert(null, comment).toString()); } @Test public void test_lines() { String comment = "/** Hello\n doc\n \n another\nline\n\nthird*/"; RmlDocConverter rDoc = new RmlDocConverter(); assertEquals("

Hello doc

another line

third

", rDoc.convert(null, comment).toString()); } @Test public void test_tags() { String comment = "/**\n Hello doc\n @param p1 multi\n line @param p2 desc2\n@return string */"; RmlDocConverter rDoc = new RmlDocConverter(); assertEquals("

Hello doc

" + "" + "" + "" + "

Param:

p1 - multi line

p2 - desc2

Return:

string

", rDoc.convert(null, comment).toString()); } } ================================================ FILE: src/test/java/com/reason/lang/dune/CommentParsingTest.java ================================================ package com.reason.lang.dune; import com.intellij.psi.*; import com.reason.ide.files.*; import org.junit.*; public class CommentParsingTest extends DuneParsingTestCase { @Test public void test_single_comment() { DuneFile e = parseDuneCode("; duplicate module names in the whole build."); assertInstanceOf(e.getFirstChild(), PsiComment.class); assertEquals("; duplicate module names in the whole build.", e.getFirstChild().getText()); } } ================================================ FILE: src/test/java/com/reason/lang/dune/DuneParsingTestCase.java ================================================ package com.reason.lang.dune; import com.intellij.lang.*; import com.intellij.psi.stubs.*; import com.reason.lang.*; import com.reason.lang.core.stub.*; abstract class DuneParsingTestCase extends BaseParsingTestCase { public DuneTypes myTypes = DuneTypes.INSTANCE; protected DuneParsingTestCase() { super("", "", new DuneParserDefinition()); } @Override protected void setUp() throws Exception { super.setUp(); StubElementTypeHolderEP stubElementTypeHolderEP = new StubElementTypeHolderEP(); stubElementTypeHolderEP.holderClass = ResStubBasedElementTypes.class.getName(); registerExtension(StubElementTypeHolderEP.EP_NAME, stubElementTypeHolderEP); LanguageASTFactory.INSTANCE.addExplicitExtension(DuneLanguage.INSTANCE, new DuneASTFactory()); } protected ORLanguageProperties getLangProps() { return ORLanguageProperties.cast(myLanguage); } } ================================================ FILE: src/test/java/com/reason/lang/dune/DuneStanzaParsingTest.java ================================================ package com.reason.lang.dune; import com.intellij.psi.*; import com.reason.ide.files.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.impl.*; import org.junit.*; import java.util.*; @SuppressWarnings("ConstantConditions") public class DuneStanzaParsingTest extends DuneParsingTestCase { @Test public void test_stanza() { DuneFile e = parseDuneCode("(library (name x)) (version 1)"); Collection stanzas = ORUtil.findImmediateChildrenOfClass(e, RPsiDuneStanza.class); assertSize(2, stanzas); assertEquals("library", e.getStanza("library").getName()); assertEquals("version", e.getStanza("version").getName()); } @Test public void test_stanza_fields() { RPsiDuneStanza e = parseDuneCode("(library (name x) (wrapped true))").getStanza("library"); assertEquals("library", e.getName()); assertSize(2, e.getFields()); assertNotNull(e.getField("name")); assertEquals("(name x)", first(e.getFields()).getText()); assertEquals("x", e.getField("name").getValue()); assertNotNull(e.getField("wrapped")); assertEquals("(wrapped true)", second(e.getFields()).getText()); assertEquals("true", e.getField("wrapped").getValue()); } @Test public void test_chain() { PsiFile e = parseRawCode("(library (name x)) (version 1)"); Collection stanzas = ORUtil.findImmediateChildrenOfClass(e, RPsiDuneStanza.class); assertSize(2, stanzas); } } ================================================ FILE: src/test/java/com/reason/lang/dune/DuneVarParsingTest.java ================================================ package com.reason.lang.dune; import com.reason.lang.core.psi.impl.*; import org.junit.*; public class DuneVarParsingTest extends DuneParsingTestCase { @Test public void test_basic() { RPsiDuneVar e = firstOfType(parseRawCode("%{x}"), RPsiDuneVar.class); assertEquals("%{x}", e.getText()); } } ================================================ FILE: src/test/java/com/reason/lang/dune/SExprParsingTest.java ================================================ package com.reason.lang.dune; import com.reason.lang.core.*; import com.reason.lang.core.psi.impl.*; import org.junit.*; @SuppressWarnings("ConstantConditions") public class SExprParsingTest extends DuneParsingTestCase { @Test public void test_s_expr() { RPsiDuneStanza e = firstOfType(parseDuneCode("( ( () ) )"), RPsiDuneStanza.class); assertNoParserError(e); RPsiDuneSExpr e1 = ORUtil.findImmediateFirstChildOfClass(e, RPsiDuneSExpr.class); assertEquals("( () )", e1.getText()); RPsiDuneSExpr e11 = ORUtil.findImmediateFirstChildOfClass(e1, RPsiDuneSExpr.class); assertEquals("()", e11.getText()); } } ================================================ FILE: src/test/java/com/reason/lang/ocaml/AndParsingTest.java ================================================ package com.reason.lang.ocaml; import com.intellij.psi.*; import com.intellij.psi.util.*; import com.reason.ide.files.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import org.junit.*; import java.util.*; @SuppressWarnings("ConstantConditions") public class AndParsingTest extends OclParsingTestCase { @Test public void test_let_chaining() { List lets = ORUtil.findImmediateChildrenOfClass(parseCode("let rec lx x = x + 1 and ly y = 3 + (lx y)"), RPsiLet.class); assertSize(2, lets); assertEquals("lx", lets.get(0).getName()); assertEquals("ly", lets.get(1).getName()); } @Test public void test_let_chaining_in_function() { List lets = ORUtil.findImmediateChildrenOfClass(parseCode(""" let fn x = let ax = Instance.to_array x and ay = Instance.to_array y in () """), RPsiLet.class); assertSize(1, lets); assertEquals("fn", lets.getFirst().getName()); } @Test public void test_module_chaining() { List es = childrenOfType(parseCode(""" module rec X : sig end = struct end and Y : sig end = struct end """), RPsiInnerModule.class); assertSize(2, es); assertEquals("X", es.get(0).getName()); assertEquals("Y", es.get(1).getName()); } @Test public void test_module_type_chaining() { PsiFile file = parseCode("module rec X : sig end and Y : sig end"); // in a .mli file List mods = new ArrayList<>(moduleExpressions(file)); assertSize(2, mods); assertEquals("X", mods.get(0).getName()); assertEquals("Y", mods.get(1).getName()); } @Test public void test_pattern_chaining() { PsiFile file = parseCode(""" match optSign with | Some sign -> let mtb1 = 1 and mtb2 = 2 """); Collection exps = expressions(file); assertInstanceOf(file.getFirstChild(), RPsiSwitch.class); assertEquals(0, exps.size()); RPsiPatternMatchBody body = PsiTreeUtil.findChildOfType(file, RPsiPatternMatchBody.class); assertEquals("let mtb1 = 1\n and mtb2 = 2", body.getText()); Collection lets = PsiTreeUtil.findChildrenOfType(body, RPsiLet.class); assertSize(2, lets); } @Test public void test_type_chaining() { Collection types = typeExpressions(parseCode(""" type update = | NoUpdate and 'state self = {state: 'state;} """)); assertSize(2, types); assertEquals("update", first(types).getName()); assertEquals("self", second(types).getName()); } @Test public void test_type_chaining_lIdent() { List types = childrenOfType(parseCode(""" type t = y (* test *) and y = string """), RPsiType.class); assertEquals(2, types.size()); assertEquals("t", first(types).getName()); assertEquals("y", second(types).getName()); } // https://github.com/giraud/reasonml-idea-plugin/issues/135 @Test public void test_GH_135() { List lets = ORUtil.findImmediateChildrenOfClass(parseCode(""" let f1 = function | _ -> () and missing = () """), RPsiLet.class); assertSize(2, lets); assertEquals("f1", lets.get(0).getName()); assertEquals("missing", lets.get(1).getName()); } // https://github.com/giraud/reasonml-idea-plugin/issues/175 @Test public void test_GH_175() { List lets = ORUtil.findImmediateChildrenOfClass(parseCode(""" let f1 = let f11 = function | _ -> "" in () and f2 = let f21 = function | _ -> "" in () and f3 = () """), RPsiLet.class); assertSize(3, lets); assertEquals("f1", lets.get(0).getName()); assertEquals("f2", lets.get(1).getName()); assertEquals("f3", lets.get(2).getName()); } // https://github.com/giraud/reasonml-idea-plugin/issues/271 @Test public void test_GH_271() { List lets = ORUtil.findImmediateChildrenOfClass(parseCode(""" let parser_of_token_list a = let loop x = () in () and parser_of_symbol b = () """), RPsiLet.class); assertSize(2, lets); assertEquals("parser_of_token_list", lets.get(0).getName()); assertEquals("parser_of_symbol", lets.get(1).getName()); } // https://github.com/giraud/reasonml-idea-plugin/issues/272 @Test public void test_GH_272() { FileBase file = parseCode(""" let x = match xx with | Y -> let fn y = 1 in () and z = 1 """); List exps = ORUtil.findImmediateChildrenOfClass(file, RPsiLet.class); assertEquals(2, exps.size()); assertEquals("x", exps.get(0).getName()); assertEquals("z", exps.get(1).getName()); RPsiPatternMatchBody body = PsiTreeUtil.findChildOfType(file, RPsiPatternMatchBody.class); assertEquals("let fn y = 1", PsiTreeUtil.findChildOfType(body, RPsiLet.class).getText()); } } ================================================ FILE: src/test/java/com/reason/lang/ocaml/AnnotationParsingTest.java ================================================ package com.reason.lang.ocaml; import com.reason.ide.files.FileBase; import com.reason.lang.core.ORUtil; import com.reason.lang.core.psi.impl.RPsiAnnotation; import com.reason.lang.core.psi.RPsiLet; import com.reason.lang.core.psi.impl.RPsiLetBinding; import org.junit.*; @SuppressWarnings("ConstantConditions") public class AnnotationParsingTest extends OclParsingTestCase { @Test public void test_algebraic_let() { RPsiLet e = firstOfType(parseCode("let find_reference = Coqlib.find_reference [@ocaml.warning \"-3\"]"), RPsiLet.class); RPsiAnnotation attribute = ORUtil.findImmediateFirstChildOfClass(e.getBinding(), RPsiAnnotation.class); assertEquals("[@ocaml.warning \"-3\"]", attribute.getText()); assertEquals("@ocaml.warning", attribute.getName()); } @Test public void test_block_let() { RPsiLet e = firstOfType(parseCode("let val_to_int (x:t) = (Obj.magic x : int) [@@ocaml.inline always]"), RPsiLet.class); RPsiLetBinding b = e.getBinding(); RPsiAnnotation attribute = ORUtil.findImmediateFirstChildOfClass(b, RPsiAnnotation.class); assertEquals("[@@ocaml.inline always]", attribute.getText()); assertEquals("@@ocaml.inline", attribute.getName()); } @Test public void test_floating_let() { FileBase f = parseCode("let prefix_small_string = 0x20\n [@@@ocaml.warning \"-32\"]"); RPsiLet e = firstOfType(f, RPsiLet.class); RPsiLetBinding b = e.getBinding(); assertEquals("0x20", b.getText()); RPsiAnnotation attribute = firstOfType(f, RPsiAnnotation.class); assertEquals("@@@ocaml.warning", attribute.getName()); } } ================================================ FILE: src/test/java/com/reason/lang/ocaml/AssertParsingTest.java ================================================ package com.reason.lang.ocaml; import com.reason.lang.core.psi.impl.*; import org.junit.*; public class AssertParsingTest extends OclParsingTestCase { @Test public void test_basic() { RPsiAssert assertExp = firstOfType(parseCode("assert (x > 2)"), RPsiAssert.class); assertNotNull(assertExp); assertNotNull(assertExp.getAssertion()); } } ================================================ FILE: src/test/java/com/reason/lang/ocaml/BeginParsingTest.java ================================================ package com.reason.lang.ocaml; import com.reason.lang.core.psi.*; import org.junit.*; @SuppressWarnings("ConstantConditions") public class BeginParsingTest extends OclParsingTestCase { @Test public void test_basic() { RPsiLet exp = firstOfType(parseCode("let _ = begin end"), RPsiLet.class); assertNotNull(exp); assertEquals("begin end", exp.getBinding().getText()); } } ================================================ FILE: src/test/java/com/reason/lang/ocaml/ChainingParsingTest.java ================================================ package com.reason.lang.ocaml; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import org.junit.*; import java.util.*; @SuppressWarnings("ConstantConditions") public class ChainingParsingTest extends OclParsingTestCase { // env.ml L33 @Test public void test_let_semi_colon() { RPsiLet e = firstOfType(parseCode("let fail s = Format.eprintf \"%s@\\n%!\" fail_msg; exit 1\n"), RPsiLet.class); assertTrue(e.isFunction()); RPsiFunction f = e.getFunction(); RPsiFunctionBody b = f.getBody(); List fc = ORUtil.findImmediateChildrenOfClass(b, RPsiFunctionCall.class); assertEquals("eprintf \"%s@\\n%!\" fail_msg", fc.get(0).getText()); assertEquals("exit 1", fc.get(1).getText()); } @Test public void test_while() { RPsiWhile e = firstOfType(parseCode("let _ = while true do printf \"File format: %ld\\n%!\" version; exit 1 done"), RPsiWhile.class); List fc = ORUtil.findImmediateChildrenOfClass(e.getBody(), RPsiFunctionCall.class); assertEquals("printf \"File format: %ld\\n%!\" version", fc.get(0).getText()); assertEquals("exit 1", fc.get(1).getText()); } } ================================================ FILE: src/test/java/com/reason/lang/ocaml/ClassParsingTest.java ================================================ package com.reason.lang.ocaml; import com.intellij.psi.*; import com.intellij.psi.util.*; import com.reason.ide.files.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import org.junit.*; import java.util.*; @SuppressWarnings("ConstantConditions") public class ClassParsingTest extends OclParsingTestCase { @Test public void test_basic() { RPsiClass e = firstOfType(parseCode("class foo = object end"), RPsiClass.class); assertEquals("foo", e.getName()); } @Test public void test_classType() { RPsiClass e = firstOfType(parseCode( "class type restricted_point_type = object method get_x : int method bump : unit end"), RPsiClass.class); assertEquals("restricted_point_type", e.getName()); } @Test public void test_fields() { RPsiClass e = firstOfType(parseCode("class foo = object val mutable a = [] val b = 2 end"), RPsiClass.class); assertSize(2, e.getFields()); } @Test public void test_methods() { RPsiClass e = firstOfType(parseCode("class foo = object method get_x = x method get_y = y end"), RPsiClass.class); assertSize(2, e.getMethods()); } @Test public void test_both() { RPsiClass e = firstOfType(parseCode("class foo = object val mutable x = [] method get_x = x end"), RPsiClass.class); assertSize(1, e.getFields()); assertSize(1, e.getMethods()); } @Test public void test_class_constructor_constraint() { RPsiClass e = firstOfType(parseCode( "class ['a] circle (c : 'a) = object constraint 'a = #point val mutable center = c method set_center c = center <- c method move = center#move end"), RPsiClass.class); assertEquals("circle", e.getName()); assertNotNull(e.getParameters()); assertNotNull(e.getConstructor()); assertSize(1, e.getFields()); assertSize(2, e.getMethods()); } // https://github.com/giraud/reasonml-idea-plugin/issues/268 @Test public void test_GH_268() { RPsiClass e = firstOfType(parseCode( "class tag : text_tag -> object method as_tag : text_tag method connect : tag_signals end"), RPsiClass.class); assertSize(2, e.getMethods()); } // https://github.com/giraud/reasonml-idea-plugin/issues/444 @Test public void test_inherit_object_no_parameter() { // GH_444 RPsiObject e = firstOfType(parseCode(""" let pf = object (self) inherit GObj.widget method destroy : unit -> unit end """), RPsiObject.class); RPsiInherit ei = PsiTreeUtil.findChildOfType(e, RPsiInherit.class); assertTextEquals("inherit GObj.widget", ei.getText()); assertTextEquals("widget", ei.getClassTypeIdentifier().getText()); assertSize(0, ei.getParameters()); } // https://github.com/giraud/reasonml-idea-plugin/issues/444 @Test public void test_inherit_object_parenless_parameter() { // GH_444 RPsiObject e = firstOfType(parseCode(""" let pf = object (self) inherit GObj.widget view#as_widget method destroy : unit -> unit end """), RPsiObject.class); RPsiInherit ei = PsiTreeUtil.findChildOfType(e, RPsiInherit.class); assertTextEquals("inherit GObj.widget view#as_widget", ei.getText()); assertTextEquals("widget", ei.getClassTypeIdentifier().getText()); assertNull(PsiTreeUtil.findChildOfType(ei, RPsiParameterDeclaration.class)); assertSize(1, ei.getParameters()); assertTextEquals("view#as_widget", ei.getParameters().get(0).getText()); } // https://github.com/giraud/reasonml-idea-plugin/issues/269 @Test public void test_GH_269() { RPsiClass e = firstOfType(parseCode(""" class type ops = object method go_to_insert : unit task method go_to_mark : GText.mark -> unit task method process_next_phrase : unit task method get_n_errors : int method get_errors : (int * string) list method get_slaves_status : int * int * string CString.Map.t method handle_failure : handle_exn_rty -> unit task method destroy : unit -> unit end """), RPsiClass.class); assertSize(8, e.getMethods()); ArrayList methods = new ArrayList<>(e.getMethods()); assertEquals("go_to_insert", methods.get(0).getName()); assertEquals("unit task", methods.get(0).getSignature().getText()); assertEquals("go_to_mark", methods.get(1).getName()); assertEquals("GText.mark -> unit task", methods.get(1).getSignature().getText()); assertEquals("process_next_phrase", methods.get(2).getName()); assertEquals("unit task", methods.get(2).getSignature().getText()); assertEquals("get_n_errors", methods.get(3).getName()); assertEquals("int", methods.get(3).getSignature().getText()); assertEquals("get_errors", methods.get(4).getName()); assertEquals("(int * string) list", methods.get(4).getSignature().getText()); assertEquals("get_slaves_status", methods.get(5).getName()); assertEquals("int * int * string CString.Map.t", methods.get(5).getSignature().getText()); assertEquals("handle_failure", methods.get(6).getName()); assertEquals("handle_exn_rty -> unit task", methods.get(6).getSignature().getText()); assertEquals("destroy", methods.get(7).getName()); assertEquals("unit -> unit", methods.get(7).getSignature().getText()); } // https://github.com/giraud/reasonml-idea-plugin/issues/310 @Test public void test_GH_310() { FileBase file = parseCode(""" class type control = object method detach : unit -> unit end type errpage = (int * string) list page"""); List es = expressions(file); assertSize(2, es); assertInstanceOf(es.get(0), RPsiClass.class); assertEquals("control", es.get(0).getName()); assertInstanceOf(es.get(1), RPsiType.class); assertEquals("errpage", es.get(1).getName()); } // https://github.com/giraud/reasonml-idea-plugin/issues/491 @Test public void test_GH_491_object_initializer() { RPsiClass e = firstOfType(parseCode(""" class c = object (self) method tag = { tag_bold = bold#active; } initializer let x = 1 in let fn y = x + y in ignore(fn 2) end """), RPsiClass.class); assertSize(0, e.getFields()); assertSize(1, e.getMethods()); assertTextEquals(""" initializer let x = 1 in let fn y = x + y in ignore(fn 2)""", e.getInitializer().getText()); } } ================================================ FILE: src/test/java/com/reason/lang/ocaml/CommentParsingTest.java ================================================ package com.reason.lang.ocaml; import com.intellij.psi.*; import com.intellij.psi.util.*; import com.reason.ide.files.*; import com.reason.lang.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import org.junit.*; public class CommentParsingTest extends OclParsingTestCase { @Test public void test_constant() { PsiComment e = firstOfType(parseCode("(* *)"), PsiComment.class); assertEquals("(* *)", e.getText()); } @Test public void test_constant_2() { PsiComment e = firstOfType(parseCode("(* \"this is a string *)\" *)"), PsiComment.class); assertEquals("(* \"this is a string *)\" *)", e.getText()); } // GH: https://github.com/giraud/reasonml-idea-plugin/issues/469 @Test public void test_double_quotes() { FileBase e = parseCode(""" let _ = '"' in (* '"' *) let _ = '"' in (* '"' *) """); PsiElement e0 = e.getFirstChild(); assertInstanceOf(e0, RPsiLet.class); assertEquals("let _ = '\"'", e0.getText()); PsiElement e1 = ORUtil.nextSibling(ORUtil.nextSibling(e0)); assertInstanceOf(e1, PsiComment.class); assertEquals("(* '\"' *)", e1.getText()); PsiElement e2 = ORUtil.nextSibling(e1); assertInstanceOf(e2, RPsiLet.class); assertEquals("let _ = '\"'", e2.getText()); PsiElement e3 = ORUtil.nextSibling(ORUtil.nextSibling(e2)); assertInstanceOf(e3, PsiComment.class); assertEquals("(* '\"' *)", e3.getText()); } } ================================================ FILE: src/test/java/com/reason/lang/ocaml/DirectiveParsingTest.java ================================================ package com.reason.lang.ocaml; import com.intellij.psi.util.*; import com.reason.ide.files.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import org.junit.*; @SuppressWarnings("ConstantConditions") public class DirectiveParsingTest extends OclParsingTestCase { @Test public void test_if() { FileBase f = parseCode("#if BS then\nx\n#else\ny\n #end"); RPsiDirective e = PsiTreeUtil.findChildOfType(f, RPsiDirective.class); assertNotNull(e); assertEquals("#if BS then\nx\n#else\ny\n #end", e.getText()); } @Test public void test_endif() { FileBase f = parseCode("#if BS then\nx\n#else\ny\n#endif"); RPsiDirective e = PsiTreeUtil.findChildOfType(f, RPsiDirective.class); assertNotNull(e); assertEquals("#if BS then\nx\n#else\ny\n#endif", e.getText()); } @Test public void test_let_binding() { RPsiLet e = firstOfType(parseCode("let usage_b buf speclist errmsg =\n" + "#if 0 \n" + " a;\n" + "#else\n" + " b;\n" + "#end\n" + " c\n"), RPsiLet.class); assertEquals("#if 0 \n a;\n#else\n b;\n#end\n c", e.getFunction().getBody().getText()); RPsiDirective d = PsiTreeUtil.findChildOfType(e, RPsiDirective.class); assertEquals("#if 0 \n a;\n#else\n b;\n#end", d.getText()); } @Test public void test_oCamlBeforeDirective() { RPsiVal e = firstOfType(parseCode("val bool_of_string_opt : string -> bool option\n(** This is a comment *)\n\n#if BS then\n#end"), RPsiVal.class); RPsiSignature signature = e.getSignature(); assertEquals("string -> bool option", signature.asText(OclLanguage.INSTANCE)); } // https://github.com/giraud/reasonml-idea-plugin/issues/492 @Test public void test_GH_492_not_directive() { RPsiClassMethod e = firstOfType(parseCode(""" let pf = object method m = self#end_iter end """), RPsiClassMethod.class); assertTextEquals("method m = self#end_iter", e.getText()); } } ================================================ FILE: src/test/java/com/reason/lang/ocaml/ExceptionParsingTest.java ================================================ package com.reason.lang.ocaml; import com.reason.lang.core.psi.RPsiException; import org.junit.*; public class ExceptionParsingTest extends OclParsingTestCase { @Test public void test_basic() { RPsiException e = firstOfType(parseCode("exception Ex"), RPsiException.class); assertEquals("Ex", e.getName()); assertEquals("Dummy.Ex", e.getQualifiedName()); } @Test public void test_parameter() { RPsiException e = firstOfType(parseCode("exception Ex of string"), RPsiException.class); assertEquals("Ex", e.getName()); assertEquals("Dummy.Ex", e.getQualifiedName()); } } ================================================ FILE: src/test/java/com/reason/lang/ocaml/ExpressionsParsingTest.java ================================================ package com.reason.lang.ocaml; import com.intellij.psi.*; import org.junit.*; import java.util.*; public class ExpressionsParsingTest extends OclParsingTestCase { @Test public void testA() { PsiFile file = parseCode("module Hooks = struct let a = fun (_, info as ei) -> x end\nlet b = 1"); Collection expressions = expressions(file); assertEquals(2, expressions.size()); } @Test public void testB() { PsiFile file = parseCode("let x = function | _ -> false\nlet y = 1"); Collection expressions = expressions(file); assertEquals(2, expressions.size()); } } ================================================ FILE: src/test/java/com/reason/lang/ocaml/ExternalParsingTest.java ================================================ package com.reason.lang.ocaml; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import com.reason.lang.core.psi.impl.RPsiLowerSymbol; import org.junit.*; @SuppressWarnings("ConstantConditions") public class ExternalParsingTest extends OclParsingTestCase { @Test public void test_qualifiedName() { RPsiExternal e = firstOfType(parseCode("external ee : int = \"\""), RPsiExternal.class); assertEquals("Dummy.ee", e.getQualifiedName()); } @Test public void test_with_string() { RPsiExternal e = firstOfType(parseCode("external reactIntlJsReactClass : ReasonReact.reactClass = \"FormattedMessage\""), RPsiExternal.class); assertEquals("ReasonReact.reactClass", e.getSignature().asText(OclLanguage.INSTANCE)); assertFalse(e.isFunction()); assertEquals("FormattedMessage", e.getExternalName()); } @Test public void test_empty_string() { RPsiExternal e = firstOfType(parseCode("external reactIntlJsReactClass: ReasonReact.reactClass = \"\""), RPsiExternal.class); assertEquals("ReasonReact.reactClass", e.getSignature().asText(OclLanguage.INSTANCE)); assertFalse(e.isFunction()); assertEquals("", e.getExternalName()); } @Test public void test_string() { RPsiExternalImpl e = (RPsiExternalImpl) firstOfType(parseCode("external string : string -> reactElement = \"%identity\""), RPsiExternal.class); assertEquals("string", e.getName()); assertInstanceOf(e.getNameIdentifier(), RPsiLowerSymbol.class); assertEquals(e.getNameIdentifier().getNode().getElementType(), OclTypes.INSTANCE.LIDENT); assertEquals("string -> reactElement", e.getSignature().getText()); assertEquals("%identity", e.getExternalName()); } @Test public void test_array() { RPsiExternalImpl e = (RPsiExternalImpl) firstOfType(parseCode("external array : reactElement array -> reactElement = \"%identity\""), RPsiExternal.class); assertEquals("array", e.getName()); assertInstanceOf(e.getNameIdentifier(), RPsiLowerSymbol.class); assertEquals(e.getNameIdentifier().getNode().getElementType(), OclTypes.INSTANCE.LIDENT); assertEquals("reactElement array -> reactElement", e.getSignature().getText()); assertEquals("%identity", e.getExternalName()); } @Test public void test_raise() { RPsiExternalImpl e = (RPsiExternalImpl) firstOfType(parseCode("external raise : exn -> 'a = \"%raise\""), RPsiExternal.class); assertEquals("raise", e.getName()); assertInstanceOf(e.getNameIdentifier(), RPsiLowerSymbol.class); assertEquals(e.getNameIdentifier().getNode().getElementType(), OclTypes.INSTANCE.LIDENT); assertEquals("exn -> 'a", e.getSignature().getText()); assertEquals("%raise", e.getExternalName()); } @Test public void test_operator1() { RPsiExternalImpl e = (RPsiExternalImpl) firstOfType(parseCode("external ( = ) : 'a -> 'a -> bool = \"%equal\""), RPsiExternal.class); assertEquals("( = )", e.getName()); assertInstanceOf(e.getNameIdentifier(), RPsiScopedExpr.class); assertEquals("'a -> 'a -> bool", e.getSignature().getText()); assertEquals("%equal", e.getExternalName()); } @Test public void test_operator2() { RPsiExternalImpl e = firstOfType(parseCode("external (<>) : 'a -> 'a -> bool = \"%notequal\""), RPsiExternalImpl.class); assertEquals("(<>)", e.getName()); assertInstanceOf(e.getNameIdentifier(), RPsiScopedExpr.class); assertEquals("'a -> 'a -> bool", e.getSignature().getText()); assertEquals("%notequal", e.getExternalName()); } @Test public void test_operator3() { RPsiExternalImpl e = firstOfType(parseCode("external ( < ) : 'a -> 'a -> bool = \"%lessthan\""), RPsiExternalImpl.class); assertEquals("( < )", e.getName()); assertInstanceOf(e.getNameIdentifier(), RPsiScopedExpr.class); assertEquals("'a -> 'a -> bool", e.getSignature().getText()); assertEquals("%lessthan", e.getExternalName()); } @Test public void test_operator4() { RPsiExternalImpl e = firstOfType(parseCode("external ( > ) : 'a -> 'a -> bool = \"%greaterthan\""), RPsiExternalImpl.class); assertEquals("( > )", e.getName()); assertInstanceOf(e.getNameIdentifier(), RPsiScopedExpr.class); assertEquals("'a -> 'a -> bool", e.getSignature().getText()); assertEquals("%greaterthan", e.getExternalName()); } @Test public void test_operator5() { RPsiExternalImpl e = firstOfType(parseCode("external ( <= ) : 'a -> 'a -> bool = \"%lessequal\""), RPsiExternalImpl.class); assertEquals("( <= )", e.getName()); assertInstanceOf(e.getNameIdentifier(), RPsiScopedExpr.class); assertEquals("'a -> 'a -> bool", e.getSignature().getText()); assertEquals("%lessequal", e.getExternalName()); } @Test public void test_operator6() { RPsiExternalImpl e = firstOfType(parseCode("external ( >= ) : 'a -> 'a -> bool = \"%greaterequal\""), RPsiExternalImpl.class); assertEquals("( >= )", e.getName()); assertInstanceOf(e.getNameIdentifier(), RPsiScopedExpr.class); assertEquals("'a -> 'a -> bool", e.getSignature().getText()); assertEquals("%greaterequal", e.getExternalName()); } // https://github.com/giraud/reasonml-idea-plugin/issues/423 @Test public void test_GH_423() { RPsiExternal e = firstOfType(parseCode("external ref : 'a -> 'a ref = \"%makemutable\""), RPsiExternal.class); assertEquals("ref", e.getName()); } } ================================================ FILE: src/test/java/com/reason/lang/ocaml/FirstClassModuleParsingTest.java ================================================ package com.reason.lang.ocaml; import com.intellij.psi.*; import com.intellij.psi.util.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import org.junit.*; @SuppressWarnings("DataFlowIssue") public class FirstClassModuleParsingTest extends OclParsingTestCase { @Test public void test_first_class_let() { RPsiLet e = firstOfType(parseCode("let three = (module Three : A.I)"), RPsiLet.class); assertEquals("Three", e.getFirstClassModule().getFirstClassModuleSymbol().getText()); RPsiModuleSignature rPsiModuleSignature = ((RPsiModuleSignature) e.getSignature()); assertEquals("A.I", rPsiModuleSignature.getQName()); } @Test public void test_first_class_parameter_no_default() { RPsiLet e = firstOfType(parseCode("let fn ~p:(p : (module I)) = p"), RPsiLet.class); assertTrue(e.isFunction()); assertDoesntContain(extractUpperSymbolTypes(e), myTypes.A_VARIANT_NAME); RPsiFunction ef = e.getFunction(); assertEquals("p", ef.getBody().getText()); RPsiSignature eps = ef.getParameters().get(0).getSignature(); assertInstanceOf(eps, RPsiModuleSignature.class); } @Test public void test_first_class_parameter_with_default() { RPsiFunction e = firstOfType(parseCode("let make ?p:((p : (module Intf))= (module Impl)) = p"), RPsiFunction.class); RPsiParameterDeclaration p0 = e.getParameters().get(0); //assertTrue(p0.isNamed()); assertDoesntContain(extractUpperSymbolTypes(e), myTypes.A_VARIANT_NAME); RPsiSignature eps = p0.getSignature(); assertInstanceOf(eps, RPsiModuleSignature.class); assertEquals("(module Intf)", eps.getText()); RPsiDefaultValue epv = p0.getDefaultValue(); assertEquals("(module Impl)", epv.getText()); PsiElement epvc = epv.getFirstChild(); assertInstanceOf(epvc, RPsiFirstClass.class); } @Test public void test_unpack() { RPsiInnerModule e = firstOfType(parseCode("module New_three = (val three : I)"), RPsiInnerModule.class); assertNull(e.getBody()); assertEquals("(val three : I)", e.getUnpack().getText()); assertEquals("three", e.getUnpack().getFirstClassSymbol().getText()); assertEquals("I", e.getUnpack().getModuleReference().getText()); } @Test public void test_unpack_no_signature() { RPsiInnerModule e = firstOfType(parseCode("module M = (val selectors)"), RPsiInnerModule.class); assertNull(e.getBody()); assertFalse(e instanceof RPsiFunctor); assertEquals("M", e.getName()); assertEquals("(val selectors)", e.getUnpack().getText()); assertNull(PsiTreeUtil.findChildOfType(e, RPsiVal.class)); } @Test public void test_unpack_in_let() { RPsiInnerModule e = firstOfType(parseCode("let _ = let module M = (val m : S)"), RPsiInnerModule.class); assertNull(e.getBody()); assertFalse(e instanceof RPsiFunctor); assertEquals("M", e.getName()); assertEquals("(val m : S)", e.getUnpack().getText()); assertNull(PsiTreeUtil.findChildOfType(e, RPsiVal.class)); } } ================================================ FILE: src/test/java/com/reason/lang/ocaml/FunctionCallParsingTest.java ================================================ package com.reason.lang.ocaml; import com.intellij.psi.util.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import org.junit.*; import java.util.*; @SuppressWarnings("ConstantConditions") public class FunctionCallParsingTest extends OclParsingTestCase { @Test public void test_call() { RPsiLetBinding e = firstOfType(parseCode("let _ = string_of_int 1"), RPsiLet.class).getBinding(); RPsiFunctionCall call = PsiTreeUtil.findChildOfType(e, RPsiFunctionCall.class); assertEquals("string_of_int 1", call.getText()); assertEquals(1, call.getParameters().size()); assertEquals("1", call.getParameters().get(0).getText()); } @Test public void test_call_ints() { RPsiFunctionCall e = PsiTreeUtil.findChildOfType(parseCode("add 1 2"), RPsiFunctionCall.class); assertEquals("add 1 2", e.getText()); assertEquals(2, e.getParameters().size()); assertEquals("1", e.getParameters().get(0).getText()); assertEquals("2", e.getParameters().get(1).getText()); } @Test public void test_call_floats() { RPsiFunctionCall e = PsiTreeUtil.findChildOfType(parseCode("add 1. 2."), RPsiFunctionCall.class); assertEquals("add 1. 2.", e.getText()); assertEquals(2, e.getParameters().size()); assertEquals("1.", e.getParameters().get(0).getText()); assertEquals("2.", e.getParameters().get(1).getText()); } @Test public void test_call_many() { RPsiLetBinding e = firstOfType(parseCode("let _ = fn a b c"), RPsiLet.class).getBinding(); RPsiFunctionCall call = PsiTreeUtil.findChildOfType(e, RPsiFunctionCall.class); assertEquals("fn a b c", call.getText()); assertEquals(3, call.getParameters().size()); assertEquals("a", call.getParameters().get(0).getText()); assertEquals("b", call.getParameters().get(1).getText()); assertEquals("c", call.getParameters().get(2).getText()); } @Test public void test_inner_call() { RPsiLetBinding e = firstOfType(parseCode("let _ = fn a (b \"{\" c) d"), RPsiLet.class).getBinding(); RPsiFunctionCall f = PsiTreeUtil.findChildOfType(e, RPsiFunctionCall.class); List p = f.getParameters(); assertEquals("fn a (b \"{\" c) d", f.getText()); assertEquals(3, p.size()); assertEquals("a", p.get(0).getText()); assertEquals("(b \"{\" c)", p.get(1).getText()); assertEquals("d", p.get(2).getText()); RPsiFunctionCall f1 = PsiTreeUtil.findChildOfType(p.get(1), RPsiFunctionCall.class); assertEquals("b \"{\" c", f1.getText()); assertEquals(2, f1.getParameters().size()); assertEquals("\"{\"", f1.getParameters().get(0).getText()); assertEquals("c", f1.getParameters().get(1).getText()); } @Test public void test_call_02() { RPsiFunctionCall e = firstOfType(parseCode("let _ = hov 0 (anomaly_string () ++ str \"xxx\")"), RPsiFunctionCall.class); assertEquals("hov 0 (anomaly_string () ++ str \"xxx\")", e.getText()); List ep = e.getParameters(); assertSize(2, ep); assertEquals("(anomaly_string () ++ str \"xxx\")", ep.get(1).getText()); List ee = new ArrayList<>(PsiTreeUtil.findChildrenOfType(ep.get(1), RPsiFunctionCall.class)); assertSize(2, ee); assertEquals("anomaly_string ()", ee.get(0).getText()); assertEquals("str \"xxx\"", ee.get(1).getText()); } @Test public void test_call_03() { RPsiFunctionCall e = firstOfType(parseCode("let _ = hov 0 (str \"xxx\" ++ str txt)"), RPsiFunctionCall.class); assertEquals("hov 0 (str \"xxx\" ++ str txt)", e.getText()); List ep = e.getParameters(); assertSize(2, ep); assertEquals("(str \"xxx\" ++ str txt)", ep.get(1).getText()); List ee = new ArrayList<>(PsiTreeUtil.findChildrenOfType(ep.get(1), RPsiFunctionCall.class)); assertSize(2, ee); assertEquals("str \"xxx\"", ee.get(0).getText()); assertEquals("str txt", ee.get(1).getText()); } @Test public void test_call_04() { // env.ml L39 RPsiFunctionCall e = firstOfType(parseCode("let _ = Util.check_file_else ~dir:Coq_config.coqlibsuffix ~file:prelude"), RPsiFunctionCall.class); assertSize(2, e.getParameters()); RPsiParameterReference p0 = e.getParameters().get(0); assertEquals("~dir:Coq_config.coqlibsuffix", p0.getText()); assertEquals("dir", p0.getName()); assertEquals("Coq_config.coqlibsuffix", p0.getValue().getText()); RPsiParameterReference p1 = e.getParameters().get(1); assertEquals("~file:prelude", p1.getText()); assertEquals("file", p1.getName()); assertEquals("prelude", p1.getValue().getText()); } @Test public void test_call_05() { RPsiFunctionCall e = firstOfType(parseCode("let _ = f1 \"x\" (1)"), RPsiFunctionCall.class); List ps = e.getParameters(); assertSize(2, ps); assertEquals("\"x\"", ps.get(0).getText()); assertEquals("(1)", ps.get(1).getText()); } @Test public void test_call_06() { RPsiFunctionCall e = firstOfType(parseCode("let _ = print_usage_common co (\"Usage:\" ^ executable_name ^ \" < options > \" ^ extra_args ^ \"\n\n\")"), RPsiFunctionCall.class); List ps = e.getParameters(); assertSize(2, ps); assertEquals("co", ps.get(0).getText()); assertEquals("(\"Usage:\" ^ executable_name ^ \" < options > \" ^ extra_args ^ \"\n\n\")", ps.get(1).getText()); } @Test public void test_call_07() { RPsiFunctionCall e = firstOfType(parseCode("let _ = sscanf l \"%[^=]=%S\" (fun name value -> Some(name))"),RPsiFunctionCall.class); List ps = e.getParameters(); assertSize(3, ps); assertEquals("l", ps.get(0).getText()); assertEquals("\"%[^=]=%S\"", ps.get(1).getText()); RPsiParameterReference p2 = ps.get(2); RPsiFunction f = ORUtil.findImmediateFirstChildOfClass(p2, RPsiFunction.class); assertEquals("fun name value -> Some(name)", f.getText()); } @Test // coq::checker/analyze.ml public void test_parens_01() { RPsiLet e = firstOfType(parseCode("let memory = make size (Struct ((-1), [||]))"), RPsiLet.class); RPsiFunctionCall fc = PsiTreeUtil.findChildOfType(e, RPsiFunctionCall.class); assertEquals("make", fc.getName()); assertSize(2, fc.getParameters()); assertEquals("size", fc.getParameters().get(0).getText()); assertEquals("(Struct ((-1), [||]))", fc.getParameters().get(1).getText()); assertEquals("make size (Struct ((-1), [||]))", e.getBinding().getText()); assertContainsElements(extractUpperSymbolTypes(e), myTypes.A_VARIANT_NAME); } @Test // coq::checker/votour.ml public void test_parens_02() { RPsiPatternMatchBody e = firstOfType(parseCode("let _ = match cond with | BLOCK -> loop tl (1 :: pos) ((v, hd, 0 :: pos) :: accu) |_ -> raise_notrace Exit"), RPsiPatternMatchBody.class); RPsiFunctionCall fc = PsiTreeUtil.findChildOfType(e, RPsiFunctionCall.class); assertEquals("loop", fc.getName()); assertSize(3, fc.getParameters()); } @Test public void test_xxx() { RPsiFunctionCall e = firstOfType(parseCode("let _ = list_iteri (fun i ((start, stop), value) -> tree.(k) <- (i, Some i))"), RPsiFunctionCall.class); RPsiFunction ef = PsiTreeUtil.findChildOfType(e, RPsiFunction.class); assertSize(1, e.getParameters()); assertEquals("(fun i ((start, stop), value) -> tree.(k) <- (i, Some i))", e.getParameters().get(0).getText()); assertEquals("tree.(k) <- (i, Some i)", ef.getBody().getText()); } } ================================================ FILE: src/test/java/com/reason/lang/ocaml/FunctionParsingTest.java ================================================ package com.reason.lang.ocaml; import com.intellij.psi.util.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import org.junit.*; import java.util.*; @SuppressWarnings("ConstantConditions") public class FunctionParsingTest extends OclParsingTestCase { @Test public void test_single_param() { RPsiLet e = firstOfType(parseCode("let fn x = x"), RPsiLet.class); assertTrue(e.isFunction()); RPsiFunction function = e.getFunction(); List parameters = function.getParameters(); assertSize(1, parameters); assertInstanceOf(first(parameters).getNameIdentifier(), RPsiLowerSymbol.class); assertEquals("Dummy.fn[x]", first(parameters).getQualifiedName()); assertNotNull(function.getBody()); } @Test public void test_multiple_params() { RPsiLet e = firstOfType(parseCode("let add x y = x + y"), RPsiLet.class); assertTrue(e.isFunction()); RPsiFunction function = e.getFunction(); assertSize(2, function.getParameters()); assertInstanceOf(first(function.getParameters()).getNameIdentifier(), RPsiLowerSymbol.class); assertInstanceOf(second(function.getParameters()).getNameIdentifier(), RPsiLowerSymbol.class); assertNotNull(function.getBody()); } @Test public void test_let_binding() { RPsiLet e = firstOfType(parseCode("let getAttributes node = let attr = \"r\" in attr"), RPsiLet.class); assertTrue(e.isFunction()); RPsiFunction function = e.getFunction(); assertSize(1, function.getParameters()); assertNotNull(function.getBody()); } @Test public void test_let_binding_2() { RPsiLet e = firstOfType(parseCode("let visit_vo f = Printf.printf \"a\"; Printf.printf \"b\""), RPsiLet.class); assertTrue(e.isFunction()); RPsiFunction function = e.getFunction(); assertEquals("Printf.printf \"a\"; Printf.printf \"b\"", function.getBody().getText()); } @Test public void test_fun() { RPsiLet e = firstOfType(parseCode("let _ = fun (_, info as ei) -> x"), RPsiLet.class); assertTrue(e.isFunction()); RPsiFunction function = e.getFunction(); assertSize(1, function.getParameters()); assertEquals("(_, info as ei)", PsiTreeUtil.findChildOfType(function, RPsiParameters.class).getText()); assertEquals("x", function.getBody().getText()); } @Test public void test_fun_signature() { RPsiLet e = firstOfType(parseCode("let _: int -> int = fun x y -> x + y"), RPsiLet.class); assertTrue(e.isFunction()); assertEquals("fun x y -> x + y", e.getBinding().getText()); RPsiFunction f = e.getFunction(); List p = f.getParameters(); assertSize(2, p); assertEquals("x", p.get(0).getText()); assertEquals("y", p.get(1).getText()); assertEquals("x + y", f.getBody().getText()); } @Test public void test_complex_params() { RPsiLet e = firstOfType(parseCode("let resolve_morphism env ?(hook=(fun _ -> ())) args' (b, cStr) = let x = 1"), RPsiLet.class); assertNoParserError(e); assertTrue(e.isFunction()); RPsiFunction ef = e.getFunction(); assertSize(4, ef.getParameters()); assertEquals("env", ef.getParameters().get(0).getText()); assertEquals("?(hook=(fun _ -> ()))", ef.getParameters().get(1).getText()); assertEquals("args'", ef.getParameters().get(2).getText()); assertEquals("(b, cStr)", ef.getParameters().get(3).getText()); assertEquals("let x = 1", ef.getBody().getText()); } @Test public void test_parameters_named_symbols() { RPsiLet e = firstOfType(parseCode("let make ?newOther ~newValue ~id:(id:string) ~foo ?pouet ~values:(values:'a Js.t option) children) = 1"), RPsiLet.class); RPsiFunction function = (RPsiFunction) e.getBinding().getFirstChild(); List parameters = new ArrayList<>(function.getParameters()); assertSize(7, parameters); assertEquals("newOther", parameters.get(0).getName()); assertEquals("newValue", parameters.get(1).getName()); assertEquals("id", parameters.get(2).getName()); assertEquals("foo", parameters.get(3).getName()); assertEquals("pouet", parameters.get(4).getName()); assertEquals("values", parameters.get(5).getName()); assertEquals("children", parameters.get(6).getName()); } @Test public void test_rollback() { RPsiFunction f = firstOfType(parseCode("let _ = let x = 1 in let y = 2 in fun () -> 3"), RPsiFunction.class); // test infinite rollback assertEquals("fun () -> 3", f.getText()); } @Test public void test_option_anon_function() { RPsiFunction e = firstOfType(parseCode("let _ = { onCancelCreation = Some (fun _ -> navigate \"..\") }"), RPsiFunction.class); assertSize(1, e.getParameters()); assertEquals("_", e.getParameters().getFirst().getText()); assertEquals("navigate \"..\"", e.getBody().getText()); } // https://github.com/giraud/reasonml-idea-plugin/issues/291 @Test public void test_GH_291() { RPsiLet e = firstOfType(parseCode("let fn = function | OpenedModule -> true | _ -> false"), RPsiLet.class); assertTrue(e.isFunction()); assertEquals("function | OpenedModule -> true | _ -> false", e.getBinding().getText()); RPsiFunction f = e.getFunction(); assertEquals("| OpenedModule -> true | _ -> false", f.getBody().getText()); } // https://github.com/giraud/reasonml-idea-plugin/issues/456 @Test public void test_GH_456() { RPsiLet e = firstOfType(parseCode("let append ~param1 param2 = param1 + param2"), RPsiLet.class); RPsiFunction ef = (RPsiFunction) e.getBinding().getFirstChild(); List efps = new ArrayList<>(ef.getParameters()); assertSize(2, efps); RPsiParameterDeclaration efp0 = efps.get(0); assertEquals("param1", efp0.getName()); RPsiParameterDeclaration efp1 = efps.get(1); assertEquals("param2", efp1.getName()); assertEquals("param1 + param2", ef.getBody().getText()); } // https://github.com/giraud/reasonml-idea-plugin/issues/456 @Test public void test_GH_456_tuples() { RPsiLet e = firstOfType(parseCode("let append ~combine (b1,ll1) (b2,ll2) = (combine b1 b2, ll1 @ ll2)"), RPsiLet.class); RPsiFunction ef = (RPsiFunction) e.getBinding().getFirstChild(); List efps = new ArrayList<>(ef.getParameters()); assertSize(3, efps); RPsiParameterDeclaration efp0 = efps.get(0); assertEquals("combine", efp0.getName()); RPsiParameterDeclaration efp1 = efps.get(1); assertEquals("(b1,ll1)", efp1.getText()); RPsiParameterDeclaration efp2 = efps.get(2); assertEquals("(b2,ll2)", efp2.getText()); assertEquals("(combine b1 b2, ll1 @ ll2)", ef.getBody().getText()); } } ================================================ FILE: src/test/java/com/reason/lang/ocaml/FunctorCallParsingTest.java ================================================ package com.reason.lang.ocaml; import com.intellij.psi.*; import com.intellij.psi.util.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import org.junit.*; import java.util.*; @SuppressWarnings("ConstantConditions") public class FunctorCallParsingTest extends OclParsingTestCase { @Test public void test_instantiation() { RPsiInnerModule e = first(moduleExpressions(parseCode("module Printing = Make(struct let encode = encode_record end)"))); assertTrue(e.isFunctorCall()); RPsiFunctorCall call = PsiTreeUtil.findChildOfType(e, RPsiFunctorCall.class); assertEquals("Make(struct let encode = encode_record end)", call.getText()); assertEquals(myTypes.A_MODULE_NAME, call.getReferenceIdentifier().getNode().getElementType()); assertSize(1, call.getParameters()); assertEquals("struct let encode = encode_record end", call.getParameters().iterator().next().getText()); RPsiLet let = PsiTreeUtil.findChildOfType(e, RPsiLet.class); assertEquals("Dummy.Printing.Make[0].encode", let.getQualifiedName()); } @Test public void test_parens() { RPsiFunctorCall e = firstOfType(parseCode("module EHashtbl = Make(struct let equal = (==) end)"), RPsiFunctorCall.class); assertNoParserError(e); assertSize(1, e.getParameters()); assertEquals("Make(struct let equal = (==) end)", e.getText()); } @Test public void test_let_operator() { RPsiFunctorCall e = firstOfType(parseCode("include Monad.Make(struct let (>>) a k = (); fun () -> a (); k () end)"), RPsiFunctorCall.class); assertNoParserError(e); assertSize(1, e.getParameters()); assertEquals("struct let (>>) a k = (); fun () -> a (); k () end", e.getParameters().get(0).getText()); } @Test public void test_functor_instanciation_chaining() { PsiFile file = parseCode("module KeyTable = Hashtbl.Make(KeyHash)\ntype infos"); List es = new ArrayList<>(expressions(file)); assertEquals(2, es.size()); RPsiInnerModule module = (RPsiInnerModule) es.get(0); assertTrue(module.isFunctorCall()); RPsiFunctorCall call = PsiTreeUtil.findChildOfType(module, RPsiFunctorCall.class); assertEquals("Make(KeyHash)", call.getText()); assertEquals("Make", call.getName()); assertNull(PsiTreeUtil.findChildOfType(module, RPsiParameterDeclaration.class)); } } ================================================ FILE: src/test/java/com/reason/lang/ocaml/FunctorParsingTest.java ================================================ package com.reason.lang.ocaml; import com.intellij.psi.*; import com.intellij.psi.tree.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import org.junit.*; import java.util.*; @SuppressWarnings("ConstantConditions") public class FunctorParsingTest extends OclParsingTestCase { @Test public void test_basic() { RPsiFunctor e = firstOfType(parseCode("module Make (M:T0) (N:T1) : S = struct end"), RPsiFunctor.class); assertNoParserError(e); assertEquals("S", e.getReturnType().getText()); assertEquals("struct end", e.getBody().getText()); List eps = e.getParameters(); assertSize(2, eps); assertEquals("(M:T0)", eps.get(0).getText()); // parens outside ? assertEquals("T0", eps.get(0).getSignature().getText()); assertEquals("(N:T1)", eps.get(1).getText()); assertEquals("T1", eps.get(1).getSignature().getText()); assertEquals(myTypes.C_PARAM_DECLARATION, eps.get(0).getNode().getElementType()); assertDoesntContain(extractUpperSymbolTypes(e), myTypes.A_VARIANT_NAME); } @Test public void test_struct() { RPsiFunctor e = firstOfType(parseCode("module Make (_:sig type t end) : S = struct end"), RPsiFunctor.class); assertEquals("struct end", e.getBody().getText()); assertEquals("S", e.getReturnType().getText()); List uTypes = extractUpperSymbolTypes(e); assertDoesntContain(uTypes, myTypes.A_VARIANT_NAME); } @Test public void test_implicit_result() { RPsiFunctor e = firstOfType(parseCode("module Make (M:Def) = struct end"), RPsiFunctor.class); assertNoParserError(e); assertEquals("struct end", e.getBody().getText()); } @Test public void test_with_constraints() { Collection expressions = expressions(parseCode("module Make (M: Input) : S with type +'a t = 'a M.t and type b = M.b = struct end")); assertEquals(1, expressions.size()); RPsiFunctor f = (RPsiFunctor) first(expressions); assertNoParserError(f); assertEquals("(M: Input)", first(f.getParameters()).getText()); assertEquals("S", f.getReturnType().getText()); List constraints = new ArrayList<>(f.getConstraints()); assertEquals(2, constraints.size()); assertEquals("type +'a t = 'a M.t", constraints.get(0).getText()); assertEquals("type b = M.b", constraints.get(1).getText()); assertEquals("struct end", f.getBody().getText()); } @Test public void test_with_constraints_parens() { RPsiFunctor e = firstOfType(parseCode("module Make(M: SeqType) : (S with type t = M.t) = struct end"), RPsiFunctor.class); List ec = e.getConstraints(); assertNoParserError(e); assertEquals("(M: SeqType)", e.getParameters().get(0).getText()); assertEquals("SeqType", e.getParameters().get(0).getSignature().getText()); assertEquals("S", e.getReturnType().getText()); assertEquals(1, ec.size()); assertEquals("type t = M.t", ec.get(0).getText()); assertEquals("struct end", e.getBody().getText()); } @Test public void test_signature() { Collection functors = functorExpressions(parseCode( """ module GlobalBindings (M : sig val relation_classes : string list val morphisms : string list val arrow : evars -> evars * constr end) = struct open M end """)); assertEquals(1, functors.size()); RPsiFunctor functor = first(functors); assertNoParserError(functor); assertEquals("GlobalBindings", functor.getName()); assertEquals("Dummy.GlobalBindings", functor.getQualifiedName()); Collection parameters = functor.getParameters(); assertSize(1, parameters); assertEquals("M", first(parameters).getName()); assertNotNull(functor.getBody()); } @Test public void test_functor_inside_module() { RPsiModule e = firstOfType(parseCode(""" module Core = struct module Make() = struct type t end end """), RPsiModule.class); assertEquals("Core", e.getModuleName()); assertFalse(e instanceof RPsiFunctor); RPsiFunctor ef = firstOfType(e.getBody(), RPsiFunctor.class); assertEquals("Make", ef.getModuleName()); assertTrue(ef instanceof RPsiFunctor); }} ================================================ FILE: src/test/java/com/reason/lang/ocaml/IfParsingTest.java ================================================ package com.reason.lang.ocaml; import com.intellij.psi.*; import com.intellij.psi.util.*; import com.reason.ide.files.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.RPsiIfStatement; import com.reason.lang.core.psi.impl.*; import org.junit.*; import java.util.*; @SuppressWarnings("ConstantConditions") public class IfParsingTest extends OclParsingTestCase { @Test public void test_basic() { PsiFile psiFile = parseCode("let _ = if x then ()"); RPsiIfStatement e = firstOfType(psiFile, RPsiIfStatement.class); assertNotNull(e); assertNotNull(e.getCondition()); RPsiScopedExpr ifScope = PsiTreeUtil.findChildOfType(e, RPsiScopedExpr.class); assertNotNull(ifScope); assertEquals("()", ifScope.getText()); } @Test public void test_basic_then_else() { PsiFile psiFile = parseCode("let _ = if x then 1 else 2"); RPsiIfStatement e = firstOfType(psiFile, RPsiIfStatement.class); assertNotNull(e); assertNotNull(e.getCondition()); List scopes = new ArrayList<>(PsiTreeUtil.findChildrenOfType(e, RPsiScopedExpr.class)); assertEquals(2, scopes.size()); assertEquals("1", scopes.get(0).getText()); assertEquals("2", scopes.get(1).getText()); } @Test public void test_with_in() { FileBase file = parseCode("let _ = if x then let init = y in let data = z"); assertEquals(1, ORUtil.findImmediateChildrenOfClass(file, RPsiLet.class).size()); assertNotNull(firstOfType(file, RPsiIfStatement.class)); } @Test public void test_function() { RPsiLet e = firstOfType(parseCode("let init l f = if l = 0 then [||] else x"), RPsiLet.class); RPsiIfStatement i = PsiTreeUtil.findChildOfType(e, RPsiIfStatement.class); assertEquals("l = 0", i.getCondition().getText()); assertEquals("[||]", i.getThenExpression().getText()); assertEquals("x", i.getElseExpression().getText()); } @Test public void test_comment_else() { RPsiIfStatement e = firstOfType(parseCode("let _ = if cond then 1 else (* !! *) let z = true in 2"), RPsiIfStatement.class); assertEquals("cond", e.getCondition().getText()); assertEquals("1", e.getThenExpression().getText()); assertEquals("let z = true in 2", e.getElseExpression().getText()); } @Test public void test_ternary_lident() { RPsiLet e = firstOfType(parseCode("let _ = a ? b : c"), RPsiLet.class); RPsiTernary t = ORUtil.findImmediateFirstChildOfClass(e.getBinding(), RPsiTernary.class); assertEquals("a", t.getCondition().getText()); assertEquals("b", t.getThenExpression().getText()); assertEquals("c", t.getElseExpression().getText()); } @Test public void test_ternary_parens() { RPsiLet e = firstOfType(parseCode("let _ = (a) ? b : c"), RPsiLet.class); RPsiTernary t = ORUtil.findImmediateFirstChildOfClass(e.getBinding(), RPsiTernary.class); assertEquals("(a)", t.getCondition().getText()); assertEquals("b", t.getThenExpression().getText()); assertEquals("c", t.getElseExpression().getText()); } @Test public void test_ternary_cond() { PsiFile psiFile = parseCode("let _ = a == a' || (x < y) ? b : c"); RPsiTernary e = firstOfType(psiFile, RPsiTernary.class); assertNotNull(e); assertNotNull(e.getCondition()); assertEquals("a == a' || (x < y)", e.getCondition().getText()); assertEquals("b", e.getThenExpression().getText()); assertEquals("c", e.getElseExpression().getText()); } @Test public void test_ternary_call() { RPsiLet e = firstOfType(parseCode("let _ = fn(a) ? b : c"), RPsiLet.class); RPsiTernary t = ORUtil.findImmediateFirstChildOfClass(e.getBinding(), RPsiTernary.class); // assertEquals("fn(a)", t.getCondition().getText()); assertEquals("b", t.getThenExpression().getText()); assertEquals("c", t.getElseExpression().getText()); } @Test public void test_named_param() { RPsiLet e = firstOfType(parseCode("let add_rec_path ~unix_path ~coq_root =\n" + " if exists_dir unix_path then\n" + " let dirs = all_subdirs ~unix_path \n" + " else\n" + " Feedback.msg_warning (str \"Cannot open \" ++ str unix_path)"), RPsiLet.class); RPsiIfStatement i = PsiTreeUtil.findChildOfType(e, RPsiIfStatement.class); assertEquals("exists_dir unix_path", i.getCondition().getText()); assertEquals("let dirs = all_subdirs ~unix_path", i.getThenExpression().getText()); assertEquals("Feedback.msg_warning (str \"Cannot open \" ++ str unix_path)", i.getElseExpression().getText()); } @Test public void test_GH_xxx() { RPsiClassMethod e = firstOfType(parseCode("module M = struct\n" + " let o = object\n" + " method m =\n" + " if 2 > 1 then ()\n" + " end\n" + " let x = 0\n" + "end"), RPsiClassMethod.class); assertEquals("method m =\n if 2 > 1 then ()", e.getText()); } } ================================================ FILE: src/test/java/com/reason/lang/ocaml/ImmediateObjectParsingTest.java ================================================ package com.reason.lang.ocaml; import com.intellij.psi.util.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import org.junit.*; public class ImmediateObjectParsingTest extends OclParsingTestCase { @Test public void test_immediate_object() { RPsiType e = firstOfType(parseCode("type t = "), RPsiType.class); RPsiObject object = PsiTreeUtil.findChildOfType(e, RPsiObject.class);// not exactly... assertNotNull(object); assertSize(2, object.getFields()); } @Test public void test_js_object() { RPsiType e = firstOfType(parseCode("type t = Js.t"), RPsiType.class); RPsiObject object = PsiTreeUtil.findChildOfType(e, RPsiObject.class); assertNotNull(object); assertEquals("", object.getText()); assertSize(2, object.getFields()); } } ================================================ FILE: src/test/java/com/reason/lang/ocaml/IncludeParsingTest.java ================================================ package com.reason.lang.ocaml; import com.intellij.psi.util.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import org.junit.*; @SuppressWarnings("ConstantConditions") public class IncludeParsingTest extends OclParsingTestCase { @Test public void test_one() { RPsiInclude e = firstOfType(parseCode("include Belt"), RPsiInclude.class); assertNull(PsiTreeUtil.findChildOfType(e, RPsiFunctorCall.class)); assertEquals("Belt", e.getIncludePath()); assertEquals("Belt", ORUtil.findImmediateLastChildOfType(e, myTypes.A_MODULE_NAME).getText()); } @Test public void test_path() { RPsiInclude e = firstOfType(parseCode("include Belt.Array"), RPsiInclude.class); assertEquals("Belt.Array", e.getIncludePath()); assertEquals("Array", ORUtil.findImmediateLastChildOfType(e, myTypes.A_MODULE_NAME).getText()); } @Test public void test_functor() { RPsiInclude e = firstOfType(parseCode("include Make(struct type t end)"), RPsiInclude.class); assertTrue(e.useFunctor()); RPsiFunctorCall c = PsiTreeUtil.findChildOfType(e, RPsiFunctorCall.class); assertEquals("Make", c.getName()); assertEquals(myTypes.A_MODULE_NAME, c.getReferenceIdentifier().getNode().getElementType()); assertEquals("Make", e.getIncludePath()); } @Test public void test_functor_path() { RPsiInclude e = firstOfType(parseCode("include A.Make(struct type t end)"), RPsiInclude.class); assertTrue(e.useFunctor()); assertEquals("A.Make", e.getIncludePath()); } @Test public void test_with_type() { RPsiInclude e = firstOfType(parseCode("include S with type t = Tok.t"), RPsiInclude.class); assertEquals("S", e.getIncludePath()); assertEquals("include S with type t = Tok.t", e.getText()); } @Test public void test_with_path_type() { RPsiInclude e = firstOfType(parseCode("include Grammar.S with type te = Tok.t and type 'c pattern = 'c Tok.p\ntype t"), RPsiInclude.class); // Coq: pcoq.ml assertEquals("Grammar.S", e.getIncludePath()); assertEquals("include Grammar.S with type te = Tok.t and type 'c pattern = 'c Tok.p", e.getText()); } @Test public void test_GH_497_include_inlined_module() { RPsiInclude e = firstOfType(parseCode("include module type of struct include Env.Path end"), RPsiInclude.class); // Coq: boot/path.mli assertEmpty(e.getIncludePath()); assertNotNull(PsiTreeUtil.findChildOfType(e, RPsiModule.class)); } } ================================================ FILE: src/test/java/com/reason/lang/ocaml/LetParsingTest.java ================================================ package com.reason.lang.ocaml; import com.intellij.psi.*; import com.intellij.psi.util.*; import com.reason.ide.files.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import org.junit.*; import java.util.*; @SuppressWarnings("ConstantConditions") public class LetParsingTest extends OclParsingTestCase { @Test public void test_constant() { FileBase file = parseCode("let x = 1 let y = 2"); List lets = ORUtil.findImmediateChildrenOfClass(file, RPsiLet.class); assertEquals(2, lets.size()); assertEquals("x", lets.get(0).getName()); assertFalse(lets.get(0).isFunction()); assertNotNull(first(PsiTreeUtil.findChildrenOfType(lets.get(0), RPsiLetBinding.class))); } @Test public void test_underscore() { RPsiLet e = firstOfType(parseCode("let _ = ()"), RPsiLet.class); assertNull(e.getName()); } @Test public void test_binding() { RPsiLet let = firstOfType(parseCode("let obj = [%bs.obj { a = \"b\" }];"), RPsiLet.class); assertFalse(let.isFunction()); assertNotNull(first(PsiTreeUtil.findChildrenOfType(let, RPsiLetBinding.class))); } @Test public void test_binding_with_function() { RPsiLet e = firstOfType(parseCode("let add x y = x + y"), RPsiLet.class); assertNotNull(first(PsiTreeUtil.findChildrenOfType(e, RPsiLetBinding.class))); assertEquals("x y = x + y", e.getBinding().getText()); } @Test public void test_scope_with_some() { RPsiLet let = firstOfType(parseCode("let l = (p) => { switch (a) { | Some(a) => a; (); | None => () }; Some(z); };"), RPsiLet.class); RPsiLetBinding binding = first(PsiTreeUtil.findChildrenOfType(let, RPsiLetBinding.class)); assertNotNull(binding); } @Test public void test_scope_with_lIdent() { RPsiLet e = firstOfType(parseCode("let fn p = Js.log p; returnObj"), RPsiLet.class); assertTrue(e.isFunction()); assertEquals("fn", e.getName()); } @Test public void test_record() { RPsiLet let = firstOfType(parseCode("let r = { one = 1; two = 2 }"), RPsiLet.class); RPsiLetBinding binding = first(PsiTreeUtil.findChildrenOfType(let, RPsiLetBinding.class)); assertNotNull(binding); RPsiRecord record = PsiTreeUtil.findChildOfType(binding, RPsiRecord.class); assertNotNull(record); Collection fields = record.getFields(); assertSize(2, fields); Iterator itFields = fields.iterator(); assertEquals("one = 1", itFields.next().getText()); assertEquals("two = 2", itFields.next().getText()); } @Test public void test_rec() { RPsiLet let = firstOfType(parseCode("let rec lx x = x + 1"), RPsiLet.class); assertTrue(let.isFunction()); assertEquals("lx", let.getName()); } @Test public void test_in_do_loop() { FileBase file = parseCode("let x l = for i = 0 to l - 1 do let x = 1 done"); RPsiLet let = firstOfType(file, RPsiLet.class); assertTrue(let.isFunction()); assertEquals("l = for i = 0 to l - 1 do let x = 1 done", let.getBinding().getText()); } @Test public void test_with_semi_separator() { FileBase file = parseCode("let rec read_num = Printf.printf; let l = 1"); Collection lets = ORUtil.findImmediateChildrenOfClass(file, RPsiLet.class); assertEquals(1, lets.size()); } @Test public void test_like_local_open() { RPsiOpen open = firstOfType(parseCode("let open Univ"), RPsiOpen.class); assertEquals("let open Univ", open.getText()); } @Test public void test_like_module() { FileBase file = parseCode("let module Repr = (val repr : S)"); RPsiInnerModule module = first(moduleExpressions(file)); assertEquals(1, childrenCount(file)); assertEquals("Repr", module.getName()); } @Test public void test_chaining() { FileBase file = parseCode("let visit_vo f = let segments = [| a; b; |] in let repr = x"); List lets = ORUtil.findImmediateChildrenOfClass(file, RPsiLet.class); assertEquals(1, lets.size()); } @Test public void test_case1() { FileBase file = parseCode("let format_open {o_loc; o_name; o_items; _} = " + "Printf.printf \"O|%s|%s|%s\\n\" (format_location o_loc) o_name (join_list \", \" !o_items)"); RPsiLet e = firstOfType(file, RPsiLet.class); RPsiLetBinding binding = e.getBinding(); assertInstanceOf(binding.getFirstChild(), RPsiFunction.class); RPsiFunction function = (RPsiFunction) binding.getFirstChild(); assertEquals("{o_loc; o_name; o_items; _}", first(function.getParameters()).getText()); assertEquals("Printf.printf \"O|%s|%s|%s\\n\" (format_location o_loc) o_name (join_list \", \" !o_items)", function.getBody().getText()); RPsiFunctionCall fc = ORUtil.findImmediateFirstChildOfClass(function.getBody(), RPsiFunctionCall.class); assertEquals("(format_location o_loc)", fc.getParameters().get(1).getText()); } @Test public void test_qualifiedName() { RPsiLet root = firstOfType(parseCode("let root = x"), RPsiLet.class); RPsiLet inner = PsiTreeUtil.findChildOfType(firstOfType(parseCode("let root = let inner = x in inner"), RPsiLet.class), RPsiLet.class); RPsiInnerModule mod = first(moduleExpressions(parseCode("module M = struct let m = 1 end"))); assertEquals("Dummy.root", root.getQualifiedName()); assertEquals("Dummy.root.inner", inner.getQualifiedName()); assertEquals("Dummy.M.m", ORUtil.findImmediateFirstChildOfClass(mod.getBody(), RPsiLet.class).getQualifiedName()); } @Test public void test_deconstruction() { RPsiLet e = firstOfType(parseCode("let (a, b) = x"), RPsiLet.class); assertTrue(e.isDeconstruction()); List names = e.getDeconstructedElements(); assertSize(2, names); assertEquals("a", names.get(0).getText()); assertInstanceOf(names.get(0), RPsiLowerName.class); assertEquals("b", names.get(1).getText()); assertInstanceOf(names.get(1), RPsiLowerName.class); } @Test public void test_deconstruction_parenless() { RPsiLet e = firstOfType(parseCode("let a, b = x"), RPsiLet.class); assertTrue(e.isDeconstruction()); List names = e.getDeconstructedElements(); assertSize(2, names); assertEquals("a", names.get(0).getText()); assertInstanceOf(names.get(0), RPsiLowerName.class); assertEquals("b", names.get(1).getText()); assertInstanceOf(names.get(1), RPsiLowerName.class); } @Test public void test_deconstruction_nested() { // belt_Map offset 2272 RPsiLet e = firstOfType(parseCode("let (l,r),b = Dict.split ~cmp m.data x"), RPsiLet.class); assertTrue(e.isDeconstruction()); List names = e.getDeconstructedElements(); assertSize(3, names); assertEquals("l", names.get(0).getText()); assertInstanceOf(names.get(0), RPsiLowerName.class); assertEquals("r", names.get(1).getText()); assertInstanceOf(names.get(1), RPsiLowerName.class); assertEquals("b", names.get(2).getText()); assertInstanceOf(names.get(2), RPsiLowerName.class); } @Test public void test_deconstruction_braces() { RPsiLet e = firstOfType(parseCode("let { a; b } = x"), RPsiLet.class); assertTrue(e.isDeconstruction()); List names = e.getDeconstructedElements(); assertSize(2, names); assertEquals("a", names.get(0).getText()); assertInstanceOf(names.get(0), RPsiLowerName.class); assertEquals("b", names.get(1).getText()); assertInstanceOf(names.get(1), RPsiLowerName.class); } @Test public void test_List() { RPsiLet e = firstOfType(parseCode("let tokens = [ \"bullet\"; \"string\"; \"unicode_id_part\"; ]"), RPsiLet.class); assertEquals("[ \"bullet\"; \"string\"; \"unicode_id_part\"; ]", e.getBinding().getText()); } @Test public void test_optional_param() { RPsiLet e = firstOfType(parseCode("let prod_to_str ?(plist=false) prod = String.concat \" \" (prod_to_str_r plist prod)"), RPsiLet.class); assertTrue(e.isFunction()); assertEquals("?(plist=false) prod = String.concat \" \" (prod_to_str_r plist prod)", e.getBinding().getText()); } @Test public void test_comparison() { RPsiLet e = firstOfType(parseCode("let lt x y = (x lxor 0x4) < (y lxor 0x4)\ntype t"), RPsiLet.class); RPsiFunctionBody b = e.getFunction().getBody(); assertEquals("(x lxor 0x4) < (y lxor 0x4)", b.getText()); } @Test public void test_semi() { RPsiLet e = firstOfType(parseCode("let _ = for i = 0 to len - 1 do done; get_data p"), RPsiLet.class); assertEquals("for i = 0 to len - 1 do done; get_data p", e.getBinding().getText()); } @Test public void test_name_unit() { RPsiLet e = firstOfType(parseCode("let () = 1 + 2"), RPsiLet.class); assertNotNull(PsiTreeUtil.findChildOfType(e, RPsiUnit.class)); assertNull(e.getName()); } @Test public void test_parens() { RPsiLetBinding e = firstOfType(parseCode("let tmp = uget t ((pred n)-i)"), RPsiLetBinding.class); assertNoParserError(e); assertEquals("uget t ((pred n)-i)", e.getText()); } // should it be parsed like a function ? // https://github.com/giraud/reasonml-idea-plugin/issues/309 @Test public void test_infix_operator() { RPsiLet e = firstOfType(parseCode("let (|?) m (key, cb) = m |> Ext_json.test key cb"), RPsiLet.class); assertEquals("(|?)", e.getName()); assertNull(PsiTreeUtil.findChildOfType(e, RPsiPatternMatch.class)); assertEquals("m |> Ext_json.test key cb", e.getBinding().getText()); } // https://github.com/giraud/reasonml-idea-plugin/issues/105 @Test public void test_GH_105() { FileBase file = parseCode("let string = \"x\""); RPsiLet e = firstOfType(file, RPsiLet.class); assertFalse(e.isFunction()); assertEquals("string", e.getName()); } @Test public void test_GH_105a() { FileBase file = parseCode("let string s = \"x\""); RPsiLet e = firstOfType(file, RPsiLet.class); assertTrue(e.isFunction()); assertEquals("string", e.getName()); } @Test public void test_GH_105b() { FileBase file = parseCode("let int = 1"); RPsiLet e = firstOfType(file, RPsiLet.class); assertFalse(e.isFunction()); assertEquals("int", e.getName()); } @Test public void test_GH_105c() { FileBase file = parseCode("let bool = 1"); RPsiLet e = firstOfType(file, RPsiLet.class); assertFalse(e.isFunction()); assertEquals("bool", e.getName()); } // https://github.com/giraud/reasonml-idea-plugin/issues/116 @Test public void test_GH_116() { FileBase file = parseCode("let ((), proofview, _, _) = Proofview.apply (Global.env ()) tac pr.proofview"); RPsiLet e = firstOfType(file, RPsiLet.class); assertSize(2, e.getDeconstructedElements()); assertFalse(e.isFunction()); assertEquals("Proofview.apply (Global.env ()) tac pr.proofview", e.getBinding().getText()); } // https://github.com/giraud/reasonml-idea-plugin/issues/121 @Test public void test_GH_121() { Collection lets = ORUtil.findImmediateChildrenOfClass(parseCode("let rec f x y = match x with | [] -> return y\n let x = 1"), RPsiLet.class); assertSize(2, lets); } // https://github.com/giraud/reasonml-idea-plugin/issues/270 // https://caml.inria.fr/pub/docs/manual-ocaml/polymorphism.html#ss:explicit-polymorphism @Test public void test_GH_270() { RPsiLet e = firstOfType(parseCode("let rec parser_of_tree : type s tr r. s ty_entry -> int -> int -> (s, tr, r) ty_tree -> r parser_t =\n" + " fun entry nlevn alevn -> ()"), RPsiLet.class); assertEquals("parser_of_tree", e.getName()); assertTrue(e.isFunction()); assertEquals("s ty_entry -> int -> int -> (s, tr, r) ty_tree -> r parser_t", e.getSignature().getText()); } // https://github.com/giraud/reasonml-idea-plugin/issues/278 @Test public void test_GH_278() { RPsiLet e = firstOfType(parseCode("let (//) = Ext_path.combine"), RPsiLet.class); assertEquals("(//)", e.getName()); assertNull(PsiTreeUtil.findChildOfType(e, PsiComment.class)); } // https://github.com/giraud/reasonml-idea-plugin/issues/406 @Test public void test_GH_406() { RPsiLet e = firstOfType(parseCode(""" let is_uppercase s = let x = 0 in let open CamomileLibraryDefault.Camomile in let open UReStr in let open UReStr.Make(UTF8) in let y = 0 in let z = 0 in false"""), RPsiLet.class); assertNoParserError(e); RPsiFunctionBody eb = e.getFunction().getBody(); RPsiLet[] ebls = PsiTreeUtil.getChildrenOfType(eb, RPsiLet.class); assertEquals("let x = 0", ebls[0].getText()); assertEquals("let y = 0", ebls[1].getText()); assertEquals("let z = 0", ebls[2].getText()); assertSize(3, ebls); RPsiOpen[] ebos = PsiTreeUtil.getChildrenOfType(eb, RPsiOpen.class); assertEquals("let open CamomileLibraryDefault.Camomile", ebos[0].getText()); assertEquals("let open UReStr", ebos[1].getText()); assertEquals("let open UReStr.Make(UTF8)", ebos[2].getText()); assertSize(3, ebos); } // https://github.com/giraud/reasonml-idea-plugin/issues/407 @Test public void test_GH_407() { RPsiLetImpl e = firstOfType(parseCode("let (!!) r = !r"), RPsiLetImpl.class); assertFalse(e.isDeconstruction()); assertInstanceOf(e.getNameIdentifier(), RPsiScopedExpr.class); assertEquals("(!!)", e.getName()); } // https://github.com/giraud/reasonml-idea-plugin/issues/409 @Test public void test_GH_409() { RPsiLet e = firstOfType(parseCode(""" let f () = let b : bool = 1 = 1 in let x = 0 in x"""), RPsiLet.class); assertNoParserError(e); RPsiFunctionBody eb = e.getFunction().getBody(); RPsiLet[] ebls = PsiTreeUtil.getChildrenOfType(eb, RPsiLet.class); assertEquals("let b : bool = 1 = 1", ebls[0].getText()); assertEquals("1 = 1", PsiTreeUtil.getChildOfType(ebls[0].getBinding(), RPsiBinaryCondition.class).getText()); assertEquals("let x = 0", ebls[1].getText()); assertSize(2, ebls); } // https://github.com/giraud/reasonml-idea-plugin/issues/409 @Test public void test_GH_409a() { RPsiLet e = firstOfType(parseCode(""" let f () = let b : bool = ignore () = () in let x = 0 in x"""), RPsiLet.class); assertNoParserError(e); RPsiFunctionBody eb = e.getFunction().getBody(); RPsiLet[] ebls = PsiTreeUtil.getChildrenOfType(eb, RPsiLet.class); assertEquals("let b : bool = ignore () = ()", ebls[0].getText()); assertEquals("ignore () = ()", PsiTreeUtil.getChildOfType(ebls[0].getBinding(), RPsiBinaryCondition.class).getText()); assertEquals("let x = 0", ebls[1].getText()); assertSize(2, ebls); } // https://github.com/giraud/reasonml-idea-plugin/issues/409 @Test public void test_GH_409_binary_condition() { RPsiLet e = firstOfType(parseCode("let b = ignore \"\" = ()"), RPsiLet.class); assertNoParserError(e); assertEquals("ignore \"\" = ()", e.getBinding().getText()); assertEquals("ignore \"\" = ()", PsiTreeUtil.getChildOfType(e.getBinding(), RPsiBinaryCondition.class).getText()); assertEquals("ignore \"\"", PsiTreeUtil.findChildOfType(e, RPsiFunctionCall.class).getText()); } } ================================================ FILE: src/test/java/com/reason/lang/ocaml/LocalOpenParsingTest.java ================================================ package com.reason.lang.ocaml; import com.intellij.psi.*; import com.intellij.psi.util.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import org.junit.*; import java.util.*; @SuppressWarnings("ConstantConditions") public class LocalOpenParsingTest extends OclParsingTestCase { @Test public void test_local_open() { List lets = ORUtil.findImmediateChildrenOfClass(parseCode("let _ = Int64.(x + y / of_int 2) let x = 1"), RPsiLet.class); assertSize(2 , lets); RPsiLocalOpen localOpen = PsiTreeUtil.findChildOfType(lets.get(0).getBinding(), RPsiLocalOpen.class); assertEquals("(x + y / of_int 2)", localOpen.getText()); assertFalse(lets.get(1).isFunction()); assertEquals("1", lets.get(1).getBinding().getText()); } @Test public void test_not_local_open() { PsiElement expression = parseCode("Js.log(\"nok\")").getFirstChild(); assertFalse(expression instanceof RPsiLocalOpen); } // https://github.com/giraud/reasonml-idea-plugin/issues/294 @Test public void test_GH_294() { Collection es = expressions(parseCode("let _ = let open P in function | A -> true | _ -> false")); assertSize(1, es); RPsiLet e = (RPsiLet) es.iterator().next(); assertEquals("let open P in function | A -> true | _ -> false", e.getBinding().getText()); assertEquals("P", PsiTreeUtil.findChildOfType(e, RPsiOpen.class).getPath()); } } ================================================ FILE: src/test/java/com/reason/lang/ocaml/LoopParsingTest.java ================================================ package com.reason.lang.ocaml; import com.intellij.psi.util.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import org.junit.*; import java.util.*; @SuppressWarnings("ConstantConditions") public class LoopParsingTest extends OclParsingTestCase { @Test public void test_for() { RPsiLetBinding e = firstOfType(parseCode("let _ = for i = 1 to pred l do unsafe_set rest i (f i) done"), RPsiLet.class).getBinding(); RPsiForLoop l = PsiTreeUtil.findChildOfType(e, RPsiForLoop.class); assertEquals("for i = 1 to pred l do unsafe_set rest i (f i) done", l.getText()); } // https://github.com/reasonml-editor/reasonml-idea-plugin/issues/176 @Test public void test_GH_176() { RPsiLet e = firstOfType(parseCode("let x = while true do match x with | _ -> () done"), RPsiLet.class); RPsiWhile while_ = (RPsiWhile) e.getBinding().getFirstChild(); assertEquals("true", while_.getCondition().getText()); } // https://github.com/reasonml-editor/reasonml-idea-plugin/issues/189 @Test public void test_GH_189() { List es = ORUtil.findImmediateChildrenOfClass(parseCode("let utf8_length s = while !p < len do () done; ()\nlet foo x = x"), RPsiLet.class); RPsiWhile while_ = (RPsiWhile) ((RPsiFunction) es.get(0).getBinding().getFirstChild()).getBody().getFirstChild(); assertEquals("!p < len", while_.getCondition().getText()); assertEquals("do () done", while_.getBody().getText()); assertSize(2, es); assertEquals("foo", second(es).getName()); } } ================================================ FILE: src/test/java/com/reason/lang/ocaml/MatchParsingTest.java ================================================ package com.reason.lang.ocaml; import com.intellij.psi.*; import com.intellij.psi.util.*; import com.reason.ide.files.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import org.junit.*; import java.util.*; @SuppressWarnings("ConstantConditions") public class MatchParsingTest extends OclParsingTestCase { @Test public void test_match() { FileBase psiFile = parseCode("let path_of_dirpath dir = match DirPath.repr dir with [] -> failwith \"path_of_dirpath\""); assertEquals(1, childrenCount(psiFile)); RPsiLet let = firstOfType(psiFile, RPsiLet.class); assertTrue(let.isFunction()); PsiTreeUtil.findChildOfType(let, RPsiFunction.class); } @Test public void test_match_01() { FileBase psiFileModule = parseCode("let _ = match c with | VtMeta -> let _ = x"); assertEquals(1, childrenCount(psiFileModule)); RPsiLet let = firstOfType(psiFileModule, RPsiLet.class); RPsiLetBinding binding = let.getBinding(); RPsiSwitch match = PsiTreeUtil.findChildOfType(binding, RPsiSwitch.class); assertEquals("VtMeta -> let _ = x", match.getPatterns().get(0).getText()); } @Test public void test_match_with_exception_01() { RPsiPatternMatch e = firstOfType(parseCode("match x with | exception Failure -> Printf.printf"), RPsiPatternMatch.class); assertNoParserError(e); assertEquals("exception Failure -> Printf.printf", e.getText()); } @Test public void test_match_with_exception_02() { RPsiPatternMatch e = firstOfType(parseCode("match x with | (* comment *) exception Failure -> Printf.printf"), RPsiPatternMatch.class); assertNoParserError(e); assertEquals("exception Failure -> Printf.printf", e.getText()); } @Test public void test_complex_match() { FileBase file = parseCode(""" begin match Repr.repr o with | BLOCK (0, [|id; o|]) -> [|(Int, id, 0 :: pos); (tpe, o, 1 :: pos)|] | _ -> raise Exit end"""); PsiElement[] children = file.getChildren(); assertEquals(1, childrenCount((file))); assertInstanceOf(children[0], RPsiScopedExpr.class); } @Test public void test_pattern_token_type() { PsiFile psiFile = parseCode("let _ = match action with | Incr -> counter + 1"); RPsiSwitch switch_ = first(PsiTreeUtil.findChildrenOfType(psiFile, RPsiSwitch.class)); Collection patterns = PsiTreeUtil.findChildrenOfType(switch_, RPsiPatternMatch.class); assertSize(1, patterns); RPsiPatternMatch patternMatch = patterns.iterator().next(); RPsiUpperSymbol variant = ORUtil.findImmediateFirstChildOfClass(patternMatch, RPsiUpperSymbol.class); assertEquals("Incr", variant.getText()); assertEquals("counter + 1", patternMatch.getBody().getText()); } @Test public void test_pattern_match() { PsiFile psiFile = parseCode("let _ = match p with | Typedtree.Partial -> \"Partial\" | Total -> \"Total\""); RPsiSwitch e = first(PsiTreeUtil.findChildrenOfType(psiFile, RPsiSwitch.class)); List patterns = new ArrayList<>(PsiTreeUtil.findChildrenOfType(e, RPsiPatternMatch.class)); assertSize(2, patterns); RPsiPatternMatch m1 = patterns.get(0); assertEquals("Typedtree", PsiTreeUtil.findChildOfType(m1, RPsiUpperSymbol.class).getText()); assertEquals(myTypes.A_VARIANT_NAME, ORUtil.findImmediateLastChildOfClass(m1, RPsiUpperSymbol.class).getNode().getElementType()); assertEquals("\"Partial\"", m1.getBody().getText()); RPsiPatternMatch m2 = patterns.get(1); assertEquals("\"Total\"", m2.getBody().getText()); } @Test public void test_function_shortcut() { RPsiLet e = firstOfType(parseCode("let f x = function | Variant -> 1"), RPsiLet.class); RPsiFunction fun = (RPsiFunction) e.getBinding().getFirstChild(); RPsiSwitch shortcut = ORUtil.findImmediateFirstChildOfClass(fun.getBody(), RPsiSwitch.class); assertNotNull(ORUtil.findImmediateFirstChildOfClass(shortcut, RPsiPatternMatch.class)); assertEquals("1", PsiTreeUtil.findChildOfType(shortcut, RPsiPatternMatchBody.class).getText()); } @Test public void test_function_shortcut_no_pipe() { RPsiLet e = firstOfType(parseCode("let f x = function Variant -> 1"), RPsiLet.class); RPsiFunction fun = (RPsiFunction) e.getBinding().getFirstChild(); RPsiSwitch shortcut = ORUtil.findImmediateFirstChildOfClass(fun.getBody(), RPsiSwitch.class); assertNotNull(ORUtil.findImmediateFirstChildOfClass(shortcut, RPsiPatternMatch.class)); assertEquals("1", PsiTreeUtil.findChildOfType(shortcut, RPsiPatternMatchBody.class).getText()); } @Test public void test_function_shortcut_many() { RPsiLet e = firstOfType( parseCode(""" let rec db_output_prodn = function | Sterm s -> if cond then first else second | Sedit2 ("FILE", file) -> let file_suffix_regex = a in printf "i" | Snterm s -> sprintf "(Snterm %s) " s """), RPsiLet.class); RPsiSwitch shortcut = PsiTreeUtil.findChildOfType(e, RPsiSwitch.class); List patterns = shortcut.getPatterns(); assertSize(3, patterns); assertEquals("Sterm s -> if cond then first else second", patterns.get(0).getText()); assertEquals("Sedit2 (\"FILE\", file) -> let file_suffix_regex = a in printf \"i\"", patterns.get(1).getText()); assertEquals("Snterm s -> sprintf \"(Snterm %s) \" s", patterns.get(2).getText()); } @Test public void test_group() { PsiFile psiFile = parseCode("let _ = match x with | V1(y) -> Some y | Empty | Unknown -> None"); RPsiSwitch e = first(PsiTreeUtil.findChildrenOfType(psiFile, RPsiSwitch.class)); List patterns = e.getPatterns(); assertSize(3, patterns); assertEquals("V1(y) -> Some y", patterns.get(0).getText()); assertEquals("Empty", patterns.get(1).getText()); assertEquals("Unknown -> None", patterns.get(2).getText()); } @Test public void test_semi() { RPsiPatternMatchBody e = firstOfType(parseCode("let _ = match x with | X -> let y = 1 in fn (y); print y"), RPsiPatternMatchBody.class); assertEquals("let y = 1 in fn (y); print y", e.getText()); } @Test // coq/analyze.ml public void test_begin_end() { RPsiPatternMatchBody e = firstOfType(parseCode(""" let _ = match (Obj.magic data) with | CODE_CUSTOM_FIXED -> begin match input_cstring chan with | "_j" -> Rint64 (input_intL chan) | s -> Printf.eprintf "Unhandled custom code: %s" s; assert false end"""), RPsiPatternMatchBody.class); assertEquals(""" begin match input_cstring chan with | "_j" -> Rint64 (input_intL chan) | s -> Printf.eprintf "Unhandled custom code: %s" s; assert false end""", e.getText()); RPsiSwitch m = PsiTreeUtil.findChildOfType(e, RPsiSwitch.class); assertEquals("input_cstring chan", m.getCondition().getText()); assertSize(2, m.getPatterns()); } @Test // coq/checker/checkInductive.ml public void test_match_or() { RPsiPatternMatch e = firstOfType(parseCode("let check_arity env ar1 ar2 = match ar1, ar2 with | (RegularArity _ | TemplateArity _), _ -> assert false"), RPsiPatternMatch.class); assertEquals("(RegularArity _ | TemplateArity _), _ -> assert false", e.getText()); assertEquals("assert false", e.getBody().getText()); } @Test // coq/checker/checkTypes.ml public void test_match_no_pipe() { RPsiPatternMatch e = firstOfType(parseCode("let _ = match pl, params with Some u::pl, LocalAssum (na,ty)::params -> check_kind env ty u"), RPsiPatternMatch.class); assertEquals("Some u::pl, LocalAssum (na,ty)::params -> check_kind env ty u", e.getText()); assertEquals("check_kind env ty u", e.getBody().getText()); } @Test // coq/checker/validate.ml public void test_match_02() { RPsiPatternMatch e = firstOfType(parseCode("let _ = match v with | List v0 -> val_sum \"list\" 1 [|[|Annot(\"elem\",v0);v|]|] mem ctx o"), RPsiPatternMatch.class); assertEquals("List v0 -> val_sum \"list\" 1 [|[|Annot(\"elem\",v0);v|]|] mem ctx o", e.getText()); assertEquals("val_sum \"list\" 1 [|[|Annot(\"elem\",v0);v|]|] mem ctx o", e.getBody().getText()); RPsiFunctionCall fc = ORUtil.findImmediateFirstChildOfClass(e.getBody(), RPsiFunctionCall.class); assertSize(6, fc.getParameters()); } // https://github.com/giraud/reasonml-idea-plugin/issues/312 @Test public void test_GH_312() { PsiFile f = parseCode("match fn ?arg with |None -> false |Some f -> true"); RPsiSwitch e = first(PsiTreeUtil.findChildrenOfType(f, RPsiSwitch.class)); List patterns = e.getPatterns(); assertSize(2, patterns); assertEquals("None -> false", patterns.get(0).getText()); assertEquals("Some f -> true", patterns.get(1).getText()); } // https://github.com/giraud/reasonml-idea-plugin/issues/340 // Not an object ! @Test public void test_GH_340() { List es = ORUtil.findImmediateChildrenOfClass(parseCode("let fn cond i j = match cond with | Some i, Some j -> i < j\n let fn2 s = ()"), RPsiLet.class); assertSize(2, es); assertEquals("let fn cond i j = match cond with | Some i, Some j -> i < j", es.get(0).getText()); assertEquals("let fn2 s = ()", es.get(1).getText()); } // https://github.com/giraud/reasonml-idea-plugin/issues/441 @Test public void test_GH_441() { RPsiSwitch e = firstOfType(parseCode(""" let _ = match cond with | Anonymous -> hov 0 (spc () ++ pbody0 ++ spc ()) | Name id -> hov 1 (spc () ++ pbody1 ++ spc ()) """), RPsiSwitch.class); assertSize(2, e.getPatterns()); RPsiPatternMatchBody eb0 = e.getPatterns().get(0).getBody(); assertEquals("hov 0 (spc () ++ pbody0 ++ spc ())", eb0.getText()); assertSize(2, ((RPsiFunctionCall) eb0.getFirstChild()).getParameters()); assertEquals("0", ((RPsiFunctionCall) eb0.getFirstChild()).getParameters().get(0).getText()); List eb0cs = new ArrayList<>(PsiTreeUtil.findChildrenOfType(((RPsiFunctionCall) eb0.getFirstChild()).getParameters().get(1), RPsiFunctionCall.class)); assertEquals("spc ()", eb0cs.get(0).getText()); assertEquals("spc ()", eb0cs.get(1).getText()); RPsiPatternMatchBody eb1 = e.getPatterns().get(1).getBody(); assertEquals("hov 1 (spc () ++ pbody1 ++ spc ())", eb1.getText()); assertSize(2, ((RPsiFunctionCall) eb1.getFirstChild()).getParameters()); assertEquals("1", ((RPsiFunctionCall) eb1.getFirstChild()).getParameters().get(0).getText()); List eb1cs = new ArrayList<>(PsiTreeUtil.findChildrenOfType(((RPsiFunctionCall) eb0.getFirstChild()).getParameters().get(1), RPsiFunctionCall.class)); assertEquals("spc ()", eb1cs.get(0).getText()); assertEquals("spc ()", eb1cs.get(1).getText()); } // https://github.com/giraud/reasonml-idea-plugin/issues/442 @Test public void test_GH_442() { RPsiFunction e = firstOfType(parseCode(""" let fn x = function | [] -> best | hd :: tl -> let y = 1 in if cond then expr1 else expr2 in stmt """), RPsiFunction.class); RPsiSwitch es = PsiTreeUtil.findChildOfType(e, RPsiSwitch.class); assertSize(2, es.getPatterns()); assertEquals("best", es.getPatterns().get(0).getBody().getText()); assertEquals(""" let y = 1 in if cond then expr1 else expr2""", es.getPatterns().get(1).getBody().getText()); } // https://github.com/giraud/reasonml-idea-plugin/issues/498 @Test public void test_GH_498() { RPsiLet e = firstOfType(parseCode(""" let fn = function | T -> hov 0 (str "Type error: " ++ (match te with | T1 ((n,a,b), {uj_val = hd; uj_type = hdty}, args) -> "t1" | T2 _ -> "t2" ))"""), RPsiLet.class); assertTrue(e.isFunction()); assertTextEquals(""" | T -> hov 0 (str "Type error: " ++ (match te with | T1 ((n,a,b), {uj_val = hd; uj_type = hdty}, args) -> "t1" | T2 _ -> "t2" ))""", e.getFunction().getBody().getText()); } } ================================================ FILE: src/test/java/com/reason/lang/ocaml/ModuleParsingTest.java ================================================ package com.reason.lang.ocaml; import com.intellij.psi.*; import com.intellij.psi.util.*; import com.reason.ide.files.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import org.junit.*; import java.util.*; @SuppressWarnings("ConstantConditions") public class ModuleParsingTest extends OclParsingTestCase { @Test public void test_empty() { Collection modules = moduleExpressions(parseCode("module M = struct end")); assertEquals(1, modules.size()); RPsiInnerModule e = first(modules); assertEquals("M", e.getName()); assertEquals(OclTypes.INSTANCE.A_MODULE_NAME, e.getNameIdentifier().getNode().getElementType()); assertEquals("Dummy.M", e.getQualifiedName()); assertEquals("struct end", e.getBody().getText()); } @Test public void test_alias() { RPsiInnerModule e = firstOfType(parseCode("module M = Y"), RPsiInnerModule.class); assertEquals("M", e.getName()); assertEquals("Y", e.getAlias()); assertEquals("Y", e.getAliasSymbol().getText()); assertEquals(myTypes.A_MODULE_NAME, e.getAliasSymbol().getNode().getElementType()); } @Test public void test_alias_path() { RPsiInnerModule e = firstOfType(parseCode("module M = Y.Z"), RPsiInnerModule.class); assertEquals("M", e.getName()); assertEquals("Y.Z", e.getAlias()); assertEquals("Z", e.getAliasSymbol().getText()); assertEquals(myTypes.A_MODULE_NAME, e.getAliasSymbol().getNode().getElementType()); } @Test public void test_module_type() { RPsiInnerModule e = first(moduleExpressions(parseCode("module type Intf = sig val x : bool end"))); assertEquals("Intf", e.getName()); assertTrue(e.isModuleType()); assertInstanceOf(e.getBody(), RPsiModuleBinding.class); assertEquals("bool", firstOfType(e, RPsiVal.class).getSignature().getText()); } @Test public void test_module_signature() { PsiFile file = parseCode("module Level : sig end\ntype t"); assertEquals(2, expressions(file).size()); RPsiInnerModule module = firstOfType(file, RPsiInnerModule.class); assertEquals("Level", module.getName()); assertEquals("sig end", module.getModuleSignature().getText()); } @Test public void test_module_chaining() { PsiFile file = parseCode("module A = sig type t end\nmodule B = struct end"); assertEquals(2, moduleExpressions(file).size()); } @Test public void test_signature_with_constraint() { // From coq: PCoq FileBase file = parseCode("module G : sig end\n with type 'a Entry.e = 'a Extend.entry = struct end"); assertEquals(1, expressions(file).size()); RPsiInnerModule e = firstOfType(file, RPsiInnerModule.class); assertEquals("G", e.getName()); assertEquals("sig end", e.getModuleSignature().getText()); assertEquals("struct end", e.getBody().getText()); } @Test public void test_module_constraint() { PsiFile file = parseCode("module Constraint : Set.S with type elt = univ_constraint\ntype t"); assertEquals(2, expressions(file).size()); RPsiInnerModule module = firstOfType(file, RPsiInnerModule.class); assertEquals("Constraint", module.getName()); RPsiModuleSignature modType = module.getModuleSignature(); assertEquals("Set.S", modType.getText()); } @Test public void test_interface_with_many_constraints() { RPsiInnerModule e = firstOfType(parseCode("module M : (I with type s := t and type u := v) = struct type w end"), RPsiInnerModule.class); assertEquals("M", e.getName()); assertEquals("I", e.getModuleSignature().getText()); assertEquals(myTypes.A_MODULE_NAME, e.getModuleSignature().getFirstChild().getNode().getElementType()); assertSize(2, e.getConstraints()); assertEquals("type s := t", e.getConstraints().get(0).getText()); assertEquals("type u := v", e.getConstraints().get(1).getText()); assertEquals("struct type w end", e.getBody().getText()); } @Test public void test_module_alias_body() { PsiFile file = parseCode("module M = struct module O = B.Option let _ = O.m end"); assertEquals(1, expressions(file).size()); RPsiInnerModule e = first(moduleExpressions(file)); assertEquals("M", e.getName()); Collection expressions = PsiTreeUtil.findChildrenOfType(e.getBody(), RPsiQualifiedPathElement.class); assertSize(2, expressions); } @Test public void test_rec_signature() { RPsiInnerModule e = firstOfType(parseCode(""" module rec A : sig type output = (Constr.constr * UState.t) option type task end = struct end """), RPsiInnerModule.class); assertEquals("A", e.getName()); RPsiModuleSignature es = e.getModuleSignature(); assertNull(PsiTreeUtil.findChildOfType(es, RPsiSignature.class)); assertEquals("sig\n type output = (Constr.constr * UState.t) option\n type task\nend", es.getText()); assertEquals("struct end", e.getBody().getText()); } @Test public void test_decode_first_class_module_with_in() { RPsiInnerModule e = firstOfType(parseCode("let module Visit = Visit(Repr) in printf x"), RPsiInnerModule.class); assertFalse(e instanceof RPsiFunctor); assertEquals("Visit", e.getName()); assertEquals("Visit(Repr)", e.getBody().getText()); assertNull(PsiTreeUtil.findChildOfType(e, RPsiVal.class)); } @Test // coq::clib/cArray.ml public void test_signature() { RPsiModuleSignature e = firstOfType(parseCode("module Smart : sig val map : ('a -> 'a) -> 'a array -> 'a array end"), RPsiModuleSignature.class); RPsiVal ev = PsiTreeUtil.findChildOfType(e, RPsiVal.class); assertNoParserError(ev); assertEquals("map", ev.getName()); assertEquals("('a -> 'a) -> 'a array -> 'a array", ev.getSignature().getText()); } @Test public void test_signature_many() { List es = childrenOfType(parseCode(""" module A: sig val a: int end module B: sig val b: int end """), RPsiModuleSignature.class); RPsiModuleSignature e0 = es.get(0); assertNoParserError(e0); assertEquals("sig val a: int end", e0.getText()); RPsiModuleSignature e1 = es.get(1); assertNoParserError(e1); assertEquals("sig val b: int end", e1.getText()); } // https://github.com/giraud/reasonml-idea-plugin/issues/91 @Test public void test_GH_91() { FileBase file = parseCode("module Branch : (module type of Vcs_.Branch with type t = Vcs_.Branch.t)\ntype id"); assertEquals(2, expressions(file).size()); assertEquals("Branch", first(moduleExpressions(file)).getName()); RPsiInnerModule e = (RPsiInnerModule) expressions(file).iterator().next(); RPsiModuleSignature modType = e.getModuleSignature(); assertEquals("module type of Vcs_.Branch", modType.getText()); assertNull(PsiTreeUtil.findChildOfType(modType, RPsiInnerModule.class)); assertEquals("Branch", modType.getName()); //assertEquals("Vcs_.Branch", modType.getQualifiedName()); } } ================================================ FILE: src/test/java/com/reason/lang/ocaml/OclParsingTestCase.java ================================================ package com.reason.lang.ocaml; import com.intellij.lang.*; import com.intellij.psi.stubs.*; import com.reason.lang.*; import com.reason.lang.core.stub.*; import com.reason.lang.core.type.*; abstract class OclParsingTestCase extends BaseParsingTestCase { public ORLangTypes myTypes = OclTypes.INSTANCE; protected OclParsingTestCase() { super("", "ml", new OclParserDefinition()); } @Override protected void setUp() throws Exception { super.setUp(); StubElementTypeHolderEP stubElementTypeHolderEP = new StubElementTypeHolderEP(); stubElementTypeHolderEP.holderClass = OclStubBasedElementTypes.class.getName(); registerExtension(StubElementTypeHolderEP.EP_NAME, stubElementTypeHolderEP); LanguageASTFactory.INSTANCE.addExplicitExtension(OclLanguage.INSTANCE, new OclASTFactory()); } protected ORLanguageProperties getLangProps() { return ORLanguageProperties.cast(myLanguage); } } ================================================ FILE: src/test/java/com/reason/lang/ocaml/OpenParsingTest.java ================================================ package com.reason.lang.ocaml; import com.intellij.psi.util.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import org.junit.*; @SuppressWarnings("ConstantConditions") public class OpenParsingTest extends OclParsingTestCase { @Test public void test_one() { RPsiOpen e = firstOfType(parseCode("open Belt"), RPsiOpen.class); assertNull(PsiTreeUtil.findChildOfType(e, RPsiFunctorCall.class)); assertEquals("Belt", e.getPath()); assertEquals("Belt", ORUtil.findImmediateLastChildOfType(e, myTypes.A_MODULE_NAME).getText()); } @Test public void test_path() { RPsiOpen e = firstOfType(parseCode("open Belt.Array"), RPsiOpen.class); assertEquals("Belt.Array", e.getPath()); assertEquals("Array", ORUtil.findImmediateLastChildOfType(e, myTypes.A_MODULE_NAME).getText()); } @Test public void test_chaining() { RPsiOpen e = firstOfType(parseCode("open Belt Array"), RPsiOpen.class); assertEquals("Belt", e.getPath()); } @Test public void test_eol() { RPsiOpen e = firstOfType(parseCode("open Core.Async\nlet x = 1"), RPsiOpen.class); assertEquals("Core.Async", e.getPath()); } @Test public void test_functor() { RPsiOpen e = firstOfType(parseCode("open Make(struct type t end)"), RPsiOpen.class); assertTrue(e.useFunctor()); RPsiFunctorCall c = PsiTreeUtil.findChildOfType(e, RPsiFunctorCall.class); assertEquals("Make", c.getName()); assertEquals(myTypes.A_MODULE_NAME, c.getReferenceIdentifier().getNode().getElementType()); assertEquals("Make", e.getPath()); } @Test public void test_functor_with_path() { RPsiOpen e = firstOfType(parseCode("open A.Make(struct type t end)"), RPsiOpen.class); assertTrue(e.useFunctor()); assertEquals("Make", PsiTreeUtil.findChildOfType(e, RPsiFunctorCall.class).getName()); assertEquals("A.Make", e.getPath()); } } ================================================ FILE: src/test/java/com/reason/lang/ocaml/OperatorParsingTest.java ================================================ package com.reason.lang.ocaml; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import org.junit.*; import java.util.*; public class OperatorParsingTest extends OclParsingTestCase { // https://github.com/giraud/reasonml-idea-plugin/issues/314 @Test public void test_GH_314_structural_difference() { List lets = ORUtil.findImmediateChildrenOfClass(parseCode("let is_traced () = !debug <> DebugOff\n let name_vfun () = ()"), RPsiLet.class); assertSize(2, lets); assertEquals("is_traced", lets.get(0).getName()); assertEquals("name_vfun", lets.get(1).getName()); } } ================================================ FILE: src/test/java/com/reason/lang/ocaml/PolyVariantParsingTest.java ================================================ package com.reason.lang.ocaml; import com.intellij.psi.*; import com.intellij.psi.util.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import org.junit.*; import java.util.*; @SuppressWarnings("ConstantConditions") public class PolyVariantParsingTest extends OclParsingTestCase { @Test public void test_basic_let_LIdent() { RPsiLet e = firstOfType(parseCode("let x = `red"), RPsiLet.class); RPsiLowerSymbol ev = firstOfType(e.getBinding(), RPsiLowerSymbol.class); assertEquals("`red", ev.getText()); assertEquals(myTypes.POLY_VARIANT, ev.getNode().getElementType()); } @Test public void test_basic_let_UIdent() { RPsiUpperSymbol e = firstOfType(parseCode("let _ = `Red"), RPsiUpperSymbol.class); assertEquals("`Red", e.getText()); assertEquals(myTypes.POLY_VARIANT, e.getNode().getElementType()); } @Test public void test_basic_type_LIdent() { RPsiType e = firstOfType(parseCode("type t = [ | `red | `blue | `yellow ]"), RPsiType.class); List evs = new ArrayList<>(e.getVariants()); assertSize(3, evs); assertEquals("#red", evs.getFirst().getName()); assertEquals("#blue", evs.get(1).getName()); assertEquals("#yellow", evs.get(2).getName()); } @Test public void test_basic_type_UIdent() { RPsiType e = firstOfType(parseCode("type t = [ | `Red | `Blue | `Yellow ]"), RPsiType.class); List evs = new ArrayList<>(e.getVariants()); assertSize(3, evs); assertEquals("#Red", evs.getFirst().getName()); assertEquals("#Blue", evs.get(1).getName()); assertEquals("#Yellow", evs.get(2).getName()); } @Test public void test_pattern_match_constant() { PsiFile psiFile = parseCode("let unwrapValue = fun | `String s -> toJsUnsafe s | `bool b -> toJsUnsafe (Js.Boolean.to_js_boolean b)"); Collection expressions = expressions(psiFile); assertEquals(1, expressions.size()); Collection matches = PsiTreeUtil.findChildrenOfType(first(expressions), RPsiPatternMatch.class); assertEquals(2, matches.size()); } @Test public void test_open_variant() { RPsiType e = firstOfType(parseCode("type t = [> `a | Other.t | `c ]"), RPsiType.class); RPsiPolyVariantConstraint c = PsiTreeUtil.findChildOfType(e, RPsiPolyVariantConstraint.class); assertTrue(c.isOpen()); assertSize(3, PsiTreeUtil.findChildrenOfType(c, RPsiVariantDeclaration.class)); } @Test public void test_closed_variant() { RPsiType e = firstOfType(parseCode("type t = [< `a | Other.t | `c ]"), RPsiType.class); RPsiPolyVariantConstraint c = PsiTreeUtil.findChildOfType(e, RPsiPolyVariantConstraint.class); assertFalse(c.isOpen()); assertSize(3, PsiTreeUtil.findChildrenOfType(c, RPsiVariantDeclaration.class)); } @Test public void test_with_path() { RPsiPolyVariantConstraint e = firstOfType(parseCode("let visibility: [< Css.Types.Length.t | Css.Types.Visibility.t ] => layoutRule"), RPsiPolyVariantConstraint.class); List v = new ArrayList<>(PsiTreeUtil.findChildrenOfType(e, RPsiVariantDeclaration.class)); assertSize(2, v); assertEquals("Css.Types.Length.t", v.get(0).getText()); assertEquals("Css.Types.Visibility.t", v.get(1).getText()); } } ================================================ FILE: src/test/java/com/reason/lang/ocaml/RecordParsingTest.java ================================================ package com.reason.lang.ocaml; import com.intellij.psi.util.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import org.junit.*; import java.util.*; @SuppressWarnings("ConstantConditions") public class RecordParsingTest extends OclParsingTestCase { @Test public void test_declaration() { RPsiType e = firstOfType(parseCode("type r = { a: int; b: string list }"), RPsiType.class); RPsiRecord record = (RPsiRecord) e.getBinding().getFirstChild(); List fields = record.getFields(); assertEquals("a", fields.get(0).getName()); assertEquals("int", fields.get(0).getSignature().asText(getLangProps())); assertEquals("b", fields.get(1).getName()); assertEquals("string list", fields.get(1).getSignature().asText(getLangProps())); } @Test public void test_usage() { RPsiLet e = firstOfType(parseCode("let r = { a = 1; b = 2; c = 3 }"), RPsiLet.class); RPsiRecord record = (RPsiRecord) e.getBinding().getFirstChild(); List fields = record.getFields(); assertSize(3, fields); assertEquals("a", fields.get(0).getName()); assertEquals("1", fields.get(0).getValue().getText()); assertNull(fields.get(0).getSignature()); assertEquals("b", fields.get(1).getName()); assertEquals("2", fields.get(1).getValue().getText()); assertNull(fields.get(1).getSignature()); assertEquals("c", fields.get(2).getName()); assertEquals("3", fields.get(2).getValue().getText()); assertNull(fields.get(2).getSignature()); } @Test public void test_usage_deep() { RPsiLet e = firstOfType(parseCode("let r = { a = [| 1; 2 |]; b = { b1 = { b11 = 3 } }; c = 4 }"), RPsiLet.class); RPsiRecord record = (RPsiRecord) e.getBinding().getFirstChild(); List fields = new ArrayList<>(record.getFields()); assertSize(3, fields); assertEquals("a", fields.get(0).getName()); assertEquals("[| 1; 2 |]", fields.get(0).getValue().getText()); assertEquals("b", fields.get(1).getName()); assertEquals("{ b1 = { b11 = 3 } }", fields.get(1).getValue().getText()); assertEquals("c", fields.get(2).getName()); assertEquals("4", fields.get(2).getValue().getText()); List allFields = new ArrayList<>(PsiTreeUtil.findChildrenOfType(fields.get(1), RPsiRecordField.class)); assertEquals("b1", allFields.get(0).getName()); assertEquals("b11", allFields.get(1).getName()); } @Test public void test_mixin() { RPsiRecord e = firstOfType(parseCode("let x = { component with otherField = 1}"), RPsiRecord.class); assertEquals("component", PsiTreeUtil.findChildOfType(e, RPsiMixinField.class).getText()); RPsiRecordField field = e.getFields().iterator().next(); assertEquals("otherField", field.getName()); } @Test public void test_annotations() { RPsiType e = firstOfType(parseCode("type props = { key: string [@bs.optional]; ariaLabel: string [@bs.optional] [@bs.as \"aria-label\"]; }"), RPsiType.class); RPsiRecord record = (RPsiRecord) e.getBinding().getFirstChild(); List fields = new ArrayList<>(record.getFields()); assertSize(2, fields); assertEquals("key", fields.get(0).getName()); assertEquals("ariaLabel", fields.get(1).getName()); } // https://github.com/giraud/reasonml-idea-plugin/issues/440 @Test public void test_GH_440() { RPsiFunction e = firstOfType(parseCode("let fn {a; b=(x,y)} = a"), RPsiFunction.class); assertSize(1, e.getParameters()); RPsiParameterDeclaration ep0 = e.getParameters().get(0); RPsiRecord r = (RPsiRecord) ep0.getFirstChild(); assertSize(2, r.getFields()); RPsiRecordField f0 = r.getFields().get(0); assertEquals("a", f0.getName()); assertNull(f0.getValue()); RPsiRecordField f1 = r.getFields().get(1); assertEquals("b", f1.getName()); RPsiFieldValue value = f1.getValue(); RPsiTuple f1t = (RPsiTuple) value.getFirstChild(); assertEquals("(x,y)", f1t.getText()); assertEquals("a", e.getBody().getText()); } } ================================================ FILE: src/test/java/com/reason/lang/ocaml/SamplesParsingTest.java ================================================ package com.reason.lang.ocaml; import org.jetbrains.annotations.*; import org.junit.*; import java.io.*; public class SamplesParsingTest extends OclParsingTestCase { @Override protected @NotNull String getTestDataPath() { return "src/test/testData/com/reason/lang/samples"; } @Test public void test_stream() throws IOException { parseFile("stream"); } @Test public void test_belt_map() throws IOException { parseFile("belt_Map"); } } ================================================ FILE: src/test/java/com/reason/lang/ocaml/SignatureParsingTest.java ================================================ package com.reason.lang.ocaml; import com.intellij.psi.tree.*; import com.intellij.psi.util.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import org.junit.*; import java.util.*; @SuppressWarnings("ConstantConditions") public class SignatureParsingTest extends OclParsingTestCase { @Test public void test_let() { RPsiLet e = firstOfType(parseCode("let x:int = 1"), RPsiLet.class); RPsiSignature signature = e.getSignature(); assertEquals("int", signature.asText(getLangProps())); assertFalse(signature.getItems().get(0).isOptional()); } @Test public void test_OCamlBeforeDirective() { RPsiVal e = firstOfType(parseCode("val bool_of_string_opt : string -> bool option\n(** This is a comment *)\n\n#if BS then\n#end"), RPsiVal.class); RPsiSignature signature = e.getSignature(); assertEquals("string -> bool option", signature.asText(getLangProps())); } @Test public void test_val() { RPsiVal e = firstOfType(parseCode("val map : 'a option -> ('a -> 'b) -> 'b option"), RPsiVal.class); RPsiSignature signature = e.getSignature(); List items = signature.getItems(); assertEquals("'a option -> ('a -> 'b) -> 'b option", signature.asText(getLangProps())); assertFalse(items.get(0).isOptional()); assertFalse(items.get(1).isOptional()); assertFalse(items.get(2).isOptional()); } @Test public void test_trimming() { RPsiLet let = firstOfType( parseCode("let statelessComponent:\n string ->\n componentSpec(\n stateless,\n stateless,\n noRetainedProps,\n noRetainedProps,\n actionless,\n )\n"), RPsiLet.class); RPsiSignature signature = let.getSignature(); assertEquals("string -> componentSpec(stateless, stateless, noRetainedProps, noRetainedProps, actionless)", signature.asText(getLangProps())); } @Test public void test_parsing_named_params() { RPsiLet let = firstOfType(parseCode("let padding: v:length -> h:length -> rule"), RPsiLet.class); RPsiSignature signature = let.getSignature(); assertEquals(3, signature.getItems().size()); assertEquals("v:length -> h:length -> rule", signature.asText(getLangProps())); assertFalse(signature.getItems().get(0).isOptional()); assertEquals("v", signature.getItems().get(0).getName()); assertFalse(signature.getItems().get(1).isOptional()); assertEquals("h", signature.getItems().get(1).getName()); assertEquals("rule", signature.getItems().get(2).getText()); } @Test public void test_optional_fun() { RPsiLet let = firstOfType(parseCode("let x: int -> string option -> string = fun a -> fun b -> c"), RPsiLet.class); RPsiSignature signature = let.getSignature(); assertEquals("int -> string option -> string", signature.asText(getLangProps())); List items = let.getSignature().getItems(); assertEquals("int", items.get(0).getText()); assertFalse(items.get(0).isOptional()); assertEquals("string option", items.get(1).getText()); assertFalse(items.get(1).isOptional()); assertEquals("string", items.get(2).getText()); assertSize(3, items); } @Test public void test_optional_fun_parameters() { RPsiLet let = firstOfType(parseCode("let x a b ?(c= false) ?(d= 1.) = 3"), RPsiLet.class); RPsiFunction function = (RPsiFunction) let.getBinding().getFirstChild(); List parameters = new ArrayList<>(function.getParameters()); assertSize(4, parameters); assertFalse(parameters.get(0).isOptional()); assertFalse(parameters.get(1).isOptional()); assertTrue(parameters.get(2).isOptional()); assertEquals("Dummy.x[c]", parameters.get(2).getQualifiedName()); assertEquals("false", parameters.get(2).getDefaultValue().getText()); assertTrue(parameters.get(3).isOptional()); assertEquals("Dummy.x[d]", parameters.get(3).getQualifiedName()); assertEquals("1.", parameters.get(3).getDefaultValue().getText()); } @Test public void test_optional_fun_parameters_typed() { RPsiLet let = firstOfType(parseCode("let x (a : int) (b : string option) ?c:((c : bool)= false) ?d:((d : float)=1.) = 3"), RPsiLet.class); //val lcs : -> t RPsiFunction function = (RPsiFunction) let.getBinding().getFirstChild(); List parameters = new ArrayList<>(function.getParameters()); assertSize(4, parameters); assertEquals("Dummy.x[a]", parameters.get(0).getQualifiedName()); assertFalse(parameters.get(0).isOptional()); assertEquals("Dummy.x[b]", parameters.get(1).getQualifiedName()); assertFalse(parameters.get(1).isOptional()); assertEquals("Dummy.x[c]", parameters.get(2).getQualifiedName()); assertEquals("bool", parameters.get(2).getSignature().asText(getLangProps())); assertEquals("false", parameters.get(2).getDefaultValue().getText()); assertTrue(parameters.get(2).isOptional()); assertEquals("Dummy.x[d]", parameters.get(3).getQualifiedName()); assertEquals("float", parameters.get(3).getSignature().asText(getLangProps())); assertEquals("1.", parameters.get(3).getDefaultValue().getText()); assertTrue(parameters.get(3).isOptional()); } @Test public void test_named_optional_in_val() { RPsiSignature e = firstOfType(parseCode("val diff: ?equal:(elem -> bool) -> t"), RPsiSignature.class); assertNoParserError(e); assertSize(2, e.getItems()); assertEquals("?equal:(elem -> bool)", e.getItems().get(0).getText()); assertEquals("t", e.getItems().get(1).getText()); } @Test public void test_unit_fun_parameter() { RPsiLet e = firstOfType(parseCode("let x (a : int) () = a"), RPsiLet.class); RPsiFunction function = (RPsiFunction) e.getBinding().getFirstChild(); List parameters = new ArrayList<>(function.getParameters()); assertSize(2, parameters); assertEquals("(a : int)", parameters.get(0).getText()); assertEquals("()", parameters.get(1).getText()); } @Test public void test_signature_items() { RPsiLet e = firstOfType(parseCode("let createAction: < children : React.element; dispatch : ([ `Arity_1 of Redux.Actions.opaqueFsa ], unit) Js.Internal.fn; url : 'url > Js.t -> React.element;"), RPsiLet.class); RPsiSignature signature = e.getSignature(); assertEquals(2, signature.getItems().size()); } @Test public void test_jsObject() { RPsiLet e = firstOfType(parseCode("let x: < a: string; b: 'a > Js.t -> string"), RPsiLet.class); RPsiSignature signature = e.getSignature(); assertEquals(2, signature.getItems().size()); RPsiSignatureItem jsObj = signature.getItems().get(0); List fields = new ArrayList<>(PsiTreeUtil.findChildrenOfType(jsObj, RPsiObjectField.class)); assertSize(2, fields); assertEquals(fields.get(0).getName(), "a"); assertEquals(fields.get(1).getName(), "b"); } @Test public void test_option() { RPsiVal e = firstOfType(parseCode("val x: string array option"), RPsiVal.class); RPsiOption option = PsiTreeUtil.findChildOfType(e, RPsiOption.class); assertEquals("string array option", option.getText()); } @Test public void test_option_of_option() { List es = childrenOfType(parseCode("val view : 'a t -> ('a option * 'a t) option"), RPsiSignatureItem.class); RPsiSignatureItem e0 = es.get(0); RPsiSignatureItem e1 = es.get(1); RPsiOption e1o = PsiTreeUtil.findChildOfType(e1, RPsiOption.class); RPsiOption e1oo = PsiTreeUtil.findChildOfType(e1o, RPsiOption.class); assertNoParserError(e0); assertNoParserError(e1); assertEquals("'a t", e0.getText()); assertEquals("('a option * 'a t) option", e1.getText()); assertEquals("('a option * 'a t) option", e1o.getText()); assertEquals("'a option", e1oo.getText()); } @Test public void test_option_named_params() { RPsiExternal e = firstOfType(parseCode("external add : x:int option -> int = \"\""), RPsiExternal.class); RPsiOption option = PsiTreeUtil.findChildOfType(e, RPsiOption.class); assertEquals("int option", option.getText()); } @Test // coq:: clib/diff2.mli public void test_functor() { RPsiFunctor e = firstOfType(parseCode("module M: functor (I: T) -> (S with type t = I.t)"), RPsiFunctor.class); assertNoParserError(e); assertEquals("M", e.getName()); assertEquals("S", e.getReturnType().getText()); assertNotEmpty(e.getConstraints()); } @Test public void test_closed_variant() { RPsiLet e = firstOfType(parseCode("let x: [< Css.Types.Length.t | Css.Types.Visibility.t] -> unit = fun _ -> ()"), RPsiLet.class); List et = extractUpperSymbolTypes(e); assertDoesntContain(et, myTypes.A_VARIANT_NAME, myTypes.UIDENT); assertContainsElements(et, myTypes.A_MODULE_NAME); } @Test public void test_open_variant() { RPsiLet e = firstOfType(parseCode("let x: [> Css.Types.Length.t | Css.Types.Visibility.t] -> unit = fun _ -> ()"), RPsiLet.class); List et = extractUpperSymbolTypes(e); assertDoesntContain(et, myTypes.A_VARIANT_NAME, myTypes.UIDENT); assertContainsElements(et, myTypes.A_MODULE_NAME); } } ================================================ FILE: src/test/java/com/reason/lang/ocaml/TryWithParsingTest.java ================================================ package com.reason.lang.ocaml; import com.intellij.psi.*; import com.reason.ide.files.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.impl.*; import org.junit.*; @SuppressWarnings("ConstantConditions") public class TryWithParsingTest extends OclParsingTestCase { @Test public void basic() { RPsiTry e = firstOfType(parseCode("try x with Not_found -> ()"), RPsiTry.class); assertEquals("try", e.getFirstChild().getText()); assertNotNull(e.getBody()); assertEquals("x", e.getBody().getText()); assertNotNull(e.getHandlers()); assertSize(1, e.getHandlers()); RPsiTryHandler eh = e.getHandlers().get(0); assertEquals("Not_found -> ()", eh.getText()); assertEquals("()", eh.getBody().getText()); assertEquals(myTypes.A_EXCEPTION_NAME, eh.getFirstChild().getNode().getElementType()); } @Test public void test_in() { FileBase file = parseCode("try x with Not_found -> assert false in otherExpression"); assertEquals(1, childrenCount(file)); } @Test // coq/util.ml public void test_call() { RPsiTry e = firstOfType(parseCode("let system_getenv name = try Sys.getenv name with Not_found -> getenv_from_file name "), RPsiTry.class); assertEquals("Sys.getenv name", e.getBody().getText()); assertSize(1, e.getHandlers()); RPsiTryHandler handler0 = e.getHandlers().get(0); assertEquals("Not_found -> getenv_from_file name", handler0.getText()); assertEquals("getenv_from_file name", handler0.getBody().getText()); } @Test public void test_let() { FileBase file = parseCode("let e = try let t = 6 with Not_found -> ()"); assertEquals(1, childrenCount(file)); } @Test public void test_try() { RPsiTry try_ = firstOfType(parseCode("try f() with e -> let e = CErrors.push e"), RPsiTry.class); assertEquals("e -> let e = CErrors.push e", try_.getHandlers().get(0).getText()); } @Test // coq/util.ml public void test_semi() { RPsiTry e = firstOfType(parseCode("let _ = try\n let rc = f ic in\n close_in ic;\n rc\n with e -> close_in ic; raise e"), RPsiTry.class); assertEquals("let rc = f ic in\n close_in ic;\n rc", e.getBody().getText()); assertEquals("e -> close_in ic; raise e", e.getHandlers().get(0).getText()); } @Test // coq/util.ml public void test_util() { RPsiTry e = firstOfType(parseCode("let getenv_from_file name =\n" + " try\n" + " with_ic (base ^ \"/coq_environment.txt\") (fun ic ->\n" + " let rec find () =\n" + " match cond with\n" + " | _ -> ()\n" + " in\n" + " find ())\n" + " with\n" + " | Sys_error s -> let () = close_in chan in fatal msg\n" + " | End_of_file -> raise Not_found\n"), RPsiTry.class); assertEquals("with_ic (base ^ \"/coq_environment.txt\") (fun ic ->\n" + " let rec find () =\n" + " match cond with\n" + " | _ -> ()\n" + " in\n" + " find ())" , e.getBody().getText()); RPsiFunctionCall fc = ORUtil.findImmediateFirstChildOfClass(e.getBody(), RPsiFunctionCall.class); assertEquals("with_ic", fc.getName()); assertSize(2, fc.getParameters()); assertSize(2, e.getHandlers()); } @Test public void test_GH_256() { RPsiTry e = firstOfType(parseCode("try find nt with Not_found -> (error \"Missing nt '%s' for splice\" nt; []) in let splice_prods = xxx"), RPsiTry.class); PsiElement handlers = ORUtil.findImmediateFirstChildOfType(e, myTypes.C_TRY_HANDLERS); assertEquals("Not_found -> (error \"Missing nt '%s' for splice\" nt; [])", handlers.getText()); } } ================================================ FILE: src/test/java/com/reason/lang/ocaml/TypeParsingTest.java ================================================ package com.reason.lang.ocaml; import com.intellij.psi.*; import com.intellij.psi.tree.*; import com.intellij.psi.util.*; import com.reason.ide.files.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import org.junit.*; import java.util.*; import java.util.stream.*; import static com.intellij.psi.util.PsiTreeUtil.*; @SuppressWarnings("ConstantConditions") public class TypeParsingTest extends OclParsingTestCase { @Test public void test_abstract_type() { RPsiType type = first(typeExpressions(parseCode("type t"))); assertEquals("t", type.getName()); } @Test public void test_recursive_type() { RPsiType type = first(typeExpressions(parseCode("type rec 'a tree = | Leaf of 'a | Tree of 'a tree * 'a tree"))); assertEquals("tree", type.getName()); } @Test public void test_path() { RPsiType e = first(typeExpressions(parseCode("type t = A.B.other"))); assertEquals("t", e.getName()); assertFalse(e.isAbstract()); assertEquals("A.B.other", e.getBinding().getText()); assertNull(PsiTreeUtil.findChildOfType(e, RPsiVariantDeclaration.class)); List modules = ORUtil.findImmediateChildrenOfClass(e.getBinding(), RPsiUpperSymbol.class); assertSize(2, modules); List es = modules.stream().map(u -> u.getNode().getElementType()).collect(Collectors.toList()); assertEquals(List.of(myTypes.A_MODULE_NAME, myTypes.A_MODULE_NAME), es); } @Test public void test_option() { RPsiType e = first(typeExpressions(parseCode("type t = string array option"))); RPsiOption option = PsiTreeUtil.findChildOfType(e, RPsiOption.class); assertNotNull(option); assertEquals("string array option", option.getText()); } @Test public void test_bindingWithVariant() { RPsiType e = first(typeExpressions(parseCode("type t = | Tick"))); RPsiTypeBinding binding = first(findChildrenOfType(e, RPsiTypeBinding.class)); assertNotNull(binding); } @Test public void test_extensible_variant() { RPsiType e = firstOfType(parseCode("type Mod.variant +=\n | Add of Path.t List.t"), RPsiType.class); assertNoParserError(e); assertEquals("variant", e.getName()); assertEquals("| Add of Path.t List.t", e.getBinding().getText()); } @Test public void test_binding_with_record() { RPsiTypeBinding e = firstOfType(parseCode(""" type 'a t = { string_f_apply: ('a -> unit); string_help: string; list_f_edit: ('a -> 'a) option; } """), RPsiTypeBinding.class); RPsiRecord er = ORUtil.findImmediateFirstChildOfClass(e, RPsiRecord.class); assertNoParserError(e); assertSize(3, er.getFields()); RPsiRecordField er0 = er.getFields().get(0); List er0i = er0.getSignature().getItems(); assertEquals("string_f_apply", er0.getName()); assertSize(2, er0i); RPsiRecordField er1 = er.getFields().get(1); assertEquals("string_help", er1.getName()); List er1i = er1.getSignature().getItems(); assertSize(1, er1i); assertEquals("string", er1i.get(0).getText()); RPsiRecordField er2 = er.getFields().get(2); assertEquals("list_f_edit", er2.getName()); List er2i = er2.getSignature().getItems(); assertSize(1, er2i); assertInstanceOf(er2i.get(0).getFirstChild(), RPsiOption.class); assertEquals("('a -> 'a) option", er2i.get(0).getText()); } @Test public void test_binding_with_functor() { RPsiFunctorCall e = firstOfType(parseCode("val domain : 'a map -> Set.Make(M).t"), RPsiFunctorCall.class); assertNoParserError(e); assertEquals("Make(M)", e.getText()); } @Test public void test_type_special_props() { RPsiType e = first(typeExpressions(parseCode("type props = { " + "string: string; " + "ref: Dom.element Js.nullable => unit; " + "method: string; }"))); RPsiRecord record = (RPsiRecord) e.getBinding().getFirstChild(); List fields = record.getFields(); assertEquals(3, fields.size()); assertEquals("string", fields.get(0).getName()); assertEquals(myTypes.LIDENT, fields.get(0).getNameIdentifier().getNode().getElementType()); assertEquals("string", fields.get(0).getSignature().getText()); assertEquals("ref", fields.get(1).getName()); assertEquals(myTypes.LIDENT, fields.get(1).getNameIdentifier().getNode().getElementType()); assertEquals("Dom.element Js.nullable => unit", fields.get(1).getSignature().getText()); assertEquals("method", fields.get(2).getName()); assertEquals(myTypes.LIDENT, fields.get(2).getNameIdentifier().getNode().getElementType()); assertEquals("string", fields.get(2).getSignature().getText()); } @Test public void test_closed_object() { RPsiType e = first(typeExpressions(parseCode("type t = \n type x"))); PsiElement b = e.getBinding(); assertEquals("", b.getText()); assertInstanceOf(b.getFirstChild(), RPsiObject.class); } @Test public void test_open_object() { RPsiType e = first(typeExpressions(parseCode("type 'a t = < .. > as 'a\n type x"))); PsiElement b = e.getBinding(); assertEquals("< .. > as 'a", b.getText()); assertInstanceOf(b.getFirstChild(), RPsiObject.class); } @Test public void test_binding_with_record_as() { RPsiTypeBinding typeBinding = first(findChildrenOfType(first(typeExpressions(parseCode( "type 'branch_type branch_info = { kind : [> `Master] as 'branch_type; pos : id; }"))), RPsiTypeBinding.class)); RPsiRecord record = PsiTreeUtil.findChildOfType(typeBinding, RPsiRecord.class); List fields = new ArrayList<>(record.getFields()); assertEquals(2, fields.size()); assertEquals("kind", fields.get(0).getName()); assertEquals("pos", fields.get(1).getName()); } @Test public void test_chain_definitions() { FileBase file = parseCode("type 'branch_type branch_info = 'branch_type Vcs_.branch_info = { kind: [> `Master] as 'branch_type; root: id; pos: id; }"); assertEquals(1, childrenCount(file)); } @Test public void test_parameterized() { RPsiType e = first(typeExpressions(parseCode("type ('a, 'b) declaration_arity = RegularArity of 'a * 'b"))); assertEquals("declaration_arity", e.getName()); assertEquals("RegularArity of 'a * 'b", e.getBinding().getText()); assertSize(2, e.getParameters().getParametersList()); assertEquals("'a", e.getParameters().getParametersList().get(0).getText()); assertEquals("'b", e.getParameters().getParametersList().get(1).getText()); RPsiVariantDeclaration ev = PsiTreeUtil.findChildOfType(e.getBinding(), RPsiVariantDeclaration.class); assertSize(2, ev.getParametersList()); assertEquals("'a", ev.getParametersList().get(0).getText()); assertEquals("'b", ev.getParametersList().get(1).getText()); } @Test public void test_apply_params() { RPsiType e = first(typeExpressions(parseCode("type 'value t = (key,'value,Comparator.identity) Belt.Map.t"))); assertEmpty(PsiTreeUtil.findChildrenOfType(e, RPsiParameters.class)); } @Test public void test_qname_functor() { RPsiType e = first(typeExpressions(parseCode(""" module Coll = Hash.Make(struct type nonrec t = t let equal = Bsb_pkg_types.equal let hash (x : t) = Hashtbl.hash x end)"""))); assertEquals("Dummy.Coll.Make[0].t", e.getQualifiedName()); } // https://github.com/giraud/reasonml-idea-plugin/issues/295 @Test public void test_GH_295() { Collection es = expressions(parseCode("module Set = struct end type x = string")); assertSize(2, es); Iterator it = es.iterator(); assertInstanceOf(it.next(), RPsiInnerModule.class); assertInstanceOf(it.next(), RPsiType.class); } // https://github.com/giraud/reasonml-idea-plugin/issues/326 @Test public void test_GH_326() { RPsiType e = firstOfType(parseCode("type t = { buffer: GText.buffer; mutable breakpoints: breakpoint list }"), RPsiType.class); RPsiRecord r = (RPsiRecord) e.getBinding().getFirstChild(); List f = r.getFields(); assertSize(2, f); assertEquals("buffer", f.get(0).getName()); assertEquals("breakpoints", f.get(1).getName()); } // https://github.com/giraud/reasonml-idea-plugin/issues/360 @Test public void test_GH_360() { RPsiVal e = firstOfType(parseCode("val push : exn -> Exninfo.iexn\n [@@ocaml.deprecated \"please use [Exninfo.capture]\"]"), RPsiVal.class); RPsiSignature signature = e.getSignature(); assertEquals("exn -> Exninfo.iexn", signature.getText()); } // https://github.com/giraud/reasonml-idea-plugin/issues/423 @Test public void test_GH_423() { RPsiType e = firstOfType(parseCode("type 'a ref = { mutable contents: 'a }"), RPsiType.class); assertEquals("ref", e.getName()); } } ================================================ FILE: src/test/java/com/reason/lang/ocaml/ValParsingTest.java ================================================ package com.reason.lang.ocaml; import com.intellij.psi.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import org.jetbrains.annotations.*; import org.junit.*; import java.util.*; @SuppressWarnings("ConstantConditions") public class ValParsingTest extends OclParsingTestCase { @Test public void test_qualified_name() { RPsiVal e = firstOfType(parseCode("val x : int"), RPsiVal.class); assertEquals("Dummy.x", e.getQualifiedName()); assertFalse(e.isFunction()); RPsiSignature signature = e.getSignature(); assertEquals("int", signature.getText()); } @Test public void test_name() { RPsiVal e = firstOfType(parseCode("val x : int"), RPsiVal.class); assertInstanceOf(e.getNameIdentifier(), RPsiLowerSymbol.class); assertEquals("x", e.getName()); } @Test public void test_special_name() { RPsiVal e = firstOfType(parseCode("val (>>=) : 'a -> 'a t"), RPsiVal.class); assertInstanceOf(e.getNameIdentifier(), RPsiScopedExpr.class); assertEquals("(>>=)", e.getName()); } @Test public void test_function() { RPsiVal e = firstOfType(parseCode("val init: int -> (int -> 'a) -> 'a array"), RPsiVal.class); assertTrue(e.isFunction()); assertEquals("init", e.getName()); assertEquals("int -> (int -> 'a) -> 'a array", e.getSignature().getText()); List is = e.getSignature().getItems(); assertEquals("int", is.get(0).getText()); assertEquals("int", is.get(1).getText()); assertEquals("'a", is.get(2).getText()); assertEquals("'a array", is.get(3).getText()); } } ================================================ FILE: src/test/java/com/reason/lang/ocaml/VariantCallParsingTest.java ================================================ package com.reason.lang.ocaml; import com.intellij.psi.*; import com.intellij.psi.util.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import jpsplugin.com.reason.*; import org.junit.*; import java.util.*; import java.util.stream.*; @SuppressWarnings("ConstantConditions") public class VariantCallParsingTest extends OclParsingTestCase { @Test public void test_basic() { RPsiLetBinding binding = firstOfType(parseCode("let x = Var"), RPsiLet.class).getBinding(); assertEquals("Var", binding.getText()); assertNull(ORUtil.findImmediateFirstChildOfClass(binding, RPsiVariantDeclaration.class)); assertEquals(myTypes.A_VARIANT_NAME, PsiTreeUtil.findChildOfType(binding, RPsiUpperSymbol.class).getNode().getElementType()); } @Test public void test_params() { RPsiLetBinding binding = firstOfType(parseCode("let x = Var(a, b, c)"), RPsiLet.class).getBinding(); assertEquals("Var(a, b, c)", binding.getText()); assertNull(PsiTreeUtil.findChildOfType(binding, RPsiVariantDeclaration.class)); assertNull(PsiTreeUtil.findChildOfType(binding, RPsiSignatureItem.class)); assertEquals(myTypes.A_VARIANT_NAME, PsiTreeUtil.findChildOfType(binding, RPsiUpperSymbol.class).getNode().getElementType()); } @Test public void test_with_param() { RPsiLetBinding binding = firstOfType(parseCode("let x = Var(1)"), RPsiLet.class).getBinding(); assertEquals("Var(1)", binding.getText()); assertNull(ORUtil.findImmediateFirstChildOfClass(binding, RPsiVariantDeclaration.class)); assertEquals(myTypes.A_VARIANT_NAME, PsiTreeUtil.findChildOfType(binding, RPsiUpperSymbol.class).getNode().getElementType()); } @Test public void test_pattern_match() { RPsiSwitch e = firstOfType(parseCode( "let _ = match action with | UpdateDescription(desc) -> let open ReasonReact.SideEffects in (fun _self -> onDescriptionChange desc)"), RPsiSwitch.class); RPsiPatternMatchBody body = PsiTreeUtil.findChildOfType(e, RPsiPatternMatchBody.class); assertEquals("let open ReasonReact.SideEffects in (fun _self -> onDescriptionChange desc)", body.getText()); Collection uppers = PsiTreeUtil.findChildrenOfType(body, RPsiUpperSymbol.class); assertEquals("ReasonReact, SideEffects", Joiner.join(", ", uppers.stream().map(PsiElement::getText).collect(Collectors.toList()))); } } ================================================ FILE: src/test/java/com/reason/lang/ocaml/VariantDeclarationParsingTest.java ================================================ package com.reason.lang.ocaml; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import org.junit.*; import java.util.*; @SuppressWarnings("ConstantConditions") public class VariantDeclarationParsingTest extends OclParsingTestCase { @Test public void test_basic() { RPsiType e = first(typeExpressions(parseCode("type t = | Black | White"))); List declarations = ORUtil.findImmediateChildrenOfClass(e.getBinding(), RPsiVariantDeclaration.class); assertEquals(2, declarations.size()); assertEquals("Black", declarations.get(0).getVariant().getText()); assertEquals(myTypes.A_VARIANT_NAME, declarations.get(0).getVariant().getNode().getElementType()); assertEquals("White", declarations.get(1).getVariant().getText()); assertEquals(myTypes.A_VARIANT_NAME, declarations.get(1).getVariant().getNode().getElementType()); } @Test public void test_no_pipe_first() { RPsiType e = first(typeExpressions(parseCode("type t = V1 | V2"))); List declarations = ORUtil.findImmediateChildrenOfClass(e.getBinding(), RPsiVariantDeclaration.class); assertEquals(2, declarations.size()); assertEquals("V1", declarations.get(0).getVariant().getText()); assertEquals(myTypes.A_VARIANT_NAME, declarations.get(0).getVariant().getNode().getElementType()); assertEquals("V2", declarations.get(1).getVariant().getText()); assertEquals(myTypes.A_VARIANT_NAME, declarations.get(1).getVariant().getNode().getElementType()); } @Test public void test_no_pipe_first_constructor() { RPsiType e = first(typeExpressions(parseCode("type t = V1 of string | V2"))); List declarations = ORUtil.findImmediateChildrenOfClass(e.getBinding(), RPsiVariantDeclaration.class); assertEquals(2, declarations.size()); assertEquals("V1", declarations.get(0).getVariant().getText()); assertEquals(myTypes.A_VARIANT_NAME, declarations.get(0).getVariant().getNode().getElementType()); assertEquals("V2", declarations.get(1).getVariant().getText()); } @Test public void test_constructor() { RPsiType e = first(typeExpressions(parseCode("type t = | Hex of string | Rgb of int * int * int"))); List declarations = ORUtil.findImmediateChildrenOfClass(e.getBinding(), RPsiVariantDeclaration.class); assertEquals(2, declarations.size()); assertEquals("Hex", declarations.get(0).getVariant().getText()); assertEquals(myTypes.A_VARIANT_NAME, declarations.get(0).getVariant().getNode().getElementType()); assertEquals(1, declarations.get(0).getParametersList().size()); assertEquals("Rgb", declarations.get(1).getVariant().getText()); assertEquals(myTypes.A_VARIANT_NAME, declarations.get(1).getVariant().getNode().getElementType()); assertEquals(3, declarations.get(1).getParametersList().size()); } @Test public void test_mixed() { RPsiType e = first(typeExpressions(parseCode("type t = | Cannot of reason | Loose | Strict"))); List declarations = ORUtil.findImmediateChildrenOfClass(e.getBinding(), RPsiVariantDeclaration.class); assertEquals(3, declarations.size()); assertEquals("Cannot", declarations.get(0).getVariant().getText()); assertEquals(1, declarations.get(0).getParametersList().size()); assertEquals("Loose", declarations.get(1).getVariant().getText()); assertEquals("Strict", declarations.get(2).getVariant().getText()); } @Test public void test_generic() { RPsiVariantDeclaration e = firstOfType(parseCode("type cases_pattern_expr_r = | CPatRecord of (qualid * cases_pattern_expr) list"), RPsiVariantDeclaration.class); assertEquals("CPatRecord of (qualid * cases_pattern_expr) list", e.getText()); assertEquals("(qualid * cases_pattern_expr) list", e.getParametersList().get(0).getText()); } @Test // coq:: coqpp/coqpp_ast.mli public void test_parens() { RPsiVariantDeclaration e = firstOfType(parseCode("type symb = | SymbRules of ((string option * symb) list * code) list"), RPsiVariantDeclaration.class); assertNoParserError(e); assertEquals("SymbRules of ((string option * symb) list * code) list", e.getText()); assertEquals("((string option * symb) list * code) list", e.getParametersList().get(0).getText()); } } ================================================ FILE: src/test/java/com/reason/lang/reason/AndParsingTest.java ================================================ package com.reason.lang.reason; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import org.junit.*; import java.util.*; public class AndParsingTest extends RmlParsingTestCase { @Test public void test_let_chaining() { List lets = ORUtil.findImmediateChildrenOfClass(parseCode("let rec lx = x => x + 1 and ly = y => 3 + lx(y)"), RPsiLet.class); assertEquals(2, lets.size()); assertEquals("lx", lets.get(0).getName()); assertEquals("ly", lets.get(1).getName()); } @Test public void test_module_chaining() { List es = childrenOfType(parseCode(""" module rec X: {} = {} and Y: {} = {}; """), RPsiInnerModule.class); assertEquals(2, es.size()); assertEquals("X", es.get(0).getName()); assertEquals("Y", es.get(1).getName()); } @Test public void test_module_type_chaining() { List mods = childrenOfType(parseCode(""" // in a .rei file module rec X : {} and Y : {}; """), RPsiInnerModule.class); assertSize(2, mods); assertEquals("X", mods.get(0).getName()); assertEquals("Y", mods.get(1).getName()); } @Test public void test_type_chaining() { List types = childrenOfType(parseCode(""" type update = | NoUpdate and self('state) = {state: 'state}; """), RPsiType.class); assertEquals(2, types.size()); assertEquals("update", first(types).getName()); assertEquals("self", second(types).getName()); } @Test public void test_type_chaining_lIdent() { List types = childrenOfType(parseCode(""" type t = y /* test */ and y = string; """), RPsiType.class); assertEquals(2, types.size()); assertEquals("t", first(types).getName()); assertEquals("y", second(types).getName()); } // https://github.com/giraud/reasonml-idea-plugin/issues/135 @Test public void test_GH_135() { List lets = ORUtil.findImmediateChildrenOfClass(parseCode(""" let f1 = fun | _ => () and missing = (); """), RPsiLet.class); assertSize(2, lets); assertEquals("f1", lets.get(0).getName()); assertEquals("missing", lets.get(1).getName()); } } ================================================ FILE: src/test/java/com/reason/lang/reason/AnnotationParsingTest.java ================================================ package com.reason.lang.reason; import com.intellij.psi.*; import com.reason.lang.core.psi.impl.*; import org.junit.*; import java.util.*; @SuppressWarnings("ConstantConditions") public class AnnotationParsingTest extends RmlParsingTestCase { @Test public void test_annotation_name() { assertEquals("@bs.module", firstOfType(parseCode("[@bs.module]"), RPsiAnnotation.class).getName()); assertEquals("@bs.val", firstOfType(parseCode("[@bs.val]"), RPsiAnnotation.class).getName()); } @Test public void test_annotation_with_string() { RPsiAnnotation annotation = firstOfType(parseCode("[@bs.module \"xyz\"]"), RPsiAnnotation.class); assertEquals("@bs.module", annotation.getName()); } @Test public void test_chaining() { List es = new ArrayList<>(expressions(parseCode("[@bs.module \"xyz\"] [@react.component]"))); assertSize(2, es); assertEquals("@bs.module", es.get(0).getName()); assertEquals("@react.component", es.get(1).getName()); } } ================================================ FILE: src/test/java/com/reason/lang/reason/AssertParsingTest.java ================================================ package com.reason.lang.reason; import com.reason.lang.core.psi.impl.*; import org.junit.*; @SuppressWarnings("ConstantConditions") public class AssertParsingTest extends RmlParsingTestCase { @Test public void test_basic() { RPsiAssert e = firstOfType(parseCode("assert (i < Array.length(t));"), RPsiAssert.class); assertNotNull(e); assertEquals("(i < Array.length(t))", e.getAssertion().getText()); } } ================================================ FILE: src/test/java/com/reason/lang/reason/ClassParsingTest.java ================================================ package com.reason.lang.reason; import com.intellij.psi.*; import com.intellij.psi.util.*; import com.reason.ide.files.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import org.junit.*; import java.util.*; @SuppressWarnings("ConstantConditions") public class ClassParsingTest extends RmlParsingTestCase { @Test public void test_basic() { RPsiClass e = firstOfType(parseCode("class foo = { as _; }"), RPsiClass.class); assertEquals("foo", e.getName()); assertEquals("{ as _; }", e.getClassBody().getText()); } @Test public void test_classType() { RPsiClass e = firstOfType(parseCode("class type restricted_point_type = { pub get_x: int; pub bump: unit; }"), RPsiClass.class); assertEquals("restricted_point_type", e.getName()); } @Test public void test_fields() { RPsiClass e = firstOfType(parseCode("class foo = { as _; val mutable a = []; val b = 2; }"), RPsiClass.class); assertSize(2, e.getFields()); } @Test public void test_methods() { RPsiClass e = firstOfType(parseCode("class foo = { as _; pub get_x = x; pub get_y = y; }"), RPsiClass.class); assertSize(2, e.getMethods()); } @Test public void test_both() { RPsiClass e = firstOfType(parseCode("class foo = { as _; val mutable x = []; pub get_x = x; }"), RPsiClass.class); assertSize(1, e.getFields()); assertSize(1, e.getMethods()); } @Test public void test_classConstruct() { RPsiClass e = firstOfType(parseCode("class c (m: int) = { as self; pub m = m; initializer (all_c := [(self :> c), ...all_c^]); }"), RPsiClass.class); assertSize(0, e.getParameters()); assertNotNull(e.getConstructor()); assertSize(1, PsiTreeUtil.findChildrenOfType(e, RPsiClassConstructor.class)); } @Test public void test_classConstraint() { RPsiClass e = firstOfType(parseCode("class circle ('a) (c: 'a) = { as _; constraint 'a = #point; val mutable center = c; pub set_center = c => center = c; pub move = center#move; }"), RPsiClass.class); assertEquals("circle", e.getName()); assertNotNull(e.getParameters()); assertNotNull(e.getConstructor()); assertSize(1, e.getFields()); assertSize(2, e.getMethods()); } // https://github.com/giraud/reasonml-idea-plugin/issues/310 @Test public void test_GH_310() { FileBase file = parseCode("class type control = { pub detach: unit => unit; };\n type errpage = page(list((int, string)));"); List es = expressions(file); assertSize(2, es); assertInstanceOf(es.get(0), RPsiClass.class); assertEquals("control", es.get(0).getName()); assertInstanceOf(es.get(1), RPsiType.class); assertEquals("errpage", es.get(1).getName()); } // https://github.com/giraud/reasonml-idea-plugin/issues/269 @Test public void test_GH_269() { RPsiClass e = firstOfType(parseCode(""" class type ops = { pub go_to_insert: task(unit); pub go_to_mark: GText.mark => task(unit); pub process_next_phrase: task(unit); pub get_n_errors: int; pub get_errors: list((int, string)); pub get_slaves_status: (int, int, CString.Map.t(string)); pub handle_failure: handle_exn_rty => task(unit); pub destroy: unit => unit; }; """), RPsiClass.class); assertSize(8, e.getMethods()); ArrayList methods = new ArrayList<>(e.getMethods()); assertEquals("go_to_insert", methods.get(0).getName()); assertEquals("task(unit)", methods.get(0).getSignature().getText()); assertEquals("go_to_mark", methods.get(1).getName()); assertEquals("GText.mark => task(unit)", methods.get(1).getSignature().getText()); assertEquals("process_next_phrase", methods.get(2).getName()); assertEquals("task(unit)", methods.get(2).getSignature().getText()); assertEquals("get_n_errors", methods.get(3).getName()); assertEquals("int", methods.get(3).getSignature().getText()); assertEquals("get_errors", methods.get(4).getName()); assertEquals("list((int, string))", methods.get(4).getSignature().getText()); assertEquals("get_slaves_status", methods.get(5).getName()); assertEquals("(int, int, CString.Map.t(string))", methods.get(5).getSignature().getText()); assertEquals("handle_failure", methods.get(6).getName()); assertEquals("handle_exn_rty => task(unit)", methods.get(6).getSignature().getText()); assertEquals("destroy", methods.get(7).getName()); assertEquals("unit => unit", methods.get(7).getSignature().getText()); } // https://github.com/giraud/reasonml-idea-plugin/issues/491 @Test public void test_GH_491_object_initializer() { RPsiClass e = firstOfType(parseCode(""" class c = { as self; pub tag = { tag_bold: true; }; initializer { let x = 1; let fn = y => x + y; ignore(fn(2)); }; }; """), RPsiClass.class); assertSize(0, e.getFields()); assertSize(1, e.getMethods()); assertTextEquals(""" initializer { let x = 1; let fn = y => x + y; ignore(fn(2)); }""", e.getInitializer().getText()); } } ================================================ FILE: src/test/java/com/reason/lang/reason/CommentTest.java ================================================ package com.reason.lang.reason; import com.intellij.psi.*; import org.junit.*; public class CommentTest extends RmlParsingTestCase { @Test public void test_constant() { PsiComment e = firstOfType(parseCode("/* */"), PsiComment.class); assertEquals("/* */", e.getText()); } @Test public void test_constant_2() { PsiComment e = firstOfType(parseCode("/* \"this is a string */\" */"), PsiComment.class); assertEquals("/* \"this is a string */\" */", e.getText()); } } ================================================ FILE: src/test/java/com/reason/lang/reason/ComponentJsx3ParsingTest.java ================================================ package com.reason.lang.reason; import com.intellij.psi.util.*; import com.reason.ide.files.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import org.junit.*; import java.util.*; import java.util.stream.*; @SuppressWarnings("ConstantConditions") public class ComponentJsx3ParsingTest extends RmlParsingTestCase { @Test public void test_file_component() { FileBase e = parseCode("[@react.component]\nlet make = () => {
};"); assertTrue(e.isComponent()); } @Test public void test_inner_component() { RPsiInnerModule e = firstOfType(parseCode("module X = {\n [@react.component]\n let make = (~name) => {
}\n};"), RPsiInnerModule.class); assertTrue(e.isComponent()); } @Test public void test_close() { RPsiLet e = firstOfType(parseCode("[@react.component] let make = () => { \"X\"->React.string; };"), RPsiLet.class); RPsiTag tag = PsiTreeUtil.findChildOfType(e, RPsiTag.class); List innerTags = ORUtil.findImmediateChildrenOfClass(tag.getBody(), RPsiTag.class); assertSize(2, innerTags); assertEquals("\"X\"->React.string", innerTags.get(0).getText()); assertEquals("", innerTags.get(1).getText()); } } ================================================ FILE: src/test/java/com/reason/lang/reason/ExceptionParsingTest.java ================================================ package com.reason.lang.reason; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import org.junit.*; public class ExceptionParsingTest extends RmlParsingTestCase { @Test public void test_basic() { RPsiException e = firstOfType(parseCode("exception Ex;"), RPsiException.class); assertNoParserError(e); assertEquals("Ex", e.getName()); assertEquals("Dummy.Ex", e.getQualifiedName()); assertInstanceOf(e.getNameIdentifier(), RPsiUpperSymbol.class); } @Test public void test_parameter() { RPsiException e = firstOfType(parseCode("exception Ex(string);"), RPsiException.class); assertNoParserError(e); assertEquals("Ex", e.getName()); assertEquals("Dummy.Ex", e.getQualifiedName()); assertInstanceOf(e.getNameIdentifier(), RPsiUpperSymbol.class); } } ================================================ FILE: src/test/java/com/reason/lang/reason/ExternalParsingTest.java ================================================ package com.reason.lang.reason; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import org.junit.*; @SuppressWarnings("ConstantConditions") public class ExternalParsingTest extends RmlParsingTestCase { @Test public void test_signature() { RPsiExternal e = firstOfType(parseCode("external props : (string) => string;"), RPsiExternal.class); RPsiSignature signature = e.getSignature(); assertEquals("(string) => string", signature.getText()); assertTrue(e.isFunction()); } @Test public void test_named_param() { RPsiExternal e = firstOfType(parseCode("external props : (~value:string) => string;"), RPsiExternal.class); RPsiSignature signature = e.getSignature(); assertEquals("(~value:string) => string", signature.getText()); assertTrue(e.isFunction()); } @Test public void test_with_string() { RPsiExternal e = firstOfType(parseCode("external reactIntlJsReactClass: ReasonReact.reactClass = \"FormattedMessage\""), RPsiExternal.class); assertEquals("ReasonReact.reactClass", e.getSignature().asText(getLangProps())); assertFalse(e.isFunction()); assertEquals("FormattedMessage", e.getExternalName()); } @Test public void test_with_empty_string() { RPsiExternal e = firstOfType(parseCode("external reactIntlJsReactClass: ReasonReact.reactClass = \"\""), RPsiExternal.class); assertEquals("ReasonReact.reactClass", e.getSignature().asText(getLangProps())); assertFalse(e.isFunction()); assertEquals("", e.getExternalName()); } @Test public void test_string() { RPsiExternal e = firstOfType(parseCode("external string : string => reactElement = \"%identity\""), RPsiExternal.class); assertEquals("string", e.getName()); assertInstanceOf(e.getNameIdentifier(), RPsiLowerSymbol.class); assertEquals("string => reactElement", e.getSignature().getText()); assertEquals("%identity", e.getExternalName()); } @Test public void test_array() { RPsiExternal e = firstOfType(parseCode("external array : array(reactElement) => reactElement = \"%identity\""), RPsiExternal.class); assertEquals("array", e.getName()); assertEquals("array(reactElement) => reactElement", e.getSignature().getText()); assertEquals("%identity", e.getExternalName()); } @Test public void test_operator2() { RPsiExternal e = firstOfType(parseCode("external (!=): ('a, 'a) => bool = \"%notequal\";"), RPsiExternal.class); assertEquals("(!=)", e.getName()); assertEquals("('a, 'a) => bool", e.getSignature().getText()); assertEquals("%notequal", e.getExternalName()); } } ================================================ FILE: src/test/java/com/reason/lang/reason/FirstClassModuleParsingTest.java ================================================ package com.reason.lang.reason; import com.intellij.psi.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import org.junit.*; @SuppressWarnings("DataFlowIssue") public class FirstClassModuleParsingTest extends RmlParsingTestCase { @Test public void test_first_class_let() { RPsiLet e = firstOfType(parseCode("let three: module A.I = (module Three);"), RPsiLet.class); assertEquals("Three", e.getFirstClassModule().getFirstClassModuleSymbol().getText()); RPsiModuleSignature es = ((RPsiModuleSignature) e.getSignature()); assertEquals("A.I", es.getQName()); assertEquals("I", es.getNameIdentifier().getText()); } @Test public void test_first_class_parameter_no_default() { RPsiLet e = firstOfType(parseCode("let fn = (~p: (module A.I)) => p;"), RPsiLet.class); assertTrue(e.isFunction()); assertDoesntContain(extractUpperSymbolTypes(e), myTypes.A_VARIANT_NAME); RPsiFunction ef = e.getFunction(); assertEquals("p", ef.getBody().getText()); RPsiSignature eps = ef.getParameters().get(0).getSignature(); assertInstanceOf(eps, RPsiModuleSignature.class); assertEquals("A.I", ((RPsiModuleSignature) eps).getQName()); } @Test public void test_first_class_parameter_with_default() { RPsiFunction e = firstOfType(parseCode("let make = (~selectors: (module SelectorsIntf)=(module Selectors)) => {};"), RPsiFunction.class); RPsiParameterDeclaration p0 = e.getParameters().get(0); assertTrue(p0.isNamed()); assertDoesntContain(extractUpperSymbolTypes(e), myTypes.A_VARIANT_NAME); RPsiSignature eps = p0.getSignature(); assertInstanceOf(eps, RPsiModuleSignature.class); assertEquals("(module SelectorsIntf)", eps.getText()); RPsiDefaultValue epv = p0.getDefaultValue(); assertEquals("(module Selectors)", epv.getText()); PsiElement epvc = epv.getFirstChild(); assertInstanceOf(epvc, RPsiFirstClass.class); } @Test public void test_unpack() { RPsiInnerModule e = firstOfType(parseCode("module New_three = (val three : I);"), RPsiInnerModule.class); assertNull(e.getBody()); assertEquals("(val three : I)", e.getUnpack().getText()); assertEquals("I", e.getUnpack().getModuleReference().getText()); } @Test public void test_unpack_no_signature() { RPsiInnerModule e = firstOfType(parseCode("module M = (val selectors);"), RPsiInnerModule.class); assertFalse(e instanceof RPsiFunctor); assertEquals("M", e.getName()); assertEquals("(val selectors)", e.getUnpack().getText()); } } ================================================ FILE: src/test/java/com/reason/lang/reason/FunParsingTest.java ================================================ package com.reason.lang.reason; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import org.junit.*; import java.util.*; @SuppressWarnings("ConstantConditions") public class FunParsingTest extends RmlParsingTestCase { @Test public void test_fun() { RPsiLet e = firstOfType(parseCode("let timeUnitToString = fun | Second => \"s\" | Minute => \"m\" | Hour => \"h\";"), RPsiLet.class); RPsiLetBinding binding = e.getBinding(); assertEquals("fun | Second => \"s\" | Minute => \"m\" | Hour => \"h\"", binding.getText()); } @Test public void test_chaining() { Collection es = ORUtil.findImmediateChildrenOfClass(parseCode("let a = fun | Second => \"s\"; let b = fun | Minute => \"m\";"), RPsiLet.class); assertEquals("fun | Second => \"s\"", first(es).getBinding().getText()); assertEquals("fun | Minute => \"m\"", second(es).getBinding().getText()); } } ================================================ FILE: src/test/java/com/reason/lang/reason/FunctionCallParsingTest.java ================================================ package com.reason.lang.reason; import com.intellij.psi.util.*; import com.reason.ide.files.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import org.junit.*; import java.util.*; @SuppressWarnings("ConstantConditions") public class FunctionCallParsingTest extends RmlParsingTestCase { @Test public void test_call() { RPsiLetBinding e = firstOfType(parseCode("let _ = string_of_int(1)"), RPsiLet.class).getBinding(); RPsiFunctionCall call = PsiTreeUtil.findChildOfType(e, RPsiFunctionCall.class); assertNotNull(ORUtil.findImmediateFirstChildOfClass(call, RPsiLowerSymbol.class)); assertNull(PsiTreeUtil.findChildOfType(e, RPsiParameterDeclaration.class)); assertEquals("string_of_int(1)", call.getText()); assertEquals(1, call.getParameters().size()); } @Test public void test_call2() { RPsiLet e = firstOfType(parseCode("let _ = Belt.Option.map(self.state.timerId^, Js.Global.clearInterval)"), RPsiLet.class); RPsiFunctionCall fnCall = PsiTreeUtil.findChildOfType(e.getBinding(), RPsiFunctionCall.class); List parameters = fnCall.getParameters(); assertEquals(2, parameters.size()); assertEquals("self.state.timerId^", parameters.get(0).getText()); assertEquals("Js.Global.clearInterval", parameters.get(1).getText()); } @Test public void test_call3() { RPsiLet e = firstOfType(parseCode("let _ = subscriber->Topic.unsubscribe()"), RPsiLet.class); RPsiFunctionCall fnCall = PsiTreeUtil.findChildOfType(e.getBinding(), RPsiFunctionCall.class); assertEmpty(fnCall.getParameters()); } @Test public void test_end_comma() { RPsiLet e = firstOfType(parseCode("let _ = style([ color(red), ])"), RPsiLet.class); assertEquals("style([ color(red), ])", e.getBinding().getText()); RPsiFunctionCall f = PsiTreeUtil.findChildOfType(e.getBinding(), RPsiFunctionCall.class); assertNull(PsiTreeUtil.findChildOfType(f, RPsiDeconstruction.class)); assertSize(1, f.getParameters()); } @Test public void test_unit_last() { RPsiLetBinding e = firstOfType(parseCode("let _ = f(1, ());"), RPsiLet.class).getBinding(); RPsiFunctionCall fnCall = PsiTreeUtil.findChildOfType(e, RPsiFunctionCall.class); assertSize(2, fnCall.getParameters()); } @Test public void test_optional_param() { RPsiFunctionCall e = firstOfType(parseCode("let _ = fn(~margin?, ());"), RPsiFunctionCall.class); assertSize(2, e.getParameters()); assertEquals("~margin?", e.getParameters().get(0).getText()); assertEquals("()", e.getParameters().get(1).getText()); } @Test public void test_inner_parenthesis() { RPsiLet e = firstOfType(parseCode("let _ = f(a, (b, c));"), RPsiLet.class); RPsiFunctionCall call = PsiTreeUtil.findChildOfType(e, RPsiFunctionCall.class); assertSize(2, call.getParameters()); RPsiParameterReference p1 = call.getParameters().get(1); assertEquals("(b, c)", p1.getText()); assertNull(PsiTreeUtil.findChildOfType(p1, RPsiParameters.class)); assertNull(PsiTreeUtil.findChildOfType(p1, RPsiParameterReference.class)); } @Test public void test_params() { FileBase f = parseCode("call(~decode=x => Ok(), ~task=() => y,);"); RPsiFunctionCall fnCall = ORUtil.findImmediateFirstChildOfClass(f, RPsiFunctionCall.class); assertSize(2, fnCall.getParameters()); } @Test public void test_param_name() { List expressions = letAllExpressions(parseCode("describe(\"context\", () => { test(\"should do something\", () => { let inner = 1; }) })")); RPsiLet e = first(expressions); assertEquals("Dummy.describe[1].test[1].inner", e.getQualifiedName()); } @Test public void test_nested_parenthesis() { RPsiFunctionCall f = firstOfType(parseCode("set(x->keep(((y, z)) => y), xx);"), RPsiFunctionCall.class); assertEquals("set(x->keep(((y, z)) => y), xx)", f.getText()); assertEquals("x->keep(((y, z)) => y)", f.getParameters().get(0).getText()); assertEquals("xx", f.getParameters().get(1).getText()); } @Test public void test_body() { RPsiLet e = firstOfType(parseCode("let _ = x => { M.{k: v} };"), RPsiLet.class); RPsiFunctionBody body = PsiTreeUtil.findChildOfType(e, RPsiFunctionBody.class); assertEquals("{ M.{k: v} }", body.getText()); } @Test public void test_in_functor() { // 0 | | | | | | | | | RPsiFunctor e = firstOfType(parseCode("module Make = (M: Intf) : Result => { let fn = target => (. store) => call(input, item => item); };"), RPsiFunctor.class); RPsiFunctionCall fc = PsiTreeUtil.findChildOfType(e, RPsiFunctionCall.class); assertEquals("call(input, item => item)", fc.getText()); } @Test public void test_ternary_in_named_param() { RPsiFunctionCall e = firstOfType(parseCode("fn(~x=a ? b : c);"), RPsiFunctionCall.class); assertSize(1, e.getParameters()); RPsiParameterReference p0 = e.getParameters().get(0); assertEquals("x", p0.getName()); assertEquals("a ? b : c", p0.getValue().getText()); assertInstanceOf(p0.getValue().getFirstChild(), RPsiTernary.class); } @Test public void test_assignment() { RPsiFunctionCall e = firstOfType(parseCode("let _ = fn(x => myRef.current = x);"), RPsiFunctionCall.class); assertEquals("x => myRef.current = x", e.getParameters().get(0).getText()); } // https://github.com/giraud/reasonml-idea-plugin/issues/120 @Test public void test_GH_120() { RPsiLet e = firstOfType(parseCode("let _ = f(x == U.I, 1)"), RPsiLet.class); RPsiFunctionCall fnCall = PsiTreeUtil.findChildOfType(e, RPsiFunctionCall.class); assertSize(2, fnCall.getParameters()); } } ================================================ FILE: src/test/java/com/reason/lang/reason/FunctionParsingTest.java ================================================ package com.reason.lang.reason; import com.intellij.psi.util.*; import com.reason.ide.files.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import org.junit.*; import java.util.*; @SuppressWarnings("ConstantConditions") public class FunctionParsingTest extends RmlParsingTestCase { @Test public void test_anonymous_function() { RPsiLet e = firstOfType(parseCode("let _ = Belt.map(items, (. item) => value)"), RPsiLet.class); RPsiFunction function = PsiTreeUtil.findChildOfType(e, RPsiFunction.class); assertSize(1, function.getParameters()); assertEquals("item", first(function.getParameters()).getText()); assertInstanceOf(first(function.getParameters()).getNameIdentifier(), RPsiLowerSymbol.class); assertEquals("value", function.getBody().getText()); } @Test public void test_brace_function() { RPsiLet e = firstOfType(parseCode("let x = (x, y) => { x + y; }"), RPsiLet.class); RPsiFunction function = (RPsiFunction) e.getBinding().getFirstChild(); assertSize(2, function.getParameters()); assertEquals("(x, y) => { x + y; }", function.getText()); assertNotNull(function.getBody()); } @Test public void test_destructuration() { RPsiLet e = firstOfType(parseCode("let _ = (a, {b, _}) => b;"), RPsiLet.class); assertTrue(e.isFunction()); assertSize(2, e.getFunction().getParameters()); } @Test public void test_parenless_function() { RPsiLet e = firstOfType(parseCode("let _ = x => x + 10;"), RPsiLet.class); assertTrue(e.isFunction()); RPsiFunction function = (RPsiFunction) e.getBinding().getFirstChild(); assertSize(1, function.getParameters()); assertInstanceOf(first(function.getParameters()), RPsiParameterDeclaration.class); assertNotNull(function.getBody()); } @Test public void test_dot_function() { RPsiLet e = firstOfType(parseCode("let _ = (. x) => x;"), RPsiLet.class); assertTrue(e.isFunction()); RPsiFunction function = e.getFunction(); assertSize(1, function.getParameters()); assertEquals("x", first(function.getParameters()).getText()); assertEquals("x", function.getBody().getText()); } @Test public void test_inner_function() { RPsiLet e = firstOfType(parseCode("let _ = error => Belt.Array.mapU(errors, (. error) => error##message);"), RPsiLet.class); RPsiFunction functionOuter = (RPsiFunction) e.getBinding().getFirstChild(); assertEquals("Belt.Array.mapU(errors, (. error) => error##message)", functionOuter.getBody().getText()); RPsiFunction functionInner = PsiTreeUtil.findChildOfType(functionOuter, RPsiFunction.class); assertEquals("error##message", functionInner.getBody().getText()); } @Test public void test_inner_function_braces() { RPsiLet e = firstOfType(parseCode("let _ = error => { Belt.Array.mapU(errors, (. error) => error##message); };"), RPsiLet.class); RPsiFunction functionOuter = (RPsiFunction) e.getBinding().getFirstChild(); assertEquals("{ Belt.Array.mapU(errors, (. error) => error##message); }", functionOuter.getBody().getText()); RPsiFunction functionInner = PsiTreeUtil.findChildOfType(functionOuter, RPsiFunction.class); assertEquals("error##message", functionInner.getBody().getText()); } @Test public void test_inner_function_no_parens() { RPsiLet e = firstOfType(parseCode("let _ = funcall(result => 2);"), RPsiLet.class); RPsiFunction functionInner = PsiTreeUtil.findChildOfType(e, RPsiFunction.class); assertEquals("2", functionInner.getBody().getText()); } @Test public void test_parameter_anon_function() { FileBase e = parseCode("describe('a', () => test('b', () => true));"); List funcs = new ArrayList<>(PsiTreeUtil.findChildrenOfType(e, RPsiFunction.class)); assertSize(2, funcs); assertEquals("() => test('b', () => true)", funcs.get(0).getText()); assertEquals("() => true", funcs.get(1).getText()); } @Test public void test_option_anon_function() { RPsiFunction e = firstOfType(parseCode("let _ = { onCancelCreation: Some(_ => navigate(\".\")) };"), RPsiFunction.class); assertSize(1, e.getParameters()); assertEquals("_", e.getParameters().getFirst().getText()); assertEquals("navigate(\".\")", e.getBody().getText()); } @Test public void test_parameters_named_symbols() { RPsiLet e = firstOfType(parseCode("let make = (~id:string, ~values: option(Js.t('a)), children) => null;"), RPsiLet.class); RPsiFunction function = (RPsiFunction) e.getBinding().getFirstChild(); List parameters = new ArrayList<>(function.getParameters()); assertSize(3, parameters); assertEquals("id", parameters.get(0).getName()); assertEquals("values", parameters.get(1).getName()); assertEquals("children", parameters.get(2).getName()); } @Test public void test_parameters_named_symbols2() { RPsiLet e = firstOfType(parseCode( "let make = (~text, ~id=?, ~values=?, ~className=\"\", ~tag=\"span\", ~transform=\"unset\", ~marginLeft=\"0\", ~onClick=?, ~onKeyPress=?, _children, ) => {}"), RPsiLet.class); RPsiFunction function = (RPsiFunction) e.getBinding().getFirstChild(); assertSize(10, function.getParameters()); assertEmpty(PsiTreeUtil.findChildrenOfType(e, RPsiTernary.class)); } @Test public void test_parameters_named_symbols3() { RPsiLet e = firstOfType(parseCode("let fn = (~a:t, ~b=2, ~c) => ();"), RPsiLet.class); RPsiFunction function = (RPsiFunction) e.getBinding().getFirstChild(); assertSize(3, function.getParameters()); } @Test public void test_paren_function() { RPsiLet e = firstOfType(parseCode("let _ = (x,y) => x + y;"), RPsiLet.class); assertTrue(e.isFunction()); RPsiFunction function = e.getFunction(); assertSize(2, function.getParameters()); assertEquals("x", first(function.getParameters()).getText()); assertEquals("y", second(function.getParameters()).getText()); assertEquals("x + y", function.getBody().getText()); } @Test public void test_unit_function() { RPsiLet e = firstOfType(parseCode("let _ = () => 1;"), RPsiLet.class); assertTrue(e.isFunction()); RPsiFunction function = e.getFunction(); assertSize(0, function.getParameters()); assertEquals("1", function.getBody().getText()); } @Test public void test_parameters_LIdent() { RPsiLet e = firstOfType(parseCode("let make = (id, values, children) => null;"), RPsiLet.class); RPsiFunction function = (RPsiFunction) e.getBinding().getFirstChild(); List parameters = new ArrayList<>(function.getParameters()); assertSize(3, parameters); assertEquals("id", parameters.get(0).getName()); assertEquals("values", parameters.get(1).getName()); assertEquals("children", parameters.get(2).getName()); } @Test public void test_record_function() { RPsiLet e = firstOfType(parseCode("let make = (children) => { ...component, render: self =>
, }"), RPsiLet.class); RPsiFunctionBody body = e.getFunction().getBody(); RPsiFunction innerFunction = PsiTreeUtil.findChildOfType(body, RPsiFunction.class); assertSize(1, innerFunction.getParameters()); assertEquals("self", first(innerFunction.getParameters()).getName()); assertEquals("
", innerFunction.getBody().getText()); } @Test public void test_underscore() { RPsiLet e = firstOfType(parseCode("let onCancel = _ => { setUpdatedAttribute(_ => initialAttribute); };"), RPsiLet.class); assertTrue(e.isFunction()); RPsiFunction f1 = e.getFunction(); assertSize(1, f1.getParameters()); assertEquals("{ setUpdatedAttribute(_ => initialAttribute); }", f1.getBody().getText()); assertEquals("_ => { setUpdatedAttribute(_ => initialAttribute); }", e.getBinding().getText()); RPsiFunctionCall call = PsiTreeUtil.findChildOfType(e.getBinding(), RPsiFunctionCall.class); RPsiParameterReference callParam = call.getParameters().get(0); RPsiFunction f2 = PsiTreeUtil.findChildOfType(callParam, RPsiFunction.class); assertSize(1, f2.getParameters()); assertEquals("initialAttribute", f2.getBody().getText()); } @Test public void test_signature() { RPsiFunction e = firstOfType(parseCode("let _ = (~p: (option(string), option(int)) => unit) => p;"), RPsiFunction.class); RPsiParameterDeclaration p0 = e.getParameters().get(0); assertEquals("(option(string), option(int)) => unit", p0.getSignature().getText()); assertEquals("option(string)", p0.getSignature().getItems().get(0).getText()); assertEquals("option(int)", p0.getSignature().getItems().get(1).getText()); assertEquals("unit", p0.getSignature().getItems().get(2).getText()); } @Test public void test_curry_uncurry() { RPsiFunction e = firstOfType(parseCode("let fn = p => (. p1) => p + p1;"), RPsiFunction.class); assertEquals("p", e.getParameters().get(0).getText()); assertEquals("(. p1) => p + p1", e.getBody().getText()); } @Test public void test_rollback_01() { RPsiFunction e = firstOfType(parseCode("let _ = { let x = 1; let y = 2; () => 3; };"), RPsiFunction.class); // test infinite rollback assertEquals("() => 3", e.getText()); } @Test public void test_rollback_02() { List es = childrenOfType(parseCode("let _ = (() => 1, () => 2);"), RPsiFunction.class); // test infinite rollback assertEquals("() => 1", es.get(0).getText()); assertEquals("() => 2", es.get(1).getText()); } @Test public void test_rollback_03() { RPsiInnerModule e = firstOfType(parseCode("module M: I with type t = t = {" + " let fn = p => (. p1) => { let _ = a; let _ = x => x; }; " + "};"), RPsiInnerModule.class); assertSize(1, e.getConstraints()); RPsiFunction f = PsiTreeUtil.findChildOfType(e, RPsiFunction.class); assertEquals("p", f.getParameters().get(0).getText()); assertEquals("(. p1) => { let _ = a; let _ = x => x; }", f.getBody().getText()); } @Test public void test_in_Some() { RPsiSwitch e = firstOfType(parseCode("let _ = switch (a, b) { | (Some(_), None) => Some((. ()) => 1) | (_, _) => None }"), RPsiSwitch.class); assertNoParserError(e); assertSize(2, e.getPatterns()); RPsiFunction f = PsiTreeUtil.findChildOfType(e, RPsiFunction.class); assertEquals("(. ()) => 1", f.getText()); } @Test public void test_rollback_04() { RPsiFunction e = firstOfType(parseCode("let fn = () => { let _ = 1; (x) => 2; };"), RPsiFunction.class); assertNoParserError(e); assertEquals("{ let _ = 1; (x) => 2; }", e.getBody().getText()); RPsiLet el = firstOfType(e.getBody(), RPsiLet.class); assertEquals("let _ = 1", el.getText()); RPsiFunction ef = firstOfType(e.getBody(), RPsiFunction.class); assertEquals("(x) => 2", ef.getText()); } // https://github.com/giraud/reasonml-idea-plugin/issues/113 @Test public void test_GH_113() { RPsiFunction e = firstOfType(parseCode("let _ = () => switch (isBuggy()) { | _ => \"buggy\" };"), RPsiFunction.class); assertSize(0, e.getParameters()); RPsiFunctionBody b = e.getBody(); assertInstanceOf(b.getFirstChild(), RPsiSwitch.class); RPsiSwitch s = (RPsiSwitch) b.getFirstChild(); assertEquals("(isBuggy())", s.getCondition().getText()); } } ================================================ FILE: src/test/java/com/reason/lang/reason/FunctorCallParsingTest.java ================================================ package com.reason.lang.reason; import com.intellij.psi.*; import com.intellij.psi.util.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import org.junit.*; import java.util.*; @SuppressWarnings("ConstantConditions") public class FunctorCallParsingTest extends RmlParsingTestCase { @Test public void test_instantiation() { RPsiInnerModule e = first(moduleExpressions(parseCode("module Printing = Make({ let encode = encode_record; });"))); assertTrue(e.isFunctorCall()); RPsiFunctorCall call = PsiTreeUtil.findChildOfType(e, RPsiFunctorCall.class); assertEquals("Make({ let encode = encode_record; })", call.getText()); assertEquals(myTypes.A_MODULE_NAME, call.getReferenceIdentifier().getNode().getElementType()); assertSize(1, call.getParameters()); assertEquals("{ let encode = encode_record; }", call.getParameters().iterator().next().getText()); RPsiLet let = PsiTreeUtil.findChildOfType(e, RPsiLet.class); assertEquals("Dummy.Printing.Make[0].encode", let.getQualifiedName()); } @Test public void test_with_path() { RPsiInnerModule e = first(moduleExpressions(parseCode("module X = A.B.Make({})"))); assertTrue(e.isFunctorCall()); RPsiFunctorCall call = PsiTreeUtil.findChildOfType(e, RPsiFunctorCall.class); assertEquals("Make({})", call.getText()); assertEquals("Make", call.getName()); } @Test public void test_chaining() { PsiFile file = parseCode("module KeyTable = Hashtbl.Make(KeyHash);\ntype infos;"); List es = new ArrayList<>(expressions(file)); assertEquals(2, es.size()); RPsiInnerModule module = (RPsiInnerModule) es.get(0); assertTrue(module.isFunctorCall()); RPsiFunctorCall call = PsiTreeUtil.findChildOfType(module, RPsiFunctorCall.class); assertEquals("Make(KeyHash)", call.getText()); assertEquals("Make", call.getName()); assertNull(PsiTreeUtil.findChildOfType(module, RPsiParameterDeclaration.class)); } } ================================================ FILE: src/test/java/com/reason/lang/reason/FunctorParsingTest.java ================================================ package com.reason.lang.reason; import com.intellij.psi.tree.*; import com.intellij.psi.util.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import org.junit.*; import java.util.*; @SuppressWarnings("ConstantConditions") public class FunctorParsingTest extends RmlParsingTestCase { @Test public void test_basic() { RPsiFunctor e = firstOfType(parseCode("module Make = (M:T0, N:T1): S => {};"), RPsiFunctor.class); assertNoParserError(e); assertEquals("S", e.getReturnType().getText()); assertEquals("{}", e.getBody().getText()); assertNull(PsiTreeUtil.findChildOfType(e.getBody(), RPsiScopedExpr.class)); List eps = e.getParameters(); assertSize(2, eps); assertEquals("M:T0", eps.get(0).getText()); assertEquals("T0", eps.get(0).getSignature().getText()); assertEquals("N:T1", eps.get(1).getText()); assertEquals("T1", eps.get(1).getSignature().getText()); assertEquals(myTypes.C_PARAM_DECLARATION, eps.get(0).getNode().getElementType()); assertDoesntContain(extractUpperSymbolTypes(e), myTypes.A_VARIANT_NAME); } @Test public void test_struct() { RPsiFunctor e = firstOfType(parseCode("module Make = ({ type t; }): S => {};"), RPsiFunctor.class); assertNoParserError(e); assertEquals("{}", e.getBody().getText()); assertEquals("S", e.getReturnType().getText()); List uTypes = extractUpperSymbolTypes(e); assertDoesntContain(uTypes, myTypes.A_VARIANT_NAME); } @Test public void test_implicit_result() { RPsiFunctor e = firstOfType(parseCode("module Make = (M:Def) => {};"), RPsiFunctor.class); assertNoParserError(e); assertEquals("{}", e.getBody().getText()); } @Test public void test_with_constraints() { RPsiFunctor e = firstOfType(parseCode("module Make = (M: Input) : (S with type t('a) = M.t('a) and type b = M.b) => {}"), RPsiFunctor.class); assertNoParserError(e); assertEquals("M: Input", first(e.getParameters()).getText()); assertEquals("S", e.getReturnType().getText()); assertEquals("{}", e.getBody().getText()); List ec = e.getConstraints(); assertEquals(2, ec.size()); assertEquals("type t('a) = M.t('a)", ec.get(0).getText()); assertEquals("type b = M.b", ec.get(1).getText()); } @Test public void test_signature() { Collection functors = functorExpressions(parseCode(""" module GlobalBindings = (M: { let relation_classes: list(string); let morphisms: list(string); let arrow: evars => evars; }, ) => { open M } """)); assertEquals(1, functors.size()); RPsiFunctor f = first(functors); assertNoParserError(f); assertEquals("GlobalBindings", f.getName()); assertEquals("Dummy.GlobalBindings", f.getQualifiedName()); Collection parameters = f.getParameters(); assertSize(1, parameters); assertEquals("M", first(parameters).getName()); assertNotNull(f.getBody()); assertNull(PsiTreeUtil.findChildOfType(f.getBody(), RPsiScopedExpr.class)); } @Test public void test_functor_inside_module() { RPsiModule e = firstOfType(parseCode(""" module Core = { module Make = () => { type t; } } """), RPsiModule.class); assertEquals("Core", e.getModuleName()); assertFalse(e instanceof RPsiFunctor); RPsiFunctor ef = firstOfType(e.getBody(), RPsiFunctor.class); assertEquals("Make", ef.getModuleName()); assertTrue(ef instanceof RPsiFunctor); } } ================================================ FILE: src/test/java/com/reason/lang/reason/IfParsingTest.java ================================================ package com.reason.lang.reason; import com.intellij.psi.*; import com.intellij.psi.util.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.RPsiIfStatement; import com.reason.lang.core.psi.impl.*; import org.junit.*; import java.util.*; @SuppressWarnings("ConstantConditions") public class IfParsingTest extends RmlParsingTestCase { @Test public void test_basic_if() { RPsiIfStatement e = firstOfType(parseCode("if (x) { (); }"), RPsiIfStatement.class); assertNoParserError(e); assertNotNull(e); assertNotNull(e.getCondition()); assertEquals("(x)", e.getCondition().getText()); assertEquals("{ (); }", e.getThenExpression().getText()); } @Test public void test_many_parens() { RPsiIfStatement e = firstOfType(parseCode("if ((x)) { (); }"), RPsiIfStatement.class); assertNoParserError(e); assertNotNull(e); assertNotNull(e.getCondition()); assertEquals("((x))", e.getCondition().getText()); assertEquals("{ (); }", e.getThenExpression().getText()); } @Test public void test_if_else() { PsiFile psiFile = parseCode("let test = x => if (x) { 1; } else { 2; };"); RPsiIfStatement e = firstOfType(psiFile, RPsiIfStatement.class); assertNotNull(e); assertNotNull(e.getCondition()); assertEquals("{ 1; }", e.getThenExpression().getText()); assertEquals("{ 2; }", e.getElseExpression().getText()); } @Test public void test_if_else_noBrace() { PsiFile psiFile = parseCode("let test = x => if (x) 1 else 2;"); RPsiIfStatement e = firstOfType(psiFile, RPsiIfStatement.class); assertNotNull(e); assertNotNull(e.getCondition()); assertEquals("1", e.getThenExpression().getText()); assertEquals("2", e.getElseExpression().getText()); } @Test public void test_ternary_lident() { PsiFile psiFile = parseCode("let _ = a ? b : c;"); RPsiTernary e = firstOfType(psiFile, RPsiTernary.class); assertNotNull(e); assertNotNull(e.getCondition()); assertEquals("a", e.getCondition().getText()); assertEquals("b", e.getThenExpression().getText()); assertEquals("c", e.getElseExpression().getText()); } @Test public void test_ternary_parens() { PsiFile psiFile = parseCode("let _ = (a) ? b : c;"); RPsiTernary e = firstOfType(psiFile, RPsiTernary.class); assertNotNull(e); assertNotNull(e.getCondition()); assertEquals("(a)", e.getCondition().getText()); assertEquals("b", e.getThenExpression().getText()); assertEquals("c", e.getElseExpression().getText()); } @Test public void test_ternary_cond() { PsiFile psiFile = parseCode("let _ = a == a' || (x < y) ? b : c;"); RPsiTernary e = firstOfType(psiFile, RPsiTernary.class); assertNotNull(e); assertNotNull(e.getCondition()); assertEquals("a == a' || (x < y)", e.getCondition().getText()); assertEquals("b", e.getThenExpression().getText()); assertEquals("c", e.getElseExpression().getText()); } @Test public void test_ternary_call() { PsiFile psiFile = parseCode("let _ = fn(a) ? b : c;"); RPsiTernary e = firstOfType(psiFile, RPsiTernary.class); assertNotNull(e); assertNotNull(e.getCondition()); assertEquals("fn(a)", e.getCondition().getText()); assertEquals("b", e.getThenExpression().getText()); assertEquals("c", e.getElseExpression().getText()); } @Test public void test_ternary_fun_record() { RPsiRecord e = firstOfType(parseCode("let x = (x) => {a: [| X.y ? true : false |] };"), RPsiRecord.class); assertEquals("{a: [| X.y ? true : false |] }", e.getText()); RPsiTernary t = PsiTreeUtil.findChildOfType(e, RPsiTernary.class); assertEquals("X.y", t.getCondition().getText()); assertEquals("true", t.getThenExpression().getText()); assertEquals("false", t.getElseExpression().getText()); } @Test public void test_ternary_array() { RPsiScopedExpr e = firstOfType(parseCode("let x = [| x ? a : b, y ? c : d |];"), RPsiScopedExpr.class); List ts = ORUtil.findImmediateChildrenOfClass(e, RPsiTernary.class); assertEquals("x ? a : b", ts.get(0).getText()); assertEquals("y ? c : d", ts.get(1).getText()); } @Test public void test_ternary_list() { RPsiScopedExpr e = firstOfType(parseCode("let x = [ x ? a : b, y ? c : d ];"), RPsiScopedExpr.class); List ts = ORUtil.findImmediateChildrenOfClass(e, RPsiTernary.class); assertEquals("x ? a : b", ts.get(0).getText()); assertEquals("y ? c : d", ts.get(1).getText()); } @Test public void test_ternary_tuple() { RPsiScopedExpr e = firstOfType(parseCode("let x = ( x ? a : b, y ? c : d );"), RPsiScopedExpr.class); List ts = ORUtil.findImmediateChildrenOfClass(e, RPsiTernary.class); assertEquals("x ? a : b", ts.get(0).getText()); assertEquals("y ? c : d", ts.get(1).getText()); } @Test public void test_ternary_function_parameters() { RPsiParameters e = firstOfType(parseCode("let x = fn( x ? a : b, y ? c : d );"), RPsiParameters.class); assertSize(2, PsiTreeUtil.findChildrenOfType(e, RPsiParameterReference.class)); List ts = new ArrayList<>(PsiTreeUtil.findChildrenOfType(e, RPsiTernary.class)); assertEquals("x ? a : b", ts.get(0).getText()); assertEquals("y ? c : d", ts.get(1).getText()); } @Test public void test_ternary_functor_parameters() { RPsiParameters e = firstOfType(parseCode("module M = Make( x ? a : b, y ? c : d );"), RPsiParameters.class); assertSize(2, PsiTreeUtil.findChildrenOfType(e, RPsiParameterReference.class)); List ts = new ArrayList<>(PsiTreeUtil.findChildrenOfType(e, RPsiTernary.class)); assertEquals("x ? a : b", ts.get(0).getText()); assertEquals("y ? c : d", ts.get(1).getText()); } @Test public void test_ternary_ternary() { RPsiLet e = firstOfType(parseCode("let x = x ? a : y ? c : d;"), RPsiLet.class); List ts = new ArrayList<>(PsiTreeUtil.findChildrenOfType(e, RPsiTernary.class)); assertEquals("x ? a : y ? c : d", ts.get(0).getText()); assertEquals("y ? c : d", ts.get(1).getText()); } @Test public void test_ternary_switch() { RPsiLet e = firstOfType(parseCode("let compare = switch (index) { | 0 => appliedCount > appliedCount' ? (-1) : 0 };"), RPsiLet.class); List ts = new ArrayList<>(PsiTreeUtil.findChildrenOfType(e, RPsiTernary.class)); assertEquals("appliedCount > appliedCount' ? (-1) : 0", ts.get(0).getText()); } @Test public void test_ternary_fun() { RPsiLet e = firstOfType(parseCode("let fn = x => x ? Some(x) : None"), RPsiLet.class); List ts = new ArrayList<>(PsiTreeUtil.findChildrenOfType(e, RPsiTernary.class)); assertEquals("x ? Some(x) : None", ts.get(0).getText()); } @Test public void test_ternary_let_binding() { RPsiLet e = firstOfType(parseCode("let x = { let a = 1; x ? 1 : 2; }"), RPsiLet.class); List ts = new ArrayList<>(PsiTreeUtil.findChildrenOfType(e, RPsiTernary.class)); assertEquals("x ? 1 : 2", ts.get(0).getText()); } } ================================================ FILE: src/test/java/com/reason/lang/reason/IncludeParsingTest.java ================================================ package com.reason.lang.reason; import com.intellij.psi.util.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import org.junit.*; @SuppressWarnings("ConstantConditions") public class IncludeParsingTest extends RmlParsingTestCase { @Test public void test_one() { RPsiInclude e = firstOfType(parseCode("include Belt;"), RPsiInclude.class); assertNull(PsiTreeUtil.findChildOfType(e, RPsiFunctorCall.class)); assertEquals("Belt", e.getIncludePath()); assertEquals("Belt", ORUtil.findImmediateLastChildOfType(e, myTypes.A_MODULE_NAME).getText()); } @Test public void test_path() { RPsiInclude e = firstOfType(parseCode("include Belt.Array;"), RPsiInclude.class); assertEquals("Belt.Array", e.getIncludePath()); assertEquals("Array", ORUtil.findImmediateLastChildOfType(e, myTypes.A_MODULE_NAME).getText()); } @Test public void test_functor() { RPsiInclude e = firstOfType(parseCode("include Make({ type t; })"), RPsiInclude.class); assertTrue(e.useFunctor()); RPsiFunctorCall c = PsiTreeUtil.findChildOfType(e, RPsiFunctorCall.class); assertEquals("Make", c.getName()); assertEquals(myTypes.A_MODULE_NAME, c.getReferenceIdentifier().getNode().getElementType()); assertEquals("Make", e.getIncludePath()); } @Test public void test_functor_with_path() { RPsiInclude e = firstOfType(parseCode("include A.Make({ type t; })"), RPsiInclude.class); assertTrue(e.useFunctor()); assertEquals("Make", PsiTreeUtil.findChildOfType(e, RPsiFunctorCall.class).getName()); assertEquals("A.Make", e.getIncludePath()); } } ================================================ FILE: src/test/java/com/reason/lang/reason/JsObjectParsingTest.java ================================================ package com.reason.lang.reason; import com.intellij.psi.*; import com.intellij.psi.util.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import org.junit.*; import java.util.*; @SuppressWarnings("ConstantConditions") public class JsObjectParsingTest extends RmlParsingTestCase { @Test public void test_basic() { RPsiLet e = firstOfType(parseCode("let x = {\"a\": 1, \"b\": 0};"), RPsiLet.class); RPsiLetBinding binding = e.getBinding(); RPsiJsObject object = PsiTreeUtil.findChildOfType(binding, RPsiJsObject.class); assertNotNull(object); Collection fields = object.getFields(); assertEquals(2, fields.size()); } @Test public void test_definition() { RPsiType e = firstOfType(parseCode(""" type t = {. "a": UUID.t, "b": { "c": array(int) } }; """), RPsiType.class); PsiElement binding = e.getBinding(); RPsiJsObject object = PsiTreeUtil.findChildOfType(binding, RPsiJsObject.class); assertNotNull(object); List fields = new ArrayList<>(object.getFields()); assertSize(2, fields); assertEquals("a", fields.getFirst().getName()); assertEquals("UUID.t", fields.getFirst().getSignature().getText()); assertEquals("b", fields.get(1).getName()); assertEquals("{ \"c\": array(int) }", fields.get(1).getSignature().getText()); RPsiSignatureItem si0 = fields.get(1).getSignature().getItems().getFirst(); List si0fs = ((RPsiJsObject) si0.getFirstChild()).getFields(); assertEquals("c", si0fs.getFirst().getName()); assertEquals("array(int)", si0fs.getFirst().getSignature().getText()); } @Test public void test_in_function() { RPsiLet e = firstOfType(parseCode("let x = fn(~props={\"a\": id, \"b\": 0});"), RPsiLet.class); RPsiLetBinding binding = e.getBinding(); RPsiJsObject object = PsiTreeUtil.findChildOfType(binding, RPsiJsObject.class); List fields = new ArrayList<>(object.getFields()); assertEquals(2, fields.size()); assertEquals("a", fields.get(0).getName()); assertEquals("b", fields.get(1).getName()); } @Test public void test_declaring_open() { RPsiLet e = firstOfType(parseCode( "let style = {" + "\"marginLeft\": marginLeft, " + "\"marginRight\": marginRight," + "\"fontSize\": \"inherit\"," + "\"fontWeight\": bold ? \"bold\" : \"inherit\"," + "\"textTransform\": transform == \"uc\" ? \"uppercase\" : \"unset\",};"), RPsiLet.class); RPsiLetBinding binding = e.getBinding(); RPsiJsObject object = PsiTreeUtil.findChildOfType(binding, RPsiJsObject.class); assertNotNull(object); Collection fields = object.getFields(); assertEquals(5, fields.size()); assertSize(0, PsiTreeUtil.findChildrenOfType(object, RPsiSignature.class)); } @Test public void test_module_open() { RPsiLet e = firstOfType(parseCode( "let computingProperties = createStructuredSelector(" + " ComputingReducers.{ \"lastUpdate\": selectors.getLastUpdate },\n" + " );"), RPsiLet.class); RPsiLetBinding binding = e.getBinding(); RPsiParameters call = PsiTreeUtil.findChildOfType(binding, RPsiParameters.class); RPsiLocalOpen open = PsiTreeUtil.findChildOfType(call, RPsiLocalOpen.class); RPsiJsObject jsObject = ORUtil.findImmediateFirstChildOfClass(open, RPsiJsObject.class); assertNotNull(jsObject); } @Test public void test_deep() { RPsiLet e = firstOfType(parseCode("let oo = {\"f1\": {\"f11\": 111}, \"f2\": o,\"f3\": {\"f33\": 333} }"), RPsiLet.class); RPsiJsObject o = ORUtil.findImmediateFirstChildOfClass(e.getBinding(), RPsiJsObject.class); List fields = new ArrayList<>(o.getFields()); assertSize(3, fields); assertInstanceOf(fields.get(0).getValue().getFirstChild(), RPsiJsObject.class); } } ================================================ FILE: src/test/java/com/reason/lang/reason/JsxParsingTest.java ================================================ package com.reason.lang.reason; import com.intellij.psi.*; import com.intellij.psi.util.*; import com.reason.lang.core.*; import com.reason.lang.core.psi.*; import com.reason.lang.core.psi.impl.*; import org.junit.*; import java.util.*; @SuppressWarnings("ConstantConditions") public class JsxParsingTest extends RmlParsingTestCase { @Test public void test_empty_tag() { RPsiTag e = firstOfType(parseCode("
children
"), RPsiTag.class); RPsiTagStart tag = PsiTreeUtil.findChildOfType(e, RPsiTagStart.class); assertEquals("div", tag.getNameIdentifier().getText()); assertEquals("
", tag.getText()); assertEquals("children", e.getBody().getText()); assertEquals("
", PsiTreeUtil.findChildOfType(e, RPsiTagClose.class).getText()); } @Test public void test_tag_name() { RPsiTag e = firstOfType(parseCode(" }/>"), RPsiTag.class); RPsiTagStart tag = PsiTreeUtil.findChildOfType(e, RPsiTagStart.class); assertEquals("Comp", tag.getNameIdentifier().getText()); } @Test public void test_tag_name_with_dot() { RPsiLet let = firstOfType(parseCode("let _ = "), RPsiLet.class); RPsiTag tag = first(PsiTreeUtil.findChildrenOfType(let, RPsiTag.class)); assertEquals("Container.Test", tag.getName()); RPsiTagStart tagStart = first(PsiTreeUtil.findChildrenOfType(let, RPsiTagStart.class)); assertInstanceOf(tagStart.getNameIdentifier(), RPsiUpperTagName.class); assertEquals("Test", tagStart.getNameIdentifier().getText()); PsiElement nextSibling = tagStart.getFirstChild().getNextSibling(); assertEquals(myTypes.A_UPPER_TAG_NAME, nextSibling.getNode().getElementType()); nextSibling = nextSibling.getNextSibling().getNextSibling(); assertEquals(myTypes.A_UPPER_TAG_NAME, nextSibling.getNode().getElementType()); RPsiTagClose tagClose = first(PsiTreeUtil.findChildrenOfType(let, RPsiTagClose.class)); nextSibling = tagClose.getFirstChild().getNextSibling(); assertEquals(myTypes.A_UPPER_TAG_NAME, nextSibling.getNode().getElementType()); nextSibling = nextSibling.getNextSibling().getNextSibling(); assertEquals(myTypes.A_UPPER_TAG_NAME, nextSibling.getNode().getElementType()); } @Test public void test_inner_closing_tag() { RPsiTag e = firstOfType(parseCode("
"), RPsiTag.class); assertEquals("
", PsiTreeUtil.findChildOfType(e, RPsiTagStart.class).getText()); assertEquals("
", PsiTreeUtil.findChildOfType(e, RPsiTagBody.class).getText()); assertEquals("
", PsiTreeUtil.findChildOfType(e, RPsiTagClose.class).getText()); } @Test public void test_multiple_closing_tag() { RPsiTag e = firstOfType(parseCode("
"), RPsiTag.class); assertEquals("
", PsiTreeUtil.findChildOfType(e, RPsiTagStart.class).getText()); assertEquals("
", PsiTreeUtil.findChildOfType(e, RPsiTagBody.class).getText()); assertEquals("
", PsiTreeUtil.findChildOfType(e, RPsiTagClose.class).getText()); } @Test public void test_option_tag() { RPsiTag e = firstOfType(parseCode(""), RPsiTag.class); RPsiTagStart tag = PsiTreeUtil.findChildOfType(e, RPsiTagStart.class); assertEquals("", PsiTreeUtil.findChildOfType(e, RPsiTagClose.class).getText()); } @Test public void test_option_closeable_tag() { // option here is not a ReasonML keyword RPsiLet let = firstOfType(parseCode("let _ =