├── src ├── main │ ├── resources │ │ ├── fileTemplates │ │ │ └── internal │ │ │ │ ├── Zephir File.zep.ft │ │ │ │ ├── Zephir Class.zep.ft │ │ │ │ └── Zephir Interface.zep.ft │ │ ├── icons │ │ │ ├── zephir.png │ │ │ └── zephir@2x.png │ │ └── META-INF │ │ │ ├── plugin.xml │ │ │ ├── pluginIcon.svg │ │ │ └── pluginIcon_dark.svg │ ├── kotlin │ │ └── com │ │ │ └── zephir │ │ │ ├── lang │ │ │ └── core │ │ │ │ ├── lexer │ │ │ │ └── ZephirLexerAdapter.kt │ │ │ │ ├── parser │ │ │ │ ├── manual │ │ │ │ │ └── ZephirManualParseRules.kt │ │ │ │ └── ZephirParserDefinition.kt │ │ │ │ ├── psi │ │ │ │ ├── ZephirElementType.kt │ │ │ │ ├── ZephirFile.kt │ │ │ │ └── ZephirTokenType.kt │ │ │ │ ├── completion │ │ │ │ ├── ZephirCompletionPriority.kt │ │ │ │ ├── ZephirCompletionSuggestor.kt │ │ │ │ ├── ZephirCompletionContributor.kt │ │ │ │ ├── ZephirCompletionProvider.kt │ │ │ │ └── suggestors │ │ │ │ │ ├── ZephirClassScopeKeywordsSuggestor.kt │ │ │ │ │ ├── ZephirFileScopeKeywordsSuggestor.kt │ │ │ │ │ └── ZephirMethodScopeCompletionSuggestor.kt │ │ │ │ ├── stubs │ │ │ │ └── ZephirStubFileElementType.kt │ │ │ │ └── ZephirLanguage.kt │ │ │ └── ide │ │ │ ├── highlight │ │ │ ├── ZephirSyntaxHighlighterFactory.kt │ │ │ └── ZephirSyntaxHighlighter.kt │ │ │ ├── icons │ │ │ └── ZephirIcons.kt │ │ │ ├── commenter │ │ │ └── ZephirCommenter.kt │ │ │ ├── typing │ │ │ └── ZephirBraceMatcher.kt │ │ │ ├── color │ │ │ ├── ZephirColor.kt │ │ │ └── ZephirColorSettingsPage.kt │ │ │ └── actions │ │ │ └── ZephirCreateFileAction.kt │ └── grammar │ │ ├── Lexer.skeleton │ │ ├── Zephir.flex │ │ └── Zephir.bnf └── test │ ├── testData │ └── parser │ │ ├── HelloWorld.zep │ │ ├── Issue12.zep │ │ ├── HelloWorld.txt │ │ └── Issue12.txt │ └── kotlin │ └── ZephirParserTest.kt ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── settings.gradle.kts ├── .gitattributes ├── .github ├── pull_request_template.md ├── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md └── workflows │ ├── check.yml │ └── build.yml ├── gradle.properties ├── .gitignore ├── LICENSE ├── .ci └── check-license.sh ├── README.md ├── gradlew.bat ├── CHANGELOG.md ├── CONTRIBUTING.md └── gradlew /src/main/resources/fileTemplates/internal/Zephir File.zep.ft: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/test/testData/parser/HelloWorld.zep: -------------------------------------------------------------------------------- 1 | namespace Zephir; 2 | 3 | class HelloWorld 4 | { 5 | 6 | } 7 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zephir-lang/idea-plugin/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /src/main/resources/fileTemplates/internal/Zephir Class.zep.ft: -------------------------------------------------------------------------------- 1 | namespace ${NAMESPACE}; 2 | 3 | class ${NAME} 4 | { 5 | 6 | } 7 | -------------------------------------------------------------------------------- /src/main/resources/icons/zephir.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zephir-lang/idea-plugin/HEAD/src/main/resources/icons/zephir.png -------------------------------------------------------------------------------- /src/main/resources/fileTemplates/internal/Zephir Interface.zep.ft: -------------------------------------------------------------------------------- 1 | namespace ${NAMESPACE}; 2 | 3 | interface ${NAME} 4 | { 5 | 6 | } 7 | -------------------------------------------------------------------------------- /src/main/resources/icons/zephir@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zephir-lang/idea-plugin/HEAD/src/main/resources/icons/zephir@2x.png -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.5.1-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /src/test/testData/parser/Issue12.zep: -------------------------------------------------------------------------------- 1 | namespace Zephir; 2 | 3 | class Issue12 { 4 | public function test() { 5 | let sql = preg_replace( 6 | "/\?/", 7 | this->val(value), 8 | sql, 9 | 1 10 | ); 11 | } 12 | } 13 | 14 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014-2020 Zephir Team 2 | // 3 | // This source file is subject the MIT license, that is bundled with 4 | // this package in the file LICENSE, and is available through the 5 | // world-wide-web at the following url: 6 | // 7 | // https://github.com/zephir-lang/idea-plugin/blob/master/LICENSE 8 | 9 | rootProject.name = "Zephir" 10 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2014-2020 Zephir Team 2 | # 3 | # This source file is subject the MIT license, that is bundled with 4 | # this package in the file LICENSE, and is available through the 5 | # world-wide-web at the following url: 6 | # 7 | # https://github.com/zephir-lang/idea-plugin/blob/master/LICENSE 8 | 9 | # These are explicitly windows files and should use crlf 10 | *.bat text eol=crlf 11 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | Hello! 2 | 3 | * Type: bug fix | new feature | code quality | documentation 4 | * Link to issue: 5 | 6 | In raising this pull request, I confirm the following: 7 | 8 | - [ ] I have checked that another pull request for this purpose does not exist 9 | - [ ] I wrote some tests for this PR 10 | - [ ] I updated the CHANGELOG 11 | 12 | Small description of change: 13 | 14 | Thanks 15 | -------------------------------------------------------------------------------- /src/main/kotlin/com/zephir/lang/core/lexer/ZephirLexerAdapter.kt: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014-2020 Zephir Team 2 | // 3 | // This source file is subject the MIT license, that is bundled with 4 | // this package in the file LICENSE, and is available through the 5 | // world-wide-web at the following url: 6 | // 7 | // https://github.com/zephir-lang/idea-plugin/blob/master/LICENSE 8 | 9 | package com.zephir.lang.core.lexer 10 | 11 | import com.intellij.lexer.FlexAdapter 12 | 13 | class ZephirLexerAdapter : FlexAdapter(_ZephirLexer()) 14 | -------------------------------------------------------------------------------- /src/main/kotlin/com/zephir/lang/core/parser/manual/ZephirManualParseRules.kt: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014-2020 Zephir Team 2 | // 3 | // This source file is subject the MIT license, that is bundled with 4 | // this package in the file LICENSE, and is available through the 5 | // world-wide-web at the following url: 6 | // 7 | // https://github.com/zephir-lang/idea-plugin/blob/master/LICENSE 8 | 9 | package com.zephir.lang.core.parser.manual 10 | 11 | import com.intellij.lang.parser.GeneratedParserUtilBase 12 | 13 | class ZephirManualParseRules : GeneratedParserUtilBase() 14 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2014-2020 Zephir Team 2 | # 3 | # This source file is subject the MIT license, that is bundled with 4 | # this package in the file LICENSE, and is available through the 5 | # world-wide-web at the following url: 6 | # 7 | # https://github.com/zephir-lang/idea-plugin/blob/master/LICENSE 8 | 9 | kotlin.code.style=official 10 | 11 | pluginName=Zephir 12 | pluginDescription=Zephir plugin for the IntelliJ Platform 13 | version=0.4.1 14 | repository=https://github.com/zephir-lang/idea-plugin 15 | 16 | ideVersion=2020.1.1 17 | 18 | # Allocate more memory to compile the Java files 19 | org.gradle.jvmargs=-Xms128m -Xmx1024m 20 | -------------------------------------------------------------------------------- /src/main/kotlin/com/zephir/lang/core/psi/ZephirElementType.kt: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014-2020 Zephir Team 2 | // 3 | // This source file is subject the MIT license, that is bundled with 4 | // this package in the file LICENSE, and is available through the 5 | // world-wide-web at the following url: 6 | // 7 | // https://github.com/zephir-lang/idea-plugin/blob/master/LICENSE 8 | 9 | package com.zephir.lang.core.psi 10 | 11 | import com.intellij.psi.tree.IElementType 12 | import com.zephir.lang.core.ZephirLanguage 13 | 14 | /** The type of intermediate PSI tree nodes */ 15 | class ZephirElementType(debugName: String) : IElementType(debugName, ZephirLanguage) 16 | -------------------------------------------------------------------------------- /src/test/testData/parser/HelloWorld.txt: -------------------------------------------------------------------------------- 1 | Zephir File 2 | ZephirNamespaceStatementImpl(NAMESPACE_STATEMENT) 3 | PsiElement(namespace)('namespace') 4 | PsiWhiteSpace(' ') 5 | ZephirComplexIdImpl(COMPLEX_ID) 6 | ZephirIdImpl(ID) 7 | PsiElement(IDENTIFIER)('Zephir') 8 | PsiElement(;)(';') 9 | PsiWhiteSpace('\n\n') 10 | ZephirClassDefinitionImpl(CLASS_DEFINITION) 11 | PsiElement(class)('class') 12 | PsiWhiteSpace(' ') 13 | ZephirIdImpl(ID) 14 | PsiElement(IDENTIFIER)('HelloWorld') 15 | PsiWhiteSpace('\n') 16 | ZephirClassBodyImpl(CLASS_BODY) 17 | PsiElement({)('{') 18 | PsiWhiteSpace('\n\n') 19 | PsiElement(})('}') 20 | -------------------------------------------------------------------------------- /src/main/kotlin/com/zephir/lang/core/completion/ZephirCompletionPriority.kt: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014-2020 Zephir Team 2 | // 3 | // This source file is subject the MIT license, that is bundled with 4 | // this package in the file LICENSE, and is available through the 5 | // world-wide-web at the following url: 6 | // 7 | // https://github.com/zephir-lang/idea-plugin/blob/master/LICENSE 8 | 9 | package com.zephir.lang.core.completion 10 | 11 | class ZephirCompletionPriority { 12 | companion object { 13 | var METHOD_SCOPE_PRIORITY = 2.0 14 | var CLASS_METHOD_PRIORITY = 1.7 15 | var CLASS_CONSTS_PRIORITY = 1.6 16 | var CLASS_PROPERTY_PRIORITY = 1.5 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/kotlin/com/zephir/lang/core/completion/ZephirCompletionSuggestor.kt: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014-2020 Zephir Team 2 | // 3 | // This source file is subject the MIT license, that is bundled with 4 | // this package in the file LICENSE, and is available through the 5 | // world-wide-web at the following url: 6 | // 7 | // https://github.com/zephir-lang/idea-plugin/blob/master/LICENSE 8 | 9 | package com.zephir.lang.core.completion 10 | 11 | import com.intellij.codeInsight.completion.CompletionParameters 12 | import com.intellij.codeInsight.completion.CompletionResultSet 13 | 14 | interface ZephirCompletionSuggestor { 15 | fun addCompletions(parameters: CompletionParameters, result: CompletionResultSet) 16 | } 17 | -------------------------------------------------------------------------------- /src/test/kotlin/ZephirParserTest.kt: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014-2020 Zephir Team 2 | // 3 | // This source file is subject the MIT license, that is bundled with 4 | // this package in the file LICENSE, and is available through the 5 | // world-wide-web at the following url: 6 | // 7 | // https://github.com/zephir-lang/idea-plugin/blob/master/LICENSE 8 | 9 | import com.intellij.testFramework.ParsingTestCase 10 | import com.zephir.lang.core.parser.ZephirParserDefinition 11 | 12 | class ZephirParserTest : ParsingTestCase("parser", "zep", ZephirParserDefinition()) { 13 | fun testHelloWorld() = doTest(true) 14 | 15 | fun testIssue12() = doTest(true) 16 | 17 | override fun getTestDataPath() = "src/test/testData" 18 | } 19 | -------------------------------------------------------------------------------- /src/main/kotlin/com/zephir/lang/core/stubs/ZephirStubFileElementType.kt: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014-2020 Zephir Team 2 | // 3 | // This source file is subject the MIT license, that is bundled with 4 | // this package in the file LICENSE, and is available through the 5 | // world-wide-web at the following url: 6 | // 7 | // https://github.com/zephir-lang/idea-plugin/blob/master/LICENSE 8 | 9 | package com.zephir.lang.core.stubs 10 | 11 | import com.intellij.psi.stubs.PsiFileStub 12 | import com.intellij.psi.tree.IStubFileElementType 13 | import com.zephir.lang.core.ZephirLanguage 14 | import com.zephir.lang.core.psi.ZephirFile 15 | 16 | class ZephirStubFileElementType : IStubFileElementType>(ZephirLanguage) 17 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "[NFR]: " 5 | labels: new feature request 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /src/main/kotlin/com/zephir/ide/highlight/ZephirSyntaxHighlighterFactory.kt: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014-2020 Zephir Team 2 | // 3 | // This source file is subject the MIT license, that is bundled with 4 | // this package in the file LICENSE, and is available through the 5 | // world-wide-web at the following url: 6 | // 7 | // https://github.com/zephir-lang/idea-plugin/blob/master/LICENSE 8 | 9 | package com.zephir.ide.highlight 10 | 11 | import com.intellij.openapi.fileTypes.SingleLazyInstanceSyntaxHighlighterFactory 12 | import com.intellij.openapi.fileTypes.SyntaxHighlighter 13 | 14 | class ZephirSyntaxHighlighterFactory : SingleLazyInstanceSyntaxHighlighterFactory() { 15 | override fun createHighlighter(): SyntaxHighlighter = ZephirSyntaxHighlighter() 16 | } 17 | -------------------------------------------------------------------------------- /src/main/kotlin/com/zephir/lang/core/psi/ZephirFile.kt: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014-2020 Zephir Team 2 | // 3 | // This source file is subject the MIT license, that is bundled with 4 | // this package in the file LICENSE, and is available through the 5 | // world-wide-web at the following url: 6 | // 7 | // https://github.com/zephir-lang/idea-plugin/blob/master/LICENSE 8 | 9 | package com.zephir.lang.core.psi 10 | 11 | import com.intellij.psi.FileViewProvider 12 | import com.intellij.extapi.psi.PsiFileBase 13 | import com.zephir.lang.core.ZephirFileType 14 | import com.zephir.lang.core.ZephirLanguage 15 | 16 | class ZephirFile(viewProvider: FileViewProvider) : PsiFileBase(viewProvider, ZephirLanguage) { 17 | override fun getFileType() = ZephirFileType 18 | override fun toString() = "Zephir File" 19 | } 20 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "[BUG]: " 5 | labels: 'bug, status: unverified' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | 12 | 13 | **To Reproduce** 14 | Code to reproduce the behavior: 15 | 16 | ```zep 17 | // paste code here 18 | ``` 19 | 20 | **Expected behavior** 21 | 22 | 23 | ## Screenshots 24 | 25 | 26 | ## Details 27 | - Zephir plugin version: 28 | - IDE name and version: 29 | - Operating System: 30 | 31 | ## Additional context 32 | 33 | -------------------------------------------------------------------------------- /src/main/kotlin/com/zephir/ide/icons/ZephirIcons.kt: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014-2020 Zephir Team 2 | // 3 | // This source file is subject the MIT license, that is bundled with 4 | // this package in the file LICENSE, and is available through the 5 | // world-wide-web at the following url: 6 | // 7 | // https://github.com/zephir-lang/idea-plugin/blob/master/LICENSE 8 | 9 | package com.zephir.ide.icons 10 | 11 | import javax.swing.Icon 12 | import com.intellij.openapi.util.IconLoader 13 | 14 | object ZephirIcons { 15 | /** Basic file icon, matching the rest of IntelliJ's file icons. */ 16 | val FILE = getIcon("zephir.png") 17 | 18 | /** 19 | * Returns the absolute path of the given [icon]. 20 | */ 21 | private fun getIcon(icon: String): Icon { 22 | return IconLoader.getIcon("/icons/$icon") 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2014-2020 Zephir Team 2 | # 3 | # This source file is subject the MIT license, that is bundled with 4 | # this package in the file LICENSE, and is available through the 5 | # world-wide-web at the following url: 6 | # 7 | # https://github.com/zephir-lang/idea-plugin/blob/master/LICENSE 8 | 9 | # Please do not use this ignore file to define platform specific files. 10 | # 11 | # For these purposes create a global .gitignore file, which is a list of rules 12 | # for ignoring files in every Git repository on your computer. 13 | # 14 | # https://help.github.com/articles/ignoring-files/#create-a-global-gitignore 15 | 16 | # Ignore Gradle project-specific cache directory 17 | .gradle/ 18 | 19 | # Ignore Gradle build output directory 20 | build/ 21 | 22 | # Ignore generated code 23 | gen/ 24 | 25 | # This file is required to use standalone gradle 26 | !gradle/wrapper/gradle-wrapper.jar 27 | -------------------------------------------------------------------------------- /src/main/kotlin/com/zephir/lang/core/completion/ZephirCompletionContributor.kt: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014-2020 Zephir Team 2 | // 3 | // This source file is subject the MIT license, that is bundled with 4 | // this package in the file LICENSE, and is available through the 5 | // world-wide-web at the following url: 6 | // 7 | // https://github.com/zephir-lang/idea-plugin/blob/master/LICENSE 8 | 9 | package com.zephir.lang.core.completion 10 | 11 | import com.intellij.codeInsight.completion.CompletionContributor 12 | import com.intellij.codeInsight.completion.CompletionType 13 | import com.intellij.patterns.PlatformPatterns.psiElement 14 | import com.zephir.lang.core.ZephirLanguage 15 | 16 | // TODO(serghei): re-visit this later 17 | class ZephirCompletionContributor : CompletionContributor() { 18 | init { 19 | extend(CompletionType.BASIC, psiElement().withLanguage(ZephirLanguage), ZephirCompletionProvider()) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/kotlin/com/zephir/lang/core/ZephirLanguage.kt: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014-2020 Zephir Team 2 | // 3 | // This source file is subject the MIT license, that is bundled with 4 | // this package in the file LICENSE, and is available through the 5 | // world-wide-web at the following url: 6 | // 7 | // https://github.com/zephir-lang/idea-plugin/blob/master/LICENSE 8 | 9 | package com.zephir.lang.core 10 | 11 | import com.intellij.lang.Language 12 | import com.intellij.openapi.fileTypes.LanguageFileType 13 | import com.zephir.ide.icons.ZephirIcons 14 | 15 | object ZephirLanguage : Language("Zephir", "text/zephir", "text/x-zephir", "application/x-zephir") { 16 | /** Zephir is case sensitive language */ 17 | override fun isCaseSensitive(): Boolean { 18 | return true 19 | } 20 | } 21 | 22 | object ZephirFileType : LanguageFileType(ZephirLanguage) { 23 | private const val EXTENSION = "zep" 24 | 25 | override fun getIcon() = ZephirIcons.FILE 26 | override fun getName() = language.id 27 | override fun getDefaultExtension() = EXTENSION 28 | override fun getDescription() = "Zephir language file" 29 | } 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2014-2020 Zephir Team 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /.ci/check-license.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Copyright (c) 2014-2020 Zephir Team 4 | # 5 | # This source file is subject the MIT license, that is bundled with 6 | # this package in the file LICENSE, and is available through the 7 | # world-wide-web at the following url: 8 | # 9 | # https://github.com/zephir-lang/idea-plugin/blob/master/LICENSE 10 | 11 | # -e Exit immediately if a command exits with a non-zero status. 12 | # -u Treat unset variables as an error when substituting. 13 | set -eu 14 | 15 | COPYRIGHT_NOTICE=$(cat <<-EOF 16 | // Copyright \\(c\\) 2014-2020 Zephir Team 17 | // 18 | // This source file is subject the MIT license, that is bundled with 19 | // this package in the file LICENSE, and is available through the 20 | // world-wide-web at the following url: 21 | // 22 | // https://github.com/zephir-lang/idea-plugin/blob/master/LICENSE 23 | EOF 24 | ) 25 | 26 | COPYRIGHT_NOTICE=$(echo "$COPYRIGHT_NOTICE" | tr '\r\n' ' ') 27 | 28 | NO_LICENSE=$(find . -type f \( -iname '*.kt' -o -iname '*.kts' -o -iname '*.bnf' -o -iname '*.flex' \) -print0 | \ 29 | xargs -0 pcregrep -L -M "${COPYRIGHT_NOTICE// /\\s}" || true) 30 | 31 | if [ -n "$NO_LICENSE" ]; then 32 | echo "Files without license notice (or with a wrong notice format) are:" 33 | echo "$NO_LICENSE" 34 | exit 1 35 | else 36 | echo "License OK!" 37 | fi 38 | -------------------------------------------------------------------------------- /src/main/kotlin/com/zephir/lang/core/completion/ZephirCompletionProvider.kt: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014-2020 Zephir Team 2 | // 3 | // This source file is subject the MIT license, that is bundled with 4 | // this package in the file LICENSE, and is available through the 5 | // world-wide-web at the following url: 6 | // 7 | // https://github.com/zephir-lang/idea-plugin/blob/master/LICENSE 8 | 9 | package com.zephir.lang.core.completion 10 | 11 | import com.intellij.codeInsight.completion.CompletionParameters 12 | import com.intellij.codeInsight.completion.CompletionProvider 13 | import com.intellij.codeInsight.completion.CompletionResultSet 14 | import com.intellij.util.ProcessingContext 15 | import com.zephir.lang.core.completion.suggestors.* 16 | 17 | // TODO(serghei): re-visit this later 18 | class ZephirCompletionProvider : CompletionProvider() { 19 | private val suggestors = listOf( 20 | ZephirFileScopeKeywordsSuggestor, 21 | ZephirMethodScopeCompletionSuggestor, 22 | ZephirClassScopeKeywordsSuggestor 23 | ) 24 | 25 | override fun addCompletions( 26 | parameters: CompletionParameters, 27 | context: ProcessingContext, 28 | result: CompletionResultSet 29 | ) { 30 | suggestors.forEach { 31 | it.addCompletions(parameters, result) 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/kotlin/com/zephir/lang/core/completion/suggestors/ZephirClassScopeKeywordsSuggestor.kt: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014-2020 Zephir Team 2 | // 3 | // This source file is subject the MIT license, that is bundled with 4 | // this package in the file LICENSE, and is available through the 5 | // world-wide-web at the following url: 6 | // 7 | // https://github.com/zephir-lang/idea-plugin/blob/master/LICENSE 8 | 9 | package com.zephir.lang.core.completion.suggestors 10 | 11 | import com.intellij.codeInsight.completion.CompletionParameters 12 | import com.intellij.codeInsight.completion.CompletionResultSet 13 | import com.intellij.codeInsight.lookup.LookupElementBuilder 14 | import com.zephir.lang.core.completion.ZephirCompletionSuggestor 15 | import com.zephir.lang.core.psi.ZephirFile 16 | 17 | object ZephirClassScopeKeywordsSuggestor : ZephirCompletionSuggestor { 18 | private val keywords = arrayOf( 19 | "function", "protected", "public", "private", "static", "inline", "final", "abstract" 20 | ) 21 | 22 | override fun addCompletions(parameters: CompletionParameters, result: CompletionResultSet) { 23 | val psiElement = parameters.originalPosition 24 | val parent = psiElement?.parent ?: return 25 | 26 | if (parent is ZephirFile) { 27 | for (keyword in keywords) { 28 | result.addElement(LookupElementBuilder.create(keyword)) 29 | } 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/kotlin/com/zephir/ide/commenter/ZephirCommenter.kt: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014-2020 Zephir Team 2 | // 3 | // This source file is subject the MIT license, that is bundled with 4 | // this package in the file LICENSE, and is available through the 5 | // world-wide-web at the following url: 6 | // 7 | // https://github.com/zephir-lang/idea-plugin/blob/master/LICENSE 8 | 9 | package com.zephir.ide.commenter 10 | 11 | import com.intellij.lang.CodeDocumentationAwareCommenter 12 | import com.intellij.psi.PsiComment 13 | import com.intellij.psi.tree.IElementType 14 | import com.zephir.lang.core.psi.ZephirTypes 15 | 16 | class ZephirCommenter : CodeDocumentationAwareCommenter { 17 | override fun isDocumentationComment(element: PsiComment?): Boolean { 18 | return element!!.tokenType === ZephirTypes.CODE_BLOCK 19 | } 20 | 21 | override fun getLineCommentPrefix() = "//" 22 | override fun getDocumentationCommentTokenType(): IElementType? = ZephirTypes.COMMENT_BLOCK 23 | override fun getCommentedBlockCommentPrefix(): String? = null 24 | override fun getCommentedBlockCommentSuffix(): String? = null 25 | override fun getLineCommentTokenType(): IElementType? = ZephirTypes.COMMENT 26 | override fun getBlockCommentTokenType(): IElementType? = ZephirTypes.COMMENT_BLOCK 27 | override fun getBlockCommentPrefix() = "/*" 28 | override fun getBlockCommentSuffix() = "*/" 29 | override fun getDocumentationCommentLinePrefix() = "*" 30 | override fun getDocumentationCommentPrefix() = "/**" 31 | override fun getDocumentationCommentSuffix() = "*/" 32 | } 33 | -------------------------------------------------------------------------------- /src/main/kotlin/com/zephir/lang/core/completion/suggestors/ZephirFileScopeKeywordsSuggestor.kt: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014-2020 Zephir Team 2 | // 3 | // This source file is subject the MIT license, that is bundled with 4 | // this package in the file LICENSE, and is available through the 5 | // world-wide-web at the following url: 6 | // 7 | // https://github.com/zephir-lang/idea-plugin/blob/master/LICENSE 8 | 9 | package com.zephir.lang.core.completion.suggestors 10 | 11 | import com.intellij.codeInsight.completion.CompletionParameters 12 | import com.intellij.codeInsight.completion.CompletionResultSet 13 | import com.intellij.codeInsight.lookup.LookupElementBuilder 14 | import com.zephir.lang.core.completion.ZephirCompletionSuggestor 15 | import com.zephir.lang.core.psi.ZephirFile 16 | 17 | object ZephirFileScopeKeywordsSuggestor : ZephirCompletionSuggestor { 18 | private val keywords = arrayOf( 19 | "internal", 20 | "inline", 21 | "namespace", 22 | "use", 23 | "as", 24 | "interface", 25 | "class", 26 | "extends", 27 | "implements", 28 | "final", 29 | "abstract" 30 | ) 31 | 32 | override fun addCompletions(parameters: CompletionParameters, result: CompletionResultSet) { 33 | val psiElement = parameters.originalPosition 34 | val parent = psiElement?.parent ?: return 35 | 36 | if (parent is ZephirFile) { 37 | for (keyword in keywords) { 38 | result.addElement(LookupElementBuilder.create(keyword)) 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Zephir plugin for the IntelliJ Platform 2 | 3 | [![Build Status][actions-badge]][actions link] 4 | 5 | A IntelliJ plugin for editing [Zephir][zephir-lang] code. Provides syntax 6 | definition, autocompletion and syntax check support. 7 | 8 | Zephir is a high level programming language that eases the creation and 9 | maintainability of extensions for PHP. Zephir extensions are exported to C code 10 | that can be compiled and optimized by major C compilers such as gcc/clang/vc++. 11 | Functionality is exposed to the PHP language. 12 | 13 | ## Highlighted Features 14 | 15 | - Syntax highlighting 16 | - Basic syntax definition 17 | - Basic autocompletion support 18 | - Basic syntax check support 19 | 20 | ## Install 21 | 22 | You can install the plugin from within the JetBrains IDE by going to 23 | **Settings > Plugins > Marketplace** and then searching for "Zephir". After 24 | installing the plugin, restart the IDE and then open your existing Zephir 25 | project or create a new project. 26 | 27 | ## Contributing 28 | 29 | You're encouraged to contribute to the plugin if you've found any issues or 30 | missing functionality that you would want to see. Check out 31 | [CONTRIBUTING.md](./CONTRIBUTING.md) to learn how to set up the project. 32 | 33 | ## Links 34 | 35 | - [Zephir Language][zephir-lang] 36 | - [Plugin page][plugin-page] 37 | 38 | ## License 39 | 40 | Copyright (c) 2014-2020 Zephir Team. 41 | 42 | Licensed under the [MIT](./LICENSE) License. 43 | 44 | [actions link]: https://github.com/zephir-lang/idea-plugin/actions 45 | [actions-badge]: https://github.com/zephir-lang/idea-plugin/workflows/build/badge.svg 46 | [zephir-lang]: https://zephir-lang.com/en 47 | [plugin-page]: http://plugins.jetbrains.com/plugin/7558 48 | -------------------------------------------------------------------------------- /src/main/kotlin/com/zephir/lang/core/parser/ZephirParserDefinition.kt: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014-2020 Zephir Team 2 | // 3 | // This source file is subject the MIT license, that is bundled with 4 | // this package in the file LICENSE, and is available through the 5 | // world-wide-web at the following url: 6 | // 7 | // https://github.com/zephir-lang/idea-plugin/blob/master/LICENSE 8 | 9 | package com.zephir.lang.core.parser 10 | 11 | import com.intellij.lang.ASTNode 12 | import com.intellij.lang.ParserDefinition 13 | import com.intellij.openapi.project.Project 14 | import com.intellij.psi.FileViewProvider 15 | import com.intellij.psi.PsiElement 16 | import com.intellij.psi.TokenType 17 | import com.intellij.psi.tree.TokenSet 18 | import com.zephir.lang.core.stubs.ZephirStubFileElementType 19 | import com.zephir.lang.core.psi.ZEPHIR_COMMENTS 20 | import com.zephir.lang.core.psi.ZephirFile 21 | import com.zephir.lang.core.lexer.ZephirLexerAdapter 22 | import com.zephir.lang.core.psi.ZephirTypes 23 | 24 | class ZephirParserDefinition : ParserDefinition { 25 | override fun getFileNodeType() = FILE 26 | override fun getWhitespaceTokens() = TokenSet.create(TokenType.WHITE_SPACE) 27 | override fun getCommentTokens() = ZEPHIR_COMMENTS 28 | override fun getStringLiteralElements() = TokenSet.create(ZephirTypes.STRING, ZephirTypes.SCHAR) 29 | 30 | override fun createFile(viewProvider: FileViewProvider) = ZephirFile(viewProvider) 31 | 32 | override fun createParser(project: Project?) = ZephirParser() 33 | override fun createLexer(project: Project?) = ZephirLexerAdapter() 34 | 35 | override fun createElement(node: ASTNode?): PsiElement = ZephirTypes.Factory.createElement(node) 36 | 37 | companion object { 38 | val FILE = ZephirStubFileElementType() 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /.github/workflows/check.yml: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2014-2020 Zephir Team 2 | # 3 | # This source file is subject the MIT license, that is bundled with 4 | # this package in the file LICENSE, and is available through the 5 | # world-wide-web at the following url: 6 | # 7 | # https://github.com/zephir-lang/idea-plugin/blob/master/LICENSE 8 | 9 | name: check 10 | 11 | on: 12 | push: 13 | branches: 14 | - master 15 | - development 16 | pull_request: 17 | 18 | jobs: 19 | shell-check: 20 | # To prevent build a particular commit use 21 | # git commit -m "......... [ci skip]" 22 | if: "!contains(github.event.head_commit.message, '[ci skip]')" 23 | 24 | name: Shell Check 25 | runs-on: ubuntu-latest 26 | 27 | steps: 28 | - name: Checkout Code 29 | uses: actions/checkout@v2 30 | with: 31 | fetch-depth: 1 32 | 33 | - name: Check License 34 | run: shellcheck -s bash .ci/*.sh 35 | 36 | check-license: 37 | # To prevent build a particular commit use 38 | # git commit -m "......... [ci skip]" 39 | if: "!contains(github.event.head_commit.message, '[ci skip]')" 40 | 41 | name: Check License 42 | runs-on: ubuntu-latest 43 | 44 | steps: 45 | - name: Setup APT Repositories 46 | run: | 47 | sudo rm -f /etc/apt/sources.list.d/dotnetdev.list 48 | sudo rm -f /etc/apt/sources.list.d/azure*.list 49 | 50 | - name: Install System Dependencies 51 | run: | 52 | sudo apt-get update --quiet --yes 1>/dev/null 53 | sudo apt-get install --no-install-recommends -q -y pcregrep 54 | 55 | - name: Checkout Code 56 | uses: actions/checkout@v2 57 | with: 58 | fetch-depth: 1 59 | 60 | - name: Check License 61 | run: .ci/check-license.sh 62 | -------------------------------------------------------------------------------- /src/main/kotlin/com/zephir/ide/typing/ZephirBraceMatcher.kt: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014-2020 Zephir Team 2 | // 3 | // This source file is subject the MIT license, that is bundled with 4 | // this package in the file LICENSE, and is available through the 5 | // world-wide-web at the following url: 6 | // 7 | // https://github.com/zephir-lang/idea-plugin/blob/master/LICENSE 8 | 9 | package com.zephir.ide.typing 10 | 11 | import com.intellij.lang.BracePair 12 | import com.intellij.lang.PairedBraceMatcher 13 | import com.intellij.psi.PsiFile 14 | import com.intellij.psi.TokenType 15 | import com.intellij.psi.tree.IElementType 16 | import com.intellij.psi.tree.TokenSet 17 | import com.zephir.lang.core.psi.ZephirTypes.* 18 | import com.zephir.lang.core.psi.ZEPHIR_COMMENTS 19 | 20 | // TODO(serghei): re-visit this later to implement com.intellij.codeInsight.highlighting.PairedBraceMatcherAdapter 21 | class ZephirBraceMatcher : PairedBraceMatcher { 22 | override fun getCodeConstructStart(file: PsiFile?, openingBraceOffset: Int): Int = openingBraceOffset 23 | override fun getPairs(): Array = PAIRS 24 | override fun isPairedBracesAllowedBeforeType(lbraceType: IElementType, contextType: IElementType?) = 25 | contextType in InsertPairBraceBefore 26 | 27 | companion object { 28 | private val PAIRS: Array = arrayOf( 29 | BracePair(BRACKET_OPEN, BRACKET_CLOSE, true /* structural */), 30 | BracePair(SBRACKET_OPEN, SBRACKET_CLOSE, false), 31 | BracePair(PARENTHESES_OPEN, PARENTHESES_CLOSE, false), 32 | BracePair(LESS, GREATER, false) 33 | ) 34 | 35 | private val InsertPairBraceBefore = TokenSet.orSet( 36 | ZEPHIR_COMMENTS, 37 | TokenSet.create( 38 | TokenType.WHITE_SPACE, 39 | DOTCOMMA, 40 | COMMA, 41 | PARENTHESES_CLOSE, 42 | SBRACKET_CLOSE, 43 | BRACKET_CLOSE, 44 | BRACKET_OPEN 45 | ) 46 | ) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/kotlin/com/zephir/ide/color/ZephirColor.kt: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014-2020 Zephir Team 2 | // 3 | // This source file is subject the MIT license, that is bundled with 4 | // this package in the file LICENSE, and is available through the 5 | // world-wide-web at the following url: 6 | // 7 | // https://github.com/zephir-lang/idea-plugin/blob/master/LICENSE 8 | 9 | package com.zephir.ide.color 10 | 11 | import com.intellij.openapi.editor.HighlighterColors 12 | import com.intellij.openapi.editor.colors.TextAttributesKey 13 | import com.intellij.openapi.options.colors.AttributesDescriptor 14 | import com.intellij.openapi.editor.DefaultLanguageHighlighterColors as Default 15 | 16 | enum class ZephirColor(humanName: String, default: TextAttributesKey) { 17 | BRACES("Braces and Operators//Braces", Default.BRACES), 18 | BRACKETS("Braces and Operators//Brackets", Default.BRACKETS), 19 | COMMA("Braces and Operators//Comma", Default.COMMA), 20 | OPERATOR("Braces and Operators//Operators", Default.OPERATION_SIGN), 21 | PARENTHESIS("Braces and Operators//Parentheses", Default.PARENTHESES), 22 | SEMICOLON("Braces and Operators//Semicolon", Default.SEMICOLON), 23 | COLON("Braces and Operators//Colon", Default.OPERATION_SIGN), 24 | LINE_COMMENT("Comments", Default.LINE_COMMENT), 25 | IDENTIFIER("Identifiers//Default", Default.IDENTIFIER), 26 | DATA_TYPE("Identifiers//Primitive Type Hint", Default.METADATA), 27 | KEYWORD("Keywords", Default.KEYWORD), 28 | NUMBER("Numbers", Default.NUMBER), 29 | C_BLOCK("C Block", Default.DOC_COMMENT), 30 | BLOCK_COMMENT("PHPDoc//Text", Default.BLOCK_COMMENT), 31 | STRING("Strings//Characters", Default.STRING); 32 | 33 | // TODO(serghei): Do we need this? Also see com.zephir.ide.highlight.ZephirSyntaxHighlighter 34 | // BAD_CHAR("Unknown Character", HighlighterColors.BAD_CHARACTER), 35 | 36 | val textAttributesKey = TextAttributesKey.createTextAttributesKey("com.zephir.$name", default) 37 | val attributesDescriptor = AttributesDescriptor(humanName, textAttributesKey) 38 | } 39 | -------------------------------------------------------------------------------- /src/main/kotlin/com/zephir/ide/highlight/ZephirSyntaxHighlighter.kt: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014-2020 Zephir Team 2 | // 3 | // This source file is subject the MIT license, that is bundled with 4 | // this package in the file LICENSE, and is available through the 5 | // world-wide-web at the following url: 6 | // 7 | // https://github.com/zephir-lang/idea-plugin/blob/master/LICENSE 8 | 9 | package com.zephir.ide.highlight 10 | 11 | import com.intellij.openapi.editor.colors.TextAttributesKey 12 | import com.intellij.openapi.fileTypes.SyntaxHighlighterBase 13 | import com.intellij.psi.TokenType 14 | import com.intellij.psi.tree.IElementType 15 | import com.zephir.ide.color.ZephirColor 16 | import com.zephir.lang.core.lexer.ZephirLexerAdapter 17 | import com.zephir.lang.core.psi.ZEPHIR_DATA_TYPES 18 | import com.zephir.lang.core.psi.ZEPHIR_KEYWORDS 19 | import com.zephir.lang.core.psi.ZEPHIR_OPERATORS 20 | import com.zephir.lang.core.psi.ZephirTypes.* 21 | 22 | class ZephirSyntaxHighlighter : SyntaxHighlighterBase() { 23 | override fun getTokenHighlights(tokenType: IElementType?): Array = 24 | pack(map(tokenType)?.textAttributesKey) 25 | 26 | override fun getHighlightingLexer() = ZephirLexerAdapter() 27 | 28 | companion object { 29 | fun map(tokenType: IElementType?): ZephirColor? = 30 | when (tokenType) { 31 | IDENTIFIER -> ZephirColor.IDENTIFIER 32 | BRACKET_OPEN, BRACKET_CLOSE -> ZephirColor.BRACES 33 | SBRACKET_OPEN, SBRACKET_CLOSE -> ZephirColor.BRACKETS 34 | PARENTHESES_OPEN, PARENTHESES_CLOSE -> ZephirColor.PARENTHESIS 35 | DOTCOMMA -> ZephirColor.SEMICOLON 36 | COMMA -> ZephirColor.COMMA 37 | DOUBLECOLON -> ZephirColor.COLON 38 | INTEGER, DOUBLE -> ZephirColor.NUMBER 39 | STRING, SCHAR -> ZephirColor.STRING 40 | COMMENT -> ZephirColor.LINE_COMMENT 41 | COMMENT_BLOCK -> ZephirColor.BLOCK_COMMENT 42 | CBLOCK -> ZephirColor.C_BLOCK 43 | in ZEPHIR_OPERATORS -> ZephirColor.OPERATOR 44 | in ZEPHIR_KEYWORDS -> ZephirColor.KEYWORD 45 | in ZEPHIR_DATA_TYPES -> ZephirColor.DATA_TYPE 46 | // TODO(serghei): Do we need this? Also see com.zephir.ide.color.ZephirColor 47 | // TokenType.BAD_CHARACTER -> ZephirColor.BAD_CHAR 48 | else -> null 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2014-2020 Zephir Team 2 | # 3 | # This source file is subject the MIT license, that is bundled with 4 | # this package in the file LICENSE, and is available through the 5 | # world-wide-web at the following url: 6 | # 7 | # https://github.com/zephir-lang/idea-plugin/blob/master/LICENSE 8 | 9 | name: build 10 | 11 | on: 12 | push: 13 | branches: 14 | - master 15 | - development 16 | pull_request: 17 | 18 | jobs: 19 | build: 20 | # To prevent build a particular commit use 21 | # git commit -m "......... [ci skip]" 22 | if: "!contains(github.event.head_commit.message, '[ci skip]')" 23 | 24 | name: Build Plugin 25 | runs-on: ubuntu-latest 26 | 27 | steps: 28 | - name: Checkout Code 29 | uses: actions/checkout@v2 30 | with: 31 | fetch-depth: 5 32 | 33 | - name: Set Up JDK 1.8 34 | uses: actions/setup-java@v1 35 | with: 36 | java-version: 1.8 37 | 38 | - name: Grant Execute Permission for Gradle 39 | run: chmod +x gradlew 40 | 41 | - name: Cache Gradle packages 42 | uses: actions/cache@v1 43 | with: 44 | path: ~/.gradle/caches 45 | key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }} 46 | restore-keys: ${{ runner.os }}-gradle 47 | 48 | - name: Displays All Dependencies 49 | run: ./gradlew dependencies 50 | 51 | - name: Bundle the Project as a Distribution 52 | run: ./gradlew buildPlugin 53 | 54 | - name: Upload Artifact 55 | uses: actions/upload-artifact@v2 56 | with: 57 | name: intellij-zephir-distributions 58 | path: build/distributions 59 | 60 | - name: Runs the Unit Tests 61 | run: ./gradlew test 62 | 63 | - name: Validate Plugin 64 | run: ./gradlew verifyPlugin 65 | 66 | - name: Save Test Results 67 | if: ${{ always() }} 68 | run: | 69 | mkdir -p ./tests-result 70 | find . -type f -regex ".*/build/test-results/.*xml" -exec cp {} ./tests-result/ \; 71 | 72 | - name: Upload Tests Result 73 | if: ${{ always() }} 74 | uses: actions/upload-artifact@v2 75 | with: 76 | name: intellij-zephir-tests-result 77 | path: tests-result 78 | 79 | - name: Upload Tests Report 80 | if: ${{ always() }} 81 | uses: actions/upload-artifact@v2 82 | with: 83 | name: intellij-zephir-tests-reports 84 | path: build/reports/tests 85 | 86 | - name: Success Reporting 87 | if: ${{ always() }} 88 | run: git log --format=fuller -5 89 | 90 | -------------------------------------------------------------------------------- /src/main/kotlin/com/zephir/ide/color/ZephirColorSettingsPage.kt: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014-2020 Zephir Team 2 | // 3 | // This source file is subject the MIT license, that is bundled with 4 | // this package in the file LICENSE, and is available through the 5 | // world-wide-web at the following url: 6 | // 7 | // https://github.com/zephir-lang/idea-plugin/blob/master/LICENSE 8 | 9 | package com.zephir.ide.color 10 | 11 | import com.intellij.openapi.editor.colors.TextAttributesKey 12 | import com.intellij.openapi.options.colors.ColorDescriptor 13 | import com.intellij.openapi.options.colors.ColorSettingsPage 14 | import com.zephir.ide.highlight.ZephirSyntaxHighlighter 15 | import com.zephir.ide.icons.ZephirIcons 16 | 17 | class ZephirColorSettingsPage : ColorSettingsPage { 18 | private val attributes = ZephirColor.values().map { 19 | it.attributesDescriptor 20 | }.toTypedArray() 21 | 22 | override fun getHighlighter() = ZephirSyntaxHighlighter() 23 | override fun getIcon() = ZephirIcons.FILE 24 | override fun getAttributeDescriptors() = attributes 25 | override fun getColorDescriptors(): Array = ColorDescriptor.EMPTY_ARRAY 26 | override fun getDisplayName() = "Zephir" 27 | override fun getDemoText() = demoCodeText 28 | 29 | override fun getAdditionalHighlightingTagToDescriptorMap(): MutableMap { 30 | // TODO(serghei): Implement me 31 | return mutableMapOf() 32 | } 33 | } 34 | 35 | private const val demoCodeText = """ 36 | %{ 37 | // top statement before namespace, add to after headers 38 | #define MAX_FACTOR 10 39 | }% 40 | 41 | namespace Acme; 42 | 43 | %{ 44 | // top statement before class, add to after 45 | // headers test include .h 46 | #include "kernel/require.h" 47 | }% 48 | 49 | // a single line comment 50 | use Acme\Some\Buz; 51 | 52 | class Foo extends Bar implements Baz, Buz 53 | { 54 | const FOO_BAR = 30 * 24 * 60; 55 | 56 | /** 57 | * @var int 58 | */ 59 | public num; 60 | 61 | /** @var string */ 62 | static private str; 63 | 64 | public function __construct(int num, string str, array arr) 65 | { 66 | var abc; 67 | int a = 0; 68 | string s = "test"; 69 | char c = 'A'; 70 | 71 | %{ 72 | a = MAX_FACTOR; 73 | %} 74 | 75 | if fetch abc, arr[str] { 76 | let this->num = [abc: a, c: c]; 77 | } elseif isset arr[num] { 78 | let self::str = [str: a, c: c]; 79 | } 80 | } 81 | 82 | private function calc( 83 | l, 84 | r, 85 | op 86 | ) -> | null { 87 | return op->calc(l, r); 88 | } 89 | } 90 | """ 91 | -------------------------------------------------------------------------------- /src/main/kotlin/com/zephir/lang/core/psi/ZephirTokenType.kt: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014-2020 Zephir Team 2 | // 3 | // This source file is subject the MIT license, that is bundled with 4 | // this package in the file LICENSE, and is available through the 5 | // world-wide-web at the following url: 6 | // 7 | // https://github.com/zephir-lang/idea-plugin/blob/master/LICENSE 8 | 9 | package com.zephir.lang.core.psi 10 | 11 | import com.intellij.psi.tree.IElementType 12 | import com.intellij.psi.tree.TokenSet 13 | import com.zephir.lang.core.ZephirLanguage 14 | import com.zephir.lang.core.psi.ZephirTypes.* 15 | 16 | /** The type of PSI tree leaf nodes (tokens) */ 17 | class ZephirTokenType(debugName: String) : IElementType(debugName, ZephirLanguage) 18 | 19 | fun tokenSetOf(vararg tokens: IElementType) = TokenSet.create(*tokens) 20 | 21 | val ZEPHIR_COMMENTS = tokenSetOf( 22 | COMMENT, 23 | COMMENT_BLOCK 24 | ) 25 | 26 | val ZEPHIR_KEYWORDS = tokenSetOf( 27 | LET, 28 | ECHO, 29 | CONST, 30 | IF, 31 | ELSE, 32 | ELSEIF, 33 | SWITCH, 34 | CASE, 35 | DEFAULT, 36 | DO, 37 | WHILE, 38 | FOR, 39 | LOOP, 40 | REVERSE, 41 | BREAK, 42 | CONTINUE, 43 | IN, 44 | NEW, 45 | RETURN, 46 | REQUIRE, 47 | CLONE, 48 | EMPTY, 49 | TYPEOF, 50 | INSTANCEOF, 51 | LIKELY, 52 | UNLIKELY, 53 | ISSET, 54 | UNSET, 55 | THROW, 56 | FETCH, 57 | TRY, 58 | CATCH, 59 | NAMESPACE, 60 | USE, 61 | AS, 62 | 63 | // OOP 64 | INTERNAL, 65 | INTEFACE, 66 | CLASS, 67 | FUNCTION, 68 | EXTENDS, 69 | IMPLEMENTS, 70 | ABSTRACT, 71 | FINAL, 72 | PUBLIC, 73 | PROTECTED, 74 | PRIVATE, 75 | STATIC, 76 | INLINE, 77 | DEPRECATED, 78 | 79 | // const value - primitives of language 80 | TRUE, 81 | FALSE, 82 | NULL 83 | ) 84 | 85 | val ZEPHIR_DATA_TYPES = tokenSetOf( 86 | TYPE_VAR, 87 | TYPE_VOID, 88 | TYPE_INT, 89 | TYPE_UINT, 90 | TYPE_LONG, 91 | TYPE_ULONG, 92 | TYPE_CHAR, 93 | TYPE_UCHAR, 94 | TYPE_DOUBLE, 95 | TYPE_BOOL, 96 | TYPE_STRING, 97 | TYPE_ARRAY, 98 | TYPE_OBJECT, 99 | TYPE_CALLABLE, 100 | TYPE_RESOURCE 101 | ) 102 | 103 | val ZEPHIR_OPERATORS = tokenSetOf( 104 | AT, 105 | NOT, 106 | AND, 107 | OR, 108 | BITWISE_AND, 109 | BITWISE_OR, 110 | BITWISE_XOR, 111 | BITWISE_SHIFTLEFT, 112 | BITWISE_SHIFTRIGHT, 113 | ASSIGN, 114 | ADDASSIGN, 115 | SUBASSIGN, 116 | MULASSIGN, 117 | EXPASSIGN, 118 | DIVASSIGN, 119 | MODASSIGN, 120 | CONCATASSIGN, 121 | EQUALS, 122 | NOTEQUALS, 123 | IDENTICAL, 124 | NOTIDENTICAL, 125 | LESSEQUAL, 126 | GREATEREQUAL, 127 | LESS, 128 | GREATER, 129 | ARROW, 130 | DOT, 131 | ADD, 132 | SUB, 133 | MUL, 134 | EXP, 135 | DIV, 136 | MOD, 137 | INCR, 138 | DECR, 139 | COLON, 140 | QUESTION 141 | ) 142 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | com.zephir 3 | Zephir 4 | Zephir Team 5 | 6 | Provides Zephir language support for IntelliJ IDEs.


8 | 9 | GitHub | 10 | Issues | 11 | Donate

12 | 13 |

14 | Zephir is a high level language that eases the creation and maintainability 15 | of extensions for PHP. Zephir extensions are exported to C code that can be compiled 16 | and optimized by major C compilers such as gcc/clang/vc++. Functionality is exposed 17 | to the PHP language. 18 |

19 | 20 |

Highlighted Features

21 |
    22 |
  • Syntax highlighting
  • 23 |
  • Basic syntax definition
  • 24 |
  • Basic autocompletion support
  • 25 |
  • Basic syntax check support
  • 26 |
27 | ]]>
28 | 29 | com.intellij.modules.platform 30 | com.intellij.modules.lang 31 | 32 | 33 | 34 | 36 | 37 | 38 | 39 | 40 | 42 | 43 | 44 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 64 | 65 | 66 | 67 |
68 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto init 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto init 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :init 68 | @rem Get command-line arguments, handling Windows variants 69 | 70 | if not "%OS%" == "Windows_NT" goto win9xME_args 71 | 72 | :win9xME_args 73 | @rem Slurp the command line arguments. 74 | set CMD_LINE_ARGS= 75 | set _SKIP=2 76 | 77 | :win9xME_args_slurp 78 | if "x%~1" == "x" goto execute 79 | 80 | set CMD_LINE_ARGS=%* 81 | 82 | :execute 83 | @rem Setup the command line 84 | 85 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 86 | 87 | 88 | @rem Execute Gradle 89 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 90 | 91 | :end 92 | @rem End local scope for the variables with windows NT shell 93 | if "%ERRORLEVEL%"=="0" goto mainEnd 94 | 95 | :fail 96 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 97 | rem the _cmd.exe /c_ return code! 98 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 99 | exit /b 1 100 | 101 | :mainEnd 102 | if "%OS%"=="Windows_NT" endlocal 103 | 104 | :omega 105 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/pluginIcon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 40 | 42 | 43 | 45 | image/svg+xml 46 | 48 | 49 | 50 | 51 | 52 | 57 | 60 | 63 | 68 | 69 | 72 | 77 | 78 | 81 | 86 | 87 | 90 | 95 | 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/pluginIcon_dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 40 | 42 | 43 | 45 | image/svg+xml 46 | 48 | 49 | 50 | 51 | 52 | 57 | 60 | 63 | 68 | 69 | 72 | 77 | 78 | 81 | 86 | 87 | 90 | 95 | 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /src/main/kotlin/com/zephir/ide/actions/ZephirCreateFileAction.kt: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014-2020 Zephir Team 2 | // 3 | // This source file is subject the MIT license, that is bundled with 4 | // this package in the file LICENSE, and is available through the 5 | // world-wide-web at the following url: 6 | // 7 | // https://github.com/zephir-lang/idea-plugin/blob/master/LICENSE 8 | 9 | package com.zephir.ide.actions 10 | 11 | import com.intellij.icons.AllIcons 12 | import com.intellij.ide.actions.CreateFileFromTemplateAction 13 | import com.intellij.ide.actions.CreateFileFromTemplateDialog 14 | import com.intellij.ide.fileTemplates.FileTemplate 15 | import com.intellij.ide.fileTemplates.FileTemplateManager 16 | import com.intellij.ide.fileTemplates.actions.AttributesDefaults 17 | import com.intellij.ide.fileTemplates.ui.CreateFromTemplateDialog 18 | import com.intellij.openapi.project.Project 19 | import com.intellij.openapi.util.io.FileUtilRt 20 | import com.intellij.psi.PsiDirectory 21 | import com.intellij.psi.PsiFile 22 | import com.intellij.util.IncorrectOperationException 23 | import com.zephir.lang.core.ZephirFileType 24 | import java.util.* 25 | 26 | // TODO(serghei): Put all natural language strings into the resource bundle src/main/resources/.../zephir.properties 27 | // TODO(serghei): Add a note about resource bundle to CONTRIBUTING.md 28 | class ZephirCreateFileAction : CreateFileFromTemplateAction( 29 | "Zephir File/Class", 30 | "Creates new Zephir file or class", 31 | ZephirFileType.icon 32 | ) { 33 | override fun buildDialog( 34 | project: Project, 35 | directory: PsiDirectory, 36 | builder: CreateFileFromTemplateDialog.Builder 37 | ) { 38 | // TODO(serghei): setValidator(ZephirNameValidator) 39 | builder.setTitle("New Zephir File/Class") 40 | .addKind("File", ZephirFileType.icon, "Zephir File") 41 | .addKind("Class", AllIcons.Nodes.Class, "Zephir Class") 42 | .addKind("Interface", AllIcons.Nodes.Interface, "Zephir Interface") 43 | } 44 | 45 | override fun getActionName(directory: PsiDirectory?, newName: String, templateName: String?): String = 46 | "Zephir File/Class" 47 | 48 | override fun createFileFromTemplate(name: String, template: FileTemplate, dir: PsiDirectory): PsiFile? { 49 | val project = dir.project 50 | val className = FileUtilRt.getNameWithoutExtension(name) 51 | val properties = createProperties(project, className) 52 | 53 | val element = try { 54 | CreateFromTemplateDialog( 55 | project, 56 | dir, 57 | template, 58 | AttributesDefaults(className).withFixedName(true), 59 | properties 60 | ).create() 61 | } catch (e: IncorrectOperationException) { 62 | LOG.error("Error while creating new file", e) 63 | throw e 64 | } catch (e: Exception) { 65 | LOG.error("Error while creating new file", e) 66 | null 67 | } 68 | 69 | return element?.containingFile 70 | } 71 | 72 | private companion object { 73 | fun createProperties(project: Project, className: String): Properties { 74 | val properties = FileTemplateManager.getInstance(project).defaultProperties 75 | 76 | // TODO(serghei): Do this better. 77 | // For example we can transform \Acme\Service to: 78 | // - "Acme" as a namespace 79 | // - "Service" as a class 80 | properties += "NAMESPACE" to project.name 81 | properties += "NAME" to className 82 | 83 | return properties 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](http://keepachangelog.com) 5 | and this project adheres to [Semantic Versioning](http://semver.org). 6 | 7 | ## [Unreleased][Unreleased] 8 | ## [0.4.0][0.4.0] - 2020-05-24 9 | ### Added 10 | - Amended Color Scheme configuration 11 | - Introduced recognition of angle brackets as paired ones 12 | - Added ability to create an empty Zephir File 13 | 14 | ### Changed 15 | - Migrated build to Gradle 16 | - Rewrote plugin from Java to Kotlin 17 | - Redesigned logic, determining when a closed brace/bracket should be inserted after an opened one 18 | 19 | ### Fixed 20 | - Corrected `New > Zephir File` dialog to use project's directory as a namespace 21 | 22 | ### Removed 23 | - Drop support of IDE's versions below `191` 24 | 25 | ## [0.3.6][0.3.6] - 2020-05-18 26 | ### Changed 27 | - Updated file icons 28 | - Corrected `change-notes` section to store changes only for the latest version of the plugin 29 | 30 | ## [0.3.5][0.3.5] - 2020-05-13 31 | ### Removed 32 | - Drop support of IDE's versions below `182` 33 | 34 | ## [0.3.4][0.3.4] - 2020-05-12 35 | ### Fixed 36 | - Replaced usage of deprecated API by new one 37 | 38 | ## [0.3.3][0.3.3] - 2019-09-07 39 | ### Fixed 40 | - Fixed recognizing strings with regular expressions 41 | 42 | ## [0.3.2][0.3.2] - 2017-12-27 43 | ### Added 44 | - Completion list now shows members of class 45 | 46 | ### Changed 47 | - Improved syntax support 48 | 49 | ## [0.3.1][0.3.1] - 2017-05-18 50 | ### Fixed 51 | - Fixed many bugs with syntax recognition 52 | - Fixed extra space in completion for method params 53 | 54 | ## [0.3.0][0.3.0] - 2017-05-12 55 | ### Added 56 | - Added few words to highlight 57 | 58 | ### Changed 59 | - Improved completion 60 | 61 | ### Fixed 62 | - Fixed many bugs with syntax recognition 63 | 64 | ## [0.2.5][0.2.5] - 2017-05-06 65 | ### Fixed 66 | - Fixed `switch` keyword detection 67 | 68 | ## [0.2.4][0.2.4] - 2014-09-22 69 | ### Added 70 | - Added brace matching support 71 | 72 | ## [0.2.3][0.2.3] - 2014-09-20 73 | ### Fixed 74 | - Fixed build 75 | 76 | ## [0.2.2][0.2.2] - 2014-09-15 77 | ### Added 78 | - Added color settings page 79 | 80 | ### Changed 81 | - Improve highlighter 82 | 83 | ## [0.2.1][0.2.2] - 2014-09-15 84 | ### Changed 85 | - Disabled "New Zephir class" dialog 86 | 87 | ## [0.2][0.2] - 2014-09-15 88 | ### Added 89 | - Added lexer and simple syntax highlighter 90 | 91 | ## 0.1 - 2014-09-08 92 | ### Added 93 | - Initial release 94 | 95 | [Unreleased]: https://github.com/zephir-lang/idea-plugin/compare/0.4.0...HEAD 96 | [0.4.0]: https://github.com/zephir-lang/idea-plugin/compare/0.3.6...0.4.0 97 | [0.3.6]: https://github.com/zephir-lang/idea-plugin/compare/0.3.5...0.3.6 98 | [0.3.5]: https://github.com/zephir-lang/idea-plugin/compare/0.3.4...0.3.5 99 | [0.3.4]: https://github.com/zephir-lang/idea-plugin/compare/0.3.3...0.3.4 100 | [0.3.3]: https://github.com/zephir-lang/idea-plugin/compare/0.3.2...0.3.3 101 | [0.3.2]: https://github.com/zephir-lang/idea-plugin/compare/0.3.1...0.3.2 102 | [0.3.1]: https://github.com/zephir-lang/idea-plugin/compare/0.3.0...0.3.1 103 | [0.3.0]: https://github.com/zephir-lang/idea-plugin/compare/0.2.5...0.3.0 104 | [0.2.5]: https://github.com/zephir-lang/idea-plugin/compare/0.2.4...0.2.5 105 | [0.2.4]: https://github.com/zephir-lang/idea-plugin/compare/0.2.3...0.2.4 106 | [0.2.3]: https://github.com/zephir-lang/idea-plugin/compare/0.2.2...0.2.3 107 | [0.2.2]: https://github.com/zephir-lang/idea-plugin/compare/0.2.1...0.2.2 108 | [0.2.1]: https://github.com/zephir-lang/idea-plugin/compare/0.2...0.2.1 109 | [0.2]: https://github.com/zephir-lang/idea-plugin/compare/0.1...0.2 110 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Zephir plugin for the IntelliJ Platform 2 | 3 | Zephir plugin for the IntelliJ Platform is an open source project and a 4 | volunteer effort. This project welcomes contribution from everyone. The 5 | following summarizes the process for contributing changes, and some 6 | guidelines on how to set up the project. Please take a moment to review 7 | this document in order to make the contribution process easy and effective 8 | for everyone involved. 9 | 10 | ## Prerequisites 11 | 12 | ### Kotlin 13 | 14 | We use [Kotlin][kotlin] language for the plugin. If you can program in Java, 15 | you should be able to read and write Kotlin code right away. Kotlin is deeply 16 | similar to Java, but has less verbose syntax and better safety. It also shares 17 | some characteristics with Rust: type inference, immutability by default, no 18 | null pointers (apart from those that come from Java). 19 | 20 | If you are unsure how to implement something in Kotlin, ask a question in our 21 | [Discord][discord] (use `#editorsupport` channel), send a PR in Java, or use 22 | **Convert Java to Kotlin** action in the IDE. 23 | 24 | Feel free to ask question or make suggestions in our [issue tracker][issues]. 25 | 26 | ### Plugins 27 | 28 | You might want to install the following plugins: 29 | 30 | - [Grammar-Kit][g-kit] to get highlighting for the files with BNFish grammar 31 | - [PsiViewer][psi-view] to view the AST of Zephir files right in the IDE 32 | - [Kotlin][kotlin-plugin] to enable Kotlin support for your IDE 33 | - Plugin DevKit (built-in, just make sure you've enabled it) 34 | 35 | ## Getting started 36 | 37 | ### Clone 38 | 39 | ```shell script 40 | $ git clone https://github.com/zephir-lang/idea-plugin.git 41 | $ cd idea-plugin 42 | ``` 43 | 44 | ### Building & Running 45 | 46 | Run `./gradlew runIde` run configuration to build and launch development IDE 47 | with the plugin. 48 | 49 | We use gradle with [gradle-intellij][gradle-intellij] plugin to build the 50 | plugin. It comes with a wrapper script (`gradlew` in the root of the 51 | repository) which downloads appropriate version of gradle automatically as 52 | long as you have JDK installed. 53 | 54 | Common Gradle tasks are: 55 | 56 | - `./gradlew build` -- Fully build plugin and create an archive at 57 | `build/distributions` which can be installed into IntelliJ IDEA via 58 | `Install plugin from disk...` action found in `Settings > Plugins` 59 | - `./gradlew runIde` - Runs Intellij IDEA with installed plugin. Break points 60 | works like a charm. 61 | - `./gradlew test` - Runs the unit tests. To run gradle with single class and 62 | method use something like this: `gradle test --tests com.zephir.ns.Class` 63 | 64 | ### Development in IntelliJ IDEA 65 | 66 | You can get the latest IntelliJ IDEA Community Edition [here][idea], it is 67 | free. Import the plugin project as you would do with any other gradle based 68 | project. For example, Ctrl + Shift + A, 69 | `Import Project from Existing Sources...` and select `build.gradle.kts` from 70 | the root directory of the plugin. 71 | 72 | ## Contributing 73 | 74 | To find a problem to work on, look for [`help wanted`][help] issues on Github, 75 | or, even better, try to fix a problem you face yourself when using the plugin. 76 | 77 | Work in progress pull requests are very welcome! It is also a great way to ask 78 | questions. 79 | 80 | ### Code style 81 | 82 | Please use **reformat code** action to maintain consistent style. Pay attention 83 | to IDEA's warning and suggestions, and try to keep the code green. If you are 84 | sure that the warning is false positive, use an annotation to suppress it. 85 | 86 | Try to avoid copy-paste and boilerplate as much as possible. For example, 87 | proactively use `?:` to deal with nullable values. 88 | 89 | If you add a new file, please make sure that it contains a license preamble, as 90 | all other files do. 91 | 92 | [kotlin]: https://kotlinlang.org 93 | [kotlin-plugin]: https://plugins.jetbrains.com/plugin/6954-kotlin 94 | [discord]: https://discord.gg/PNFsSsr 95 | [issues]: https://github.com/zephir-lang/zephir-mode/issues 96 | [gradle-intellij]: https://github.com/JetBrains/gradle-intellij-plugin 97 | [idea]: https://www.jetbrains.com/idea/download/ 98 | [g-kit]: https://plugins.jetbrains.com/plugin/6606-grammar-kit 99 | [psi-view]: https://plugins.jetbrains.com/plugin/227-psiviewer 100 | [help]: https://github.com/zephir-lang/idea-plugin/labels/help%20wanted 101 | -------------------------------------------------------------------------------- /src/test/testData/parser/Issue12.txt: -------------------------------------------------------------------------------- 1 | Zephir File 2 | ZephirNamespaceStatementImpl(NAMESPACE_STATEMENT) 3 | PsiElement(namespace)('namespace') 4 | PsiWhiteSpace(' ') 5 | ZephirComplexIdImpl(COMPLEX_ID) 6 | ZephirIdImpl(ID) 7 | PsiElement(IDENTIFIER)('Zephir') 8 | PsiElement(;)(';') 9 | PsiWhiteSpace('\n\n') 10 | ZephirClassDefinitionImpl(CLASS_DEFINITION) 11 | PsiElement(class)('class') 12 | PsiWhiteSpace(' ') 13 | ZephirIdImpl(ID) 14 | PsiElement(IDENTIFIER)('Issue12') 15 | PsiWhiteSpace(' ') 16 | ZephirClassBodyImpl(CLASS_BODY) 17 | PsiElement({)('{') 18 | PsiWhiteSpace('\n ') 19 | ZephirMethodDefinitionImpl(METHOD_DEFINITION) 20 | ZephirVisibilityImpl(VISIBILITY) 21 | PsiElement(public)('public') 22 | PsiWhiteSpace(' ') 23 | PsiElement(function)('function') 24 | PsiWhiteSpace(' ') 25 | ZephirIdImpl(ID) 26 | PsiElement(IDENTIFIER)('test') 27 | PsiElement(()('(') 28 | PsiElement())(')') 29 | PsiWhiteSpace(' ') 30 | ZephirMethodBodyImpl(METHOD_BODY) 31 | ZephirCodeBlockImpl(CODE_BLOCK) 32 | PsiElement({)('{') 33 | PsiWhiteSpace('\n ') 34 | ZephirCodeImpl(CODE) 35 | ZephirLetStatementImpl(LET_STATEMENT) 36 | PsiElement(let)('let') 37 | PsiWhiteSpace(' ') 38 | ZephirAssignmentExprImpl(ASSIGNMENT_EXPR) 39 | ZephirVariableImpl(VARIABLE) 40 | ZephirComplexIdImpl(COMPLEX_ID) 41 | ZephirIdImpl(ID) 42 | PsiElement(IDENTIFIER)('sql') 43 | PsiWhiteSpace(' ') 44 | ZephirAssignmentOperatorImpl(ASSIGNMENT_OPERATOR) 45 | PsiElement(=)('=') 46 | PsiWhiteSpace(' ') 47 | ZephirCallExprImpl(CALL_EXPR) 48 | ZephirLiteralExprImpl(LITERAL_EXPR) 49 | ZephirVariableImpl(VARIABLE) 50 | ZephirComplexIdImpl(COMPLEX_ID) 51 | ZephirIdImpl(ID) 52 | PsiElement(IDENTIFIER)('preg_replace') 53 | PsiElement(()('(') 54 | PsiWhiteSpace('\n ') 55 | ZephirCallArgumentsImpl(CALL_ARGUMENTS) 56 | ZephirLiteralExprImpl(LITERAL_EXPR) 57 | ZephirScalarImpl(SCALAR) 58 | PsiElement(STRING)('"/\?/"') 59 | PsiElement(,)(',') 60 | PsiWhiteSpace('\n ') 61 | ZephirCallExprImpl(CALL_EXPR) 62 | ZephirLiteralExprImpl(LITERAL_EXPR) 63 | ZephirVariableImpl(VARIABLE) 64 | ZephirComplexIdImpl(COMPLEX_ID) 65 | ZephirIdImpl(ID) 66 | PsiElement(IDENTIFIER)('this') 67 | PsiElement(->)('->') 68 | ZephirVariableImpl(VARIABLE) 69 | ZephirComplexIdImpl(COMPLEX_ID) 70 | ZephirIdImpl(ID) 71 | PsiElement(IDENTIFIER)('val') 72 | PsiElement(()('(') 73 | ZephirCallArgumentsImpl(CALL_ARGUMENTS) 74 | ZephirLiteralExprImpl(LITERAL_EXPR) 75 | ZephirVariableImpl(VARIABLE) 76 | ZephirComplexIdImpl(COMPLEX_ID) 77 | ZephirIdImpl(ID) 78 | PsiElement(IDENTIFIER)('value') 79 | PsiElement())(')') 80 | PsiElement(,)(',') 81 | PsiWhiteSpace('\n ') 82 | ZephirLiteralExprImpl(LITERAL_EXPR) 83 | ZephirVariableImpl(VARIABLE) 84 | ZephirComplexIdImpl(COMPLEX_ID) 85 | ZephirIdImpl(ID) 86 | PsiElement(IDENTIFIER)('sql') 87 | PsiElement(,)(',') 88 | PsiWhiteSpace('\n ') 89 | ZephirLiteralExprImpl(LITERAL_EXPR) 90 | ZephirScalarImpl(SCALAR) 91 | PsiElement(INTEGER)('1') 92 | PsiWhiteSpace('\n ') 93 | PsiElement())(')') 94 | PsiElement(;)(';') 95 | PsiWhiteSpace('\n ') 96 | PsiElement(})('}') 97 | PsiWhiteSpace('\n') 98 | PsiElement(})('}') 99 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /src/main/grammar/Lexer.skeleton: -------------------------------------------------------------------------------- 1 | 2 | /** This character denotes the end of file */ 3 | public static final int YYEOF = -1; 4 | 5 | /** initial size of the lookahead buffer */ 6 | --- private static final int ZZ_BUFFERSIZE = ...; 7 | 8 | /** lexical states */ 9 | --- lexical states, charmap 10 | 11 | /* error codes */ 12 | private static final int ZZ_UNKNOWN_ERROR = 0; 13 | private static final int ZZ_NO_MATCH = 1; 14 | private static final int ZZ_PUSHBACK_2BIG = 2; 15 | 16 | /* error messages for the codes above */ 17 | private static final String[] ZZ_ERROR_MSG = { 18 | "Unknown internal scanner error", 19 | "Error: could not match input", 20 | "Error: pushback value was too large" 21 | }; 22 | 23 | --- isFinal list 24 | /** the input device */ 25 | private java.io.Reader zzReader; 26 | 27 | /** the current state of the DFA */ 28 | private int zzState; 29 | 30 | /** the current lexical state */ 31 | private int zzLexicalState = YYINITIAL; 32 | 33 | /** this buffer contains the current text to be matched and is 34 | the source of the yytext() string */ 35 | private CharSequence zzBuffer = ""; 36 | 37 | /** the textposition at the last accepting state */ 38 | private int zzMarkedPos; 39 | 40 | /** the current text position in the buffer */ 41 | private int zzCurrentPos; 42 | 43 | /** startRead marks the beginning of the yytext() string in the buffer */ 44 | private int zzStartRead; 45 | 46 | /** endRead marks the last character in the buffer, that has been read 47 | from input */ 48 | private int zzEndRead; 49 | 50 | /** 51 | * zzAtBOL == true <=> the scanner is currently at the beginning of a line 52 | */ 53 | private boolean zzAtBOL = true; 54 | 55 | /** zzAtEOF == true <=> the scanner is at the EOF */ 56 | private boolean zzAtEOF; 57 | 58 | /** denotes if the user-EOF-code has already been executed */ 59 | private boolean zzEOFDone; 60 | 61 | --- user class code 62 | 63 | --- constructor declaration 64 | 65 | public final int getTokenStart() { 66 | return zzStartRead; 67 | } 68 | 69 | public final int getTokenEnd() { 70 | return getTokenStart() + yylength(); 71 | } 72 | 73 | public void reset(CharSequence buffer, int start, int end, int initialState) { 74 | zzBuffer = buffer; 75 | zzCurrentPos = zzMarkedPos = zzStartRead = start; 76 | zzAtEOF = false; 77 | zzAtBOL = true; 78 | zzEndRead = end; 79 | yybegin(initialState); 80 | } 81 | 82 | /** 83 | * Refills the input buffer. 84 | * 85 | * @return {@code false}, iff there was new input. 86 | * 87 | * @exception java.io.IOException if any I/O-Error occurs 88 | */ 89 | private boolean zzRefill() throws java.io.IOException { 90 | return true; 91 | } 92 | 93 | 94 | /** 95 | * Returns the current lexical state. 96 | */ 97 | public final int yystate() { 98 | return zzLexicalState; 99 | } 100 | 101 | 102 | /** 103 | * Enters a new lexical state 104 | * 105 | * @param newState the new lexical state 106 | */ 107 | public final void yybegin(int newState) { 108 | zzLexicalState = newState; 109 | } 110 | 111 | 112 | /** 113 | * Returns the text matched by the current regular expression. 114 | */ 115 | public final CharSequence yytext() { 116 | return zzBuffer.subSequence(zzStartRead, zzMarkedPos); 117 | } 118 | 119 | 120 | /** 121 | * Returns the character at position {@code pos} from the 122 | * matched text. 123 | * 124 | * It is equivalent to yytext().charAt(pos), but faster 125 | * 126 | * @param pos the position of the character to fetch. 127 | * A value from 0 to yylength()-1. 128 | * 129 | * @return the character at position pos 130 | */ 131 | public final char yycharat(int pos) { 132 | return zzBuffer.charAt(zzStartRead+pos); 133 | } 134 | 135 | 136 | /** 137 | * Returns the length of the matched text region. 138 | */ 139 | public final int yylength() { 140 | return zzMarkedPos-zzStartRead; 141 | } 142 | 143 | 144 | /** 145 | * Reports an error that occurred while scanning. 146 | * 147 | * In a wellformed scanner (no or only correct usage of 148 | * yypushback(int) and a match-all fallback rule) this method 149 | * will only be called with things that "Can't Possibly Happen". 150 | * If this method is called, something is seriously wrong 151 | * (e.g. a JFlex bug producing a faulty scanner etc.). 152 | * 153 | * Usual syntax/scanner level error handling should be done 154 | * in error fallback rules. 155 | * 156 | * @param errorCode the code of the errormessage to display 157 | */ 158 | --- zzScanError declaration 159 | String message; 160 | try { 161 | message = ZZ_ERROR_MSG[errorCode]; 162 | } 163 | catch (ArrayIndexOutOfBoundsException e) { 164 | message = ZZ_ERROR_MSG[ZZ_UNKNOWN_ERROR]; 165 | } 166 | 167 | --- throws clause 168 | } 169 | 170 | 171 | /** 172 | * Pushes the specified amount of characters back into the input stream. 173 | * 174 | * They will be read again by then next call of the scanning method 175 | * 176 | * @param number the number of characters to be read again. 177 | * This number must not be greater than yylength()! 178 | */ 179 | --- yypushback decl (contains zzScanError exception) 180 | if ( number > yylength() ) 181 | zzScanError(ZZ_PUSHBACK_2BIG); 182 | 183 | zzMarkedPos -= number; 184 | } 185 | 186 | 187 | --- zzDoEOF 188 | /** 189 | * Resumes scanning until the next regular expression is matched, 190 | * the end of input is encountered or an I/O-Error occurs. 191 | * 192 | * @return the next token 193 | * @exception java.io.IOException if any I/O-Error occurs 194 | */ 195 | --- yylex declaration 196 | int zzInput; 197 | int zzAction; 198 | 199 | // cached fields: 200 | int zzCurrentPosL; 201 | int zzMarkedPosL; 202 | int zzEndReadL = zzEndRead; 203 | CharSequence zzBufferL = zzBuffer; 204 | 205 | --- local declarations 206 | 207 | while (true) { 208 | zzMarkedPosL = zzMarkedPos; 209 | 210 | --- start admin (line, char, col count) 211 | zzAction = -1; 212 | 213 | zzCurrentPosL = zzCurrentPos = zzStartRead = zzMarkedPosL; 214 | 215 | --- start admin (lexstate etc) 216 | 217 | zzForAction: { 218 | while (true) { 219 | 220 | --- next input, line, col, char count, next transition, isFinal action 221 | zzAction = zzState; 222 | zzMarkedPosL = zzCurrentPosL; 223 | --- line count update 224 | } 225 | 226 | } 227 | } 228 | 229 | // store back cached position 230 | zzMarkedPos = zzMarkedPosL; 231 | --- char count update 232 | 233 | if (zzInput == YYEOF && zzStartRead == zzCurrentPos) { 234 | zzAtEOF = true; 235 | --- eofvalue 236 | } 237 | else { 238 | --- actions 239 | default: 240 | --- no match 241 | } 242 | } 243 | } 244 | } 245 | 246 | --- main 247 | 248 | } 249 | -------------------------------------------------------------------------------- /src/main/kotlin/com/zephir/lang/core/completion/suggestors/ZephirMethodScopeCompletionSuggestor.kt: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014-2020 Zephir Team 2 | // 3 | // This source file is subject the MIT license, that is bundled with 4 | // this package in the file LICENSE, and is available through the 5 | // world-wide-web at the following url: 6 | // 7 | // https://github.com/zephir-lang/idea-plugin/blob/master/LICENSE 8 | 9 | package com.zephir.lang.core.completion.suggestors 10 | 11 | import com.intellij.codeInsight.completion.CompletionParameters 12 | import com.intellij.codeInsight.completion.CompletionResultSet 13 | import com.intellij.codeInsight.completion.PrioritizedLookupElement 14 | import com.intellij.codeInsight.lookup.LookupElementBuilder 15 | import com.intellij.psi.PsiElement 16 | import com.zephir.lang.core.completion.ZephirCompletionPriority 17 | import com.zephir.lang.core.completion.ZephirCompletionSuggestor 18 | import com.zephir.lang.core.psi.* 19 | import java.util.* 20 | 21 | object ZephirMethodScopeCompletionSuggestor : ZephirCompletionSuggestor { 22 | private const val MAX_SYNTAX_TREE_DEEP = 256 23 | 24 | private val keywords = arrayOf( 25 | "let", "echo", "const", "if", "else", "elseif", "switch", "case", "default", 26 | "do", "while", "for", "loop", "reverse", "break", "continue", "in", "new", "return", 27 | "require", "clone", "empty", "typeof", "instanceof", "likely", "unlikely", "fetch", 28 | "isset", "unset", "throw", "try", "catch" 29 | ) 30 | 31 | private val typeHints = arrayOf( 32 | "var", "array", "object", "callable", "resource", "int", "integer", "uint", "long", "ulong", 33 | "double", "float", "string", "char", "uchar" 34 | ) 35 | 36 | override fun addCompletions(parameters: CompletionParameters, result: CompletionResultSet) { 37 | val psiElement = parameters.originalPosition ?: return 38 | 39 | val methodDefinition = (getPsiByCurrentPos(psiElement, "method") ?: return) as ZephirMethodDefinition 40 | val classBody = (getPsiByCurrentPos(psiElement, "class") ?: return) as ZephirClassBody 41 | getPsiByCurrentPos(psiElement, "method_body") ?: return 42 | 43 | processMethodArguments(methodDefinition, result) 44 | processClassMembers(classBody, result) 45 | processClassConstants(classBody, result) 46 | processClassMethods(classBody, result) 47 | 48 | // process keywords 49 | for (keyword in keywords) { 50 | result.addElement(LookupElementBuilder.create(keyword)) 51 | } 52 | 53 | // process type hints 54 | for (typeHint in typeHints) { 55 | result.addElement(LookupElementBuilder.create(typeHint)) 56 | } 57 | } 58 | 59 | private fun processMethodArguments(methodDefinition: ZephirMethodDefinition, result: CompletionResultSet) { 60 | val methodArgs = LinkedList() 61 | for (args in methodDefinition.argumentsList) { 62 | methodArgs.addAll(args.argumentList) 63 | } 64 | 65 | for (arg in methodArgs) { 66 | val completionElement = arg.id?.text?.let { 67 | LookupElementBuilder 68 | .create(it, arg.id!!.text) 69 | .withTypeText(if (arg.type != null) arg.type!!.text else "", true) 70 | .withBoldness(true) 71 | .withLookupString(arg.id!!.text) 72 | .withTailText( 73 | if (arg.defaultValue != null) " " + arg.defaultValue!!.text else "", 74 | true 75 | ) 76 | } 77 | result.addElement( 78 | PrioritizedLookupElement.withPriority(completionElement, ZephirCompletionPriority.METHOD_SCOPE_PRIORITY) 79 | ) 80 | } 81 | } 82 | 83 | private fun processClassMembers(classBody: ZephirClassBody, result: CompletionResultSet) { 84 | for (propDef in classBody.propertyDefinitionList) { 85 | val completionElement = LookupElementBuilder 86 | .create(propDef.id.text, "this->" + propDef.id.text) 87 | .withTypeText(propDef.visibility.text, true) 88 | .withLookupString(propDef.id.text) 89 | .withTailText( 90 | if (propDef.defaultValue != null) " " + propDef.defaultValue!!.text else "", 91 | true 92 | ) 93 | 94 | result.addElement( 95 | PrioritizedLookupElement.withPriority( 96 | completionElement, 97 | ZephirCompletionPriority.CLASS_PROPERTY_PRIORITY 98 | ) 99 | ) 100 | } 101 | } 102 | 103 | private fun processClassConstants(classBody: ZephirClassBody, result: CompletionResultSet) { 104 | for (constDef in classBody.constantDefinitionList) { 105 | val completionElement = LookupElementBuilder 106 | .create(constDef.id.text, "self::" + constDef.id.text) 107 | .withTypeText(constDef.defaultValue!!.text) 108 | .withLookupString(constDef.id.text) 109 | 110 | result.addElement( 111 | PrioritizedLookupElement.withPriority(completionElement, ZephirCompletionPriority.CLASS_CONSTS_PRIORITY) 112 | ) 113 | } 114 | } 115 | 116 | private fun processClassMethods(classBody: ZephirClassBody, result: CompletionResultSet) { 117 | for (abstractDef in classBody.abstractMethodDefinitionList) { 118 | val interDef = abstractDef.interfaceMethodDefinition 119 | 120 | val completionElement = LookupElementBuilder 121 | .create(interDef.id.text, "this->" + interDef.id.text.toString() + "()") 122 | .withTypeText(if (interDef.returnType != null) interDef.returnType!!.text else "") 123 | .withLookupString(interDef.id.text) 124 | 125 | result.addElement( 126 | PrioritizedLookupElement.withPriority(completionElement, ZephirCompletionPriority.CLASS_METHOD_PRIORITY) 127 | ) 128 | } 129 | } 130 | 131 | private fun getPsiByCurrentPos(psiElement: PsiElement, objectType: String): PsiElement? { 132 | var parent = psiElement.parent 133 | 134 | if (parent == null || parent is ZephirFile) { 135 | return null 136 | } 137 | 138 | var findLimitCounter = 0 139 | 140 | do { 141 | parent = parent.parent 142 | if (parent == null || parent is ZephirFile) { 143 | return null 144 | } else if (objectType == "method" && parent is ZephirMethodDefinition) { 145 | return parent 146 | } else if (objectType == "class" && parent is ZephirClassBody) { 147 | return parent 148 | } else if (objectType == "method_body" && parent is ZephirMethodBody) { 149 | return parent 150 | } 151 | ++findLimitCounter 152 | } while (findLimitCounter < MAX_SYNTAX_TREE_DEEP) // to avoid possible infinite cycles 153 | 154 | return null 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /src/main/grammar/Zephir.flex: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014-2020 Zephir Team 2 | // 3 | // This source file is subject the MIT license, that is bundled with 4 | // this package in the file LICENSE, and is available through the 5 | // world-wide-web at the following url: 6 | // 7 | // https://github.com/zephir-lang/idea-plugin/blob/master/LICENSE 8 | 9 | package com.zephir.lang.core.lexer; 10 | 11 | import com.intellij.lexer.FlexLexer; 12 | import com.intellij.psi.tree.IElementType; 13 | import com.intellij.psi.TokenType; 14 | import static com.zephir.lang.core.psi.ZephirTypes.*; 15 | 16 | %% 17 | 18 | %{ 19 | public _ZephirLexer() { 20 | this((java.io.Reader)null); 21 | } 22 | %} 23 | 24 | %class _ZephirLexer 25 | %public 26 | %implements FlexLexer 27 | %unicode 28 | %function advance 29 | %type IElementType 30 | %eof{ return; 31 | %eof} 32 | 33 | ////////////////////////////////////////////////////////////////////////////////////////////////// 34 | // Whitespaces 35 | ////////////////////////////////////////////////////////////////////////////////////////////////// 36 | 37 | EOL = \n | \r | \r\n 38 | LINE_WS = [\ \t\f] 39 | WHITE_SPACE_CHAR = {EOL} | {LINE_WS} 40 | WHITE_SPACE = {WHITE_SPACE_CHAR}+ 41 | 42 | ////////////////////////////////////////////////////////////////////////////////////////////////// 43 | // Comments 44 | ////////////////////////////////////////////////////////////////////////////////////////////////// 45 | 46 | COMMENT = "//".* 47 | COMMENT_BLOCK = ("/*"([^*]+|[*]+[^/*])*[*]*"*/") 48 | 49 | /////////////////////////////////////////////////////////////////////////////////////////////////// 50 | // Literals 51 | /////////////////////////////////////////////////////////////////////////////////////////////////// 52 | 53 | INTEGER = ([\-]?[0-9]+)|([\-]?[0][x][0-9A-Fa-f]+) 54 | DOUBLE = ([\-]?[0-9]+[\.][0-9]+) 55 | HEX_DIGIT = [a-fA-F0-9] 56 | 57 | COMMON_ESCAPE = ( [.0\n\r\\] | "x" {HEX_DIGIT} {2} | "u" {HEX_DIGIT} {4} | "U" {HEX_DIGIT} {8} ) 58 | CHAR_TYPES = [acefntxdAGbBpPrRdDhHsSvVwWzZ] 59 | 60 | SINGLE_QUOTE = \x27 61 | DOUBLE_QUOTE = \x22 62 | 63 | STRING_LITERAL = {DOUBLE_QUOTE} ( [^\"\\] | \\[^] )* {DOUBLE_QUOTE} 64 | CHAR_LITERAL = ( {SINGLE_QUOTE} ( [^'\\] | "\\" ( {SINGLE_QUOTE} | {COMMON_ESCAPE} | {CHAR_TYPES}) ) {SINGLE_QUOTE} ) 65 | | ( {SINGLE_QUOTE} [^\x20-\x7E]{1,2} {SINGLE_QUOTE}) 66 | 67 | IDENTIFIER = [_a-zA-Z][a-zA-Z0-9_]* 68 | CBLOCK = ("%{"([^}]+|[}]+[^%{])*"}%") 69 | 70 | %% 71 | { 72 | {WHITE_SPACE} { yybegin(YYINITIAL); return TokenType.WHITE_SPACE; } 73 | 74 | "namespace" { yybegin(YYINITIAL); return NAMESPACE; } 75 | "use" { yybegin(YYINITIAL); return USE; } 76 | "as" { yybegin(YYINITIAL); return AS; } 77 | "interface" { yybegin(YYINITIAL); return INTEFACE; } 78 | "class" { yybegin(YYINITIAL); return CLASS; } 79 | "function" { yybegin(YYINITIAL); return FUNCTION; } 80 | "fn" { yybegin(YYINITIAL); return FUNCTION; } 81 | "extends" { yybegin(YYINITIAL); return EXTENDS; } 82 | "implements" { yybegin(YYINITIAL); return IMPLEMENTS; } 83 | "abstract" { yybegin(YYINITIAL); return ABSTRACT; } 84 | "final" { yybegin(YYINITIAL); return FINAL; } 85 | "public" { yybegin(YYINITIAL); return PUBLIC; } 86 | "protected" { yybegin(YYINITIAL); return PROTECTED; } 87 | "private" { yybegin(YYINITIAL); return PRIVATE; } 88 | "static" { yybegin(YYINITIAL); return STATIC; } 89 | "inline" { yybegin(YYINITIAL); return INLINE; } 90 | "internal" { yybegin(YYINITIAL); return INTERNAL; } 91 | "deprecated" { yybegin(YYINITIAL); return DEPRECATED; } 92 | "var" { yybegin(YYINITIAL); return TYPE_VAR; } 93 | "void" { yybegin(YYINITIAL); return TYPE_VOID; } 94 | "int" { yybegin(YYINITIAL); return TYPE_INT; } 95 | "integer" { yybegin(YYINITIAL); return TYPE_INT; } 96 | "uint" { yybegin(YYINITIAL); return TYPE_UINT; } 97 | "long" { yybegin(YYINITIAL); return TYPE_LONG; } 98 | "ulong" { yybegin(YYINITIAL); return TYPE_ULONG; } 99 | "char" { yybegin(YYINITIAL); return TYPE_CHAR; } 100 | "uchar" { yybegin(YYINITIAL); return TYPE_UCHAR; } 101 | "double" { yybegin(YYINITIAL); return TYPE_DOUBLE; } 102 | "float" { yybegin(YYINITIAL); return TYPE_DOUBLE; } 103 | "bool" { yybegin(YYINITIAL); return TYPE_BOOL; } 104 | "boolean" { yybegin(YYINITIAL); return TYPE_BOOL; } 105 | "string" { yybegin(YYINITIAL); return TYPE_STRING; } 106 | "array" { yybegin(YYINITIAL); return TYPE_ARRAY; } 107 | "object" { yybegin(YYINITIAL); return TYPE_OBJECT; } 108 | "callable" { yybegin(YYINITIAL); return TYPE_CALLABLE; } 109 | "resource" { yybegin(YYINITIAL); return TYPE_RESOURCE; } 110 | "null" { yybegin(YYINITIAL); return NULL; } 111 | "false" { yybegin(YYINITIAL); return FALSE; } 112 | "true" { yybegin(YYINITIAL); return TRUE; } 113 | "let" { yybegin(YYINITIAL); return LET; } 114 | "echo" { yybegin(YYINITIAL); return ECHO; } 115 | "const" { yybegin(YYINITIAL); return CONST; } 116 | "if" { yybegin(YYINITIAL); return IF; } 117 | "else" { yybegin(YYINITIAL); return ELSE; } 118 | "elseif" { yybegin(YYINITIAL); return ELSEIF; } 119 | "switch" { yybegin(YYINITIAL); return SWITCH; } 120 | "case" { yybegin(YYINITIAL); return CASE; } 121 | "default" { yybegin(YYINITIAL); return DEFAULT; } 122 | "do" { yybegin(YYINITIAL); return DO; } 123 | "while" { yybegin(YYINITIAL); return WHILE; } 124 | "for" { yybegin(YYINITIAL); return FOR; } 125 | "loop" { yybegin(YYINITIAL); return LOOP; } 126 | "reverse" { yybegin(YYINITIAL); return REVERSE; } 127 | "break" { yybegin(YYINITIAL); return BREAK; } 128 | "continue" { yybegin(YYINITIAL); return CONTINUE; } 129 | "in" { yybegin(YYINITIAL); return IN; } 130 | "new" { yybegin(YYINITIAL); return NEW; } 131 | "return" { yybegin(YYINITIAL); return RETURN; } 132 | "require" { yybegin(YYINITIAL); return REQUIRE; } 133 | "clone" { yybegin(YYINITIAL); return CLONE; } 134 | "empty" { yybegin(YYINITIAL); return EMPTY; } 135 | "typeof" { yybegin(YYINITIAL); return TYPEOF; } 136 | "instanceof" { yybegin(YYINITIAL); return INSTANCEOF; } 137 | "likely" { yybegin(YYINITIAL); return LIKELY; } 138 | "unlikely" { yybegin(YYINITIAL); return UNLIKELY; } 139 | "isset" { yybegin(YYINITIAL); return ISSET; } 140 | "unset" { yybegin(YYINITIAL); return UNSET; } 141 | "throw" { yybegin(YYINITIAL); return THROW; } 142 | "fetch" { yybegin(YYINITIAL); return FETCH; } 143 | "try" { yybegin(YYINITIAL); return TRY; } 144 | "catch" { yybegin(YYINITIAL); return CATCH; } 145 | "(" { yybegin(YYINITIAL); return PARENTHESES_OPEN; } 146 | ")" { yybegin(YYINITIAL); return PARENTHESES_CLOSE; } 147 | "{" { yybegin(YYINITIAL); return BRACKET_OPEN; } 148 | "}" { yybegin(YYINITIAL); return BRACKET_CLOSE; } 149 | "[" { yybegin(YYINITIAL); return SBRACKET_OPEN; } 150 | "]" { yybegin(YYINITIAL); return SBRACKET_CLOSE; } 151 | "@" { yybegin(YYINITIAL); return AT; } 152 | "!" { yybegin(YYINITIAL); return NOT; } 153 | "&&" { yybegin(YYINITIAL); return AND; } 154 | "||" { yybegin(YYINITIAL); return OR; } 155 | "&" { yybegin(YYINITIAL); return BITWISE_AND; } 156 | "|" { yybegin(YYINITIAL); return BITWISE_OR; } 157 | "^" { yybegin(YYINITIAL); return BITWISE_XOR; } 158 | "<<" { yybegin(YYINITIAL); return BITWISE_SHIFTLEFT; } 159 | ">>" { yybegin(YYINITIAL); return BITWISE_SHIFTRIGHT; } 160 | "=" { yybegin(YYINITIAL); return ASSIGN; } 161 | "+=" { yybegin(YYINITIAL); return ADDASSIGN; } 162 | "-=" { yybegin(YYINITIAL); return SUBASSIGN; } 163 | "*=" { yybegin(YYINITIAL); return MULASSIGN; } 164 | "**=" { yybegin(YYINITIAL); return EXPASSIGN; } 165 | "/=" { yybegin(YYINITIAL); return DIVASSIGN; } 166 | "%=" { yybegin(YYINITIAL); return MODASSIGN; } 167 | ".=" { yybegin(YYINITIAL); return CONCATASSIGN; } 168 | "==" { yybegin(YYINITIAL); return EQUALS; } 169 | "!=" { yybegin(YYINITIAL); return NOTEQUALS; } 170 | "===" { yybegin(YYINITIAL); return IDENTICAL; } 171 | "!==" { yybegin(YYINITIAL); return NOTIDENTICAL; } 172 | "<=" { yybegin(YYINITIAL); return LESSEQUAL; } 173 | ">=" { yybegin(YYINITIAL); return GREATEREQUAL; } 174 | "<" { yybegin(YYINITIAL); return LESS; } 175 | ">" { yybegin(YYINITIAL); return GREATER; } 176 | "->" { yybegin(YYINITIAL); return ARROW; } 177 | "::" { yybegin(YYINITIAL); return DOUBLECOLON; } 178 | "." { yybegin(YYINITIAL); return DOT; } 179 | "+" { yybegin(YYINITIAL); return ADD; } 180 | "-" { yybegin(YYINITIAL); return SUB; } 181 | "*" { yybegin(YYINITIAL); return MUL; } 182 | "**" { yybegin(YYINITIAL); return EXP; } 183 | "/" { yybegin(YYINITIAL); return DIV; } 184 | "%" { yybegin(YYINITIAL); return MOD; } 185 | "++" { yybegin(YYINITIAL); return INCR; } 186 | "--" { yybegin(YYINITIAL); return DECR; } 187 | ":" { yybegin(YYINITIAL); return COLON; } 188 | ";" { yybegin(YYINITIAL); return DOTCOMMA; } 189 | "," { yybegin(YYINITIAL); return COMMA; } 190 | "?" { yybegin(YYINITIAL); return QUESTION; } 191 | 192 | {COMMENT} { yybegin(YYINITIAL); return COMMENT; } 193 | {COMMENT_BLOCK} { yybegin(YYINITIAL); return COMMENT_BLOCK; } 194 | {IDENTIFIER} { yybegin(YYINITIAL); return IDENTIFIER; } 195 | {INTEGER} { yybegin(YYINITIAL); return INTEGER; } 196 | {DOUBLE} { yybegin(YYINITIAL); return DOUBLE; } 197 | {CHAR_LITERAL} { yybegin(YYINITIAL); return SCHAR; } 198 | {STRING_LITERAL} { yybegin(YYINITIAL); return STRING; } 199 | {CBLOCK} { yybegin(YYINITIAL); return CBLOCK; } 200 | } 201 | 202 | /////////////////////////////////////////////////////////////////////////////////////////////////// 203 | // Catch All 204 | /////////////////////////////////////////////////////////////////////////////////////////////////// 205 | [^] { return TokenType.BAD_CHARACTER; } 206 | -------------------------------------------------------------------------------- /src/main/grammar/Zephir.bnf: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014-2020 Zephir Team 2 | // 3 | // This source file is subject the MIT license, that is bundled with 4 | // this package in the file LICENSE, and is available through the 5 | // world-wide-web at the following url: 6 | // 7 | // https://github.com/zephir-lang/idea-plugin/blob/master/LICENSE 8 | 9 | { 10 | parserClass='com.zephir.lang.core.parser.ZephirParser' 11 | parserUtilClass='com.zephir.lang.core.parser.manual.ZephirManualParseRules' 12 | 13 | extends='com.intellij.extapi.psi.ASTWrapperPsiElement' 14 | 15 | psiClassPrefix='Zephir' 16 | psiImplClassSuffix='Impl' 17 | psiPackage='com.zephir.lang.core.psi' 18 | psiImplPackage='com.zephir.lang.core.psi.impl' 19 | 20 | elementTypeHolderClass='com.zephir.lang.core.psi.ZephirTypes' 21 | elementTypeClass='com.zephir.lang.core.psi.ZephirElementType' 22 | tokenTypeClass='com.zephir.lang.core.psi.ZephirTokenType' 23 | // TODO(serghei): Do we need this? 24 | // Also see: https://github.com/JetBrains/gradle-grammar-kit-plugin/issues/3 25 | psiImplUtilClass='com.zephir.lang.core.psi.impl.ZephirPsiImplUtil' 26 | 27 | tokens = [ 28 | // regex 29 | COMMENT = 'regexp://.*' 30 | COMMENT_BLOCK = 'regexp:(/\*([^*]+|[*]+[^/*])*[*]*\*/)' 31 | IDENTIFIER = 'regexp:[_a-zA-Z][a-zA-Z0-9_]*' 32 | INTEGER = 'regexp:([\-]?[0-9]+)|([\-]?[0][x][0-9A-Fa-f]+)' 33 | DOUBLE = 'regexp:([\-]?[0-9]+[.][0-9]+)' 34 | SCHAR = "regexp:(['] ([^'\\]|\\.)* ['])" 35 | STRING = 'regexp:(["] ([^"\\]|\\.)* ["])' 36 | CBLOCK = 'regexp:%[{]([^}]+|[}]+[^%{])*[}]%' 37 | 38 | // OOP Keywords 39 | INTERNAL = 'internal' 40 | NAMESPACE = 'namespace' 41 | USE = 'use' 42 | AS = 'as' 43 | INTEFACE = 'interface' 44 | CLASS = 'class' 45 | FUNCTION = 'function' 46 | EXTENDS = 'extends' 47 | IMPLEMENTS = 'implements' 48 | ABSTRACT = 'abstract' 49 | FINAL = 'final' 50 | PUBLIC = 'public' 51 | PROTECTED = 'protected' 52 | PRIVATE = 'private' 53 | STATIC = 'static' 54 | INLINE = 'inline' 55 | DEPRECATED = 'deprecated' 56 | 57 | // Types 58 | TYPE_VAR = 'var' 59 | TYPE_VOID = 'void' 60 | TYPE_INT = 'int' 61 | TYPE_UINT = 'uint' 62 | TYPE_LONG = 'long' 63 | TYPE_ULONG = 'ulong' 64 | TYPE_CHAR = 'char' 65 | TYPE_UCHAR = 'uchar' 66 | TYPE_DOUBLE = 'double' 67 | TYPE_BOOL = 'bool' 68 | TYPE_STRING = 'string' 69 | TYPE_ARRAY = 'array' 70 | TYPE_OBJECT = 'object' 71 | TYPE_CALLABLE = 'callable' 72 | TYPE_RESOURCE = 'resource' 73 | 74 | // Value Keywords 75 | NULL = 'null' 76 | FALSE = 'false' 77 | TRUE = 'true' 78 | 79 | // Keywords 80 | LET = 'let' 81 | ECHO = 'echo' 82 | CONST = 'const' 83 | IF = 'if' 84 | ELSE = 'else' 85 | ELSEIF = 'elseif' 86 | SWITCH = 'switch' 87 | CASE = 'case' 88 | DEFAULT = 'default' 89 | DO = 'do' 90 | WHILE = 'while' 91 | FOR = 'for' 92 | LOOP = 'loop' 93 | REVERSE = 'reverse' 94 | BREAK = 'break' 95 | CONTINUE = 'continue' 96 | IN = 'in' 97 | NEW = 'new' 98 | RETURN = 'return' 99 | REQUIRE = 'require' 100 | CLONE = 'clone' 101 | EMPTY = 'empty' 102 | TYPEOF = 'typeof' 103 | INSTANCEOF = 'instanceof' 104 | LIKELY = 'likely' 105 | UNLIKELY = 'unlikely' 106 | ISSET = 'isset' 107 | UNSET = 'unset' 108 | THROW = 'throw' 109 | FETCH = 'fetch' 110 | TRY = 'try' 111 | CATCH = 'catch' 112 | 113 | // Other 114 | PARENTHESES_OPEN = '(' 115 | PARENTHESES_CLOSE = ')' 116 | BRACKET_OPEN = '{' 117 | BRACKET_CLOSE = '}' 118 | SBRACKET_OPEN = '[' 119 | SBRACKET_CLOSE = ']' 120 | AT = '@' 121 | NOT = '!' 122 | AND = '&&' 123 | OR = '||' 124 | BITWISE_AND = '&' 125 | BITWISE_OR = '|' 126 | BITWISE_XOR = '^' 127 | BITWISE_SHIFTLEFT = '<<' 128 | BITWISE_SHIFTRIGHT = '>>' 129 | ASSIGN = '=' 130 | ADDASSIGN = '+=' 131 | SUBASSIGN = '-=' 132 | MULASSIGN = '*=' 133 | EXPASSIGN = '**=' 134 | DIVASSIGN = '/=' 135 | MODASSIGN = '%=' 136 | CONCATASSIGN = '.=' 137 | EQUALS = '==' 138 | NOTEQUALS = '!=' 139 | IDENTICAL = '===' 140 | NOTIDENTICAL = '!==' 141 | LESSEQUAL = '<=' 142 | GREATEREQUAL = '>=' 143 | LESS = '<' 144 | GREATER = '>' 145 | ARROW = '->' 146 | DOUBLECOLON = '::' 147 | DOT = '.' 148 | ADD = '+' 149 | SUB = '-' 150 | MUL = '*' 151 | EXP = '**' 152 | DIV = '/' 153 | MOD = '%' 154 | INCR = '++' 155 | DECR = '--' 156 | COLON = ':' 157 | DOTCOMMA = ';' 158 | COMMA = ',' 159 | QUESTION = '?' 160 | ] 161 | 162 | extends(".*Expr") = Expr 163 | } 164 | 165 | // This is the grammar of the Zephir language which is used by Grammar Kit 166 | // to generate the parser and the PSI classes. Rule `FooBar` corresponds 167 | // to `ZephirFooBar` PSI class. 168 | // 169 | // Naming convention: 170 | // 171 | // * tokens are lowercase: for, if, identifier 172 | // * rules are PascalCase: TopStatement, ArrayAccess 173 | // * lexer rules are UPPER_CASE: TYPE_UINT, DOUBLECOLON 174 | 175 | File ::= TopStatement* 176 | 177 | CBlock ::= CBLOCK 178 | 179 | private TopStatement ::= NamespaceStatement ImportStatement* CBlock* (ClassDefinition | InterfaceDefinition) 180 | 181 | PhpReserved ::= '$resource' | '$namespace' | '$internal' 182 | 183 | NamespaceStatement ::= 'namespace' ComplexId ';' {pin=2} 184 | 185 | ImportStatement ::= 'use' ComplexId ('as' Id)? ';' {pin=2} 186 | 187 | ClassDefinition ::= ClassModifier? 'class' Id SuperClass? ImplementsList? ClassBody {pin=3} 188 | 189 | private ClassModifier ::= 'abstract' | 'final' 190 | 191 | SuperClass ::= 'extends' ComplexId {pin=1} 192 | ImplementsList ::= 'implements' ComplexIdList {pin=1} 193 | ClassBody ::= '{' ClassMember* '}' {pin=1} 194 | 195 | private ClassMember ::= ConstantDefinition | 196 | PropertyDefinition | 197 | MethodDefinition | 198 | AbstractMethodDefinition 199 | 200 | InterfaceDefinition ::= 'interface' Id SuperInterfaces? InterfaceBody {pin=2} 201 | SuperInterfaces ::= 'extends' ComplexIdList {pin=1} 202 | InterfaceBody ::= '{' InterfaceMember* '}' {pin=1} 203 | 204 | private InterfaceMember ::= ConstantDefinition | 205 | InterfaceMethodDefinition 206 | 207 | InterfaceMethodDefinition ::= MethodHeader '(' Arguments* ')' ReturnType? ';' {pin=2} 208 | AbstractMethodDefinition ::= 'abstract' InterfaceMethodDefinition 209 | 210 | ConstantDefinition ::= 'const' Id DefaultValue? ';' {pin=2} 211 | 212 | PropertyDefinition ::= 'static'? Visibility 'static'? Id DefaultValue? MemberMetaAccess? ';' {pin=4} 213 | 214 | private MemberMetaAccess ::= '{' MemberMetaModifier (',' MemberMetaModifier)* '}' {pin=1} 215 | private MemberMetaModifier ::= 'get' | 'set' | '__toString' 216 | 217 | MethodDefinition ::= MethodHeader '(' Arguments* ')' ReturnType? MethodBody {pin=2} 218 | MethodModifiers ::= 'static' | 'inline' | 'deprecated' | 'final' 219 | MethodBody ::= CodeBlock 220 | 221 | private MethodHeader ::= (Visibility | MethodModifiers)* FUNCTION Id 222 | 223 | CodeBlock ::= '{' Code* '}' {pin=1} 224 | 225 | ReturnType ::= '->' (Type | 'null') ('|' (Type | 'null'))* {pin(".*")=1} 226 | 227 | Arguments ::= Argument (',' Argument)* {pin(".*")=1} 228 | Argument ::= Type? '!'? (PhpReserved | Id) DefaultValue? {pin=3} 229 | 230 | DefaultValue ::= '=' Expr {pin=1} 231 | Visibility ::= 'public' | 'protected' | 'private' 232 | 233 | Id ::= '$'?IDENTIFIER 234 | 235 | ComplexId ::= '$'? '\'? Id ('\' Id)* {pin(".*")=3} 236 | ComplexIdList ::= ComplexId (',' ComplexId)* {pin(".*")=1} 237 | 238 | Type ::= VoidType | ScalarType | ClassType | ResourceType 239 | ScalarType ::= 'var' | NumberType | StringType | BoolType | 'array' | 'object' | 'callable' | 'resource' 240 | 241 | private StringType ::= 'string' | 'char' | 'uchar' 242 | private BoolType ::= 'bool' | 'boolean' 243 | private NumberType ::= IntType | DoubleType | 'uint' | 'long' | 'ulong' 244 | private IntType ::= 'int' | 'integer' 245 | private DoubleType ::= 'double' | 'float' 246 | private ClassType ::= '<' ComplexId ArrayAppendExpr? '>' 247 | private ResourceType ::= '<' '$'ComplexId ArrayAppendExpr? '>' 248 | private VoidType ::= 'void' 249 | 250 | Code ::= DeclarationStatement | 251 | LetStatement | 252 | CallStatement | 253 | ChainOfCallStatement | 254 | ControlStatement | 255 | LoopStatement | 256 | SwitchStatement | 257 | IfStatement | 258 | TryStatement | 259 | ReturnStatement | 260 | EchoStatement | 261 | ThrowStatement | 262 | RequireStatement | 263 | UnsetStatement | 264 | CBlock 265 | 266 | DeclarationStatement ::= Type DeclarationStatementElement (',' DeclarationStatementElement)* ';' {pin(".*")=1} 267 | 268 | private DeclarationStatementElement ::= (Id | PhpReserved) DefaultValue? 269 | 270 | LetStatement ::= 'let' ChangeVariableExpr (',' ChangeVariableExpr)* ';' {pin(".*")=1} 271 | 272 | ChangeVariableExpr ::= AssignmentExpr | IncrementExpr 273 | AssignmentExpr ::= (Typecast | Type)? (Variable | ExplodedVariable | PhpReserved) ArrayAppendExpr? AssignmentOperator (Typecast | Type)? (PhpReserved | Expr) { rightAssociative=true } 274 | 275 | private ArrayAppendExpr ::= '[' ']' 276 | 277 | AssignmentOperator ::= '=' | '+=' | '-=' | '*=' | '**=' | '/=' | '%=' | '.=' 278 | Typecast ::= '(' ScalarType ')' | '<' ComplexId '>' 279 | 280 | IncrementExpr ::= Variable IncrementOperator {rightAssociative=true} 281 | IncrementOperator ::= '++' | '--' 282 | 283 | CallStatement ::= CallExpr ';' 284 | 285 | ControlStatement ::= ('continue' | 'break') ';' 286 | 287 | LoopStatement ::= InfinityLoopStatement | 288 | WhileLoopStatement | 289 | ForeachLoopStatement 290 | 291 | InfinityLoopStatement ::= 'loop' CodeBlock {pin=1} 292 | 293 | WhileLoopStatement ::= 'while' Expr CodeBlock {pin=2} 294 | 295 | ForeachLoopStatement ::= 'for' Id (',' Id)? 'in' 'reverse'? (PhpReserved | Expr) CodeBlock {pin=2} 296 | UnsetStatement ::= 'unset' (Variable | '(' Variable ')') ';' 297 | 298 | SwitchStatement ::= 'switch' Expr '{' CaseExpr* '}' {pin=1} 299 | StaticCallExpr ::= ((('{' Id '}') | 'static') '::') CallExpr {pin=1} 300 | 301 | CaseExpr ::= ('case' Expr | 'default') ':' Code* {pin=1} 302 | 303 | IfStatement ::= 'if' ('likely' | 'unlikely')? Expr CodeBlock ElseifStatements* ElseStatement? {pin=1} 304 | ElseifStatements ::= 'elseif' Expr CodeBlock {pin=1} 305 | ElseStatement ::= 'else' CodeBlock {pin=1} 306 | 307 | TryStatement ::= 'try' CodeBlock CatchBlock* {pin=1} 308 | CatchBlock ::= 'catch' ClassType? ComplexId (',' ComplexId)* CodeBlock {pin=3} 309 | 310 | ReturnStatement ::= 'return' (RequireStatement | (Typecast? Expr? ';')) 311 | 312 | EchoStatement ::= 'echo' Expr (',' Expr)* ';' {pin(".*")=1} 313 | 314 | ThrowStatement ::= 'throw' Expr ';' {pin=1} 315 | RequireStatement ::= RequireExpr ';' {pin=1} 316 | 317 | private RequireExpr ::= 'require' Expr 318 | 319 | Expr ::= UnaryExpr | 320 | BitExpr | 321 | BoolGroupExpr | 322 | MathExpr | 323 | PrimaryGroupExpr | 324 | SpecialGroupExpr 325 | 326 | private BoolGroupExpr ::= BoolExpr | TernaryExpr | EmptyExpr | IssetExpr | FetchExpr | InstanceofExpr 327 | private PrimaryGroupExpr ::= ChainOfCallExpr | StaticCallExpr | LiteralExpr | ParenExpr | PrecastExpr | CallbackExpr | CallExpr | CloneExpr 328 | private SpecialGroupExpr ::= NewExpr | TypeofExpr 329 | 330 | ChainOfCallStatement ::= ChainOfCallExpr ; 331 | ChainOfCallExpr ::= (ParenExpr '->') Expr 332 | 333 | BitExpr ::= Expr BitOperator Expr 334 | BitOperator ::= '&' | '|' | '^' | '>>' | '<<' 335 | 336 | MathExpr ::= Expr MathOperator Expr 337 | MathOperator ::= '+' | '-' | '*' | '**' | '/' | '%' | '.' 338 | 339 | UnaryExpr ::= ('+' | '-' | '!') Expr 340 | BoolExpr ::= Expr BoolOperator Expr 341 | BoolOperator ::= '&&' | '||' | '>' | '<' | '==' | '>=' | '<=' | '===' | '!=' | '!==' 342 | 343 | TernaryExpr ::= Expr '?' Expr ':' Expr {pin=1} 344 | LiteralExpr ::= Typecast? (Variable | scalar) 345 | ParenExpr ::= '(' Expr ')' {pin=1} 346 | PrecastExpr ::= '{' Expr '}' {pin=1} 347 | 348 | EmptyExpr ::= 'empty' Expr {pin=1} 349 | IssetExpr ::= 'isset' Expr {pin=1} 350 | FetchExpr ::= 'fetch' Id ',' Expr {pin=2} 351 | InstanceofExpr ::= Variable 'instanceof' Expr {pin=2} 352 | 353 | CallExpr ::= Expr '(' CallArguments? ')' ('->' Id '(' CallArguments? ')')* ArrayAccess? {pin=3} 354 | MagicCallExpr ::= '$'('clone' | 'fetch') '(' Arguments* ')' 355 | CallbackExpr ::= 'function' '(' Arguments* ')' CodeBlock {pin=1} 356 | CloneExpr ::= 'clone' Variable 357 | 358 | NewExpr ::= NewExprClassic | NewExprDynamic 359 | 360 | private NewExprClassic ::= 'new' ComplexId ('(' CallArguments? ')')? {pin=2} 361 | private NewExprDynamic ::= 'new' '{' Expr '}' ('(' CallArguments? ')')? {pin=2} 362 | 363 | TypeofExpr ::= 'typeof' Expr {pin=1} 364 | 365 | CallArguments ::= CallArgument (',' CallArgument)* {pin(".*")=1} 366 | 367 | private CallArgument ::= 'require'? Typecast? (PhpReserved | Expr) 368 | private ScalarShort ::= STRING | SCHAR | DOUBLE | INTEGER | ScalarValue 369 | 370 | scalar ::= ScalarShort | ArrayValue 371 | ScalarValue ::= 'null' | 'true' | 'false' 372 | 373 | ArrayValue ::= '[' ArrayBody? ']' {pin=1} 374 | 375 | private ArrayBody ::= ArrayItem (',' ArrayItem)* {pin(".*")=1} 376 | 377 | ArrayItem ::= ArrayKeyValue | Expr 378 | ArrayKeyValue ::= (ScalarShort | Expr) ':' Expr 379 | 380 | Variable ::= ComplexId (PropertyAccess | ArrayAppendExpr | ArrayAccess)* 381 | ExplodedVariable ::= '{' Variable '}' 382 | 383 | private PropertyAccess ::= ('::' | '->') (Variable | '{' (STRING | Id) '}' | MagicCallExpr) {pin=2} 384 | private ArrayAccess ::= '[' (Expr | PhpReserved) ']' {pin=1} 385 | --------------------------------------------------------------------------------