diff --git a/.github/actions/setup-env/action.yml b/.github/actions/setup-env/action.yml new file mode 100644 index 000000000..2e041d119 --- /dev/null +++ b/.github/actions/setup-env/action.yml @@ -0,0 +1,68 @@ +name: 'Setup Build Environment' +description: 'Sets up Java, Elixir, and Gradle for IntelliJ plugin builds' +inputs: + elixir-version: + description: 'Elixir version' + required: false + default: '1.13.4' + otp-version: + description: 'OTP version' + required: false + default: '24.3.4.6' + idea-version: + description: 'IntelliJ IDEA version' + required: false + default: '' + skip-gradle-cache: + description: 'Skip Gradle cache' + required: false + default: 'false' + skip-jcef: + description: 'Skip JCEF package (set to true for release/verifyPlugin jobs)' + required: false + default: 'false' + +runs: + using: composite + steps: + - name: Free Disk Space (Ubuntu) + uses: jlumbroso/free-disk-space@main + with: + tool-cache: false + android: true + dotnet: true + haskell: true + large-packages: false + docker-images: true + swap-storage: false + + - name: Setup JBR 21 + uses: actions/setup-java@v5 + with: + distribution: 'jetbrains' + java-version: 21 + java-package: ${{ inputs.skip-jcef == 'false' && 'jdk+jcef' || 'jdk' }} + + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v5 + + - name: Set up Elixir + env: + ImageOS: ubuntu24 + if: ${{ inputs.elixir-version != '' }} + uses: erlef/setup-beam@v1 + with: + otp-version: ${{ inputs.otp-version }} + elixir-version: ${{ inputs.elixir-version }} + + - name: Export environment variables + shell: bash + run: | + if [ -n "${{ inputs.elixir-version }}" ]; then + echo "OTP_RELEASE=${{ inputs.otp-version }}" >> $GITHUB_ENV + echo "ERLANG_SDK_HOME=$(erl -eval 'io:format("~s", [code:root_dir()]).' -noshell -run init stop)" >> $GITHUB_ENV + fi + chmod +x gradlew + if [ -n "${{ inputs.idea-version }}" ]; then + echo "ORG_GRADLE_PROJECT_ideaVersion=${{ inputs.idea-version }}" >> $GITHUB_ENV + fi diff --git a/.github/workflows/publish-canary.yml b/.github/workflows/publish-canary.yml new file mode 100644 index 000000000..92a86e5c0 --- /dev/null +++ b/.github/workflows/publish-canary.yml @@ -0,0 +1,32 @@ +name: Publish to Canary +on: +# release: +# types: [prereleased] + workflow_dispatch: + +jobs: + publish-to-canary: + runs-on: ubuntu-latest + steps: + - name: Check out repository 💾 + uses: actions/checkout@v5 + + - name: Setup Build Environment ☕ + uses: ./.github/actions/setup-env + with: + elixir-version: '' + + - name: Download release asset 📦 + id: download_asset + uses: dsaltares/fetch-gh-release-asset@1.1.2 + with: + repo: ${{ github.repository }} + version: tags/${{ github.ref_name }} + file: intellij-elixir-${{ github.ref_name }}.zip + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Publish to Canary 🐣 + env: + JET_BRAINS_MARKETPLACE_TOKEN: ${{ secrets.JetBrainsMarketplaceToken }} + run: > + ./gradlew publishPlugin -PdistributionFile=intellij-elixir-${{ github.ref_name }}.zip \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index de3e7e02b..9e6048263 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -6,77 +6,43 @@ on: - main jobs: - test: - runs-on: ubuntu-latest + test-and-verify: + uses: ./.github/workflows/shared-test.yml + with: + elixir-version: '1.13.4' + otp-version: '24.3.4.6' - steps: - - uses: actions/checkout@v2 - - name: Setup JBR 21 - uses: actions/setup-java@v5 - with: - distribution: 'jetbrains' - java-version: 21 - - name: Set up Elixir - uses: erlef/setup-beam@v1 - with: - otp-version: 24.3.4.6 - elixir-version: 1.13.0 - - name: Export OTP_RELEASE - run: echo "OTP_RELEASE=24.3.4.6" >> $GITHUB_ENV - - name: Export ERLANG_SDK_HOME - run: echo "ERLANG_SDK_HOME=`erl -eval 'io:format("~s", [code:root_dir()]).' -noshell -run init stop`" >> $GITHUB_ENV - - name: Grant execute permission for gradlew - run: chmod +x gradlew - - name: Compile Tests with Gradle - run: ./gradlew --stacktrace compileTestJava - - name: Get Elixir Source - run: ./gradlew --stacktrace getElixir - - name: Release Quoter - run: ./gradlew --stacktrace releaseQuoter - - name: Test with Gradle - run: ./gradlew --stacktrace test - verifyPlugin: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - name: Setup JBR 21 - uses: actions/setup-java@v5 - with: - distribution: 'jetbrains' - java-version: 21 - - name: Grant execute permission for gradlew - run: chmod +x gradlew - - name: Run Plugin Verifier - run: ./gradlew --stacktrace verifyPlugin release: - needs: [ test, verifyPlugin ] + needs: [ test-and-verify ] runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Setup JBR 21 - uses: actions/setup-java@v5 + - uses: actions/checkout@v5 + + - name: Setup Build Environment + uses: ./.github/actions/setup-env with: - distribution: 'jetbrains' - java-version: 21 - - name: Grant execute permission for gradlew - run: chmod +x gradlew + elixir-version: '' + skip-jcef: 'true' + - name: Build with Gradle run: ./gradlew buildPlugin + - name: Export ASSET_PATH - run: echo "ASSET_PATH=`ls -1 build/distributions/intellij-elixir-*.zip`" >> $GITHUB_ENV + run: echo "ASSET_PATH=$(ls -1 build/distributions/intellij-elixir-*.zip)" >> $GITHUB_ENV + - name: Export ASSET_NAME run: echo "ASSET_NAME=${ASSET_PATH#build/distributions/}" >> $GITHUB_ENV + - name: Export TAG run: | version_suffix_zip=${ASSET_NAME#intellij-elixir-} echo "TAG=v${version_suffix_zip%.zip}" >> $GITHUB_ENV + - name: Tag Commit uses: hole19/git-tag-action@master env: - # TAG set above with `set-env name=TAG` GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Create Release id: create_release uses: actions/create-release@latest @@ -86,6 +52,7 @@ jobs: tag_name: ${{ env.TAG }} release_name: ${{ env.TAG }} prerelease: true + - name: Upload Release Asset uses: actions/upload-release-asset@v1 env: @@ -95,7 +62,3 @@ jobs: asset_path: ${{ env.ASSET_PATH }} asset_name: ${{ env.ASSET_NAME }} asset_content_type: application/zip - - name: Publish - env: - JET_BRAINS_MARKETPLACE_TOKEN: ${{ secrets.JetBrainsMarketplaceToken }} - run: ./gradlew --stacktrace publishPlugin \ No newline at end of file diff --git a/.github/workflows/shared-test.yml b/.github/workflows/shared-test.yml new file mode 100644 index 000000000..206f8b6e5 --- /dev/null +++ b/.github/workflows/shared-test.yml @@ -0,0 +1,91 @@ +name: Shared Test Workflow + +on: + workflow_call: + inputs: + elixir-version: + description: 'Elixir version to use' + required: false + type: string + default: '1.13.4' + otp-version: + description: 'OTP version to use' + required: false + type: string + default: '24.3.4.6' + idea-version: + description: 'IntelliJ IDEA version for testing' + required: false + type: string + default: '253.28294.169' + upload-reports: + description: 'Upload test reports as artifacts' + required: false + type: boolean + default: false + +jobs: + test: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v5 + + - name: Setup Build Environment + uses: ./.github/actions/setup-env + with: + elixir-version: ${{ inputs.elixir-version }} + otp-version: ${{ inputs.otp-version }} + idea-version: ${{ inputs.idea-version }} + + - name: Compile Tests with Gradle + run: ./gradlew --stacktrace compileTestJava + + - name: Get Elixir Source + run: ./gradlew --stacktrace getElixir + + - name: Release Quoter + run: ./gradlew --stacktrace releaseQuoter + + - name: Test with Gradle + run: ./gradlew --stacktrace test + + - name: Publish Test Results + uses: EnricoMi/publish-unit-test-result-action@v2 + if: always() + with: + files: '**/build/test-results/**/*.xml' + check_name: Test Results + + - name: Upload Test Reports + uses: actions/upload-artifact@v4 + if: ${{ always() && inputs.upload-reports }} + with: + name: test-reports + path: | + **/build/reports/tests/** + **/build/test-results/** + retention-days: 30 + + verifyPlugin: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v5 + + - name: Setup Build Environment + uses: ./.github/actions/setup-env + with: + elixir-version: '' + + - name: Run Plugin Verifier + run: ./gradlew --stacktrace verifyPlugin + + - name: Upload Verify Reports + uses: actions/upload-artifact@v4 + if: ${{ always() && inputs.upload-reports }} + with: + name: verify-plugin-reports + path: | + **/build/reports/pluginVerifier + retention-days: 30 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 18d4539ac..a1489816d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,62 +1,16 @@ name: Test -on: pull_request +on: [ pull_request, workflow_dispatch ] concurrency: group: ${{ github.workflow }}-${{ github.head_ref }} cancel-in-progress: true jobs: - test: - runs-on: ubuntu-latest - - strategy: - matrix: - ideaVersion: [ "2024.2.6", "2024.3.6", "2025.2.1" ] - - steps: - - uses: actions/checkout@v2 - - name: Setup JBR 21 - uses: actions/setup-java@v5 - with: - distribution: 'jetbrains' - java-version: 21 - cache: 'gradle' - package-type: 'jdk+jcef' - - name: Set up Elixir - uses: erlef/setup-beam@v1 - with: - otp-version: 24.3.4.6 - elixir-version: 1.13.4 - - name: Export OTP_RELEASE - run: echo "OTP_RELEASE=24.3.4.6" >> $GITHUB_ENV - - name: Export ERLANG_SDK_HOME - run: echo "ERLANG_SDK_HOME=`erl -eval 'io:format("~s", [code:root_dir()]).' -noshell -run init stop`" >> $GITHUB_ENV - - name: Grant execute permission for gradlew - run: chmod +x gradlew - - name: Export ideaVersion to gradle - run: echo "ORG_GRADLE_PROJECT_ideaVersion=${{matrix.ideaVersion}}" >> $GITHUB_ENV - - name: Compile Tests with Gradle - run: ./gradlew --stacktrace compileTestJava - - name: Get Elixir Source - run: ./gradlew --stacktrace getElixir - - name: Release Quoter - run: ./gradlew --stacktrace releaseQuoter - - name: Test with Gradle - run: ./gradlew --stacktrace test - verifyPlugin: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - name: Setup JBR 21 - uses: actions/setup-java@v5 - with: - distribution: 'jetbrains' - java-version: 21 - cache: 'gradle' - package-type: 'jdk+jcef' - - name: Grant execute permission for gradlew - run: chmod +x gradlew - - name: Run Plugin Verifier - run: ./gradlew --stacktrace verifyPlugin + test-and-verify: + uses: ./.github/workflows/shared-test.yml + with: + elixir-version: '1.13.4' + otp-version: '24.3.4.6' + idea-version: '2024.3.6' + upload-reports: true diff --git a/.tool-versions b/.tool-versions index 55555fb4b..175bccf4c 100644 --- a/.tool-versions +++ b/.tool-versions @@ -7,4 +7,4 @@ erlang 24.3.4.6 # mac: # mise link java@jbrsdk_jcef-21.0.3-b509.4-2024.2 /path/to/jbrsdk_jcef-21.0.3-xxx-xxx-b509.4/Contents/Home # Then setting it to `java jbrsdk_jcef-21.0.3-b509.4-2024.2` -java zulu-21.36.17 +java jetbrains-21.0.8b1038.68 diff --git a/build.gradle b/build.gradle deleted file mode 100644 index beb3db599..000000000 --- a/build.gradle +++ /dev/null @@ -1,429 +0,0 @@ -import org.jetbrains.intellij.platform.gradle.IntelliJPlatformType -import org.jetbrains.intellij.platform.gradle.TestFrameworkType -import org.jetbrains.intellij.platform.gradle.tasks.RunIdeTask -import org.jetbrains.kotlin.gradle.dsl.JvmTarget -import org.jetbrains.kotlin.gradle.dsl.KotlinVersion -import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile - -plugins { - id "org.jetbrains.intellij.platform" version "2.7.2" - id "org.jetbrains.kotlin.jvm" version "2.2.10" - id "de.undercouch.download" version "5.6.0" - id 'com.adarshr.test-logger' version '4.0.0' -} - -apply plugin: "org.jetbrains.intellij.platform" - -ext { - cachePath = "${rootDir}/cache" - - elixirPath = "${cachePath}/elixir-${elixirVersion}" - - quoterVersion = "2.1.0" - - quoterUnzippedPath = "${cachePath}/elixir-${elixirVersion}-intellij_elixir-${quoterVersion}" - quoterReleasePath = "${quoterUnzippedPath}/_build/dev/rel/intellij_elixir" - quoterExe = "${quoterReleasePath}/bin/intellij_elixir" - quoterZipPath = "${cachePath}/intellij_elixir-${quoterVersion}.zip" - quoterZipRootPath = "${cachePath}/intellij_elixir-${quoterVersion}" - - if (project.hasProperty("isRelease") && isRelease) { - versionSuffix = "" - channel = "default" - } else { - def date = new Date().format("yyyyMMddHHmmss", TimeZone.getTimeZone("UTC")) - versionSuffix = "-pre+$date" - channel = "canary" - } - - version = "$pluginVersion$versionSuffix" -} - -allprojects { - apply plugin: 'java' - sourceCompatibility = javaVersion - targetCompatibility = javaVersion - java { - sourceCompatibility = JavaVersion.VERSION_21 - targetCompatibility = JavaVersion.VERSION_21 - } - tasks.withType(JavaCompile) { options.encoding = 'UTF-8' } -} -subprojects { - apply plugin: 'org.jetbrains.intellij.platform.module' - repositories { - mavenCentral() - intellijPlatform { - defaultRepositories() - } - } - dependencies { - testImplementation 'junit:junit:4.13.2' - testImplementation "org.opentest4j:opentest4j:1.3.0" - - intellijPlatform { - create(providers.gradleProperty("platformType"), providers.gradleProperty("platformVersion")) - - bundledPlugins providers.gradleProperty("platformBundledPlugins").map { it.split(',').toList() } - - instrumentationTools() - pluginVerifier() - zipSigner() - testFramework(TestFrameworkType.Platform.INSTANCE) - testFramework(TestFrameworkType.Plugin.Java.INSTANCE) - } - } - sourceSets { - main { - java.srcDirs 'src' - resources.srcDirs 'resources' - } - test { - java.srcDir 'tests' - } - } - -} -sourceSets { - main { - java.srcDirs 'src', 'gen' - resources.srcDirs 'resources' - } - test { - java.srcDir 'tests' - } -} -intellijPlatform { - // buildSearchableOptions = false - // instrumentCode = false - pluginConfiguration { - def stripTag = { text, tag -> text.replace("<${tag}>", "").replace("", "") } - def bodyInnerHTML = { path -> stripTag(stripTag(file(path).text, "html"), "body") - } - - id = providers.gradleProperty("pluginGroup") - name = providers.gradleProperty("pluginName") - version = providers.gradleProperty("pluginVersion") - changeNotes.set(bodyInnerHTML("resources/META-INF/changelog.html")) - description.set(bodyInnerHTML("resources/META-INF/description.html")) - - ideaVersion { - sinceBuild = providers.gradleProperty("pluginSinceBuild") - // We want users to be able to install the plugin on future versions, and if there is incompatibility, - // they should hopefully create an issue :-). - untilBuild = provider { null } - } - vendor { - name = providers.gradleProperty("vendorName") - email = providers.gradleProperty("vendorEmail") - url = providers.gradleProperty("pluginRepositoryUrl") - } - } - - publishPlugin { - token = provider { - System.getenv("JET_BRAINS_MARKETPLACE_TOKEN") - } - channels = publishChannels.split(',').toList() - archiveFile = layout.buildDirectory.file("distributions/${System.getenv("ASSET_NAME")}") - } - pluginVerification { - ides { - // https://www.jetbrains.com/idea/download/other.html -// ide(IntelliJPlatformType.IntellijIdeaCommunity, "2024.2.6") -// ide(IntelliJPlatformType.IntellijIdeaCommunity, "2024.3.6") - ide IntelliJPlatformType.IntellijIdeaCommunity, "2025.2.1" - } - } -} -apply plugin: "kotlin" - -// Configure all RunIdeTask instances (including the new platform-specific ones) -tasks.withType(RunIdeTask).configureEach { - // Set JVM arguments - jvmArguments.addAll(["-Didea.debug.mode=true", "-Didea.is.internal=true", "-Dlog4j2.debug=true", "-Dlogger.org=TRACE", "-XX:+AllowEnhancedClassRedefinition"]) - - // Set system properties to debug log - systemProperty "idea.log.debug.categories", "org.elixir_lang" - - // Set the maximum heap size - maxHeapSize = "7g" - - // Optionally set the working directory if specified in the project properties - if (project.hasProperty("runIdeWorkingDirectory") && !project.property("runIdeWorkingDirectory").isEmpty()) { - workingDir = file(project.property("runIdeWorkingDirectory")) - } - - def compatiblePluginsList = providers.gradleProperty("runIdeCompatiblePlugins").get().with { it.isEmpty() ? [] : it.split(",") } - if (compatiblePluginsList.size() > 0) { - dependencies { - intellijPlatform { - compatiblePlugins(compatiblePluginsList) - } - } - } -} - -kotlin { - jvmToolchain(21) -} - -tasks.withType(KotlinJvmCompile).configureEach { - compilerOptions { - jvmTarget.set(JvmTarget.JVM_21) - freeCompilerArgs.add("-Xjvm-default=all") - apiVersion.set(KotlinVersion.KOTLIN_2_2) - } -} - -def compilationPackages = ['org/intellij/elixir/build/**', 'org/intellij/elixir/jps/**'] - -test { - environment "ELIXIR_LANG_ELIXIR_PATH", elixirPath - environment "ELIXIR_EBIN_DIRECTORY", "${elixirPath}/lib/elixir/ebin/" - environment "ELIXIR_VERSION", elixirVersion - setScanForTestClasses(false) - include("**/Issue*.class") - include("**/*Test.class") - include("**/*TestCase.class") - useJUnit { - exclude compilationPackages - } - testLogging { - exceptionFormat = 'full' - } -} - -// Get the list of platforms from gradle.properties -def runIdePlatformsList = providers.gradleProperty("runIdePlatforms").get().split(",") - -intellijPlatformTesting { - runIde { - runIdePlatformsList.each { platform -> - "run${platform}" { - type = IntelliJPlatformType."${platform}" - version = providers.gradleProperty("platformVersion${platform}").get() - - prepareSandboxTask { - sandboxDirectory = project.layout.buildDirectory.dir("${platform.toLowerCase()}-sandbox") - } - } - - // if enableEAPIDEs is true, create an EAP instance - if (providers.gradleProperty("enableEAPIDEs").get().toLowerCase() == "true") { - "run${platform}EAP" { - type = IntelliJPlatformType."${platform}" - version = providers.gradleProperty("platformVersion${platform}EAP").get() - useInstaller = false - - prepareSandboxTask { - sandboxDirectory = project.layout.buildDirectory.dir("${platform.toLowerCase()}_eap-sandbox") - } - } - } - } - } -} - -task testCompilation(type: Test, group: 'Verification', dependsOn: [classes, testClasses]) { - useJUnit { - include compilationPackages - } - testLogging { - exceptionFormat = 'full' - } -} - -repositories { - maven { url = 'https://maven-central.storage.googleapis.com' } - mavenCentral() - intellijPlatform { - defaultRepositories() - } -} - -dependencies { - intellijPlatform { - create(providers.gradleProperty("platformType"), providers.gradleProperty("platformVersion")) - - bundledPlugins(providers.gradleProperty("platformBundledPlugins").map { it.split(',').toList() }) - instrumentationTools() - pluginVerifier() - zipSigner() - testFramework(TestFrameworkType.Platform.INSTANCE) - testFramework(TestFrameworkType.Plugin.Java.INSTANCE) - } - - implementation project(':jps-builder') - implementation project(':jps-shared') - implementation files('lib/OtpErlang.jar') - implementation group: 'commons-io', name: 'commons-io', version: '2.5' - - testImplementation 'junit:junit:4.13.2' - testImplementation "org.opentest4j:opentest4j:1.3.0" - - testImplementation group: 'org.mockito', name: 'mockito-core', version: '2.2.9' - testImplementation group: 'org.objenesis', name: 'objenesis', version: '2.4' -} -compileJava { - dependsOn ':jps-shared:composedJar' - dependsOn ':jps-builder:composedJar' -} - -apply plugin: 'idea' -idea { - project { - jdkName = javaVersion - languageLevel = javaVersion - } - module { - generatedSourceDirs += file('gen') - } -} - -tasks.register('getElixir') { - doLast { - def folder = new File(elixirPath as String) - - if (!folder.isDirectory() || folder.list().size() == 0) { - download.run { - src "https://github.com/elixir-lang/elixir/archive/v${elixirVersion}.zip" - dest "${rootDir}/cache/Elixir.${elixirVersion}.zip" - overwrite false - } - } - - def binFolder = new File("${elixirPath}/bin") - if (!binFolder.isDirectory() || folder.list().size() == 0) { - copy { - from zipTree("${rootDir}/cache/Elixir.${elixirVersion}.zip") - into "${rootDir}/cache/" - } - - exec { - workingDir elixirPath - commandLine "make" - } - } - } -} - -task getQuoter { - doLast { - download.run { - src "https://github.com/KronicDeth/intellij_elixir/archive/v${quoterVersion}.zip" - dest quoterZipPath - overwrite false - } - - def folder = new File(quoterUnzippedPath) - if (!folder.isDirectory() || folder.list().size() == 0) { - copy { - from zipTree(quoterZipPath) - into cachePath - } - - def quoterZipRootFile = new File(quoterZipRootPath) - - quoterZipRootFile.renameTo(quoterUnzippedPath) - } - } -} - -task getQuoterDeps(dependsOn: getQuoter) { - doLast { - exec { - workingDir quoterUnzippedPath - commandLine 'mix', 'do', 'local.rebar', '--force,', 'local.hex', '--force,', 'deps.get' - // standardOutput = System.out - // errorOutput = System.err - } - } -} - -task releaseQuoter(dependsOn: getQuoterDeps) { - doLast { - def file = new File(quoterExe) - - if (!file.canExecute()) { - exec { - workingDir quoterUnzippedPath - commandLine 'mix', 'do', 'local.rebar', '--force,', 'local.hex', '--force,', 'deps.get,', 'release' - // standardOutput = System.out - // errorOutput = System.err - } - } - } -} - -compileTestJava { - dependsOn ":jps-builder:composedJar" - dependsOn ":jps-shared:composedJar" - dependsOn getElixir - dependsOn getQuoter -} - -task runQuoter(type: Exec, dependsOn: releaseQuoter) { - environment "RELEASE_COOKIE", "intellij_elixir" - environment "RELEASE_DISTRIBUTION", "name" - environment "RELEASE_NAME", "intellij_elixir@127.0.0.1" - executable quoterExe - args "daemon" -} - -task stopQuoter(type: Exec, dependsOn: releaseQuoter) { - environment "RELEASE_COOKIE", "intellij_elixir" - environment "RELEASE_DISTRIBUTION", "name" - environment "RELEASE_NAME", "intellij_elixir@127.0.0.1" - executable quoterExe - args "stop" -} - -runIde { - systemProperty "idea.log.debug.categories", "org.elixir_lang=TRACE" - // When wanting to disable EDT slow assertion.. - // systemProperty "ide.slow.operations.assertion", "true" - - // -Didea.debug.mode=true: - // This enables debug mode for IntelliJ IDEA. It can be useful when developing plugins to get more detailed logging and debugging information. - // -Didea.is.internal=true: - // This flag indicates that the plugin is running in an internal mode, which may enable additional features or logging that are typically only available to IntelliJ developers. - // -Dlog4j2.debug=true: - // This enables debug logging for Log4j 2, which is the logging framework used by IntelliJ IDEA. It can help in troubleshooting logging-related issues in your plugin. - // -Dlogger.org=TRACE: - // This sets the logging level for the "org" package to TRACE, which is the most verbose logging level. This can be useful for detailed logging of plugin activities. - // -XX:+AllowEnhancedClassRedefinition: - // This is a JVM flag that allows for more flexible class redefinition during runtime. It can be beneficial for hot-swapping code changes without restarting the IDE. - // -Didea.ProcessCanceledException=disabled: - //This disables the throwing of ProcessCanceledException, which is typically used to cancel long-running processes in IntelliJ IDEA. Disabling it can be useful in certain debugging scenarios. - jvmArgs "-Didea.debug.mode=true", "-XX:+AllowEnhancedClassRedefinition", "-Didea.is.internal=true", "-Dlog4j2.debug=true", "-Dlogger.org=TRACE", "-Didea.ProcessCanceledException=disabled" - maxHeapSize = "7g" - // get from runIdeWorkingDirectory - if (project.hasProperty("runIdeWorkingDirectory") && !project.property("runIdeWorkingDirectory").isEmpty()) { - workingDir = file(project.property("runIdeWorkingDirectory")) - } -} - -test { - dependsOn runQuoter - finalizedBy stopQuoter -} - -apply plugin: 'idea' -idea { - project { - jdkName = javaVersion - languageLevel = javaVersion - } - module { - generatedSourceDirs += file('gen') - } -} - -// Uncomment to allow using build-scan. -// if (hasProperty('buildScan')) { -// buildScan { -// termsOfServiceUrl = 'https://gradle.com/terms-of-service' -// termsOfServiceAgree = 'yes' -// } -// } diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 000000000..c2cd2381f --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,435 @@ +/* + * Main Build Script + * Purpose: Orchestrates the build of the IntelliJ Elixir plugin. + * * Key Changes for 2025.3 / Small IDE Support: + * 1. Uses Version Catalog (libs.*) for dependency management. + * 2. Explicitly configure JPS subprojects with TestFramework dependencies. + * 3. `runQuoter` task now actively polls the daemon to prevent test race conditions. + * 4. `test` task enforces the working directory to project root to find testData. + */ + +import com.adarshr.gradle.testlogger.TestLoggerExtension +import com.adarshr.gradle.testlogger.theme.ThemeType +import de.undercouch.gradle.tasks.download.Download +import org.jetbrains.intellij.platform.gradle.IntelliJPlatformType +import org.jetbrains.intellij.platform.gradle.TestFrameworkType +import org.jetbrains.intellij.platform.gradle.models.ProductRelease +import org.jetbrains.intellij.platform.gradle.tasks.RunIdeTask +import org.jetbrains.kotlin.gradle.dsl.JvmTarget +import org.jetbrains.kotlin.gradle.dsl.KotlinVersion +import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile +import java.text.SimpleDateFormat +import java.util.* + +// Uses the Version Catalog defined in gradle/libs.versions.toml +plugins { + alias(libs.plugins.intellij.platform) + alias(libs.plugins.kotlin.jvm) + alias(libs.plugins.download) + alias(libs.plugins.test.logger) + id("java") + id("idea") +} + +// --- Version Catalog Captures --- +// Capture these early to avoid "Extension 'libs' not found" errors in subproject blocks +val javaVersionStr: String = libs.versions.java.get() +val libJunit = libs.junit +val libOpentest4j = libs.opentest4j +val libCommonsIo = libs.commons.io + +// --- Configuration Properties --- +val elixirVersion: String by project +val quoterVersion: String by project +val pluginVersion: String by project +val publishChannels: String by project +val useDynamicEapVersion: Boolean = project.property("useDynamicEapVersion").toString().toBoolean() + +val actualPlatformVersion: String = if (useDynamicEapVersion) { + // Calling the helper from buildSrc + VersionFetcher.getLatestEapBuild() +} else { + project.property("platformVersion").toString() +} + +// Setup Paths +val cachePath = layout.projectDirectory.dir("cache") +val elixirPath = cachePath.dir("elixir-$elixirVersion") +val quoterUnzippedPath = cachePath.dir("elixir-$elixirVersion-intellij_elixir-$quoterVersion") +val quoterExe = quoterUnzippedPath.file("_build/dev/rel/intellij_elixir/bin/intellij_elixir") + +// EXPORT FOR SUBPROJECTS (Required for jps-builder to access this path) +extra["elixirPath"] = elixirPath.asFile.absolutePath + +val versionSuffix = if (project.hasProperty("isRelease") && project.property("isRelease").toString().toBoolean()) { + "" +} else { + "-pre+$actualPlatformVersion-" + SimpleDateFormat("yyyyMMddHHmmss").apply { timeZone = TimeZone.getTimeZone("UTC") }.format(Date()) +} + +version = "$pluginVersion$versionSuffix" + +println("Building against IntelliJ Platform version: $actualPlatformVersion") + +// --- Global Project Configuration --- +allprojects { + apply(plugin = "java") + apply(plugin = "com.adarshr.test-logger") + + repositories { + mavenCentral() + } + + dependencies { + testImplementation(libJunit) + testImplementation(libOpentest4j) + } + + configure { + sourceCompatibility = JavaVersion.valueOf("VERSION_$javaVersionStr") + targetCompatibility = JavaVersion.valueOf("VERSION_$javaVersionStr") + } + + tasks.withType { options.encoding = "UTF-8" } + + configure { + theme = ThemeType.MOCHA + showExceptions = true + showStackTraces = true + showFullStackTraces = false + slowThreshold = 2000 + showSummary = true + showStandardStreams = false + showFailedStandardStreams = true + } +} + +// --- Subprojects (JPS) --- +subprojects { + apply(plugin = "org.jetbrains.intellij.platform.module") + + repositories { + intellijPlatform { defaultRepositories() } + } + + dependencies { + intellijPlatform { + create(providers.gradleProperty("platformType"), providers.provider { actualPlatformVersion }) + bundledPlugins(providers.gradleProperty("platformBundledPlugins").map { it.split(",") }) + bundledModules(providers.gradleProperty("platformBundledModules").map { it.split(",") }) + testFramework(TestFrameworkType.Platform) + testFramework(TestFrameworkType.Plugin.Java) + } + // JPS Builder tests extend UsefulTestCase (JUnit 3/4 style) and need explicit JUnit 4 on classpath + testImplementation(libJunit) + } + + sourceSets { + main { + java.srcDirs("src") + resources.srcDirs("resources") + } + test { + java.srcDir("tests") + } + } +} + +// --- Root Project Repositories --- +repositories { + intellijPlatform { defaultRepositories() } +} + +// --- Source Sets --- +sourceSets { + main { + java.srcDirs("src", "gen") + resources.srcDirs("resources") + } + test { + java.srcDir("tests") + } +} + +// --- IntelliJ Platform Configuration --- +intellijPlatform { + pluginConfiguration { + id = providers.gradleProperty("pluginGroup") + name = providers.gradleProperty("pluginName") + version = providers.gradleProperty("pluginVersion") + + val stripTag = { text: String, tag: String -> text.replace("<${tag}>", "").replace("", "") } + val bodyInnerHTML = { path: String -> stripTag(stripTag(file(path).readText(), "html"), "body") } + + changeNotes = bodyInnerHTML("resources/META-INF/changelog.html") + description = bodyInnerHTML("resources/META-INF/description.html") + + ideaVersion { + sinceBuild = providers.gradleProperty("pluginSinceBuild") + untilBuild = providers.provider { null } + } + vendor { + name = providers.gradleProperty("vendorName") + email = providers.gradleProperty("vendorEmail") + url = providers.gradleProperty("pluginRepositoryUrl") + } + } + + publishing { + token = providers.environmentVariable("JET_BRAINS_MARKETPLACE_TOKEN") + channels = publishChannels.split(",") + } + + pluginVerification { + ides { + select { + types = providers.gradleProperty("pluginVerifierIdeTypes") + .get() + .split(",") + .map { IntelliJPlatformType.valueOf(it.trim()) } + + channels = providers.gradleProperty("pluginVerifierChannels") + .get() + .split(",") + .map { ProductRelease.Channel.valueOf(it.trim()) } + + sinceBuild = providers.gradleProperty("pluginVerifierVersion").get() + } + } + } +} + +// --- Kotlin Configuration --- +kotlin { + jvmToolchain(javaVersionStr.toInt()) +} + +tasks.withType().configureEach { + compilerOptions { + jvmTarget = JvmTarget.valueOf("JVM_$javaVersionStr") + freeCompilerArgs.add("-Xjvm-default=all") + apiVersion = KotlinVersion.KOTLIN_2_2 + } +} + +// --- Dependencies --- +dependencies { + intellijPlatform { + create(providers.gradleProperty("platformType"), providers.provider { actualPlatformVersion }) + bundledPlugins(providers.gradleProperty("platformBundledPlugins").map { it.split(",") }) + bundledModules(providers.gradleProperty("platformBundledModules").map { it.split(",") }) + pluginVerifier() + zipSigner() + testFramework(TestFrameworkType.Platform) + testFramework(TestFrameworkType.Plugin.Java) + } + + implementation(project(":jps-builder")) + implementation(project(":jps-shared")) + implementation(files("lib/OtpErlang.jar")) + implementation(libCommonsIo) +} + +// --- Run IDE Configuration --- +tasks.withType().configureEach { + jvmArguments.addAll( + "-Didea.debug.mode=true", + "-Didea.is.internal=true", + "-Dlog4j2.debug=true", + "-Dlogger.org=TRACE", + "-XX:+AllowEnhancedClassRedefinition", + "-Didea.ProcessCanceledException=disabled" + ) + + systemProperty("idea.log.debug.categories", "org.elixir_lang") + maxHeapSize = "7g" + + if (project.hasProperty("runIdeWorkingDirectory") && project.property("runIdeWorkingDirectory").toString().isNotEmpty()) { + workingDir = file(project.property("runIdeWorkingDirectory").toString()) + } + + // Dynamic plugin loading + val compatiblePlugins = providers.gradleProperty("runIdeCompatiblePlugins").getOrElse("") + if (compatiblePlugins.isNotEmpty()) { + dependencies { + intellijPlatform { plugins(compatiblePlugins.split(",")) } + } + } +} + +// Register Platform-specific Run Tasks dynamically +val runIdePlatformsList = providers.gradleProperty("runIdePlatforms").get().split(",") +val enableEAP = providers.gradleProperty("enableEAPIDEs").get().toBoolean() + +runIdePlatformsList.forEach { platform -> + intellijPlatformTesting.runIde.register("run${platform}", Action { + type = IntelliJPlatformType.valueOf(platform) + version = providers.gradleProperty("platformVersion${platform}").get() + prepareSandboxTask { + sandboxDirectory = layout.buildDirectory.dir("${platform.lowercase()}-sandbox") + } + }) + + if (enableEAP) { + intellijPlatformTesting.runIde.register("run${platform}EAP", Action { + type = IntelliJPlatformType.valueOf(platform) + version = providers.gradleProperty("platformVersion${platform}EAP").get() + useInstaller = false + prepareSandboxTask { + sandboxDirectory = layout.buildDirectory.dir("${platform.lowercase()}_eap-sandbox") + } + }) + } +} + +// --- External Tools (Elixir & Quoter) --- +val downloadElixir by tasks.registering(Download::class) { + src("https://github.com/elixir-lang/elixir/archive/v${elixirVersion}.zip") + dest(cachePath.file("Elixir.${elixirVersion}.zip")) + overwrite(false) +} + +val unzipElixir by tasks.registering(Copy::class) { + dependsOn(downloadElixir) + from(zipTree(downloadElixir.get().dest)) + into(elixirPath.asFile) + eachFile { relativePath = RelativePath(true, *relativePath.segments.drop(1).toTypedArray()) } + include("elixir-${elixirVersion}/**") +} + +val buildElixir by tasks.registering(Exec::class) { + dependsOn(unzipElixir) + workingDir(elixirPath.asFile) + commandLine("make") + outputs.dir(elixirPath.dir("bin")) + // The apps that are part of Elixir SDK. `make` compiles them and puts output into `ebin` and `_build` dirs. + // We need to declare them as outputs for Gradle's UP-TO-DATE checks to work correctly. + // The list is based on the applications shipped with Elixir v1.13.4. + val libDir = elixirPath.dir("lib") + listOf("eex", "elixir", "ex_unit", "iex", "logger", "mix").forEach { appName -> + val appDir = libDir.dir(appName) + // All apps get an 'ebin' directory. + outputs.dir(appDir.dir("ebin")) + // All apps except 'elixir' itself also get a '_build' directory. + if (appName != "elixir") { + outputs.dir(appDir.dir("_build")) + } + } +} + +val getElixir by tasks.registering { + dependsOn(buildElixir) +} + + +val getQuoter by tasks.registering(Download::class) { + src("https://github.com/KronicDeth/intellij_elixir/archive/v${quoterVersion}.zip") + dest(cachePath.file("intellij_elixir-${quoterVersion}.zip")) + overwrite(false) +} + + +val unzipQuoter by tasks.registering(Copy::class) { + dependsOn(getQuoter) + + // 1. Target the final destination directly + into(quoterUnzippedPath) + + from(zipTree(getQuoter.get().dest)) { + // 2. Strip the top-level directory 'intellij_elixir-${quoterVersion}' on the fly + eachFile { + relativePath = RelativePath(true, *relativePath.segments.drop(1).toTypedArray()) + } + + // 3. Prevent creating the empty top-level directory itself + includeEmptyDirs = false + } +} + +val getQuoterDeps by tasks.registering(Exec::class) { + dependsOn(unzipQuoter) + workingDir(quoterUnzippedPath) + + // 1. INPUTS + // mix.exs defines the requirements + inputs.file(quoterUnzippedPath.file("mix.exs")) + .withPropertyName("mixExs") + .withPathSensitivity(PathSensitivity.RELATIVE) + + // mix.lock defines the exact versions. + // It is an INPUT because 'unzipQuoter' created it, and this task READS it. + inputs.file(quoterUnzippedPath.file("mix.lock")) + .withPropertyName("mixLock") + .withPathSensitivity(PathSensitivity.RELATIVE) + + // 2. OUTPUTS + // This task produces/populates the 'deps' directory. + outputs.dir(quoterUnzippedPath.dir("deps")) + .withPropertyName("depsDir") + + // 3. CACHING + outputs.cacheIf { true } + + commandLine("mix", "do", "local.rebar", "--force,", "local.hex", "--force,", "deps.get") +} + +val releaseQuoter by tasks.registering(Exec::class) { + dependsOn(getQuoterDeps) + + // 1. Use Directory object directly (No .asFile) + workingDir(quoterUnzippedPath) + + // 2. INPUTS: What determines if a release needs rebuilding? + // If mix.exs, lockfile, or the source code (lib/config) changes, re-run. + inputs.files( + quoterUnzippedPath.file("mix.exs"), + quoterUnzippedPath.file("mix.lock") + ).withPathSensitivity(PathSensitivity.RELATIVE) + + // Add source directories so code changes trigger a rebuild + inputs.dir(quoterUnzippedPath.dir("lib")).withPathSensitivity(PathSensitivity.RELATIVE) + inputs.dir(quoterUnzippedPath.dir("config")).withPathSensitivity(PathSensitivity.RELATIVE) + // The dependencies (generated by previous task) are inputs for this task + inputs.dir(quoterUnzippedPath.dir("deps")).withPathSensitivity(PathSensitivity.RELATIVE) + + // 3. OUTPUTS: This replaces your 'onlyIf' check. + // If this file exists and inputs match, Gradle skips this task automatically. + outputs.dir(quoterUnzippedPath.dir("_build")) + .withPropertyName("buildDir") + + // 4. Enable Cache + outputs.cacheIf { true } + + commandLine("mix", "release") +} + +fun ExecSpec.configureQuoter() { + executable(quoterExe.asFile) + environment("RELEASE_COOKIE", "intellij_elixir") + environment("RELEASE_DISTRIBUTION", "name") // Required for 127.0.0.1 + environment("RELEASE_NAME", "intellij_elixir@127.0.0.1") +} + +val runQuoter by tasks.registering(RunQuoterTask::class) { + dependsOn(releaseQuoter) + executable.set(quoterExe) +} + +val stopQuoter by tasks.registering(Exec::class) { + dependsOn(releaseQuoter) + configureQuoter() + args("stop") + doLast { + logger.lifecycle("Stopped Quoter daemon.") + } +} + +// --- Test Configuration --- +tasks.named("test") { + dependsOn("prepareTestSandbox", runQuoter) + finalizedBy(stopQuoter) + + environment("ELIXIR_LANG_ELIXIR_PATH", elixirPath.asFile.absolutePath) + environment("ELIXIR_EBIN_DIRECTORY", elixirPath.dir("lib/elixir/ebin/").asFile.absolutePath + File.separator) + environment("ELIXIR_VERSION", elixirVersion) +} diff --git a/buildSrc/.gitignore b/buildSrc/.gitignore new file mode 100644 index 000000000..d882804f5 --- /dev/null +++ b/buildSrc/.gitignore @@ -0,0 +1,3 @@ +/.idea +/build +/.kotlin diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts new file mode 100644 index 000000000..15d21c06d --- /dev/null +++ b/buildSrc/build.gradle.kts @@ -0,0 +1,12 @@ +/* + * buildSrc Configuration + * Purpose: Enables Kotlin DSL support for the code inside `buildSrc`. + * This allows `VersionFetcher.kt` to be compiled and available to the main build script. + */ +plugins { + `kotlin-dsl` +} + +repositories { + mavenCentral() +} diff --git a/buildSrc/src/main/kotlin/RunQuoterTask.kt b/buildSrc/src/main/kotlin/RunQuoterTask.kt new file mode 100644 index 000000000..1f4d1c961 --- /dev/null +++ b/buildSrc/src/main/kotlin/RunQuoterTask.kt @@ -0,0 +1,88 @@ +import org.gradle.api.DefaultTask +import org.gradle.api.GradleException +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.tasks.InputFile +import org.gradle.api.tasks.TaskAction +import org.gradle.process.ExecOperations +import java.io.ByteArrayOutputStream +import javax.inject.Inject + +abstract class RunQuoterTask : DefaultTask() { + + @get:Inject + abstract val execOps: ExecOperations + + @get:InputFile + abstract val executable: RegularFileProperty + + @TaskAction + fun run() { + val exePath = executable.get().asFile.absolutePath + val maxAttempts = 20 + + logger.lifecycle("Starting Quoter daemon using: $exePath") + + // 1. Start the Daemon + val startOutput = ByteArrayOutputStream() + val startResult = execOps.exec { + commandLine(exePath, "daemon") + environment("RELEASE_COOKIE", "intellij_elixir") + environment("RELEASE_DISTRIBUTION", "name") + environment("RELEASE_NAME", "intellij_elixir@127.0.0.1") + standardOutput = startOutput + errorOutput = startOutput + isIgnoreExitValue = true + } + + if (startResult.exitValue != 0) { + logger.error("Quoter daemon failed to launch immediately.") + logger.error("Output:\n${startOutput}") + throw GradleException("Quoter daemon failed to launch (Exit code ${startResult.exitValue})") + } + + // 2. Poll for readiness + logger.lifecycle("Waiting for Quoter daemon to start...") + + var isUp = false + var lastPidOutput = "" + + // Loop 0 to 9 + repeat(maxAttempts) { attempt -> + Thread.sleep(1000) + val pidStream = ByteArrayOutputStream() + + val pidResult = execOps.exec { + commandLine(exePath, "pid") + environment("RELEASE_COOKIE", "intellij_elixir") + environment("RELEASE_DISTRIBUTION", "name") + environment("RELEASE_NAME", "intellij_elixir@127.0.0.1") + standardOutput = pidStream + errorOutput = pidStream + isIgnoreExitValue = true + } + + lastPidOutput = pidStream.toString().trim() + + if (pidResult.exitValue == 0) { + logger.lifecycle("Quoter daemon is UP! (PID: $lastPidOutput)") + isUp = true + return // Exit the run() method entirely on success + } + + // ONLY sleep if this is NOT the last attempt + if (attempt < maxAttempts - 1) { + logger.lifecycle("Quoter daemon not ready yet (Attempt ${attempt + 1}/$maxAttempts). Retrying...") + } + } + + // If we reached here, the loop finished without returning + logger.error("Quoter daemon failed to start after $maxAttempts attempts.") + logger.error("Last 'pid' check output:\n$lastPidOutput") + + if (startOutput.size() > 0) { + logger.error("Original Daemon Startup Output:\n$startOutput") + } + + throw GradleException("Quoter daemon failed to start.") + } +} diff --git a/buildSrc/src/main/kotlin/VersionFetcher.kt b/buildSrc/src/main/kotlin/VersionFetcher.kt new file mode 100644 index 000000000..04a0658dc --- /dev/null +++ b/buildSrc/src/main/kotlin/VersionFetcher.kt @@ -0,0 +1,78 @@ +import java.net.URI +import javax.xml.parsers.DocumentBuilderFactory + +/** + * Version Fetcher Logic + * Purpose: Isolates the complex logic required to fetch the latest IntelliJ EAP/RC build number. + * Moved from the main build.gradle.kts to improve readability and separation of concerns. + */ +object VersionFetcher { + fun getLatestEapBuild(): String { + // Look at Release Candidates, then EAPs, then Normal releases + val apiUris = listOf( + URI("https://data.services.jetbrains.com/products/releases?code=IIU&type=rc&latest=true&fields=build"), + URI("https://data.services.jetbrains.com/products/releases?code=IIU&type=eap&latest=true&fields=build"), + URI("https://data.services.jetbrains.com/products/releases?code=IIU&type=release&latest=true&fields=build") + ) + + for (uri in apiUris) { + val json = runCatching { + uri.toURL().openStream().bufferedReader().use { it.readText() } + }.getOrNull() + + if (json != null) { + val regex = """"build"\s*:\s*"([0-9.]+)"""".toRegex() + val match = regex.find(json) + if (match != null) { + val selectedVersion = match.groupValues[1] + println("Version: $selectedVersion found at $uri") + return selectedVersion + } + } + } + + // Fallback logic + return fetchSnapshotVersion() + } + + private fun fetchSnapshotVersion(): String { + val snapshotsUri = URI("https://cache-redirector.jetbrains.com/www.jetbrains.com/intellij-repository/snapshots/com/jetbrains/intellij/idea/ideaIU/maven-metadata.xml") + + val versions = kotlin.runCatching { + val doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(snapshotsUri.toURL().openStream()) + doc.documentElement.normalize() + val versioning = doc.getElementsByTagName("versioning").item(0) + val versionsNode = versioning?.childNodes + buildList { + if (versionsNode != null) { + for (i in 0 until versionsNode.length) { + val n = versionsNode.item(i) + if (n.nodeName == "versions") { + val children = n.childNodes + for (j in 0 until children.length) { + val v = children.item(j) + if (v.nodeName == "version") add(v.textContent) + } + } + } + } + } + }.getOrNull().orEmpty() + + // Look for versions like 253.17525.95-EAP-SNAPSHOT and convert to 253.17525.95 + val numericEap = versions.asSequence() + .mapNotNull { v -> + val m = Regex("""^(\d+\.\d+\.\d+)-EAP-SNAPSHOT$""").matchEntire(v) + m?.groupValues?.get(1) + } + .lastOrNull() + + if (numericEap != null) { + println("Fallback Version: $numericEap found at $snapshotsUri") + return numericEap + } + + // Return result or throw exception + throw IllegalStateException("No numeric EAP build found") + } +} diff --git a/gen/org/elixir_lang/psi/stub/call/Deserialized.java b/gen/org/elixir_lang/psi/stub/call/Deserialized.java index 710354de7..eaab0d76c 100644 --- a/gen/org/elixir_lang/psi/stub/call/Deserialized.java +++ b/gen/org/elixir_lang/psi/stub/call/Deserialized.java @@ -20,8 +20,12 @@ public class Deserialized { private static final int GUARD_LENGTH = 0; private static final Logger LOGGER = Logger.getInstance(Deserialized.class); /* Set > than experimentally observed valid values. >= 15 is needed to accommodate `geo`'s 15 Protocol `impl`s for - `String.Chars` in https://github.com/KronicDeth/intellij-elixir/issues/2698. */ - private static final int SUSPECT_NAME_SET_SIZE = 20; + `String.Chars` in https://github.com/KronicDeth/intellij-elixir/issues/2698. + >= 25 is needed to accommodate `OpenApiSpex`'s 25 Protocol `impl`s for `Extendable` in + https://github.com/KronicDeth/intellij-elixir/pull/3717#issuecomment-3627833524 */ + private static final int INFO_NAME_SET_SIZE = 30; + private static final int WARN_NAME_SET_SIZE = 60; + private static final int ERROR_NAME_SET_SIZE = 100; static { int i; @@ -175,17 +179,17 @@ private static Set readNameSet(@NotNull StubInputStream dataStream) t int nameSetSize = dataStream.readVarInt(); assertGuard(dataStream, END); - if (nameSetSize >= SUSPECT_NAME_SET_SIZE) { + if (nameSetSize > ERROR_NAME_SET_SIZE) { int readAheadLength = BEGIN.length; StringBuilder stringBuilder = new StringBuilder("readNameSet nameSetSize (") .append(nameSetSize) - .append(") is suspect (>= ") - .append(SUSPECT_NAME_SET_SIZE) + .append(") is suspect (> ") + .append(ERROR_NAME_SET_SIZE) .append(")."); if (readAheadLength == 0) { stringBuilder = stringBuilder.append("StubIndex may be corrupt."); - LOGGER.warn(stringBuilder.toString()); + LOGGER.error(stringBuilder.toString()); } else { byte[] readAhead = new byte[readAheadLength]; int bytesRead = dataStream.read(readAhead); @@ -202,10 +206,9 @@ private static Set readNameSet(@NotNull StubInputStream dataStream) t } Set nameSet = new THashSet<>(nameSetSize); - if (nameSetSize >= SUSPECT_NAME_SET_SIZE) { - StringBuilder stringBuilder = new StringBuilder("readNameSet nameSet of suspect (>= ") - .append(SUSPECT_NAME_SET_SIZE) - .append(") size (").append(nameSetSize).append("):\n"); + if (nameSetSize > INFO_NAME_SET_SIZE) { + StringBuilder stringBuilder = new StringBuilder("readNameSet nameSet of size ") + .append(nameSetSize).append(":\n"); for (int i = 0; i < nameSetSize; i++) { StringRef name = dataStream.readName(); @@ -221,7 +224,13 @@ private static Set readNameSet(@NotNull StubInputStream dataStream) t stringBuilder.append(i + 1).append(". ").append(nameString).append('\n'); } - LOGGER.error(stringBuilder.toString()); + if (nameSetSize > ERROR_NAME_SET_SIZE) { + LOGGER.error(stringBuilder.toString()); + } else if (nameSetSize > WARN_NAME_SET_SIZE) { + LOGGER.warn(stringBuilder.toString()); + } else { + LOGGER.info(stringBuilder.toString()); + } } else { for (int i = 0; i < nameSetSize; i++) { StringRef name = dataStream.readName(); diff --git a/gradle.properties b/gradle.properties index 6c7776bb9..8b83f0559 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,78 +1,60 @@ -# IntelliJ Platform Artifacts Repositories -> https://plugins.jetbrains.com/docs/intellij/intellij-artifacts.html +# ============================================================================= +# Build Configuration Properties +# Purpose: Defines project constants, platform targets, and JVM settings. +# ============================================================================= + +# --- Plugin Metadata --- pluginGroup=org.elixir_lang pluginName=Elixir -pluginRepositoryUrl=https://github.com/KronicDeth/intellij-elixir/ pluginVersion=22.0.0 +pluginRepositoryUrl=https://github.com/KronicDeth/intellij-elixir/ vendorName=Elle Imhoff vendorEmail=Kronic.Deth@gmail.com -# https://youtrack.jetbrains.com/articles/IDEA-A-2100661899/IntelliJ-IDEA-2024.1-241.14494.240-build-Release-Notes -pluginSinceBuild=243.21565.180 -pluginUntilBuild= -#pluginVerificationIDEAVersions="251.26927.53" -#pluginVerificationRubyMineVersions="251.26927.47" -# Set this to open -runIdeWorkingDirectory= -# Define versions for running the IDEs, as each IDE can release at different release versions. -platformVersionIntellijIdeaCommunity=2025.2.1 -platformVersionIntellijIdeaUltimate=2025.2.1 -platformVersionRubyMine=2025.2 -platformVersionPyCharmCommunity=2025.2.1 -platformVersionPyCharmProfessional=2025.2.1 -platformVersionWebStorm=2025.2.1 +pluginSinceBuild=253.28294.334 +publishChannels=canary + +# --- Platform Targets --- +platformType=IU +platformVersion=2025.3 +# Enable this to pull in the latest EAP version. +useDynamicEapVersion=false + +# --- IDE Run Configuration Versions --- +# Controls which run configuration tasks (e.g. runRubyMine) are generated +runIdePlatforms=IntellijIdeaUltimate,RubyMine,PyCharmCommunity,PyCharmProfessional,WebStorm +enableEAPIDEs=true -enableEAPIDEs=false -platformVersionIntellijIdeaCommunityEAP=252-EAP-SNAPSHOT -platformVersionIntellijIdeaUltimateEAP=252-EAP-SNAPSHOT -platformVersionPyCharmCommunityEAP=252-EAP-SNAPSHOT -platformVersionPyCharmProfessionalEAP=252-EAP-SNAPSHOT -platformVersionRubyMineEAP=252-EAP-SNAPSHOT -platformVersionWebStormEAP=252-EAP-SNAPSHOT -# Comma-separated list of platforms to include -runIdePlatforms=IntellijIdeaCommunity,IntellijIdeaUltimate,RubyMine,PyCharmCommunity,PyCharmProfessional,WebStorm -# The versions we target, 21 is needed for IntelliJ Plugins -javaVersion=21 -javaTargetVersion=21 -# Defined in `.tool-versions`, check via `elixir --version` +# Stable Versions +platformVersionIntellijIdeaUltimate=2025.3 +platformVersionRubyMine=2025.3 +platformVersionPyCharmCommunity=2025.3 +platformVersionPyCharmProfessional=2025.3 +platformVersionWebStorm=2025.3 + +# EAP Versions (Unified 2025.3+) +platformVersionIntellijIdeaUltimateEAP=253-EAP-SNAPSHOT +platformVersionRubyMineEAP=253-EAP-SNAPSHOT +platformVersionPyCharmCommunityEAP=253-EAP-SNAPSHOT +platformVersionPyCharmProfessionalEAP=253-EAP-SNAPSHOT +platformVersionWebStormEAP=253-EAP-SNAPSHOT + +# --- Plugin Verification --- +# Comma-separated list of IDE types to verify against +pluginVerifierIdeTypes=IntellijIdeaUltimate,PyCharmProfessional,RubyMine,WebStorm +pluginVerifierVersion=2025.3 +pluginVerifierChannels=RELEASE,EAP,RC,PREVIEW + +# --- Tooling Versions --- elixirVersion=1.13.4 quoterVersion=2.1.0 -# IntelliJ Platform Properties -> https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html#configuration-intellij-extension -# Target IntelliJ Community by default -platformType=IC -platformVersion=2025.2.1 - -# Plugins which will run ONLY when running `runIde` tasks, not for testing/release. -# -# Usage: -# When using `./gradlew runIde`, pass the Gradle Property: -# -PrunIdeCompatiblePlugins="PsiViewer,com.google.ide-perf,org.jetbrains.action-tracker,com.intellij.classic.ui,krasa.CpuUsageIndicator,IdeaVIM" -# -# Recommendations: -# https://plugins.jetbrains.com/plugin/24468-classic-ui - Classic UI (old UI) -# https://plugins.jetbrains.com/plugin/7641-action-tracker - Action Tracker (Allows to record actions performed by user in IntelliJ IDEs) -# https://plugins.jetbrains.com/plugin/15104-ide-perf - IDE Performance -# https://plugins.jetbrains.com/plugin/227-psiviewer - PSI Viewer (A Program Structure Interface (PSI) tree viewer) -# IdeaVIM runIdeCompatiblePlugins= -# We need com.intellij.java to compile JPS, and markdown. -platformBundledPlugins=org.intellij.plugins.markdown,com.intellij.java,org.intellij.intelliLang +# Note: intelliLang changed to a module (intellij.platform.langInjection) in 2025.3+ +platformBundledPlugins=org.intellij.plugins.markdown,com.intellij.java +platformBundledModules=intellij.platform.langInjection -# Gradle Releases -> https://github.com/gradle/gradle/releases -# 8.5 is set because newer versions have weird run time caching issues, even with caching turned off. -# See https://github.com/gradle/gradle/issues/28974 -gradleVersion=8.14.3 -# Opt-out flag for bundling Kotlin standard library -> https://jb.gg/intellij-platform-kotlin-stdlib -kotlin.stdlib.default.dependency=false -publishChannels=canary -# These must be set, or Out of Memory (OOM) errors will occur during compiling. +# --- Gradle Performance & Memory --- org.gradle.jvmargs=-Xmx4096m kotlin.daemon.jvmargs=-Xmx4906m -# @todo Once this has been tested to be stable with the intellij-elixir codebase, enable. -# Others have it on without issues, so I'm not overly worried - just want to confirm stability. -# Can always just turn it off for CI. -# Before this, we need to upgrade download and fix the outputs for the tasks to ensure they cache. -# Gradle Configuration Cache - https://docs.gradle.org/current/userguide/configuration_cache.html -# Gradle Build Cache - https://docs.gradle.org/current/userguide/build_cache.html -# Parallel Gradle Builds - https://docs.gradle.org/current/userguide/performance.html#parallel_execution -org.gradle.configuration-cache=false -org.gradle.caching=false -org.gradle.parallel=false +org.gradle.configuration-cache=true +org.gradle.caching=true +org.gradle.parallel=true diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml new file mode 100644 index 000000000..d2ce6cce3 --- /dev/null +++ b/gradle/libs.versions.toml @@ -0,0 +1,31 @@ +# ============================================================================= +# Version Catalog +# Purpose: Centralizes dependency and plugin versions to improve maintenance. +# Usage: Access in build scripts via 'libs.versions.x' or 'libs.plugins.x'. +# ============================================================================= + +[versions] +# Plugin Versions +kotlin = "2.2.21" +intellij-platform = "2.10.5" +gradle-download = "5.6.0" +test-logger = "4.0.0" + +# Dependency Versions +commons-io = "2.21.0" +junit = "4.13.2" +opentest4j = "1.3.0" + +# Project Constants +java = "21" + +[libraries] +commons-io = { module = "commons-io:commons-io", version.ref = "commons-io" } +junit = { module = "junit:junit", version.ref = "junit" } +opentest4j = { module = "org.opentest4j:opentest4j", version.ref = "opentest4j" } + +[plugins] +intellij-platform = { id = "org.jetbrains.intellij.platform", version.ref = "intellij-platform" } +kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } +download = { id = "de.undercouch.download", version.ref = "gradle-download" } +test-logger = { id = "com.adarshr.test-logger", version.ref = "test-logger" } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 1b33c55ba..8bdaf60c7 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 7705927e9..23449a2b5 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index 23d15a936..ef07e0162 100755 --- a/gradlew +++ b/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/jps-builder/build.gradle b/jps-builder/build.gradle deleted file mode 100644 index a08226d5f..000000000 --- a/jps-builder/build.gradle +++ /dev/null @@ -1,38 +0,0 @@ -jar.archiveFileName = "jps-builder.jar" - -def compilationPackages = ['org/intellij/elixir/build/**', 'org/intellij/elixir/jps/**'] - -compileTestJava { - dependsOn(":jps-shared:composedJar") -} -compileJava { - dependsOn(":jps-shared:composedJar") -} -java { - sourceCompatibility = JavaVersion.VERSION_21 - targetCompatibility = JavaVersion.VERSION_21 -} -tasks.withType(JavaCompile) { options.encoding = 'UTF-8' } - -// Ensuring the necessary tasks are executed before tests -test { - dependsOn ':getElixir' - dependsOn ':getQuoter' - dependsOn ':runQuoter' - - environment "ELIXIR_LANG_ELIXIR_PATH", elixirPath - environment "ELIXIR_EBIN_DIRECTORY", "${elixirPath}/lib/elixir/ebin/" - environment "ELIXIR_VERSION", elixirVersion - useJUnit { - exclude compilationPackages - } - testLogging { - exceptionFormat = 'full' - } - - finalizedBy ':stopQuoter' // Ensure stopQuoter runs after tests -} - -dependencies { - implementation project(':jps-shared') -} diff --git a/jps-builder/build.gradle.kts b/jps-builder/build.gradle.kts new file mode 100644 index 000000000..9ae3f5eeb --- /dev/null +++ b/jps-builder/build.gradle.kts @@ -0,0 +1,38 @@ +tasks.jar { + archiveFileName.set("jps-builder.jar") +} + +tasks.compileTestJava { + dependsOn(":jps-shared:composedJar") +} + +tasks.compileJava { + dependsOn(":jps-shared:composedJar") +} + +java { + sourceCompatibility = JavaVersion.VERSION_21 + targetCompatibility = JavaVersion.VERSION_21 +} + +tasks.withType().configureEach { + options.encoding = "UTF-8" +} + +// Ensuring the necessary tasks are executed before tests +tasks.test { + dependsOn(":getElixir") + + val elixirPath: String by project + val elixirVersion: String by project + + environment("ELIXIR_LANG_ELIXIR_PATH", elixirPath) + environment("ELIXIR_EBIN_DIRECTORY", "${elixirPath}/lib/elixir/ebin/") + environment("ELIXIR_VERSION", elixirVersion) + + include("**/*Test.class") +} + +dependencies { + implementation(project(":jps-shared")) +} diff --git a/jps-builder/tests/org/elixir_lang/jps/BuildResult.java b/jps-builder/tests/org/elixir_lang/jps/BuildResult.java index 1b92bfcb6..fbe9cfb02 100644 --- a/jps-builder/tests/org/elixir_lang/jps/BuildResult.java +++ b/jps-builder/tests/org/elixir_lang/jps/BuildResult.java @@ -56,7 +56,7 @@ public boolean isSuccessful(){ } public void assertSuccessful(){ - Function toStringFunction = StringUtil.createToStringFunction(BuildMessage.class); + Function toStringFunction = Object::toString; Assert.assertTrue("Build failed. \nErrors:\n" + StringUtil.join(myErrorMessages, toStringFunction, "\n") + "\nInfo messages:\n" + StringUtil.join(myInfoMessages, toStringFunction, "\n"), isSuccessful()); } diff --git a/jps-shared/build.gradle b/jps-shared/build.gradle.kts similarity index 51% rename from jps-shared/build.gradle rename to jps-shared/build.gradle.kts index 0854ee58f..eb7cb0dac 100644 --- a/jps-shared/build.gradle +++ b/jps-shared/build.gradle.kts @@ -1,5 +1,11 @@ -jar.archiveFileName = "jps-shared.jar" -testClasses.enabled = false +tasks.jar { + archiveFileName.set("jps-shared.jar") +} + +tasks.testClasses { + enabled = false +} + java { sourceCompatibility = JavaVersion.VERSION_21 targetCompatibility = JavaVersion.VERSION_21 diff --git a/resources/META-INF/plugin.xml b/resources/META-INF/plugin.xml index c86172e7f..cf0bedcee 100644 --- a/resources/META-INF/plugin.xml +++ b/resources/META-INF/plugin.xml @@ -1,4 +1,4 @@ - + org.elixir_lang Elixir Elle Imhoff @@ -13,7 +13,7 @@ com.intellij.modules.java - @@ -36,8 +36,6 @@ - @@ -99,15 +97,15 @@ parentId="language.elixir" displayName="Internal Erlang SDKs" provider="org.elixir_lang.facet.sdks.erlang.Provider"/> - - + + @@ -118,7 +116,7 @@ - + @@ -138,8 +136,8 @@ level="WARNING" shortName="Credo"/> // groupId/parentId "Errors" is Editor > Inspections - - + @@ -151,7 +149,7 @@ - + @@ -198,8 +196,7 @@ implementationClass="org.elixir_lang.code_insight.line_marker_provider.Implementation"/> - + @@ -283,7 +280,7 @@ + parentId="Errors" displayName="Dialyzer"/> @@ -305,7 +302,7 @@ - + - + + + + + diff --git a/settings.gradle b/settings.gradle deleted file mode 100644 index 2267b9f98..000000000 --- a/settings.gradle +++ /dev/null @@ -1,6 +0,0 @@ -plugins { - id 'org.gradle.toolchains.foojay-resolver-convention' version '0.8.0' -} -rootProject.name = 'intellij-elixir' -include 'jps-shared' -include 'jps-builder' \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 000000000..8e503a833 --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,6 @@ +plugins { + id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0" +} +rootProject.name = "intellij-elixir" +include( "jps-shared") +include( "jps-builder") diff --git a/src/org/elixir_lang/FrameworkDetector.java b/src/org/elixir_lang/FrameworkDetector.java index b6b7ef235..3366554b2 100644 --- a/src/org/elixir_lang/FrameworkDetector.java +++ b/src/org/elixir_lang/FrameworkDetector.java @@ -9,6 +9,7 @@ import org.elixir_lang.facet.Configuration; import org.elixir_lang.facet.Type; import org.jetbrains.annotations.NotNull; +import org.elixir_lang.mix.Project; public class FrameworkDetector extends FacetBasedFrameworkDetector { private static final String ID = "Elixir"; @@ -32,6 +33,6 @@ public FileType getFileType() { @NotNull @Override public ElementPattern createSuitableFilePattern() { - return FileContentPattern.fileContent().withName("mix.exs"); + return FileContentPattern.fileContent().withName(Project.MIX_EXS); } } diff --git a/src/org/elixir_lang/beam/StubBuilder.kt b/src/org/elixir_lang/beam/StubBuilder.kt index cc7bb08a2..70425bf4a 100644 --- a/src/org/elixir_lang/beam/StubBuilder.kt +++ b/src/org/elixir_lang/beam/StubBuilder.kt @@ -30,6 +30,6 @@ class StubBuilder : BinaryFileStubBuilder { companion object { private val LOGGER = Logger.getInstance(StubBuilder::class.java) - private const val STUB_VERSION = 1 + private const val STUB_VERSION = 2 } } diff --git a/src/org/elixir_lang/beam/chunk/debug_info/v1/erl_abstract_code/abstract_code_compiler_options/abstract_code/Char.kt b/src/org/elixir_lang/beam/chunk/debug_info/v1/erl_abstract_code/abstract_code_compiler_options/abstract_code/Char.kt index ac0f947c6..0784f5912 100644 --- a/src/org/elixir_lang/beam/chunk/debug_info/v1/erl_abstract_code/abstract_code_compiler_options/abstract_code/Char.kt +++ b/src/org/elixir_lang/beam/chunk/debug_info/v1/erl_abstract_code/abstract_code_compiler_options/abstract_code/Char.kt @@ -20,11 +20,11 @@ object Char { val codePoint = term.intValue() when (codePoint) { - '\\'.toInt() -> macroStringBuilder.append("\\\\") - '\n'.toInt() -> macroStringBuilder.append("\\n") - '\r'.toInt() -> macroStringBuilder.append("\\r") - ' '.toInt() -> macroStringBuilder.append("\\s") - '\t'.toInt() -> macroStringBuilder.append("\\t") + '\\'.code -> macroStringBuilder.append("\\\\") + '\n'.code -> macroStringBuilder.append("\\n") + '\r'.code -> macroStringBuilder.append("\\r") + ' '.code -> macroStringBuilder.append("\\s") + '\t'.code -> macroStringBuilder.append("\\t") else -> macroStringBuilder.appendCodePoint(codePoint) } diff --git a/src/org/elixir_lang/beam/chunk/debug_info/v1/erl_abstract_code/abstract_code_compiler_options/abstract_code/Var.kt b/src/org/elixir_lang/beam/chunk/debug_info/v1/erl_abstract_code/abstract_code_compiler_options/abstract_code/Var.kt index ad5379a31..33c49ca2c 100644 --- a/src/org/elixir_lang/beam/chunk/debug_info/v1/erl_abstract_code/abstract_code_compiler_options/abstract_code/Var.kt +++ b/src/org/elixir_lang/beam/chunk/debug_info/v1/erl_abstract_code/abstract_code_compiler_options/abstract_code/Var.kt @@ -36,7 +36,7 @@ object Var { else -> AbstractCode.error("unknown_name:", "var name is unknown", name) } - fun nameToString(name: OtpErlangAtom) = name.atomValue().decapitalize().escapeElixirKeyword() + fun nameToString(name: OtpErlangAtom) = name.atomValue().replaceFirstChar { it.lowercase() }.escapeElixirKeyword() private fun nameToMacroStringDeclaredScope(name: OtpErlangAtom, scope: Scope): MacroStringDeclaredScope { val varName = nameToString(name) @@ -78,7 +78,7 @@ private fun String.escapeElixirKeyword(): String = if (KEYWORD_BLOCK_KEYWORD_SET.contains(this) || this == "end" || this == "fn" || this == "in" || this == "when" || this == "and" ) { - "erlangVariable${this.capitalize()}" + "erlangVariable${this.replaceFirstChar { it.uppercase() }}" } else { this } diff --git a/src/org/elixir_lang/beam/file_editor/Provider.kt b/src/org/elixir_lang/beam/file_editor/Provider.kt index fd2f82701..f7a3028c2 100644 --- a/src/org/elixir_lang/beam/file_editor/Provider.kt +++ b/src/org/elixir_lang/beam/file_editor/Provider.kt @@ -2,22 +2,24 @@ package org.elixir_lang.beam.file_editor import com.intellij.ide.scratch.ScratchFileService import com.intellij.openapi.fileEditor.FileEditorPolicy +import com.intellij.openapi.fileEditor.FileEditorProvider +import com.intellij.openapi.project.DumbAware import com.intellij.openapi.project.Project import com.intellij.openapi.vfs.VirtualFile import org.elixir_lang.beam.FileEditor import org.elixir_lang.beam.FileType import org.elixir_lang.beam.Language -class Provider: com.intellij.openapi.fileEditor.FileEditorProvider { +class Provider : FileEditorProvider, DumbAware { override fun getEditorTypeId(): String = "BEAM" override fun accept(project: Project, file: VirtualFile): Boolean = - file.fileType is FileType || - try { - ScratchFileService.getInstance().scratchesMapping.getMapping(file) is Language - } catch (e: Throwable) { - false - } + file.fileType is FileType || + try { + ScratchFileService.getInstance().scratchesMapping.getMapping(file) is Language + } catch (_: Throwable) { + false + } override fun createEditor(project: Project, file: VirtualFile): FileEditor { return FileEditor(file, project) diff --git a/src/org/elixir_lang/beam/psi/stubs/CallDefinitionType.kt b/src/org/elixir_lang/beam/psi/stubs/CallDefinitionType.kt new file mode 100644 index 000000000..63ed445ce --- /dev/null +++ b/src/org/elixir_lang/beam/psi/stubs/CallDefinitionType.kt @@ -0,0 +1,49 @@ +package org.elixir_lang.beam.psi.stubs + +import com.intellij.lang.ASTNode +import com.intellij.psi.stubs.IndexSink +import com.intellij.psi.stubs.StubElement +import com.intellij.psi.stubs.StubInputStream +import com.intellij.psi.stubs.StubOutputStream +import org.elixir_lang.beam.psi.CallDefinition +import org.elixir_lang.beam.psi.CallDefinitionElement +import org.elixir_lang.beam.psi.impl.CallDefinitionImpl +import org.elixir_lang.beam.psi.impl.CallDefinitionStubImpl +import org.elixir_lang.psi.stub.call.Deserialized +import org.elixir_lang.psi.stub.type.Named.Companion.indexStubbic +import java.io.IOException + +class CallDefinitionType(debugName: String) : ModuleElementType, CallDefinition>(debugName) { + override fun createCompositeNode(): ASTNode = CallDefinitionElement(this) + + override fun createPsi(stub: CallDefinitionStub<*>): CallDefinition = CallDefinitionImpl(stub) + + @Throws(IOException::class) + override fun deserialize(stubInputStream: StubInputStream, parentStub: StubElement<*>): CallDefinitionStub<*> { + val deserialized = Deserialized.deserialize(stubInputStream) + val callDefinitionClauseArity = deserializeCallDefinitionClauseArity(stubInputStream) + + return CallDefinitionStubImpl( + parentStub as ModuleStub<*>, + deserialized, + callDefinitionClauseArity + ) + } + + @Throws(IOException::class) + private fun deserializeCallDefinitionClauseArity(stubInputStream: StubInputStream): Int { + return Deserialized.readGuarded(stubInputStream) { it.readVarInt() } + } + + @Throws(IOException::class) + override fun serialize(stub: CallDefinitionStub<*>, stubOutputStream: StubOutputStream) { + Deserialized.serialize(stubOutputStream, stub) + Deserialized.writeGuarded(stubOutputStream) { guardedStream -> + guardedStream.writeVarInt(stub.callDefinitionClauseHeadArity()) + } + } + + override fun indexStub(stub: CallDefinitionStub<*>, sink: IndexSink) { + indexStubbic(stub, sink) + } +} diff --git a/src/org/elixir_lang/beam/psi/stubs/ModuleStubElementType.java b/src/org/elixir_lang/beam/psi/stubs/ModuleStubElementType.java index 15000e050..8d6b04041 100644 --- a/src/org/elixir_lang/beam/psi/stubs/ModuleStubElementType.java +++ b/src/org/elixir_lang/beam/psi/stubs/ModuleStubElementType.java @@ -2,23 +2,24 @@ import com.intellij.lang.ASTNode; import com.intellij.psi.PsiElement; -import com.intellij.psi.stubs.*; +import com.intellij.psi.stubs.ILightStubElementType; +import com.intellij.psi.stubs.StubElement; import com.intellij.psi.tree.ICompositeElementType; -import org.elixir_lang.ElixirLanguage; +import org.elixir_lang.beam.Language; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; public abstract class ModuleStubElementType, P extends PsiElement> extends ILightStubElementType implements ICompositeElementType { protected ModuleStubElementType(@NotNull @NonNls String debugName) { - super(debugName, ElixirLanguage.INSTANCE); + super(debugName, Language.INSTANCE); } @SuppressWarnings("MethodOverloadsMethodOfSuperclass") public abstract P createPsi(@NotNull ASTNode node); @Override - public S createStub(@NotNull P psi, StubElement parentStub) { + public @NotNull S createStub(@NotNull P psi, StubElement parentStub) { final String message = "Should not be called. Element=" + psi + "; class" + psi.getClass() + "; file=" + (psi.isValid() ? psi.getContainingFile() : "-"); @@ -29,6 +30,6 @@ public S createStub(@NotNull P psi, StubElement parentStub) { @NotNull @Override public String getExternalId() { - return "beam." + toString(); + return "beam." + this; } } diff --git a/src/org/elixir_lang/beam/psi/stubs/ModuleStubElementTypes.java b/src/org/elixir_lang/beam/psi/stubs/ModuleStubElementTypes.java index 3aeafd982..9605e0292 100644 --- a/src/org/elixir_lang/beam/psi/stubs/ModuleStubElementTypes.java +++ b/src/org/elixir_lang/beam/psi/stubs/ModuleStubElementTypes.java @@ -1,145 +1,11 @@ package org.elixir_lang.beam.psi.stubs; -import com.intellij.lang.ASTNode; -import com.intellij.psi.stubs.IndexSink; -import com.intellij.psi.stubs.StubElement; -import com.intellij.psi.stubs.StubInputStream; -import com.intellij.psi.stubs.StubOutputStream; -import org.elixir_lang.beam.psi.*; -import org.elixir_lang.beam.psi.impl.*; -import org.elixir_lang.psi.stub.call.Deserialized; -import org.elixir_lang.psi.stub.index.AllName; -import org.elixir_lang.type.Visibility; -import org.jetbrains.annotations.NotNull; +import org.elixir_lang.beam.psi.CallDefinition; +import org.elixir_lang.beam.psi.TypeDefinition; +import org.elixir_lang.beam.psi.Module; -import java.io.IOException; - -import static org.elixir_lang.psi.stub.call.Deserialized.readGuarded; -import static org.elixir_lang.psi.stub.call.Deserialized.writeGuarded; -import static org.elixir_lang.psi.stub.type.Named.indexStubbic; - -// See com.intellij.psi.impl.java.stubs.JavaStubElementTypes public interface ModuleStubElementTypes { - /** - * See {@link com.intellij.psi.impl.java.stubs.JavaStubElementTypes#CLASS} - */ - ModuleElementType, org.elixir_lang.beam.psi.Module> MODULE = new ModuleElementType, org.elixir_lang.beam.psi.Module>("Module") { - @NotNull - @Override - public ASTNode createCompositeNode() { - return new ModuleElement(this); - } - - @Override - public org.elixir_lang.beam.psi.Module createPsi(@NotNull ModuleStub stub) { - return new ModuleImpl<>(stub); - } - - @Override - public ModuleStub createStub(@NotNull org.elixir_lang.beam.psi.Module psi, StubElement parentStub) { - return super.createStub(psi, parentStub); - } - - @Override - public void serialize(@NotNull ModuleStub stub, @NotNull StubOutputStream stubOutputStream) throws IOException { - Deserialized.serialize(stubOutputStream, stub); - } - - @NotNull - @Override - public ModuleStub deserialize(@NotNull StubInputStream stubInputStream, - @NotNull StubElement parentStub) throws IOException { - Deserialized deserialized = Deserialized.deserialize(stubInputStream); - - return new ModuleStubImpl(parentStub, deserialized); - } - - @Override - public void indexStub(@NotNull ModuleStub stub, @NotNull IndexSink sink) { - indexStubbic(stub, sink); - } - }; - - ModuleElementType, TypeDefinition> TYPE_DEFINITION = new ModuleElementType, TypeDefinition>("TypeDefinition") { - @NotNull - @Override - public ASTNode createCompositeNode() { - return new TypeDefinitionElement(this); - } - - @Override - public TypeDefinition createPsi(@NotNull TypeDefinitionStub stub) { - return new TypeDefinitionImpl<>(stub); - } - - @Override - public TypeDefinitionStub createStub(@NotNull TypeDefinition psi, StubElement parentStub) { - return super.createStub(psi, parentStub); - } - - @NotNull - @Override - public TypeDefinitionStub deserialize(@NotNull StubInputStream dataStream, StubElement parentStub) throws IOException { - Visibility visibility = Visibility.valueOf(dataStream.readNameString()); - String name = dataStream.readNameString(); - int arity = dataStream.readVarInt(); - - return new TypeDefinitionStubImpl((ModuleStubImpl>) parentStub, visibility, name, arity); - } - - @Override - public void serialize(@NotNull TypeDefinitionStub stub, @NotNull StubOutputStream stubOutputStream) throws IOException { - stubOutputStream.writeName(stub.getVisibility().toString()); - stubOutputStream.writeName(stub.getName()); - stubOutputStream.writeVarInt(stub.getArity()); - } - - @Override - public void indexStub(@NotNull TypeDefinitionStub stub, @NotNull IndexSink sink) { - sink.occurrence(AllName.KEY, stub.getName()); - } - }; - - ModuleElementType, CallDefinition> CALL_DEFINITION = new ModuleElementType, CallDefinition>("CallDefinition") { - @NotNull - @Override - public ASTNode createCompositeNode() { - return new CallDefinitionElement(this); - } - - @Override - public CallDefinition createPsi(@NotNull CallDefinitionStub stub) { - return new CallDefinitionImpl<>(stub); - } - - @NotNull - @Override - public CallDefinitionStub deserialize(@NotNull StubInputStream stubInputStream, - @NotNull StubElement parentStub) throws IOException { - Deserialized deserialized = Deserialized.deserialize(stubInputStream); - int callDefinitionClauseArity = deserializeCallDefinitionClauseArity(stubInputStream); - - return new CallDefinitionStubImpl((ModuleStub) parentStub, deserialized, callDefinitionClauseArity); - } - - int deserializeCallDefinitionClauseArity(@NotNull StubInputStream stubInputStream) throws IOException { - return readGuarded(stubInputStream, StubInputStream::readVarInt); - } - - @Override - public void serialize(@NotNull CallDefinitionStub stub, - @NotNull StubOutputStream stubOutputStream) throws IOException { - Deserialized.serialize(stubOutputStream, stub); - writeGuarded( - stubOutputStream, - guardedStubOutputStream -> guardedStubOutputStream.writeVarInt(stub.callDefinitionClauseHeadArity()) - ); - } - - @Override - public void indexStub(@NotNull CallDefinitionStub stub, @NotNull IndexSink sink) { - indexStubbic(stub, sink); - } - }; - + ModuleElementType, Module> MODULE = new ModuleType("MODULE"); + ModuleElementType, TypeDefinition> TYPE_DEFINITION = new TypeDefinitionType("TYPE_DEFINITION"); + ModuleElementType, CallDefinition> CALL_DEFINITION = new CallDefinitionType("CALL_DEFINITION"); } diff --git a/src/org/elixir_lang/beam/psi/stubs/ModuleType.kt b/src/org/elixir_lang/beam/psi/stubs/ModuleType.kt new file mode 100644 index 000000000..2ed9c1fed --- /dev/null +++ b/src/org/elixir_lang/beam/psi/stubs/ModuleType.kt @@ -0,0 +1,39 @@ +package org.elixir_lang.beam.psi.stubs + +import com.intellij.lang.ASTNode +import com.intellij.psi.PsiElement +import com.intellij.psi.stubs.IndexSink +import com.intellij.psi.stubs.StubElement +import com.intellij.psi.stubs.StubInputStream +import com.intellij.psi.stubs.StubOutputStream +import org.elixir_lang.beam.psi.Module +import org.elixir_lang.beam.psi.ModuleElement +import org.elixir_lang.beam.psi.impl.ModuleImpl +import org.elixir_lang.beam.psi.impl.ModuleStubImpl +import org.elixir_lang.psi.stub.call.Deserialized +import org.elixir_lang.psi.stub.type.Named.Companion.indexStubbic +import java.io.IOException + +class ModuleType(debugName: String) : ModuleElementType, Module>(debugName) { + override fun createCompositeNode(): ASTNode = ModuleElement(this) + + override fun createPsi(stub: ModuleStub<*>): Module = ModuleImpl(stub) + + override fun createStub(psi: Module, parentStub: StubElement): ModuleStub<*> = + super.createStub(psi, parentStub) + + @Throws(IOException::class) + override fun serialize(stub: ModuleStub<*>, stubOutputStream: StubOutputStream) { + Deserialized.serialize(stubOutputStream, stub) + } + + @Throws(IOException::class) + override fun deserialize(stubInputStream: StubInputStream, parentStub: StubElement<*>): ModuleStub<*> { + val deserialized = Deserialized.deserialize(stubInputStream) + return ModuleStubImpl(parentStub, deserialized) + } + + override fun indexStub(stub: ModuleStub<*>, sink: IndexSink) { + indexStubbic(stub, sink) + } +} diff --git a/src/org/elixir_lang/beam/psi/stubs/TypeDefinitionType.kt b/src/org/elixir_lang/beam/psi/stubs/TypeDefinitionType.kt new file mode 100644 index 000000000..79f75d302 --- /dev/null +++ b/src/org/elixir_lang/beam/psi/stubs/TypeDefinitionType.kt @@ -0,0 +1,51 @@ +package org.elixir_lang.beam.psi.stubs + +import com.intellij.lang.ASTNode +import com.intellij.psi.PsiElement +import com.intellij.psi.stubs.IndexSink +import com.intellij.psi.stubs.StubElement +import com.intellij.psi.stubs.StubInputStream +import com.intellij.psi.stubs.StubOutputStream +import org.elixir_lang.beam.psi.TypeDefinition +import org.elixir_lang.beam.psi.TypeDefinitionElement +import org.elixir_lang.beam.psi.impl.TypeDefinitionImpl +import org.elixir_lang.beam.psi.impl.TypeDefinitionStubImpl +import org.elixir_lang.psi.stub.index.AllName +import org.elixir_lang.type.Visibility +import java.io.IOException + +class TypeDefinitionType(debugName: String) : ModuleElementType, TypeDefinition>(debugName) { + override fun createCompositeNode(): ASTNode = TypeDefinitionElement(this) + + override fun createPsi(stub: TypeDefinitionStub<*>): TypeDefinition = TypeDefinitionImpl(stub) + + override fun createStub(psi: TypeDefinition, parentStub: StubElement): TypeDefinitionStub<*> = + super.createStub(psi, parentStub) + + @Throws(IOException::class) + override fun deserialize(dataStream: StubInputStream, parentStub: StubElement<*>): TypeDefinitionStub<*> { + val visibility = Visibility.valueOf(dataStream.readNameString()!!) + val name = dataStream.readNameString()!! + val arity = dataStream.readVarInt() + + // FIX: Explicit generic type and cast parentStub to ModuleStub<*> + @Suppress("UNCHECKED_CAST") + return TypeDefinitionStubImpl( + parentStub as ModuleStub<*>, + visibility, + name, + arity + ) + } + + @Throws(IOException::class) + override fun serialize(stub: TypeDefinitionStub<*>, stubOutputStream: StubOutputStream) { + stubOutputStream.writeName(stub.visibility.toString()) + stubOutputStream.writeName(stub.name) + stubOutputStream.writeVarInt(stub.arity) + } + + override fun indexStub(stub: TypeDefinitionStub<*>, sink: IndexSink) { + sink.occurrence(AllName.KEY, stub.name) + } +} diff --git a/src/org/elixir_lang/beam/term/Inspect.kt b/src/org/elixir_lang/beam/term/Inspect.kt index 036016654..e3ff424e9 100644 --- a/src/org/elixir_lang/beam/term/Inspect.kt +++ b/src/org/elixir_lang/beam/term/Inspect.kt @@ -27,8 +27,8 @@ fun String.elixirEscape(): String { private fun Int.elixirEscape(): IntStream = when (this) { - '\n'.toInt() -> "\\n".codePoints() - '\r'.toInt() -> "\\r".codePoints() + '\n'.code -> "\\n".codePoints() + '\r'.code -> "\\r".codePoints() 27 -> "\\e".codePoints() else -> IntStream.of(this) } diff --git a/src/org/elixir_lang/configuration/BeforeRunTaskProvider.kt b/src/org/elixir_lang/configuration/BeforeRunTaskProvider.kt new file mode 100644 index 000000000..bd0c3e697 --- /dev/null +++ b/src/org/elixir_lang/configuration/BeforeRunTaskProvider.kt @@ -0,0 +1,8 @@ +package org.elixir_lang.configuration + +import com.intellij.execution.BeforeRunTask +import com.intellij.openapi.util.Key + +interface BeforeRunTaskProvider { + fun isBuildStep(providerID: Key>?): Boolean +} diff --git a/src/org/elixir_lang/configuration/DefaultBeforeRunTaskProvider.kt b/src/org/elixir_lang/configuration/DefaultBeforeRunTaskProvider.kt new file mode 100644 index 000000000..81f5552f7 --- /dev/null +++ b/src/org/elixir_lang/configuration/DefaultBeforeRunTaskProvider.kt @@ -0,0 +1,10 @@ +package org.elixir_lang.configuration + +import com.intellij.execution.BeforeRunTask +import com.intellij.openapi.util.Key + +class DefaultBeforeRunTaskProvider : BeforeRunTaskProvider { + override fun isBuildStep(providerID: Key>?): Boolean { + return false // WebStorm has no "Make/Build" step + } +} diff --git a/src/org/elixir_lang/configuration/EditorHelper.kt b/src/org/elixir_lang/configuration/EditorHelper.kt new file mode 100644 index 000000000..7322d0c03 --- /dev/null +++ b/src/org/elixir_lang/configuration/EditorHelper.kt @@ -0,0 +1,34 @@ +package org.elixir_lang.configuration + +import com.intellij.application.options.ModulesComboBox +import com.intellij.openapi.module.Module +import com.intellij.openapi.module.ModuleTypeManager +import com.intellij.openapi.project.Project +import java.util.function.Consumer + +object EditorHelper { + @JvmStatic + fun reset( + project: Project, + module: Module?, + modulesComboBox: ModulesComboBox, + runInModuleSetter: Consumer, + parametersPanelResetter: Runnable + ) { + // 1. Safe Module Filling (WebStorm safe) + // We look up the type dynamically. If null (WebStorm), we pass null. + val elixirModuleType = ModuleTypeManager.getInstance().findByID("ELIXIR_MODULE") + modulesComboBox.fillModules(project, elixirModuleType) + + // 2. centralized Module Selection Logic + if (module != null) { + runInModuleSetter.accept(true) + modulesComboBox.selectedModule = module + } else { + runInModuleSetter.accept(false) + } + + // 3. Reset the specific parameters panel + parametersPanelResetter.run() + } +} diff --git a/src/org/elixir_lang/configuration/JpsBeforeRunTaskProvider.kt b/src/org/elixir_lang/configuration/JpsBeforeRunTaskProvider.kt new file mode 100644 index 000000000..96c8c7392 --- /dev/null +++ b/src/org/elixir_lang/configuration/JpsBeforeRunTaskProvider.kt @@ -0,0 +1,11 @@ +package org.elixir_lang.configuration + +import com.intellij.compiler.options.CompileStepBeforeRun +import com.intellij.execution.BeforeRunTask +import com.intellij.openapi.util.Key + +class JpsBeforeRunTaskProvider : BeforeRunTaskProvider { + override fun isBuildStep(providerID: Key>?): Boolean { + return providerID === CompileStepBeforeRun.ID + } +} diff --git a/src/org/elixir_lang/credo/Service.kt b/src/org/elixir_lang/credo/Service.kt index 004d711a4..e0d429e95 100644 --- a/src/org/elixir_lang/credo/Service.kt +++ b/src/org/elixir_lang/credo/Service.kt @@ -5,6 +5,7 @@ import com.intellij.openapi.components.PersistentStateComponent import com.intellij.openapi.components.Storage import com.intellij.openapi.project.Project +@com.intellij.openapi.components.Service(com.intellij.openapi.components.Service.Level.PROJECT) @com.intellij.openapi.components.State(name = "Credo", storages = [Storage(value = "credo.xml")]) class Service : PersistentStateComponent { private var state = State() @@ -35,6 +36,7 @@ class Service : PersistentStateComponent { companion object { @JvmStatic - fun getInstance(project: Project): Service = project.getService(Service::class.java) ?: Service() + fun getInstance(project: Project): Service = project.getService(Service::class.java) + ?: error("Service not found. Make sure the @Service annotation is processed.") } } diff --git a/src/org/elixir_lang/credo/inspection_tool/Global.kt b/src/org/elixir_lang/credo/inspection_tool/Global.kt index e44b22916..783b6e69f 100644 --- a/src/org/elixir_lang/credo/inspection_tool/Global.kt +++ b/src/org/elixir_lang/credo/inspection_tool/Global.kt @@ -25,6 +25,7 @@ import com.intellij.psi.PsiManager import org.elixir_lang.Mix import org.elixir_lang.credo.Action import org.elixir_lang.jps.builder.ParametersList +import org.elixir_lang.mix.Project import org.elixir_lang.notification.setup_sdk.Notifier import org.elixir_lang.sdk.elixir.Type.Companion.mostSpecificSdk import java.nio.file.Paths @@ -48,7 +49,7 @@ class Global : GlobalInspectionTool() { .getInstance(module) .contentRoots .filter { virtualFile -> - virtualFile.findChild("mix.exs") != null + virtualFile.findChild(Project.MIX_EXS) != null } .map(VirtualFile::getPath) .toHashSet() @@ -113,7 +114,7 @@ class Global : GlobalInspectionTool() { NotificationType.ERROR ) .addAction(Action(project)) - .notify(project); + .notify(project) } // lib/level_web/ui/empty_state.ex:1:11: R: Modules should have a @moduledoc tag. @@ -160,7 +161,7 @@ class Global : GlobalInspectionTool() { end = start + 1 } else { start = lineStartOffset - end = document.getLineEndOffset(lineNumber); + end = document.getLineEndOffset(lineNumber) } val refElement = globalContext.refManager.getReference(psiFile) @@ -216,7 +217,7 @@ class Global : GlobalInspectionTool() { } } - private fun String.stripColor(): String = this.replace(Regex("\u001B\\[[;\\d]*m"), ""); + private fun String.stripColor(): String = this.replace(Regex("\u001B\\[[;\\d]*m"), "") private fun String.toHTML(): String = this.replace("\n", "
\n") override fun isGraphNeeded(): Boolean = true diff --git a/src/org/elixir_lang/debugger/stack_frame/value/Presentation.kt b/src/org/elixir_lang/debugger/stack_frame/value/Presentation.kt index b758e475f..4f62b4d32 100644 --- a/src/org/elixir_lang/debugger/stack_frame/value/Presentation.kt +++ b/src/org/elixir_lang/debugger/stack_frame/value/Presentation.kt @@ -164,8 +164,8 @@ class Presentation(private val myValue: Any) : XValuePresentation() { s.toString().all { isPrintable(it) } private fun isPrintable(c: Char): Boolean { - return (c.toInt() in 32..126 - || c == '\n' || c == '\r' || c == '\t' || c.toInt() == 11 || c == '\b' || c == '\u000c' || c.toInt() == 27 || c.toInt() == 7) /* bell */ + return (c.code in 32..126 + || c == '\n' || c == '\r' || c == '\t' || c.code == 11 || c == '\b' || c == '\u000c' || c.code == 27 || c.code == 7) /* bell */ } private fun renderErlangString(str: OtpErlangString, renderer: XValueTextRenderer) { diff --git a/src/org/elixir_lang/distillery/configuration/Editor.java b/src/org/elixir_lang/distillery/configuration/Editor.java index 44f2f4e1b..5d190f2ff 100644 --- a/src/org/elixir_lang/distillery/configuration/Editor.java +++ b/src/org/elixir_lang/distillery/configuration/Editor.java @@ -3,9 +3,9 @@ import com.intellij.application.options.ModulesComboBox; import com.intellij.openapi.module.Module; import com.intellij.openapi.options.SettingsEditor; +import org.elixir_lang.configuration.EditorHelper; import org.elixir_lang.distillery.Configuration; import org.elixir_lang.distillery.configuration.editor.ParametersPanel; -import org.elixir_lang.module.ElixirModuleType; import org.jetbrains.annotations.NotNull; import javax.swing.*; @@ -26,17 +26,13 @@ protected void resetEditorFrom(@NotNull Configuration configuration) { } public void reset(@NotNull Configuration configuration) { - modulesComboBox.fillModules(configuration.getProject(), ElixirModuleType.getInstance()); - final Module module = configuration.getConfigurationModule().getModule(); - - if (module != null) { - setRunInModuleSelected(true); - modulesComboBox.setSelectedModule(module); - } else { - setRunInModuleSelected(false); - } - - parametersPanel.reset(configuration); + EditorHelper.reset( + configuration.getProject(), + configuration.getConfigurationModule().getModule(), + modulesComboBox, + this::setRunInModuleSelected, + () -> parametersPanel.reset(configuration) + ); } @Override diff --git a/src/org/elixir_lang/elixir/configuration/Editor.java b/src/org/elixir_lang/elixir/configuration/Editor.java index 63845cf0a..dc561cd46 100644 --- a/src/org/elixir_lang/elixir/configuration/Editor.java +++ b/src/org/elixir_lang/elixir/configuration/Editor.java @@ -3,9 +3,9 @@ import com.intellij.application.options.ModulesComboBox; import com.intellij.openapi.module.Module; import com.intellij.openapi.options.SettingsEditor; +import org.elixir_lang.configuration.EditorHelper; import org.elixir_lang.elixir.Configuration; import org.elixir_lang.elixir.configuration.editor.ParametersPanel; -import org.elixir_lang.module.ElixirModuleType; import org.jetbrains.annotations.NotNull; import javax.swing.*; @@ -26,17 +26,13 @@ protected void resetEditorFrom(@NotNull Configuration configuration) { } public void reset(@NotNull Configuration configuration) { - modulesComboBox.fillModules(configuration.getProject()); - final Module module = configuration.getConfigurationModule().getModule(); - - if (module != null) { - setRunInModuleSelected(true); - modulesComboBox.setSelectedModule(module); - } else { - setRunInModuleSelected(false); - } - - parametersPanel.reset(configuration); + EditorHelper.reset( + configuration.getProject(), + configuration.getConfigurationModule().getModule(), + modulesComboBox, + this::setRunInModuleSelected, + () -> parametersPanel.reset(configuration) + ); } @Override diff --git a/src/org/elixir_lang/erl/configuration/Editor.java b/src/org/elixir_lang/erl/configuration/Editor.java index abdb40bb9..544fa1361 100644 --- a/src/org/elixir_lang/erl/configuration/Editor.java +++ b/src/org/elixir_lang/erl/configuration/Editor.java @@ -2,9 +2,9 @@ import com.intellij.application.options.ModulesComboBox; import com.intellij.openapi.module.Module; +import org.elixir_lang.configuration.EditorHelper; import org.elixir_lang.erl.Configuration; import org.elixir_lang.erl.configuration.editor.ParametersPanel; -import org.elixir_lang.module.ElixirModuleType; import org.jetbrains.annotations.NotNull; import javax.swing.*; @@ -28,17 +28,13 @@ protected void resetEditorFrom(@NotNull Configuration configuration) { } public void reset(@NotNull Configuration configuration) { - modulesComboBox.fillModules(configuration.getProject()); - final Module module = configuration.getConfigurationModule().getModule(); - - if (module != null) { - setRunInModuleSelected(true); - modulesComboBox.setSelectedModule(module); - } else { - setRunInModuleSelected(false); - } - - parametersPanel.reset(configuration); + EditorHelper.reset( + configuration.getProject(), + configuration.getConfigurationModule().getModule(), + modulesComboBox, + this::setRunInModuleSelected, + () -> parametersPanel.reset(configuration) + ); } @Override diff --git a/src/org/elixir_lang/espec/configuration/Editor.java b/src/org/elixir_lang/espec/configuration/Editor.java index b8016b4b1..cb8fd7205 100644 --- a/src/org/elixir_lang/espec/configuration/Editor.java +++ b/src/org/elixir_lang/espec/configuration/Editor.java @@ -2,9 +2,9 @@ import com.intellij.application.options.ModulesComboBox; import com.intellij.openapi.module.Module; +import org.elixir_lang.configuration.EditorHelper; import org.elixir_lang.espec.Configuration; import org.elixir_lang.espec.configuration.editor.ParametersPanel; -import org.elixir_lang.module.ElixirModuleType; import org.jetbrains.annotations.NotNull; import javax.swing.*; @@ -28,17 +28,13 @@ protected void resetEditorFrom(@NotNull Configuration configuration) { } public void reset(@NotNull Configuration configuration) { - modulesComboBox.fillModules(configuration.getProject()); - final Module module = configuration.getConfigurationModule().getModule(); - - if (module != null) { - setRunInModuleSelected(true); - modulesComboBox.setSelectedModule(module); - } else { - setRunInModuleSelected(false); - } - - parametersPanel.reset(configuration); + EditorHelper.reset( + configuration.getProject(), + configuration.getConfigurationModule().getModule(), + modulesComboBox, + this::setRunInModuleSelected, + () -> parametersPanel.reset(configuration) + ); } @Override diff --git a/src/org/elixir_lang/espec/configuration/Factory.kt b/src/org/elixir_lang/espec/configuration/Factory.kt index a1faf6dbd..09ea001cb 100644 --- a/src/org/elixir_lang/espec/configuration/Factory.kt +++ b/src/org/elixir_lang/espec/configuration/Factory.kt @@ -1,16 +1,19 @@ package org.elixir_lang.espec.configuration -import com.intellij.compiler.options.CompileStepBeforeRun import com.intellij.execution.BeforeRunTask import com.intellij.execution.configurations.ConfigurationFactory import com.intellij.execution.configurations.RunConfiguration +import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.project.Project import com.intellij.openapi.util.Key +import org.elixir_lang.configuration.BeforeRunTaskProvider import org.elixir_lang.espec.Configuration object Factory : ConfigurationFactory(Type.INSTANCE) { override fun configureBeforeRunTaskDefaults(providerID: Key>?, task: BeforeRunTask<*>?) { - if (providerID === CompileStepBeforeRun.ID) { + val provider = ApplicationManager.getApplication().getService(BeforeRunTaskProvider::class.java) + + if (provider.isBuildStep(providerID)) { task!!.isEnabled = false } } diff --git a/src/org/elixir_lang/espec/configuration/Producer.kt b/src/org/elixir_lang/espec/configuration/Producer.kt index 6cff1cc48..4f2036cff 100644 --- a/src/org/elixir_lang/espec/configuration/Producer.kt +++ b/src/org/elixir_lang/espec/configuration/Producer.kt @@ -11,6 +11,7 @@ import com.intellij.psi.* import org.elixir_lang.espec.Configuration import org.elixir_lang.espec.Gatherer import org.elixir_lang.file.containsFileWithSuffix +import org.elixir_lang.mix.Project import org.elixir_lang.psi.ElixirFile import org.elixir_lang.sdk.elixir.Type import org.elixir_lang.sdk.elixir.Type.Companion.mostSpecificSdk @@ -193,7 +194,7 @@ private fun setupConfigurationFromContextImpl( } private fun workingDirectory(directory: PsiDirectory, basePath: String?): String? = - if (directory.findFile("mix.exs") != null) { + if (directory.findFile(Project.MIX_EXS) != null) { directory.virtualFile.path } else { directory.parent?.let { workingDirectory(it, basePath) } ?: basePath @@ -208,4 +209,3 @@ private fun workingDirectory(element: PsiElement, basePath: String?): String? = private fun workingDirectory(file: PsiFile, basePath: String?): String? = workingDirectory(file.containingDirectory, basePath) - diff --git a/src/org/elixir_lang/exunit/configuration/Editor.java b/src/org/elixir_lang/exunit/configuration/Editor.java index 7b549cb8b..81f288534 100644 --- a/src/org/elixir_lang/exunit/configuration/Editor.java +++ b/src/org/elixir_lang/exunit/configuration/Editor.java @@ -2,9 +2,9 @@ import com.intellij.application.options.ModulesComboBox; import com.intellij.openapi.module.Module; +import org.elixir_lang.configuration.EditorHelper; import org.elixir_lang.exunit.Configuration; import org.elixir_lang.exunit.configuration.editor.ParametersPanel; -import org.elixir_lang.module.ElixirModuleType; import org.jetbrains.annotations.NotNull; import javax.swing.*; @@ -28,17 +28,13 @@ protected void resetEditorFrom(@NotNull Configuration configuration) { } public void reset(@NotNull Configuration configuration) { - modulesComboBox.fillModules(configuration.getProject()); - final Module module = configuration.getConfigurationModule().getModule(); - - if (module != null) { - setRunInModuleSelected(true); - modulesComboBox.setSelectedModule(module); - } else { - setRunInModuleSelected(false); - } - - parametersPanel.reset(configuration); + EditorHelper.reset( + configuration.getProject(), + configuration.getConfigurationModule().getModule(), + modulesComboBox, + this::setRunInModuleSelected, + () -> parametersPanel.reset(configuration) + ); } @Override diff --git a/src/org/elixir_lang/exunit/configuration/Factory.kt b/src/org/elixir_lang/exunit/configuration/Factory.kt index e659ff20b..fa090d177 100644 --- a/src/org/elixir_lang/exunit/configuration/Factory.kt +++ b/src/org/elixir_lang/exunit/configuration/Factory.kt @@ -1,16 +1,19 @@ package org.elixir_lang.exunit.configuration -import com.intellij.compiler.options.CompileStepBeforeRun import com.intellij.execution.BeforeRunTask import com.intellij.execution.configurations.ConfigurationFactory import com.intellij.execution.configurations.RunConfiguration +import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.project.Project import com.intellij.openapi.util.Key +import org.elixir_lang.configuration.BeforeRunTaskProvider import org.elixir_lang.exunit.Configuration object Factory : ConfigurationFactory(Type.INSTANCE) { override fun configureBeforeRunTaskDefaults(providerID: Key>?, task: BeforeRunTask<*>?) { - if (providerID === CompileStepBeforeRun.ID) { + val provider = ApplicationManager.getApplication().getService(BeforeRunTaskProvider::class.java) + + if (provider.isBuildStep(providerID)) { task!!.isEnabled = false } } diff --git a/src/org/elixir_lang/exunit/configuration/Producer.kt b/src/org/elixir_lang/exunit/configuration/Producer.kt index 041ff5c2c..ecfc5eaea 100644 --- a/src/org/elixir_lang/exunit/configuration/Producer.kt +++ b/src/org/elixir_lang/exunit/configuration/Producer.kt @@ -10,6 +10,7 @@ import com.intellij.openapi.util.Ref import com.intellij.psi.* import org.elixir_lang.exunit.Configuration import org.elixir_lang.file.containsFileWithSuffix +import org.elixir_lang.mix.Project import org.elixir_lang.psi.ElixirFile import org.elixir_lang.sdk.elixir.Type import org.elixir_lang.sdk.elixir.Type.Companion.mostSpecificSdk @@ -185,7 +186,7 @@ private fun setupConfigurationFromContextImpl( } private fun workingDirectory(directory: PsiDirectory, basePath: String?): String? = - if (directory.findFile("mix.exs") != null) { + if (directory.findFile(Project.MIX_EXS) != null) { directory.virtualFile.path } else { directory.parent?.let { workingDirectory(it, basePath) } ?: basePath diff --git a/src/org/elixir_lang/facet/SdksService.kt b/src/org/elixir_lang/facet/SdksService.kt index 5e0860c68..e1ba7b035 100644 --- a/src/org/elixir_lang/facet/SdksService.kt +++ b/src/org/elixir_lang/facet/SdksService.kt @@ -1,9 +1,11 @@ package org.elixir_lang.facet import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.components.Service import com.intellij.openapi.projectRoots.impl.ProjectJdkImpl import com.intellij.openapi.roots.ui.configuration.projectRoot.ProjectSdksModel +@Service class SdksService { private var model: ProjectSdksModel? = null @@ -27,7 +29,7 @@ class SdksService { do { model = try { ProjectSdksModel().apply { reset(null) } - } catch (e: AssertionError) { + } catch (_: AssertionError) { null } } while (model == null) diff --git a/src/org/elixir_lang/facet/sdks/Configurable.kt b/src/org/elixir_lang/facet/sdks/Configurable.kt index 05855ad39..e1a13a348 100644 --- a/src/org/elixir_lang/facet/sdks/Configurable.kt +++ b/src/org/elixir_lang/facet/sdks/Configurable.kt @@ -123,8 +123,9 @@ abstract class Configurable: SearchableConfigurable, com.intellij.openapi.option override fun sdkAdded(sdk: Sdk) { LibraryTablesRegistrar.getInstance().libraryTable.let { libraryTable -> ApplicationManager.getApplication().runWriteAction { - libraryTable.createLibrary(sdk.name).modifiableModel.apply { - addRoots(sdk) + val library = libraryTable.getLibraryByName(sdk.name) ?: libraryTable.createLibrary(sdk.name) + library.modifiableModel.apply { + replaceRoots(sdk) commit() } } @@ -193,17 +194,21 @@ abstract class Configurable: SearchableConfigurable, com.intellij.openapi.option } private fun updateSdkPanel(selectedValue: ProjectJdkImpl?) { - val selectedEditor = selectedValue?.let { - editorByProjectJdkImpl.computeIfAbsent(it, { Editor(projectSdksModel, history!!, it) }) - } + ApplicationManager.getApplication().runWriteAction { + val selectedEditor = selectedValue?.let { + editorByProjectJdkImpl.computeIfAbsent(it) { key -> + Editor(projectSdksModel, history!!, key) + } + } - sdkPanel.select(selectedEditor, true) + sdkPanel.select(selectedEditor, true) + } } } private fun Library.ModifiableModel.addRoots(roots: Array) = roots.forEach { - addRoot(it, com.intellij.openapi.roots.OrderRootType.CLASSES) + addRoot(it, OrderRootType.CLASSES) } private fun Library.ModifiableModel.clearRoots() { diff --git a/src/org/elixir_lang/find_usages/handler/Call.kt b/src/org/elixir_lang/find_usages/handler/Call.kt index 8b4a69ccd..9dc06259d 100644 --- a/src/org/elixir_lang/find_usages/handler/Call.kt +++ b/src/org/elixir_lang/find_usages/handler/Call.kt @@ -15,13 +15,15 @@ import org.elixir_lang.psi.Modular import org.elixir_lang.psi.call.Call object AlreadyResolved { - private val START = BuildNumber("", 213) - private val END = BuildNumber("", 213, 6461) + private val OLD_START = BuildNumber("", 213) + private val OLD_END = BuildNumber("", 213, 6461) + private val NEW_START = BuildNumber("", 253) // 2025.3+ val alreadyResolved by lazy { val build = ApplicationInfoEx.getInstance().build - START < build && build < END + // Elements are already resolved in 2021.3.x (213.x) and 2025.3+ (253+) + (OLD_START < build && build < OLD_END) || (build >= NEW_START) } } @@ -108,4 +110,3 @@ private fun Iterable.withEnclosingModularMacroCall(): private fun Iterable.toSecondaryElements(): List = this.flatMap { it.toSecondaryElements() } - diff --git a/src/org/elixir_lang/folding/ElixirFoldingSettings.java b/src/org/elixir_lang/folding/ElixirFoldingSettings.java index bbd3e7663..588d9bc46 100644 --- a/src/org/elixir_lang/folding/ElixirFoldingSettings.java +++ b/src/org/elixir_lang/folding/ElixirFoldingSettings.java @@ -12,7 +12,9 @@ */ @State( name = "ElixirFoldingSettings", - storages = @Storage(value = "editor.codeinsight.xml") + storages = { + @Storage(value = "editor.codeinsight.xml") + } ) public class ElixirFoldingSettings implements PersistentStateComponent { /* diff --git a/src/org/elixir_lang/formatter/MixFormatExternalFormatProcessor.kt b/src/org/elixir_lang/formatter/MixFormatExternalFormatProcessor.kt index c8d1842bb..7d7017c27 100644 --- a/src/org/elixir_lang/formatter/MixFormatExternalFormatProcessor.kt +++ b/src/org/elixir_lang/formatter/MixFormatExternalFormatProcessor.kt @@ -16,6 +16,7 @@ import com.intellij.psi.codeStyle.ExternalFormatProcessor import org.elixir_lang.Elixir.elixirSdkHasErlangSdk import org.elixir_lang.Mix import org.elixir_lang.code_style.CodeStyleSettings +import org.elixir_lang.mix.Project import org.elixir_lang.psi.ElixirFile import org.elixir_lang.sdk.elixir.Type.Companion.mostSpecificSdk import java.io.FileNotFoundException @@ -82,7 +83,7 @@ class MixFormatExternalFormatProcessor : ExternalFormatProcessor { } } ?.takeIf { contentRoot -> - contentRoot.findChild("mix.exs") != null + contentRoot.findChild(Project.MIX_EXS) != null } ?.path diff --git a/src/org/elixir_lang/iex/configuration/Editor.java b/src/org/elixir_lang/iex/configuration/Editor.java index c691af948..c4eb65597 100644 --- a/src/org/elixir_lang/iex/configuration/Editor.java +++ b/src/org/elixir_lang/iex/configuration/Editor.java @@ -3,9 +3,9 @@ import com.intellij.application.options.ModulesComboBox; import com.intellij.openapi.module.Module; import com.intellij.openapi.options.SettingsEditor; +import org.elixir_lang.configuration.EditorHelper; import org.elixir_lang.iex.Configuration; import org.elixir_lang.iex.configuration.editor.ParametersPanel; -import org.elixir_lang.module.ElixirModuleType; import org.jetbrains.annotations.NotNull; import javax.swing.*; @@ -26,17 +26,13 @@ protected void resetEditorFrom(@NotNull Configuration configuration) { } public void reset(@NotNull Configuration configuration) { - modulesComboBox.fillModules(configuration.getProject()); - final Module module = configuration.getConfigurationModule().getModule(); - - if (module != null) { - setRunInModuleSelected(true); - modulesComboBox.setSelectedModule(module); - } else { - setRunInModuleSelected(false); - } - - parametersPanel.reset(configuration); + EditorHelper.reset( + configuration.getProject(), + configuration.getConfigurationModule().getModule(), + modulesComboBox, + this::setRunInModuleSelected, + () -> parametersPanel.reset(configuration) + ); } @Override diff --git a/src/org/elixir_lang/iex/mix/configuration/Editor.java b/src/org/elixir_lang/iex/mix/configuration/Editor.java index 103c18316..f08f892a6 100644 --- a/src/org/elixir_lang/iex/mix/configuration/Editor.java +++ b/src/org/elixir_lang/iex/mix/configuration/Editor.java @@ -3,9 +3,9 @@ import com.intellij.application.options.ModulesComboBox; import com.intellij.openapi.module.Module; import com.intellij.openapi.options.SettingsEditor; +import org.elixir_lang.configuration.EditorHelper; import org.elixir_lang.iex.mix.Configuration; import org.elixir_lang.iex.mix.configuration.editor.ParametersPanel; -import org.elixir_lang.module.ElixirModuleType; import org.jetbrains.annotations.NotNull; import javax.swing.*; @@ -26,18 +26,13 @@ protected void resetEditorFrom(@NotNull Configuration configuration) { } public void reset(@NotNull Configuration configuration) { - modulesComboBox.fillModules(configuration.getProject()); - final Module module = configuration.getConfigurationModule().getModule(); - - - if (module != null) { - setRunInModuleSelected(true); - modulesComboBox.setSelectedModule(module); - } else { - setRunInModuleSelected(false); - } - - parametersPanel.reset(configuration); + EditorHelper.reset( + configuration.getProject(), + configuration.getConfigurationModule().getModule(), + modulesComboBox, + this::setRunInModuleSelected, + () -> parametersPanel.reset(configuration) + ); } @Override diff --git a/src/org/elixir_lang/mix/Dep.kt b/src/org/elixir_lang/mix/Dep.kt index 5e9f30ef1..5c049160b 100644 --- a/src/org/elixir_lang/mix/Dep.kt +++ b/src/org/elixir_lang/mix/Dep.kt @@ -49,7 +49,7 @@ data class Dep(val application: String, val path: String, val type: Type = Type. "app", "branch", "commit", "compile", "env", "git", "github", "hex", "manager", "only", "optional", "organization", "override", "ref", "repo", "runtime", GUARDIAN_RUNTIME_TYPO, "sparse", "submodules", "system_env", "tag", "targets", - EDELIVER_DISTILLERY_WARN_MISSING, "sha", "depth", "subdir" -> acc + EDELIVER_DISTILLERY_WARN_MISSING, "sha", "depth", "subdir", "warn_if_outdated" -> acc "in_umbrella" -> acc.copy(path = "apps/$name", type = Type.MODULE) "path" -> putPath(acc, keywordPair.keywordValue) diff --git a/src/org/elixir_lang/mix/PackageManager.kt b/src/org/elixir_lang/mix/PackageManager.kt index 9d78b826a..7d1efd475 100644 --- a/src/org/elixir_lang/mix/PackageManager.kt +++ b/src/org/elixir_lang/mix/PackageManager.kt @@ -2,7 +2,7 @@ package org.elixir_lang.mix import org.elixir_lang.package_manager.DepGatherer -object PackageManager : org.elixir_lang.PackageManager { - override val fileName: String = "mix.exs" +class PackageManager : org.elixir_lang.PackageManager { + override val fileName: String = Project.MIX_EXS override fun depGatherer(): DepGatherer = org.elixir_lang.mix.DepGatherer() } diff --git a/src/org/elixir_lang/mix/Project.kt b/src/org/elixir_lang/mix/Project.kt index d23d9ce3b..c034065d4 100644 --- a/src/org/elixir_lang/mix/Project.kt +++ b/src/org/elixir_lang/mix/Project.kt @@ -21,6 +21,7 @@ import java.io.EOFException import java.io.File object Project { + const val MIX_EXS = "mix.exs" private val LOG = Logger.getInstance(Project::class.java) fun addSourceDirToContent( @@ -150,7 +151,7 @@ object Project { private fun createImportedOtpApp(appRoot: VirtualFile): OtpApp? = try { - appRoot.findChild("mix.exs") + appRoot.findChild(MIX_EXS) } catch (_: EOFException) { null }?.let { diff --git a/src/org/elixir_lang/mix/Watcher.kt b/src/org/elixir_lang/mix/Watcher.kt index 5bbdeedb0..d8e8f7968 100644 --- a/src/org/elixir_lang/mix/Watcher.kt +++ b/src/org/elixir_lang/mix/Watcher.kt @@ -18,6 +18,7 @@ import com.intellij.psi.PsiManager import org.elixir_lang.DepsWatcher import org.elixir_lang.mix.library.Kind import org.elixir_lang.mix.watcher.TransitiveResolution.transitiveResolution +import org.elixir_lang.mix.Project as MixProject /** * Watches the [module]'s `mix.exs` for changes to the `deps`, so that [com.intellij.openapi.roots.Libraries.Library] @@ -33,7 +34,7 @@ class Watcher(private val project: Project) : BulkFileListener { } private fun contentsChanged(event: VFileContentChangeEvent) { - if (event.file.name == PackageManager.fileName) { + if (event.file.name == MixProject.MIX_EXS) { ModuleUtil.findModuleForFile(event.file, project)?.let { module -> val eventFileParent = event.file.parent val shouldSync = diff --git a/src/org/elixir_lang/mix/configuration/Editor.java b/src/org/elixir_lang/mix/configuration/Editor.java index 5876d722c..711557a4d 100644 --- a/src/org/elixir_lang/mix/configuration/Editor.java +++ b/src/org/elixir_lang/mix/configuration/Editor.java @@ -2,9 +2,9 @@ import com.intellij.application.options.ModulesComboBox; import com.intellij.openapi.module.Module; +import org.elixir_lang.configuration.EditorHelper; import org.elixir_lang.mix.Configuration; import org.elixir_lang.mix.configuration.editor.ParametersPanel; -import org.elixir_lang.module.ElixirModuleType; import org.jetbrains.annotations.NotNull; import javax.swing.*; @@ -28,17 +28,13 @@ protected void resetEditorFrom(@NotNull Configuration configuration) { } public void reset(@NotNull Configuration configuration) { - modulesComboBox.fillModules(configuration.getProject()); - final Module module = configuration.getConfigurationModule().getModule(); - - if (module != null) { - setRunInModuleSelected(true); - modulesComboBox.setSelectedModule(module); - } else { - setRunInModuleSelected(false); - } - - parametersPanel.reset(configuration); + EditorHelper.reset( + configuration.getProject(), + configuration.getConfigurationModule().getModule(), + modulesComboBox, + this::setRunInModuleSelected, + () -> parametersPanel.reset(configuration) + ); } @Override diff --git a/src/org/elixir_lang/mix/configuration/Factory.kt b/src/org/elixir_lang/mix/configuration/Factory.kt index 1c4098e39..991c654fa 100644 --- a/src/org/elixir_lang/mix/configuration/Factory.kt +++ b/src/org/elixir_lang/mix/configuration/Factory.kt @@ -1,11 +1,12 @@ package org.elixir_lang.mix.configuration -import com.intellij.compiler.options.CompileStepBeforeRun import com.intellij.execution.BeforeRunTask import com.intellij.execution.configurations.ConfigurationFactory import com.intellij.execution.configurations.RunConfiguration +import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.project.Project import com.intellij.openapi.util.Key +import org.elixir_lang.configuration.BeforeRunTaskProvider import org.elixir_lang.mix.Configuration /** @@ -13,7 +14,9 @@ import org.elixir_lang.mix.Configuration */ object Factory : ConfigurationFactory(Type.INSTANCE) { override fun configureBeforeRunTaskDefaults(providerID: Key>?, task: BeforeRunTask<*>?) { - if (providerID === CompileStepBeforeRun.ID) { + val provider = ApplicationManager.getApplication().getService(BeforeRunTaskProvider::class.java) + + if (provider.isBuildStep(providerID)) { task!!.isEnabled = false } } diff --git a/src/org/elixir_lang/mix/project/DirectoryConfigurator.kt b/src/org/elixir_lang/mix/project/DirectoryConfigurator.kt index 147900ba3..70a201a14 100644 --- a/src/org/elixir_lang/mix/project/DirectoryConfigurator.kt +++ b/src/org/elixir_lang/mix/project/DirectoryConfigurator.kt @@ -3,7 +3,7 @@ package org.elixir_lang.mix.project import com.intellij.configurationStore.StoreUtil import com.intellij.facet.FacetManager import com.intellij.facet.FacetType -import com.intellij.facet.impl.FacetUtil.addFacet +import com.intellij.facet.impl.FacetUtil import com.intellij.ide.impl.OpenProjectTask import com.intellij.notification.NotificationGroupManager import com.intellij.notification.NotificationType @@ -16,9 +16,6 @@ import com.intellij.openapi.progress.Task import com.intellij.openapi.project.Project import com.intellij.openapi.project.ex.ProjectManagerEx import com.intellij.openapi.roots.ModuleRootModificationUtil -import com.intellij.openapi.roots.ProjectRootManager -import com.intellij.openapi.roots.ui.configuration.ProjectStructureConfigurable -import com.intellij.openapi.roots.ui.configuration.projectRoot.ProjectSdksModel import com.intellij.openapi.util.Ref import com.intellij.openapi.util.io.FileUtil import com.intellij.openapi.vfs.VirtualFile @@ -26,7 +23,6 @@ import com.intellij.platform.PlatformProjectOpenProcessor.Companion.runDirectory import com.intellij.projectImport.ProjectAttachProcessor import com.intellij.util.PlatformUtils import kotlinx.coroutines.TimeoutCancellationException -import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withTimeout import org.elixir_lang.DepsWatcher @@ -36,10 +32,9 @@ import org.elixir_lang.mix.Watcher import java.nio.file.Path import java.nio.file.Paths import kotlin.io.path.exists -import org.elixir_lang.sdk.elixir.Type /** - * Used in Small IDEs like Rubymine that don't support [OpenProcessor]. + * Used in Small IDEs like Rubymine/Webstorm that don't support [OpenProcessor]. */ class DirectoryConfigurator : com.intellij.platform.DirectoryProjectConfigurator { companion object { @@ -102,7 +97,7 @@ class DirectoryConfigurator : com.intellij.platform.DirectoryProjectConfigurator val module = ModuleManager.getInstance(project).modules[0] if (FacetManager.getInstance(module).findFacet(Facet.ID, "Elixir") == null) { - addFacet(module, FacetType.findInstance(org.elixir_lang.facet.Type::class.java)) + FacetUtil.addFacet(module, FacetType.findInstance(org.elixir_lang.facet.Type::class.java)) ModuleRootModificationUtil.updateModel(module) { modifiableRootModel -> addFolders(modifiableRootModel, otpApp.root) diff --git a/src/org/elixir_lang/mix/project/OpenProcessor.kt b/src/org/elixir_lang/mix/project/OpenProcessor.kt index 473d22055..f35a629a6 100644 --- a/src/org/elixir_lang/mix/project/OpenProcessor.kt +++ b/src/org/elixir_lang/mix/project/OpenProcessor.kt @@ -4,13 +4,14 @@ import com.intellij.ide.util.projectWizard.WizardContext import com.intellij.openapi.vfs.VirtualFile import com.intellij.projectImport.ProjectImportBuilder import com.intellij.projectImport.ProjectOpenProcessorBase +import org.elixir_lang.mix.Project import org.elixir_lang.mix.project._import.Builder /** * Created by zyuyou on 15/7/1. */ class OpenProcessor : ProjectOpenProcessorBase() { - override val supportedExtensions = arrayOf("mix.exs") + override val supportedExtensions = arrayOf(Project.MIX_EXS) override fun doGetBuilder(): Builder = ProjectImportBuilder.EXTENSIONS_POINT_NAME.findExtensionOrFail(Builder::class.java) @@ -18,6 +19,7 @@ class OpenProcessor : ProjectOpenProcessorBase() { val projectRoot = file.parent wizardContext.projectName = projectRoot.name builder.setProjectRoot(projectRoot) - return true + // Return false to avoid the problematic runBlockingModalWithRawProgressReporter in IntelliJ 2025.3 + return false } } diff --git a/src/org/elixir_lang/mix/project/OtpApp.kt b/src/org/elixir_lang/mix/project/OtpApp.kt index 92affb750..dfbae843b 100644 --- a/src/org/elixir_lang/mix/project/OtpApp.kt +++ b/src/org/elixir_lang/mix/project/OtpApp.kt @@ -10,6 +10,7 @@ import com.intellij.openapi.vfs.VirtualFile import com.intellij.psi.PsiFileFactory import com.intellij.psi.ResolveState import org.elixir_lang.ElixirScriptFileType +import org.elixir_lang.mix.Project import org.elixir_lang.psi.CallDefinitionClause.isPublicFunction import org.elixir_lang.psi.CallDefinitionClause.nameArityInterval import org.elixir_lang.psi.ElixirAccessExpression @@ -23,7 +24,7 @@ import java.nio.file.Paths private fun app(appMixFile: VirtualFile): String = try { app(appMixFile, text(appMixFile)) - } catch (e: IOException) { + } catch (_: IOException) { appFromPath(appMixFile) } @@ -73,17 +74,17 @@ private fun appFromPath(appMixFile: VirtualFile): String = Paths.get(appMixFile. fun computeReadAction(computable: Computable): T = ApplicationManager.getApplication().runReadAction(computable) -private fun elixirFile(text: String): ElixirFile = computeReadAction(Computable { +private fun elixirFile(text: String): ElixirFile = computeReadAction { val defaultProject = ProjectManager.getInstance().defaultProject PsiFileFactory - .getInstance(defaultProject) - .createFileFromText("mix.exs", ElixirScriptFileType.INSTANCE, text) as ElixirFile -}) + .getInstance(defaultProject) + .createFileFromText(Project.MIX_EXS, ElixirScriptFileType.INSTANCE, text) as ElixirFile +} -private fun text(virtualFile: VirtualFile): String = computeReadAction(Computable { +private fun text(virtualFile: VirtualFile): String = computeReadAction { VfsUtil.loadText(virtualFile) -}) +} class OtpApp(val root: VirtualFile, appMixFile: VirtualFile) { val deps = mutableSetOf() diff --git a/src/org/elixir_lang/mix/project/_import/Builder.kt b/src/org/elixir_lang/mix/project/_import/Builder.kt index 1d98c2543..0e4614834 100644 --- a/src/org/elixir_lang/mix/project/_import/Builder.kt +++ b/src/org/elixir_lang/mix/project/_import/Builder.kt @@ -8,9 +8,7 @@ import com.intellij.openapi.module.ModifiableModuleModel import com.intellij.openapi.module.Module import com.intellij.openapi.module.ModuleManager import com.intellij.openapi.options.ConfigurationException -import com.intellij.openapi.progress.ProgressIndicator import com.intellij.openapi.progress.ProgressManager -import com.intellij.openapi.progress.Task import com.intellij.openapi.project.Project import com.intellij.openapi.projectRoots.ProjectJdkTable import com.intellij.openapi.projectRoots.Sdk @@ -42,11 +40,19 @@ class Builder : ProjectImportBuilder() { private var myFoundOtpApps = emptyList() private var mySelectedOtpApps = emptyList() private var myIsImportingProject: Boolean = false + private var myNeedsScan: Boolean = false override fun getIcon(): Icon = Icons.PROJECT override fun getName(): String = "Mix" override fun isSuitableSdkType(sdkType: SdkTypeId): Boolean = sdkType === Type.instance - override fun getList(): List = myFoundOtpApps + override fun getList(): List { + // Perform deferred scanning if needed + if (myNeedsScan && myProjectRoot != null) { + scanProjectRoot(myProjectRoot!!) + myNeedsScan = false + } + return myFoundOtpApps + } @Throws(ConfigurationException::class) override fun setList(selectedOtpApps: List?) { @@ -62,13 +68,14 @@ class Builder : ProjectImportBuilder() { myProjectRoot = null myFoundOtpApps = emptyList() mySelectedOtpApps = emptyList() + myNeedsScan = false } /** * check for reusing *.iml or *.eml * */ - override fun validate(current: Project?, dest: Project): Boolean { + override fun validate(currentProject: Project?, project: Project): Boolean { if (!findIdeaModuleFiles(mySelectedOtpApps)) { return true } @@ -151,23 +158,27 @@ class Builder : ProjectImportBuilder() { return true } + myProjectRoot = projectRoot + myNeedsScan = true + + // Return true to indicate the project root was set successfully + // Actual scanning is deferred until getList() is called + return true + } + + private fun scanProjectRoot(projectRoot: VirtualFile) { val unitTestMode = ApplicationManager.getApplication().isUnitTestMode - myProjectRoot = projectRoot if (!unitTestMode && projectRoot is VirtualDirectoryImpl) { projectRoot.refreshAndFindChild("deps") } - ProgressManager.getInstance() - .run(object : Task.Modal(ProjectImportBuilder.getCurrentProject(), "Scanning Mix Projects", true) { - override fun run(indicator: ProgressIndicator) { - myFoundOtpApps = org.elixir_lang.mix.Project.findOtpApps(projectRoot, indicator) - } - }) + // Get the current progress indicator from the ambient coroutine context, or use a no-op indicator + // This works correctly whether called from EDT, background thread, or coroutine context + val indicator = ProgressManager.getInstance().progressIndicator ?: com.intellij.openapi.progress.EmptyProgressIndicator() + myFoundOtpApps = org.elixir_lang.mix.Project.findOtpApps(projectRoot, indicator) mySelectedOtpApps = myFoundOtpApps - - return !myFoundOtpApps.isEmpty() } fun setIsImportingProject(isImportingProject: Boolean) { diff --git a/src/org/elixir_lang/mix/project/_import/step/Root.java b/src/org/elixir_lang/mix/project/_import/step/Root.java index 7b6691ea7..a71bd6c34 100644 --- a/src/org/elixir_lang/mix/project/_import/step/Root.java +++ b/src/org/elixir_lang/mix/project/_import/step/Root.java @@ -47,10 +47,9 @@ public Root(WizardContext context) { String projectFileDirectory = context.getProjectFileDirectory(); //noinspection DialogTitleCapitalization myProjectRootComponent.addBrowseFolderListener( - "Select mix.exs of a mix project to import", - "", null, FileChooserDescriptorFactory.createSingleFolderDescriptor() + .withTitle("Select mix.exs of a mix project to import") ); myProjectRootComponent.setText(projectFileDirectory); // provide project path diff --git a/src/org/elixir_lang/navigation/ChooseByNameContributor.kt b/src/org/elixir_lang/navigation/ChooseByNameContributor.kt index 3c492b358..50cb6426c 100644 --- a/src/org/elixir_lang/navigation/ChooseByNameContributor.kt +++ b/src/org/elixir_lang/navigation/ChooseByNameContributor.kt @@ -74,7 +74,7 @@ open class ChooseByNameContributor(private val stubIndexKey: StubIndexKey -> items.add(element) is CallDefinitionImpl<*> -> items.add(element) - else -> TODO() + else -> TODO("Unknown element: ${element.javaClass.name} in ChooseByNameContributor") } } diff --git a/src/org/elixir_lang/psi/Unquote.kt b/src/org/elixir_lang/psi/Unquote.kt index cc61c05dd..daf2516a6 100644 --- a/src/org/elixir_lang/psi/Unquote.kt +++ b/src/org/elixir_lang/psi/Unquote.kt @@ -102,8 +102,16 @@ object Unquote { } // (..., parameter) is ElixirParenthesesArguments -> true + // Transparent wrappers: keep walking upward is ElixirTuple, - is ElixirAccessExpression -> treeWalkUpUnquotedVariable(parent, resolveState, keepProcessing) + is ElixirAccessExpression, + is ElixirStabBody, + is ElixirStab, + is QuotableArguments, + is QuotableKeywordList, + is Call -> treeWalkUpUnquotedVariable(parent, resolveState, keepProcessing) + // Stop quietly at file boundary + is com.intellij.psi.PsiFile -> true else -> { Logger.error( Unquote::class.java, diff --git a/src/org/elixir_lang/psi/impl/QualifiableAliasImpl.kt b/src/org/elixir_lang/psi/impl/QualifiableAliasImpl.kt index 31b853a29..22b478a8a 100644 --- a/src/org/elixir_lang/psi/impl/QualifiableAliasImpl.kt +++ b/src/org/elixir_lang/psi/impl/QualifiableAliasImpl.kt @@ -7,7 +7,6 @@ import com.intellij.psi.ResolveResult import com.intellij.psi.util.CachedValueProvider import com.intellij.psi.util.CachedValuesManager import com.intellij.psi.util.isAncestor -import com.intellij.refactoring.suggested.endOffset import org.elixir_lang.errorreport.Logger import org.elixir_lang.psi.* import org.elixir_lang.psi.call.Call @@ -25,7 +24,7 @@ fun QualifiableAlias.computeReference(): PsiPolyVariantReference? = is QualifiableAlias -> // If the `parent` goes beyond this element then this element is the outermost Qualifiable alias that is still // ends in this element, so it represents the fully-qualified name. - if (endOffset < parent.endOffset) { + if (textRange.endOffset < parent.textRange.endOffset) { // The range in the element though should only be the final to match the guidance in // `com.intellij.psi.PsiReference#getRangeInElement`, which appears necessary to make completion to // work diff --git a/src/org/elixir_lang/reference/Type.kt b/src/org/elixir_lang/reference/Type.kt index 6d8db7928..2ad886d94 100644 --- a/src/org/elixir_lang/reference/Type.kt +++ b/src/org/elixir_lang/reference/Type.kt @@ -6,7 +6,6 @@ import com.intellij.psi.PsiPolyVariantReferenceBase import com.intellij.psi.ResolveResult import com.intellij.psi.impl.source.resolve.ResolveCache import com.intellij.psi.util.PsiTreeUtil -import com.intellij.refactoring.suggested.startOffset import org.elixir_lang.psi.AtUnqualifiedNoParenthesesCall import org.elixir_lang.psi.call.Call import org.elixir_lang.psi.impl.call.finalArguments @@ -52,8 +51,8 @@ class Type(typeSpec: AtUnqualifiedNoParenthesesCall<*>, type: Call) : PsiPolyVar if (typeSpec.isEquivalentTo(type)) { typeHead(typeSpec) ?.let { typeHead -> - val typeStartOffset = type.startOffset - val typeHeadStartOffset = typeHead.startOffset + val typeStartOffset = type.textRange.startOffset + val typeHeadStartOffset = typeHead.textRange.startOffset val startOffset = typeHeadStartOffset - typeStartOffset val endOffset = startOffset + typeHead.textLength diff --git a/testData/org/elixir_lang/parser_definition/eol_to_whitespace/And.txt b/testData/org/elixir_lang/parser_definition/eol_to_whitespace/And.txt new file mode 100644 index 000000000..608eda414 --- /dev/null +++ b/testData/org/elixir_lang/parser_definition/eol_to_whitespace/And.txt @@ -0,0 +1,42 @@ +Elixir File(0,44) + ElixirUnmatchedAndOperationImpl(UNMATCHED_AND_OPERATION)(0,15) + ElixirAccessExpressionImpl(ACCESS_EXPRESSION)(0,5) + ElixirAtomKeywordImpl(ATOM_KEYWORD)(0,5) + PsiElement(false)('false')(0,5) + PsiWhiteSpace('\n\n')(5,7) + ElixirAndInfixOperatorImpl(AND_INFIX_OPERATOR)(7,9) + PsiElement(&&&, &&)('&&')(7,9) + PsiWhiteSpace('\n\n')(9,11) + ElixirAccessExpressionImpl(ACCESS_EXPRESSION)(11,15) + ElixirAtomKeywordImpl(ATOM_KEYWORD)(11,15) + PsiElement(true)('true')(11,15) + ElixirEndOfExpressionImpl(END_OF_EXPRESSION)(15,17) + PsiElement(\\n, \\r\\n)('\n')(15,16) + PsiElement(\\n, \\r\\n)('\n')(16,17) + ElixirUnmatchedAndOperationImpl(UNMATCHED_AND_OPERATION)(17,26) + ElixirAccessExpressionImpl(ACCESS_EXPRESSION)(17,18) + ElixirDecimalWholeNumberImpl(DECIMAL_WHOLE_NUMBER)(17,18) + ElixirDecimalDigitsImpl(DECIMAL_DIGITS)(17,18) + PsiElement(0-9)('1')(17,18) + PsiWhiteSpace('\n\n')(18,20) + ElixirAndInfixOperatorImpl(AND_INFIX_OPERATOR)(20,23) + PsiElement(&&&, &&)('&&&')(20,23) + PsiWhiteSpace('\n\n')(23,25) + ElixirAccessExpressionImpl(ACCESS_EXPRESSION)(25,26) + ElixirDecimalWholeNumberImpl(DECIMAL_WHOLE_NUMBER)(25,26) + ElixirDecimalDigitsImpl(DECIMAL_DIGITS)(25,26) + PsiElement(0-9)('2')(25,26) + PsiWhiteSpace('\n')(26,27) + ElixirEndOfExpressionImpl(END_OF_EXPRESSION)(27,28) + PsiElement(\\n, \\r\\n)('\n')(27,28) + ElixirUnmatchedAndOperationImpl(UNMATCHED_AND_OPERATION)(28,44) + ElixirAccessExpressionImpl(ACCESS_EXPRESSION)(28,33) + ElixirAtomKeywordImpl(ATOM_KEYWORD)(28,33) + PsiElement(false)('false')(28,33) + PsiWhiteSpace('\n\n')(33,35) + ElixirAndInfixOperatorImpl(AND_INFIX_OPERATOR)(35,38) + PsiElement(`and`)('and')(35,38) + PsiWhiteSpace('\n\n')(38,40) + ElixirAccessExpressionImpl(ACCESS_EXPRESSION)(40,44) + ElixirAtomKeywordImpl(ATOM_KEYWORD)(40,44) + PsiElement(true)('true')(40,44) diff --git a/testData/org/elixir_lang/parser_definition/eol_to_whitespace/Arrow.txt b/testData/org/elixir_lang/parser_definition/eol_to_whitespace/Arrow.txt new file mode 100644 index 000000000..12fbf4715 --- /dev/null +++ b/testData/org/elixir_lang/parser_definition/eol_to_whitespace/Arrow.txt @@ -0,0 +1,124 @@ +Elixir File(0,174) + ElixirUnmatchedArrowOperationImpl(UNMATCHED_ARROW_OPERATION)(0,12) + UNMATCHED_UNQUALIFIED_NO_ARGUMENTS_CALL(0,3) + ElixirIdentifierImpl(IDENTIFIER)(0,3) + PsiElement(identifier)('one')(0,3) + PsiWhiteSpace('\n\n')(3,5) + ElixirArrowInfixOperatorImpl(ARROW_INFIX_OPERATOR)(5,7) + PsiElement(<<<, <<~, <|>, <~>, >>>, ~>>, <~, |>, ~>)('<~')(5,7) + PsiWhiteSpace('\n\n')(7,9) + UNMATCHED_UNQUALIFIED_NO_ARGUMENTS_CALL(9,12) + ElixirIdentifierImpl(IDENTIFIER)(9,12) + PsiElement(identifier)('two')(9,12) + PsiWhiteSpace('\n')(12,13) + ElixirEndOfExpressionImpl(END_OF_EXPRESSION)(13,14) + PsiElement(\\n, \\r\\n)('\n')(13,14) + ElixirUnmatchedArrowOperationImpl(UNMATCHED_ARROW_OPERATION)(14,29) + UNMATCHED_UNQUALIFIED_NO_ARGUMENTS_CALL(14,19) + ElixirIdentifierImpl(IDENTIFIER)(14,19) + PsiElement(identifier)('three')(14,19) + PsiWhiteSpace('\n\n')(19,21) + ElixirArrowInfixOperatorImpl(ARROW_INFIX_OPERATOR)(21,23) + PsiElement(<<<, <<~, <|>, <~>, >>>, ~>>, <~, |>, ~>)('|>')(21,23) + PsiWhiteSpace('\n\n')(23,25) + UNMATCHED_UNQUALIFIED_NO_ARGUMENTS_CALL(25,29) + ElixirIdentifierImpl(IDENTIFIER)(25,29) + PsiElement(identifier)('four')(25,29) + PsiWhiteSpace('\n')(29,30) + ElixirEndOfExpressionImpl(END_OF_EXPRESSION)(30,31) + PsiElement(\\n, \\r\\n)('\n')(30,31) + ElixirUnmatchedArrowOperationImpl(UNMATCHED_ARROW_OPERATION)(31,44) + UNMATCHED_UNQUALIFIED_NO_ARGUMENTS_CALL(31,35) + ElixirIdentifierImpl(IDENTIFIER)(31,35) + PsiElement(identifier)('five')(31,35) + PsiWhiteSpace('\n\n')(35,37) + ElixirArrowInfixOperatorImpl(ARROW_INFIX_OPERATOR)(37,39) + PsiElement(<<<, <<~, <|>, <~>, >>>, ~>>, <~, |>, ~>)('~>')(37,39) + PsiWhiteSpace('\n\n')(39,41) + UNMATCHED_UNQUALIFIED_NO_ARGUMENTS_CALL(41,44) + ElixirIdentifierImpl(IDENTIFIER)(41,44) + PsiElement(identifier)('six')(41,44) + PsiWhiteSpace('\n')(44,45) + ElixirEndOfExpressionImpl(END_OF_EXPRESSION)(45,46) + PsiElement(\\n, \\r\\n)('\n')(45,46) + ElixirUnmatchedArrowOperationImpl(UNMATCHED_ARROW_OPERATION)(46,63) + UNMATCHED_UNQUALIFIED_NO_ARGUMENTS_CALL(46,51) + ElixirIdentifierImpl(IDENTIFIER)(46,51) + PsiElement(identifier)('seven')(46,51) + PsiWhiteSpace('\n\n')(51,53) + ElixirArrowInfixOperatorImpl(ARROW_INFIX_OPERATOR)(53,56) + PsiElement(<<<, <<~, <|>, <~>, >>>, ~>>, <~, |>, ~>)('<<<')(53,56) + PsiWhiteSpace('\n\n')(56,58) + UNMATCHED_UNQUALIFIED_NO_ARGUMENTS_CALL(58,63) + ElixirIdentifierImpl(IDENTIFIER)(58,63) + PsiElement(identifier)('eight')(58,63) + PsiWhiteSpace('\n')(63,64) + ElixirEndOfExpressionImpl(END_OF_EXPRESSION)(64,65) + PsiElement(\\n, \\r\\n)('\n')(64,65) + ElixirUnmatchedArrowOperationImpl(UNMATCHED_ARROW_OPERATION)(65,79) + UNMATCHED_UNQUALIFIED_NO_ARGUMENTS_CALL(65,69) + ElixirIdentifierImpl(IDENTIFIER)(65,69) + PsiElement(identifier)('nine')(65,69) + PsiWhiteSpace('\n\n')(69,71) + ElixirArrowInfixOperatorImpl(ARROW_INFIX_OPERATOR)(71,74) + PsiElement(<<<, <<~, <|>, <~>, >>>, ~>>, <~, |>, ~>)('<<~')(71,74) + PsiWhiteSpace('\n\n')(74,76) + UNMATCHED_UNQUALIFIED_NO_ARGUMENTS_CALL(76,79) + ElixirIdentifierImpl(IDENTIFIER)(76,79) + PsiElement(identifier)('ten')(76,79) + PsiWhiteSpace('\n')(79,80) + ElixirEndOfExpressionImpl(END_OF_EXPRESSION)(80,81) + PsiElement(\\n, \\r\\n)('\n')(80,81) + ElixirUnmatchedArrowOperationImpl(UNMATCHED_ARROW_OPERATION)(81,100) + UNMATCHED_UNQUALIFIED_NO_ARGUMENTS_CALL(81,87) + ElixirIdentifierImpl(IDENTIFIER)(81,87) + PsiElement(identifier)('eleven')(81,87) + PsiWhiteSpace('\n\n')(87,89) + ElixirArrowInfixOperatorImpl(ARROW_INFIX_OPERATOR)(89,92) + PsiElement(<<<, <<~, <|>, <~>, >>>, ~>>, <~, |>, ~>)('<|>')(89,92) + PsiWhiteSpace('\n\n')(92,94) + UNMATCHED_UNQUALIFIED_NO_ARGUMENTS_CALL(94,100) + ElixirIdentifierImpl(IDENTIFIER)(94,100) + PsiElement(identifier)('twelve')(94,100) + PsiWhiteSpace('\n')(100,101) + ElixirEndOfExpressionImpl(END_OF_EXPRESSION)(101,102) + PsiElement(\\n, \\r\\n)('\n')(101,102) + ElixirUnmatchedArrowOperationImpl(UNMATCHED_ARROW_OPERATION)(102,125) + UNMATCHED_UNQUALIFIED_NO_ARGUMENTS_CALL(102,110) + ElixirIdentifierImpl(IDENTIFIER)(102,110) + PsiElement(identifier)('thirteen')(102,110) + PsiWhiteSpace('\n\n')(110,112) + ElixirArrowInfixOperatorImpl(ARROW_INFIX_OPERATOR)(112,115) + PsiElement(<<<, <<~, <|>, <~>, >>>, ~>>, <~, |>, ~>)('<~>')(112,115) + PsiWhiteSpace('\n\n')(115,117) + UNMATCHED_UNQUALIFIED_NO_ARGUMENTS_CALL(117,125) + ElixirIdentifierImpl(IDENTIFIER)(117,125) + PsiElement(identifier)('fourteen')(117,125) + PsiWhiteSpace('\n')(125,126) + ElixirEndOfExpressionImpl(END_OF_EXPRESSION)(126,127) + PsiElement(\\n, \\r\\n)('\n')(126,127) + ElixirUnmatchedArrowOperationImpl(UNMATCHED_ARROW_OPERATION)(127,148) + UNMATCHED_UNQUALIFIED_NO_ARGUMENTS_CALL(127,134) + ElixirIdentifierImpl(IDENTIFIER)(127,134) + PsiElement(identifier)('fifteen')(127,134) + PsiWhiteSpace('\n\n')(134,136) + ElixirArrowInfixOperatorImpl(ARROW_INFIX_OPERATOR)(136,139) + PsiElement(<<<, <<~, <|>, <~>, >>>, ~>>, <~, |>, ~>)('>>>')(136,139) + PsiWhiteSpace('\n\n')(139,141) + UNMATCHED_UNQUALIFIED_NO_ARGUMENTS_CALL(141,148) + ElixirIdentifierImpl(IDENTIFIER)(141,148) + PsiElement(identifier)('sixteen')(141,148) + PsiWhiteSpace('\n')(148,149) + ElixirEndOfExpressionImpl(END_OF_EXPRESSION)(149,150) + PsiElement(\\n, \\r\\n)('\n')(149,150) + ElixirUnmatchedArrowOperationImpl(UNMATCHED_ARROW_OPERATION)(150,174) + UNMATCHED_UNQUALIFIED_NO_ARGUMENTS_CALL(150,159) + ElixirIdentifierImpl(IDENTIFIER)(150,159) + PsiElement(identifier)('seventeen')(150,159) + PsiWhiteSpace('\n\n')(159,161) + ElixirArrowInfixOperatorImpl(ARROW_INFIX_OPERATOR)(161,164) + PsiElement(<<<, <<~, <|>, <~>, >>>, ~>>, <~, |>, ~>)('~>>')(161,164) + PsiWhiteSpace('\n\n')(164,166) + UNMATCHED_UNQUALIFIED_NO_ARGUMENTS_CALL(166,174) + ElixirIdentifierImpl(IDENTIFIER)(166,174) + PsiElement(identifier)('eighteen')(166,174) diff --git a/testData/org/elixir_lang/parser_definition/eol_to_whitespace/Association.txt b/testData/org/elixir_lang/parser_definition/eol_to_whitespace/Association.txt new file mode 100644 index 000000000..11fe1f84e --- /dev/null +++ b/testData/org/elixir_lang/parser_definition/eol_to_whitespace/Association.txt @@ -0,0 +1,21 @@ +Elixir File(0,9) + ElixirAccessExpressionImpl(ACCESS_EXPRESSION)(0,9) + ElixirMapOperationImpl(MAP_OPERATION)(0,9) + ElixirMapPrefixOperatorImpl(MAP_PREFIX_OPERATOR)(0,1) + PsiElement(%)('%')(0,1) + ElixirMapArgumentsImpl(MAP_ARGUMENTS)(1,9) + PsiElement({)('{')(1,2) + ElixirMapConstructionArgumentsImpl(MAP_CONSTRUCTION_ARGUMENTS)(2,8) + ElixirAssociationsImpl(ASSOCIATIONS)(2,8) + ElixirAssociationsBaseImpl(ASSOCIATIONS_BASE)(2,8) + ElixirContainerAssociationOperationImpl(CONTAINER_ASSOCIATION_OPERATION)(2,8) + UNMATCHED_UNQUALIFIED_NO_ARGUMENTS_CALL(2,3) + ElixirIdentifierImpl(IDENTIFIER)(2,3) + PsiElement(identifier)('a')(2,3) + PsiWhiteSpace('\n')(3,4) + PsiElement(=>)('=>')(4,6) + PsiWhiteSpace('\n')(6,7) + UNMATCHED_UNQUALIFIED_NO_ARGUMENTS_CALL(7,8) + ElixirIdentifierImpl(IDENTIFIER)(7,8) + PsiElement(identifier)('b')(7,8) + PsiElement(})('}')(8,9) diff --git a/testData/org/elixir_lang/parser_definition/eol_to_whitespace/Capture.txt b/testData/org/elixir_lang/parser_definition/eol_to_whitespace/Capture.txt new file mode 100644 index 000000000..ce860ccb6 --- /dev/null +++ b/testData/org/elixir_lang/parser_definition/eol_to_whitespace/Capture.txt @@ -0,0 +1,15 @@ +Elixir File(0,8) + ElixirUnmatchedGreaterThanOrEqualToOnePointSixCaptureNonNumericOperationImpl(UNMATCHED_GREATER_THAN_OR_EQUAL_TO_ONE_POINT_SIX_CAPTURE_NON_NUMERIC_OPERATION)(0,8) + ElixirCapturePrefixOperatorImpl(CAPTURE_PREFIX_OPERATOR)(0,1) + PsiElement(&)('&')(0,1) + PsiWhiteSpace('\n\n')(1,3) + ElixirUnmatchedMultiplicationOperationImpl(UNMATCHED_MULTIPLICATION_OPERATION)(3,8) + UNMATCHED_UNQUALIFIED_NO_ARGUMENTS_CALL(3,6) + ElixirIdentifierImpl(IDENTIFIER)(3,6) + PsiElement(identifier)('one')(3,6) + ElixirMultiplicationInfixOperatorImpl(MULTIPLICATION_INFIX_OPERATOR)(6,7) + PsiElement(/)('/')(6,7) + ElixirAccessExpressionImpl(ACCESS_EXPRESSION)(7,8) + ElixirDecimalWholeNumberImpl(DECIMAL_WHOLE_NUMBER)(7,8) + ElixirDecimalDigitsImpl(DECIMAL_DIGITS)(7,8) + PsiElement(0-9)('2')(7,8) \ No newline at end of file diff --git a/testData/org/elixir_lang/parser_definition/eol_to_whitespace/ClosingBit.txt b/testData/org/elixir_lang/parser_definition/eol_to_whitespace/ClosingBit.txt new file mode 100644 index 000000000..11f8f3428 --- /dev/null +++ b/testData/org/elixir_lang/parser_definition/eol_to_whitespace/ClosingBit.txt @@ -0,0 +1,10 @@ +Elixir File(0,7) + ElixirAccessExpressionImpl(ACCESS_EXPRESSION)(0,7) + ElixirBitStringImpl(BIT_STRING)(0,7) + PsiElement(<<)('<<')(0,2) + ElixirAccessExpressionImpl(ACCESS_EXPRESSION)(2,3) + ElixirDecimalWholeNumberImpl(DECIMAL_WHOLE_NUMBER)(2,3) + ElixirDecimalDigitsImpl(DECIMAL_DIGITS)(2,3) + PsiElement(0-9)('1')(2,3) + PsiWhiteSpace('\n\n')(3,5) + PsiElement(>>)('>>')(5,7) diff --git a/testData/org/elixir_lang/parser_definition/eol_to_whitespace/ClosingBracket.txt b/testData/org/elixir_lang/parser_definition/eol_to_whitespace/ClosingBracket.txt new file mode 100644 index 000000000..404bb4152 --- /dev/null +++ b/testData/org/elixir_lang/parser_definition/eol_to_whitespace/ClosingBracket.txt @@ -0,0 +1,10 @@ +Elixir File(0,5) + ElixirAccessExpressionImpl(ACCESS_EXPRESSION)(0,5) + ElixirListImpl(LIST)(0,5) + PsiElement([)('[')(0,1) + ElixirAccessExpressionImpl(ACCESS_EXPRESSION)(1,2) + ElixirDecimalWholeNumberImpl(DECIMAL_WHOLE_NUMBER)(1,2) + ElixirDecimalDigitsImpl(DECIMAL_DIGITS)(1,2) + PsiElement(0-9)('1')(1,2) + PsiWhiteSpace('\n\n')(2,4) + PsiElement(])(']')(4,5) diff --git a/testData/org/elixir_lang/parser_definition/eol_to_whitespace/ClosingCurly.txt b/testData/org/elixir_lang/parser_definition/eol_to_whitespace/ClosingCurly.txt new file mode 100644 index 000000000..da5eb3bc7 --- /dev/null +++ b/testData/org/elixir_lang/parser_definition/eol_to_whitespace/ClosingCurly.txt @@ -0,0 +1,53 @@ +Elixir File(0,42) + ElixirAccessExpressionImpl(ACCESS_EXPRESSION)(0,5) + ElixirTupleImpl(TUPLE)(0,5) + PsiElement({)('{')(0,1) + ElixirAccessExpressionImpl(ACCESS_EXPRESSION)(1,2) + ElixirDecimalWholeNumberImpl(DECIMAL_WHOLE_NUMBER)(1,2) + ElixirDecimalDigitsImpl(DECIMAL_DIGITS)(1,2) + PsiElement(0-9)('1')(1,2) + PsiWhiteSpace('\n\n')(2,4) + PsiElement(})('}')(4,5) + ElixirEndOfExpressionImpl(END_OF_EXPRESSION)(5,7) + PsiElement(\\n, \\r\\n)('\n')(5,6) + PsiElement(\\n, \\r\\n)('\n')(6,7) + ElixirAccessExpressionImpl(ACCESS_EXPRESSION)(7,19) + ElixirMapOperationImpl(MAP_OPERATION)(7,19) + ElixirMapPrefixOperatorImpl(MAP_PREFIX_OPERATOR)(7,8) + PsiElement(%)('%')(7,8) + ElixirMapArgumentsImpl(MAP_ARGUMENTS)(8,19) + PsiElement({)('{')(8,9) + ElixirMapConstructionArgumentsImpl(MAP_CONSTRUCTION_ARGUMENTS)(9,15) + ElixirKeywordsImpl(KEYWORDS)(9,15) + ElixirKeywordPairImpl(KEYWORD_PAIR)(9,15) + ElixirKeywordKeyImpl(KEYWORD_KEY)(9,12) + PsiElement(A-Z, a-z, _, @, 0-9. ?, !)('two')(9,12) + PsiElement(Keyword Pair Colon (:))(':')(12,13) + PsiWhiteSpace(' ')(13,14) + ElixirAccessExpressionImpl(ACCESS_EXPRESSION)(14,15) + ElixirDecimalWholeNumberImpl(DECIMAL_WHOLE_NUMBER)(14,15) + ElixirDecimalDigitsImpl(DECIMAL_DIGITS)(14,15) + PsiElement(0-9)('3')(14,15) + PsiWhiteSpace('\n\n\n')(15,18) + PsiElement(})('}')(18,19) + PsiWhiteSpace('\n')(19,20) + ElixirEndOfExpressionImpl(END_OF_EXPRESSION)(20,21) + PsiElement(\\n, \\r\\n)('\n')(20,21) + UNMATCHED_UNQUALIFIED_NO_PARENTHESES_CALL(21,42) + ElixirIdentifierImpl(IDENTIFIER)(21,26) + PsiElement(identifier)('alias')(21,26) + PsiWhiteSpace(' ')(26,27) + ElixirNoParenthesesOneArgumentImpl(NO_PARENTHESES_ONE_ARGUMENT)(27,42) + ElixirMatchedQualifiedMultipleAliasesImpl(MATCHED_QUALIFIED_MULTIPLE_ALIASES)(27,42) + ElixirAccessExpressionImpl(ACCESS_EXPRESSION)(27,31) + ElixirAliasImpl(ALIAS)(27,31) + PsiElement(Alias)('Four')(27,31) + ElixirDotInfixOperatorImpl(DOT_INFIX_OPERATOR)(31,32) + PsiElement(.)('.')(31,32) + ElixirMultipleAliasesImpl(MULTIPLE_ALIASES)(32,42) + PsiElement({)('{')(32,33) + ElixirAccessExpressionImpl(ACCESS_EXPRESSION)(33,37) + ElixirAliasImpl(ALIAS)(33,37) + PsiElement(Alias)('Five')(33,37) + PsiWhiteSpace('\n\n ')(37,41) + PsiElement(})('}')(41,42) diff --git a/testData/org/elixir_lang/parser_definition/eol_to_whitespace/ClosingParentheses.txt b/testData/org/elixir_lang/parser_definition/eol_to_whitespace/ClosingParentheses.txt new file mode 100644 index 000000000..b4dc06fac --- /dev/null +++ b/testData/org/elixir_lang/parser_definition/eol_to_whitespace/ClosingParentheses.txt @@ -0,0 +1,33 @@ +Elixir File(0,114) + ElixirAccessExpressionImpl(ACCESS_EXPRESSION)(0,50) + ElixirParentheticalStabImpl(PARENTHETICAL_STAB)(0,50) + PsiElement(()('(')(0,1) + PsiWhiteSpace('\n\n')(1,3) + PsiComment(#)('# Comment after EOL with EOL after it too')(3,44) + PsiWhiteSpace('\n\n')(44,46) + ElixirStabImpl(STAB)(46,47) + ElixirStabBodyImpl(STAB_BODY)(46,47) + ElixirAccessExpressionImpl(ACCESS_EXPRESSION)(46,47) + ElixirDecimalWholeNumberImpl(DECIMAL_WHOLE_NUMBER)(46,47) + ElixirDecimalDigitsImpl(DECIMAL_DIGITS)(46,47) + PsiElement(0-9)('1')(46,47) + PsiWhiteSpace('\n\n')(47,49) + PsiElement())(')')(49,50) + PsiWhiteSpace('\n')(50,51) + ElixirEndOfExpressionImpl(END_OF_EXPRESSION)(51,52) + PsiElement(\\n, \\r\\n)('\n')(51,52) + UNMATCHED_UNQUALIFIED_PARENTHESES_CALL(52,114) + ElixirIdentifierImpl(IDENTIFIER)(52,56) + PsiElement(identifier)('call')(52,56) + ElixirMatchedParenthesesArgumentsImpl(MATCHED_PARENTHESES_ARGUMENTS)(56,114) + ElixirParenthesesArgumentsImpl(PARENTHESES_ARGUMENTS)(56,114) + PsiElement(()('(')(56,57) + PsiWhiteSpace('\n\n')(57,59) + PsiComment(#)('# Comment in call after EOL with EOL after it too')(59,108) + PsiWhiteSpace('\n\n')(108,110) + ElixirAccessExpressionImpl(ACCESS_EXPRESSION)(110,111) + ElixirDecimalWholeNumberImpl(DECIMAL_WHOLE_NUMBER)(110,111) + ElixirDecimalDigitsImpl(DECIMAL_DIGITS)(110,111) + PsiElement(0-9)('2')(110,111) + PsiWhiteSpace('\n\n')(111,113) + PsiElement())(')')(113,114) diff --git a/testData/org/elixir_lang/parser_definition/eol_to_whitespace/Comparison.txt b/testData/org/elixir_lang/parser_definition/eol_to_whitespace/Comparison.txt new file mode 100644 index 000000000..7a6701b43 --- /dev/null +++ b/testData/org/elixir_lang/parser_definition/eol_to_whitespace/Comparison.txt @@ -0,0 +1,68 @@ +Elixir File(0,79) + ElixirUnmatchedComparisonOperationImpl(UNMATCHED_COMPARISON_OPERATION)(0,12) + UNMATCHED_UNQUALIFIED_NO_ARGUMENTS_CALL(0,3) + ElixirIdentifierImpl(IDENTIFIER)(0,3) + PsiElement(identifier)('one')(0,3) + PsiWhiteSpace('\n\n')(3,5) + ElixirComparisonInfixOperatorImpl(COMPARISON_INFIX_OPERATOR)(5,7) + PsiElement(!==, ===, !=, ==, =~)('!=')(5,7) + PsiWhiteSpace('\n\n')(7,9) + UNMATCHED_UNQUALIFIED_NO_ARGUMENTS_CALL(9,12) + ElixirIdentifierImpl(IDENTIFIER)(9,12) + PsiElement(identifier)('two')(9,12) + PsiWhiteSpace('\n')(12,13) + ElixirEndOfExpressionImpl(END_OF_EXPRESSION)(13,14) + PsiElement(\\n, \\r\\n)('\n')(13,14) + ElixirUnmatchedComparisonOperationImpl(UNMATCHED_COMPARISON_OPERATION)(14,29) + UNMATCHED_UNQUALIFIED_NO_ARGUMENTS_CALL(14,19) + ElixirIdentifierImpl(IDENTIFIER)(14,19) + PsiElement(identifier)('three')(14,19) + PsiWhiteSpace('\n\n')(19,21) + ElixirComparisonInfixOperatorImpl(COMPARISON_INFIX_OPERATOR)(21,23) + PsiElement(!==, ===, !=, ==, =~)('==')(21,23) + PsiWhiteSpace('\n\n')(23,25) + UNMATCHED_UNQUALIFIED_NO_ARGUMENTS_CALL(25,29) + ElixirIdentifierImpl(IDENTIFIER)(25,29) + PsiElement(identifier)('four')(25,29) + PsiWhiteSpace('\n')(29,30) + ElixirEndOfExpressionImpl(END_OF_EXPRESSION)(30,31) + PsiElement(\\n, \\r\\n)('\n')(30,31) + ElixirUnmatchedComparisonOperationImpl(UNMATCHED_COMPARISON_OPERATION)(31,44) + UNMATCHED_UNQUALIFIED_NO_ARGUMENTS_CALL(31,35) + ElixirIdentifierImpl(IDENTIFIER)(31,35) + PsiElement(identifier)('five')(31,35) + PsiWhiteSpace('\n\n')(35,37) + ElixirComparisonInfixOperatorImpl(COMPARISON_INFIX_OPERATOR)(37,39) + PsiElement(!==, ===, !=, ==, =~)('=~')(37,39) + PsiWhiteSpace('\n\n')(39,41) + UNMATCHED_UNQUALIFIED_NO_ARGUMENTS_CALL(41,44) + ElixirIdentifierImpl(IDENTIFIER)(41,44) + PsiElement(identifier)('six')(41,44) + PsiWhiteSpace('\n')(44,45) + ElixirEndOfExpressionImpl(END_OF_EXPRESSION)(45,46) + PsiElement(\\n, \\r\\n)('\n')(45,46) + ElixirUnmatchedComparisonOperationImpl(UNMATCHED_COMPARISON_OPERATION)(46,63) + UNMATCHED_UNQUALIFIED_NO_ARGUMENTS_CALL(46,51) + ElixirIdentifierImpl(IDENTIFIER)(46,51) + PsiElement(identifier)('seven')(46,51) + PsiWhiteSpace('\n\n')(51,53) + ElixirComparisonInfixOperatorImpl(COMPARISON_INFIX_OPERATOR)(53,56) + PsiElement(!==, ===, !=, ==, =~)('!==')(53,56) + PsiWhiteSpace('\n\n')(56,58) + UNMATCHED_UNQUALIFIED_NO_ARGUMENTS_CALL(58,63) + ElixirIdentifierImpl(IDENTIFIER)(58,63) + PsiElement(identifier)('eight')(58,63) + PsiWhiteSpace('\n')(63,64) + ElixirEndOfExpressionImpl(END_OF_EXPRESSION)(64,65) + PsiElement(\\n, \\r\\n)('\n')(64,65) + ElixirUnmatchedComparisonOperationImpl(UNMATCHED_COMPARISON_OPERATION)(65,79) + UNMATCHED_UNQUALIFIED_NO_ARGUMENTS_CALL(65,69) + ElixirIdentifierImpl(IDENTIFIER)(65,69) + PsiElement(identifier)('nine')(65,69) + PsiWhiteSpace('\n\n')(69,71) + ElixirComparisonInfixOperatorImpl(COMPARISON_INFIX_OPERATOR)(71,74) + PsiElement(!==, ===, !=, ==, =~)('===')(71,74) + PsiWhiteSpace('\n\n')(74,76) + UNMATCHED_UNQUALIFIED_NO_ARGUMENTS_CALL(76,79) + ElixirIdentifierImpl(IDENTIFIER)(76,79) + PsiElement(identifier)('ten')(76,79) diff --git a/testData/org/elixir_lang/parser_definition/eol_to_whitespace/Do.txt b/testData/org/elixir_lang/parser_definition/eol_to_whitespace/Do.txt new file mode 100644 index 000000000..7138d4fc5 --- /dev/null +++ b/testData/org/elixir_lang/parser_definition/eol_to_whitespace/Do.txt @@ -0,0 +1,13 @@ +Elixir File(0,15) + UNMATCHED_UNQUALIFIED_PARENTHESES_CALL(0,15) + ElixirIdentifierImpl(IDENTIFIER)(0,3) + PsiElement(identifier)('one')(0,3) + ElixirMatchedParenthesesArgumentsImpl(MATCHED_PARENTHESES_ARGUMENTS)(3,5) + ElixirParenthesesArgumentsImpl(PARENTHESES_ARGUMENTS)(3,5) + PsiElement(()('(')(3,4) + PsiElement())(')')(4,5) + PsiWhiteSpace('\n\n ')(5,9) + ElixirDoBlockImpl(DO_BLOCK)(9,15) + PsiElement(do)('do')(9,11) + PsiWhiteSpace('\n')(11,12) + PsiElement(end)('end')(12,15) \ No newline at end of file diff --git a/testData/org/elixir_lang/parser_definition/eol_to_whitespace/In.txt b/testData/org/elixir_lang/parser_definition/eol_to_whitespace/In.txt new file mode 100644 index 000000000..284621891 --- /dev/null +++ b/testData/org/elixir_lang/parser_definition/eol_to_whitespace/In.txt @@ -0,0 +1,12 @@ +Elixir File(0,12) + ElixirUnmatchedInOperationImpl(UNMATCHED_IN_OPERATION)(0,12) + UNMATCHED_UNQUALIFIED_NO_ARGUMENTS_CALL(0,3) + ElixirIdentifierImpl(IDENTIFIER)(0,3) + PsiElement(identifier)('one')(0,3) + PsiWhiteSpace('\n\n')(3,5) + ElixirInInfixOperatorImpl(IN_INFIX_OPERATOR)(5,7) + PsiElement(in)('in')(5,7) + PsiWhiteSpace('\n\n')(7,9) + UNMATCHED_UNQUALIFIED_NO_ARGUMENTS_CALL(9,12) + ElixirIdentifierImpl(IDENTIFIER)(9,12) + PsiElement(identifier)('two')(9,12) diff --git a/testData/org/elixir_lang/parser_definition/eol_to_whitespace/InMatch.txt b/testData/org/elixir_lang/parser_definition/eol_to_whitespace/InMatch.txt new file mode 100644 index 000000000..92fc00b60 --- /dev/null +++ b/testData/org/elixir_lang/parser_definition/eol_to_whitespace/InMatch.txt @@ -0,0 +1,70 @@ +Elixir File(0,88) + UNMATCHED_UNQUALIFIED_NO_PARENTHESES_CALL(0,34) + ElixirIdentifierImpl(IDENTIFIER)(0,4) + PsiElement(identifier)('with')(0,4) + PsiWhiteSpace(' ')(4,5) + ElixirNoParenthesesOneArgumentImpl(NO_PARENTHESES_ONE_ARGUMENT)(5,27) + ElixirMatchedInMatchOperationImpl(MATCHED_IN_MATCH_OPERATION)(5,27) + ElixirAccessExpressionImpl(ACCESS_EXPRESSION)(5,8) + ElixirAtomImpl(ATOM)(5,8) + PsiElement(:)(':')(5,6) + PsiElement(A-Z, a-z, _, @, 0-9. ?, !)('ok')(6,8) + PsiWhiteSpace('\n\n ')(8,15) + ElixirInMatchInfixOperatorImpl(IN_MATCH_INFIX_OPERATOR)(15,17) + PsiElement(<-, \\\\)('<-')(15,17) + PsiWhiteSpace('\n\n ')(17,24) + MATCHED_UNQUALIFIED_PARENTHESES_CALL(24,27) + ElixirIdentifierImpl(IDENTIFIER)(24,25) + PsiElement(identifier)('a')(24,25) + ElixirMatchedParenthesesArgumentsImpl(MATCHED_PARENTHESES_ARGUMENTS)(25,27) + ElixirParenthesesArgumentsImpl(PARENTHESES_ARGUMENTS)(25,27) + PsiElement(()('(')(25,26) + PsiElement())(')')(26,27) + PsiWhiteSpace(' ')(27,28) + ElixirDoBlockImpl(DO_BLOCK)(28,34) + PsiElement(do)('do')(28,30) + PsiWhiteSpace('\n')(30,31) + PsiElement(end)('end')(31,34) + PsiWhiteSpace('\n')(34,35) + ElixirEndOfExpressionImpl(END_OF_EXPRESSION)(35,36) + PsiElement(\\n, \\r\\n)('\n')(35,36) + UNMATCHED_UNQUALIFIED_NO_PARENTHESES_CALL(36,88) + ElixirIdentifierImpl(IDENTIFIER)(36,45) + PsiElement(identifier)('defmodule')(36,45) + PsiWhiteSpace(' ')(45,46) + ElixirNoParenthesesOneArgumentImpl(NO_PARENTHESES_ONE_ARGUMENT)(46,47) + ElixirAccessExpressionImpl(ACCESS_EXPRESSION)(46,47) + ElixirAliasImpl(ALIAS)(46,47) + PsiElement(Alias)('A')(46,47) + PsiWhiteSpace(' ')(47,48) + ElixirDoBlockImpl(DO_BLOCK)(48,88) + PsiElement(do)('do')(48,50) + PsiWhiteSpace('\n ')(50,53) + ElixirStabImpl(STAB)(53,84) + ElixirStabBodyImpl(STAB_BODY)(53,84) + UNMATCHED_UNQUALIFIED_NO_PARENTHESES_CALL(53,84) + ElixirIdentifierImpl(IDENTIFIER)(53,56) + PsiElement(identifier)('def')(53,56) + PsiWhiteSpace(' ')(56,57) + ElixirNoParenthesesOneArgumentImpl(NO_PARENTHESES_ONE_ARGUMENT)(57,84) + MATCHED_UNQUALIFIED_PARENTHESES_CALL(57,84) + ElixirIdentifierImpl(IDENTIFIER)(57,58) + PsiElement(identifier)('a')(57,58) + ElixirMatchedParenthesesArgumentsImpl(MATCHED_PARENTHESES_ARGUMENTS)(58,84) + ElixirParenthesesArgumentsImpl(PARENTHESES_ARGUMENTS)(58,84) + PsiElement(()('(')(58,59) + ElixirUnmatchedInMatchOperationImpl(UNMATCHED_IN_MATCH_OPERATION)(59,83) + UNMATCHED_UNQUALIFIED_NO_ARGUMENTS_CALL(59,60) + ElixirIdentifierImpl(IDENTIFIER)(59,60) + PsiElement(identifier)('b')(59,60) + PsiWhiteSpace('\n\n ')(60,70) + ElixirInMatchInfixOperatorImpl(IN_MATCH_INFIX_OPERATOR)(70,72) + PsiElement(<-, \\\\)('\\')(70,72) + PsiWhiteSpace('\n\n ')(72,82) + ElixirAccessExpressionImpl(ACCESS_EXPRESSION)(82,83) + ElixirDecimalWholeNumberImpl(DECIMAL_WHOLE_NUMBER)(82,83) + ElixirDecimalDigitsImpl(DECIMAL_DIGITS)(82,83) + PsiElement(0-9)('1')(82,83) + PsiElement())(')')(83,84) + PsiWhiteSpace('\n')(84,85) + PsiElement(end)('end')(85,88) diff --git a/testData/org/elixir_lang/parser_definition/eol_to_whitespace/KeywordPairColon.txt b/testData/org/elixir_lang/parser_definition/eol_to_whitespace/KeywordPairColon.txt new file mode 100644 index 000000000..3d730deef --- /dev/null +++ b/testData/org/elixir_lang/parser_definition/eol_to_whitespace/KeywordPairColon.txt @@ -0,0 +1,15 @@ +Elixir File(0,11) + ElixirAccessExpressionImpl(ACCESS_EXPRESSION)(0,11) + ElixirListImpl(LIST)(0,11) + PsiElement([)('[')(0,1) + ElixirKeywordsImpl(KEYWORDS)(1,10) + ElixirKeywordPairImpl(KEYWORD_PAIR)(1,10) + ElixirKeywordKeyImpl(KEYWORD_KEY)(1,4) + PsiElement(A-Z, a-z, _, @, 0-9. ?, !)('one')(1,4) + PsiElement(Keyword Pair Colon (:))(':')(4,5) + PsiWhiteSpace('\n\n ')(5,9) + ElixirAccessExpressionImpl(ACCESS_EXPRESSION)(9,10) + ElixirDecimalWholeNumberImpl(DECIMAL_WHOLE_NUMBER)(9,10) + ElixirDecimalDigitsImpl(DECIMAL_DIGITS)(9,10) + PsiElement(0-9)('2')(9,10) + PsiElement(])(']')(10,11) diff --git a/testData/org/elixir_lang/parser_definition/eol_to_whitespace/Match.txt b/testData/org/elixir_lang/parser_definition/eol_to_whitespace/Match.txt new file mode 100644 index 000000000..b7eab25a2 --- /dev/null +++ b/testData/org/elixir_lang/parser_definition/eol_to_whitespace/Match.txt @@ -0,0 +1,13 @@ +Elixir File(0,7) + ElixirUnmatchedMatchOperationImpl(UNMATCHED_MATCH_OPERATION)(0,7) + UNMATCHED_UNQUALIFIED_NO_ARGUMENTS_CALL(0,1) + ElixirIdentifierImpl(IDENTIFIER)(0,1) + PsiElement(identifier)('a')(0,1) + PsiWhiteSpace('\n\n')(1,3) + ElixirMatchInfixOperatorImpl(MATCH_INFIX_OPERATOR)(3,4) + PsiElement(=)('=')(3,4) + PsiWhiteSpace('\n\n')(4,6) + ElixirAccessExpressionImpl(ACCESS_EXPRESSION)(6,7) + ElixirDecimalWholeNumberImpl(DECIMAL_WHOLE_NUMBER)(6,7) + ElixirDecimalDigitsImpl(DECIMAL_DIGITS)(6,7) + PsiElement(0-9)('1')(6,7) \ No newline at end of file diff --git a/testData/org/elixir_lang/parser_definition/eol_to_whitespace/OpeningBit.txt b/testData/org/elixir_lang/parser_definition/eol_to_whitespace/OpeningBit.txt new file mode 100644 index 000000000..e54f52951 --- /dev/null +++ b/testData/org/elixir_lang/parser_definition/eol_to_whitespace/OpeningBit.txt @@ -0,0 +1,10 @@ +Elixir File(0,9) + ElixirAccessExpressionImpl(ACCESS_EXPRESSION)(0,9) + ElixirBitStringImpl(BIT_STRING)(0,9) + PsiElement(<<)('<<')(0,2) + PsiWhiteSpace('\n\n ')(2,6) + ElixirAccessExpressionImpl(ACCESS_EXPRESSION)(6,7) + ElixirDecimalWholeNumberImpl(DECIMAL_WHOLE_NUMBER)(6,7) + ElixirDecimalDigitsImpl(DECIMAL_DIGITS)(6,7) + PsiElement(0-9)('1')(6,7) + PsiElement(>>)('>>')(7,9) \ No newline at end of file diff --git a/testData/org/elixir_lang/parser_definition/eol_to_whitespace/OpeningBracket.txt b/testData/org/elixir_lang/parser_definition/eol_to_whitespace/OpeningBracket.txt new file mode 100644 index 000000000..39abf343b --- /dev/null +++ b/testData/org/elixir_lang/parser_definition/eol_to_whitespace/OpeningBracket.txt @@ -0,0 +1,10 @@ +Elixir File(0,7) + ElixirAccessExpressionImpl(ACCESS_EXPRESSION)(0,7) + ElixirListImpl(LIST)(0,7) + PsiElement([)('[')(0,1) + PsiWhiteSpace('\n\n ')(1,5) + ElixirAccessExpressionImpl(ACCESS_EXPRESSION)(5,6) + ElixirDecimalWholeNumberImpl(DECIMAL_WHOLE_NUMBER)(5,6) + ElixirDecimalDigitsImpl(DECIMAL_DIGITS)(5,6) + PsiElement(0-9)('1')(5,6) + PsiElement(])(']')(6,7) \ No newline at end of file diff --git a/testData/org/elixir_lang/parser_definition/eol_to_whitespace/OpeningCurly.txt b/testData/org/elixir_lang/parser_definition/eol_to_whitespace/OpeningCurly.txt new file mode 100644 index 000000000..79083ffc7 --- /dev/null +++ b/testData/org/elixir_lang/parser_definition/eol_to_whitespace/OpeningCurly.txt @@ -0,0 +1,54 @@ +Elixir File(0,44) + ElixirAccessExpressionImpl(ACCESS_EXPRESSION)(0,7) + ElixirTupleImpl(TUPLE)(0,7) + PsiElement({)('{')(0,1) + PsiWhiteSpace('\n\n ')(1,5) + ElixirAccessExpressionImpl(ACCESS_EXPRESSION)(5,6) + ElixirDecimalWholeNumberImpl(DECIMAL_WHOLE_NUMBER)(5,6) + ElixirDecimalDigitsImpl(DECIMAL_DIGITS)(5,6) + PsiElement(0-9)('1')(5,6) + PsiElement(})('}')(6,7) + ElixirEndOfExpressionImpl(END_OF_EXPRESSION)(7,9) + PsiElement(\\n, \\r\\n)('\n')(7,8) + PsiElement(\\n, \\r\\n)('\n')(8,9) + ElixirAccessExpressionImpl(ACCESS_EXPRESSION)(9,23) + ElixirMapOperationImpl(MAP_OPERATION)(9,23) + ElixirMapPrefixOperatorImpl(MAP_PREFIX_OPERATOR)(9,10) + PsiElement(%)('%')(9,10) + ElixirMapArgumentsImpl(MAP_ARGUMENTS)(10,23) + PsiElement({)('{')(10,11) + PsiWhiteSpace('\n\n ')(11,15) + ElixirMapConstructionArgumentsImpl(MAP_CONSTRUCTION_ARGUMENTS)(15,21) + ElixirKeywordsImpl(KEYWORDS)(15,21) + ElixirKeywordPairImpl(KEYWORD_PAIR)(15,21) + ElixirKeywordKeyImpl(KEYWORD_KEY)(15,18) + PsiElement(A-Z, a-z, _, @, 0-9. ?, !)('two')(15,18) + PsiElement(Keyword Pair Colon (:))(':')(18,19) + PsiWhiteSpace(' ')(19,20) + ElixirAccessExpressionImpl(ACCESS_EXPRESSION)(20,21) + ElixirDecimalWholeNumberImpl(DECIMAL_WHOLE_NUMBER)(20,21) + ElixirDecimalDigitsImpl(DECIMAL_DIGITS)(20,21) + PsiElement(0-9)('3')(20,21) + PsiWhiteSpace('\n')(21,22) + PsiElement(})('}')(22,23) + PsiWhiteSpace('\n')(23,24) + ElixirEndOfExpressionImpl(END_OF_EXPRESSION)(24,25) + PsiElement(\\n, \\r\\n)('\n')(24,25) + UNMATCHED_UNQUALIFIED_NO_PARENTHESES_CALL(25,44) + ElixirIdentifierImpl(IDENTIFIER)(25,30) + PsiElement(identifier)('alias')(25,30) + PsiWhiteSpace(' ')(30,31) + ElixirNoParenthesesOneArgumentImpl(NO_PARENTHESES_ONE_ARGUMENT)(31,44) + ElixirMatchedQualifiedMultipleAliasesImpl(MATCHED_QUALIFIED_MULTIPLE_ALIASES)(31,44) + ElixirAccessExpressionImpl(ACCESS_EXPRESSION)(31,35) + ElixirAliasImpl(ALIAS)(31,35) + PsiElement(Alias)('Four')(31,35) + ElixirDotInfixOperatorImpl(DOT_INFIX_OPERATOR)(35,36) + PsiElement(.)('.')(35,36) + ElixirMultipleAliasesImpl(MULTIPLE_ALIASES)(36,44) + PsiElement({)('{')(36,37) + PsiWhiteSpace('\n\n')(37,39) + ElixirAccessExpressionImpl(ACCESS_EXPRESSION)(39,43) + ElixirAliasImpl(ALIAS)(39,43) + PsiElement(Alias)('Five')(39,43) + PsiElement(})('}')(43,44) diff --git a/testData/org/elixir_lang/parser_definition/eol_to_whitespace/OpeningParentheses.txt b/testData/org/elixir_lang/parser_definition/eol_to_whitespace/OpeningParentheses.txt new file mode 100644 index 000000000..b4dc06fac --- /dev/null +++ b/testData/org/elixir_lang/parser_definition/eol_to_whitespace/OpeningParentheses.txt @@ -0,0 +1,33 @@ +Elixir File(0,114) + ElixirAccessExpressionImpl(ACCESS_EXPRESSION)(0,50) + ElixirParentheticalStabImpl(PARENTHETICAL_STAB)(0,50) + PsiElement(()('(')(0,1) + PsiWhiteSpace('\n\n')(1,3) + PsiComment(#)('# Comment after EOL with EOL after it too')(3,44) + PsiWhiteSpace('\n\n')(44,46) + ElixirStabImpl(STAB)(46,47) + ElixirStabBodyImpl(STAB_BODY)(46,47) + ElixirAccessExpressionImpl(ACCESS_EXPRESSION)(46,47) + ElixirDecimalWholeNumberImpl(DECIMAL_WHOLE_NUMBER)(46,47) + ElixirDecimalDigitsImpl(DECIMAL_DIGITS)(46,47) + PsiElement(0-9)('1')(46,47) + PsiWhiteSpace('\n\n')(47,49) + PsiElement())(')')(49,50) + PsiWhiteSpace('\n')(50,51) + ElixirEndOfExpressionImpl(END_OF_EXPRESSION)(51,52) + PsiElement(\\n, \\r\\n)('\n')(51,52) + UNMATCHED_UNQUALIFIED_PARENTHESES_CALL(52,114) + ElixirIdentifierImpl(IDENTIFIER)(52,56) + PsiElement(identifier)('call')(52,56) + ElixirMatchedParenthesesArgumentsImpl(MATCHED_PARENTHESES_ARGUMENTS)(56,114) + ElixirParenthesesArgumentsImpl(PARENTHESES_ARGUMENTS)(56,114) + PsiElement(()('(')(56,57) + PsiWhiteSpace('\n\n')(57,59) + PsiComment(#)('# Comment in call after EOL with EOL after it too')(59,108) + PsiWhiteSpace('\n\n')(108,110) + ElixirAccessExpressionImpl(ACCESS_EXPRESSION)(110,111) + ElixirDecimalWholeNumberImpl(DECIMAL_WHOLE_NUMBER)(110,111) + ElixirDecimalDigitsImpl(DECIMAL_DIGITS)(110,111) + PsiElement(0-9)('2')(110,111) + PsiWhiteSpace('\n\n')(111,113) + PsiElement())(')')(113,114) diff --git a/testData/org/elixir_lang/parser_definition/eol_to_whitespace/Or.txt b/testData/org/elixir_lang/parser_definition/eol_to_whitespace/Or.txt new file mode 100644 index 000000000..8d8472040 --- /dev/null +++ b/testData/org/elixir_lang/parser_definition/eol_to_whitespace/Or.txt @@ -0,0 +1,42 @@ +Elixir File(0,43) + ElixirUnmatchedOrOperationImpl(UNMATCHED_OR_OPERATION)(0,15) + ElixirAccessExpressionImpl(ACCESS_EXPRESSION)(0,5) + ElixirAtomKeywordImpl(ATOM_KEYWORD)(0,5) + PsiElement(false)('false')(0,5) + PsiWhiteSpace('\n\n')(5,7) + ElixirOrInfixOperatorImpl(OR_INFIX_OPERATOR)(7,9) + PsiElement(|||, ||)('||')(7,9) + PsiWhiteSpace('\n\n')(9,11) + ElixirAccessExpressionImpl(ACCESS_EXPRESSION)(11,15) + ElixirAtomKeywordImpl(ATOM_KEYWORD)(11,15) + PsiElement(true)('true')(11,15) + ElixirEndOfExpressionImpl(END_OF_EXPRESSION)(15,17) + PsiElement(\\n, \\r\\n)('\n')(15,16) + PsiElement(\\n, \\r\\n)('\n')(16,17) + ElixirUnmatchedOrOperationImpl(UNMATCHED_OR_OPERATION)(17,26) + ElixirAccessExpressionImpl(ACCESS_EXPRESSION)(17,18) + ElixirDecimalWholeNumberImpl(DECIMAL_WHOLE_NUMBER)(17,18) + ElixirDecimalDigitsImpl(DECIMAL_DIGITS)(17,18) + PsiElement(0-9)('1')(17,18) + PsiWhiteSpace('\n\n')(18,20) + ElixirOrInfixOperatorImpl(OR_INFIX_OPERATOR)(20,23) + PsiElement(|||, ||)('|||')(20,23) + PsiWhiteSpace('\n\n')(23,25) + ElixirAccessExpressionImpl(ACCESS_EXPRESSION)(25,26) + ElixirDecimalWholeNumberImpl(DECIMAL_WHOLE_NUMBER)(25,26) + ElixirDecimalDigitsImpl(DECIMAL_DIGITS)(25,26) + PsiElement(0-9)('2')(25,26) + PsiWhiteSpace('\n')(26,27) + ElixirEndOfExpressionImpl(END_OF_EXPRESSION)(27,28) + PsiElement(\\n, \\r\\n)('\n')(27,28) + ElixirUnmatchedOrOperationImpl(UNMATCHED_OR_OPERATION)(28,43) + ElixirAccessExpressionImpl(ACCESS_EXPRESSION)(28,33) + ElixirAtomKeywordImpl(ATOM_KEYWORD)(28,33) + PsiElement(false)('false')(28,33) + PsiWhiteSpace('\n\n')(33,35) + ElixirOrInfixOperatorImpl(OR_INFIX_OPERATOR)(35,37) + PsiElement(`or`)('or')(35,37) + PsiWhiteSpace('\n\n')(37,39) + ElixirAccessExpressionImpl(ACCESS_EXPRESSION)(39,43) + ElixirAtomKeywordImpl(ATOM_KEYWORD)(39,43) + PsiElement(true)('true')(39,43) diff --git a/testData/org/elixir_lang/parser_definition/eol_to_whitespace/Pipe.txt b/testData/org/elixir_lang/parser_definition/eol_to_whitespace/Pipe.txt new file mode 100644 index 000000000..d9bdbdc2f --- /dev/null +++ b/testData/org/elixir_lang/parser_definition/eol_to_whitespace/Pipe.txt @@ -0,0 +1,12 @@ +Elixir File(0,12) + ElixirUnmatchedPipeOperationImpl(UNMATCHED_PIPE_OPERATION)(0,12) + UNMATCHED_UNQUALIFIED_NO_ARGUMENTS_CALL(0,4) + ElixirIdentifierImpl(IDENTIFIER)(0,4) + PsiElement(identifier)('atom')(0,4) + PsiWhiteSpace('\n\n')(4,6) + ElixirPipeInfixOperatorImpl(PIPE_INFIX_OPERATOR)(6,7) + PsiElement(|)('|')(6,7) + PsiWhiteSpace('\n\n')(7,9) + UNMATCHED_UNQUALIFIED_NO_ARGUMENTS_CALL(9,12) + ElixirIdentifierImpl(IDENTIFIER)(9,12) + PsiElement(identifier)('pid')(9,12) diff --git a/testData/org/elixir_lang/parser_definition/eol_to_whitespace/Relational.txt b/testData/org/elixir_lang/parser_definition/eol_to_whitespace/Relational.txt new file mode 100644 index 000000000..dc7bc10b3 --- /dev/null +++ b/testData/org/elixir_lang/parser_definition/eol_to_whitespace/Relational.txt @@ -0,0 +1,62 @@ +Elixir File(0,36) + ElixirUnmatchedRelationalOperationImpl(UNMATCHED_RELATIONAL_OPERATION)(0,7) + ElixirAccessExpressionImpl(ACCESS_EXPRESSION)(0,1) + ElixirDecimalWholeNumberImpl(DECIMAL_WHOLE_NUMBER)(0,1) + ElixirDecimalDigitsImpl(DECIMAL_DIGITS)(0,1) + PsiElement(0-9)('1')(0,1) + PsiWhiteSpace('\n\n')(1,3) + ElixirRelationalInfixOperatorImpl(RELATIONAL_INFIX_OPERATOR)(3,4) + PsiElement(<, <=, >=, >)('<')(3,4) + PsiWhiteSpace('\n\n')(4,6) + ElixirAccessExpressionImpl(ACCESS_EXPRESSION)(6,7) + ElixirDecimalWholeNumberImpl(DECIMAL_WHOLE_NUMBER)(6,7) + ElixirDecimalDigitsImpl(DECIMAL_DIGITS)(6,7) + PsiElement(0-9)('2')(6,7) + ElixirEndOfExpressionImpl(END_OF_EXPRESSION)(7,9) + PsiElement(\\n, \\r\\n)('\n')(7,8) + PsiElement(\\n, \\r\\n)('\n')(8,9) + ElixirUnmatchedRelationalOperationImpl(UNMATCHED_RELATIONAL_OPERATION)(9,16) + ElixirAccessExpressionImpl(ACCESS_EXPRESSION)(9,10) + ElixirDecimalWholeNumberImpl(DECIMAL_WHOLE_NUMBER)(9,10) + ElixirDecimalDigitsImpl(DECIMAL_DIGITS)(9,10) + PsiElement(0-9)('4')(9,10) + PsiWhiteSpace('\n\n')(10,12) + ElixirRelationalInfixOperatorImpl(RELATIONAL_INFIX_OPERATOR)(12,13) + PsiElement(<, <=, >=, >)('>')(12,13) + PsiWhiteSpace('\n\n')(13,15) + ElixirAccessExpressionImpl(ACCESS_EXPRESSION)(15,16) + ElixirDecimalWholeNumberImpl(DECIMAL_WHOLE_NUMBER)(15,16) + ElixirDecimalDigitsImpl(DECIMAL_DIGITS)(15,16) + PsiElement(0-9)('3')(15,16) + ElixirEndOfExpressionImpl(END_OF_EXPRESSION)(16,18) + PsiElement(\\n, \\r\\n)('\n')(16,17) + PsiElement(\\n, \\r\\n)('\n')(17,18) + ElixirUnmatchedRelationalOperationImpl(UNMATCHED_RELATIONAL_OPERATION)(18,26) + ElixirAccessExpressionImpl(ACCESS_EXPRESSION)(18,19) + ElixirDecimalWholeNumberImpl(DECIMAL_WHOLE_NUMBER)(18,19) + ElixirDecimalDigitsImpl(DECIMAL_DIGITS)(18,19) + PsiElement(0-9)('5')(18,19) + PsiWhiteSpace('\n\n')(19,21) + ElixirRelationalInfixOperatorImpl(RELATIONAL_INFIX_OPERATOR)(21,23) + PsiElement(<, <=, >=, >)('<=')(21,23) + PsiWhiteSpace('\n\n')(23,25) + ElixirAccessExpressionImpl(ACCESS_EXPRESSION)(25,26) + ElixirDecimalWholeNumberImpl(DECIMAL_WHOLE_NUMBER)(25,26) + ElixirDecimalDigitsImpl(DECIMAL_DIGITS)(25,26) + PsiElement(0-9)('6')(25,26) + ElixirEndOfExpressionImpl(END_OF_EXPRESSION)(26,28) + PsiElement(\\n, \\r\\n)('\n')(26,27) + PsiElement(\\n, \\r\\n)('\n')(27,28) + ElixirUnmatchedRelationalOperationImpl(UNMATCHED_RELATIONAL_OPERATION)(28,36) + ElixirAccessExpressionImpl(ACCESS_EXPRESSION)(28,29) + ElixirDecimalWholeNumberImpl(DECIMAL_WHOLE_NUMBER)(28,29) + ElixirDecimalDigitsImpl(DECIMAL_DIGITS)(28,29) + PsiElement(0-9)('7')(28,29) + PsiWhiteSpace('\n\n')(29,31) + ElixirRelationalInfixOperatorImpl(RELATIONAL_INFIX_OPERATOR)(31,33) + PsiElement(<, <=, >=, >)('>=')(31,33) + PsiWhiteSpace('\n\n')(33,35) + ElixirAccessExpressionImpl(ACCESS_EXPRESSION)(35,36) + ElixirDecimalWholeNumberImpl(DECIMAL_WHOLE_NUMBER)(35,36) + ElixirDecimalDigitsImpl(DECIMAL_DIGITS)(35,36) + PsiElement(0-9)('8')(35,36) diff --git a/testData/org/elixir_lang/parser_definition/eol_to_whitespace/Semicolon.txt b/testData/org/elixir_lang/parser_definition/eol_to_whitespace/Semicolon.txt new file mode 100644 index 000000000..899aa376b --- /dev/null +++ b/testData/org/elixir_lang/parser_definition/eol_to_whitespace/Semicolon.txt @@ -0,0 +1,13 @@ +Elixir File(0,7) + ElixirAccessExpressionImpl(ACCESS_EXPRESSION)(0,1) + ElixirDecimalWholeNumberImpl(DECIMAL_WHOLE_NUMBER)(0,1) + ElixirDecimalDigitsImpl(DECIMAL_DIGITS)(0,1) + PsiElement(0-9)('1')(0,1) + PsiWhiteSpace('\n\n')(1,3) + ElixirEndOfExpressionImpl(END_OF_EXPRESSION)(3,4) + PsiElement(;)(';')(3,4) + PsiWhiteSpace('\n\n')(4,6) + ElixirAccessExpressionImpl(ACCESS_EXPRESSION)(6,7) + ElixirDecimalWholeNumberImpl(DECIMAL_WHOLE_NUMBER)(6,7) + ElixirDecimalDigitsImpl(DECIMAL_DIGITS)(6,7) + PsiElement(0-9)('2')(6,7) diff --git a/testData/org/elixir_lang/parser_definition/eol_to_whitespace/Stab.txt b/testData/org/elixir_lang/parser_definition/eol_to_whitespace/Stab.txt new file mode 100644 index 000000000..38c505e7a --- /dev/null +++ b/testData/org/elixir_lang/parser_definition/eol_to_whitespace/Stab.txt @@ -0,0 +1,29 @@ +Elixir File(0,48) + UNMATCHED_UNQUALIFIED_NO_ARGUMENTS_CALL(0,48) + ElixirIdentifierImpl(IDENTIFIER)(0,10) + PsiElement(identifier)('identifier')(0,10) + PsiWhiteSpace(' ')(10,11) + ElixirDoBlockImpl(DO_BLOCK)(11,48) + PsiElement(do)('do')(11,13) + PsiWhiteSpace('\n')(13,14) + ElixirBlockListImpl(BLOCK_LIST)(14,44) + ElixirBlockItemImpl(BLOCK_ITEM)(14,44) + ElixirBlockIdentifierImpl(BLOCK_IDENTIFIER)(14,19) + PsiElement(after)('after')(14,19) + PsiWhiteSpace('\n ')(19,22) + ElixirStabImpl(STAB)(22,44) + ElixirStabOperationImpl(STAB_OPERATION)(22,44) + ElixirStabParenthesesSignatureImpl(STAB_PARENTHESES_SIGNATURE)(22,24) + ElixirParenthesesArgumentsImpl(PARENTHESES_ARGUMENTS)(22,24) + PsiElement(()('(')(22,23) + PsiElement())(')')(23,24) + PsiWhiteSpace('\n ')(24,27) + ElixirStabInfixOperatorImpl(STAB_INFIX_OPERATOR)(27,29) + PsiElement(->)('->')(27,29) + PsiWhiteSpace('\n ')(29,34) + ElixirStabBodyImpl(STAB_BODY)(34,44) + UNMATCHED_UNQUALIFIED_NO_ARGUMENTS_CALL(34,44) + ElixirIdentifierImpl(IDENTIFIER)(34,44) + PsiElement(identifier)('expression')(34,44) + PsiWhiteSpace('\n')(44,45) + PsiElement(end)('end')(45,48) diff --git a/testData/org/elixir_lang/parser_definition/eol_to_whitespace/Two.txt b/testData/org/elixir_lang/parser_definition/eol_to_whitespace/Two.txt new file mode 100644 index 000000000..aff1e01c3 --- /dev/null +++ b/testData/org/elixir_lang/parser_definition/eol_to_whitespace/Two.txt @@ -0,0 +1,54 @@ +Elixir File(0,62) + ElixirUnmatchedTwoOperationImpl(UNMATCHED_TWO_OPERATION)(0,12) + UNMATCHED_UNQUALIFIED_NO_ARGUMENTS_CALL(0,3) + ElixirIdentifierImpl(IDENTIFIER)(0,3) + PsiElement(identifier)('one')(0,3) + PsiWhiteSpace('\n\n')(3,5) + ElixirTwoInfixOperatorImpl(TWO_INFIX_OPERATOR)(5,7) + PsiElement(++, --, <>)('++')(5,7) + PsiWhiteSpace('\n\n')(7,9) + UNMATCHED_UNQUALIFIED_NO_ARGUMENTS_CALL(9,12) + ElixirIdentifierImpl(IDENTIFIER)(9,12) + PsiElement(identifier)('two')(9,12) + PsiWhiteSpace('\n')(12,13) + ElixirEndOfExpressionImpl(END_OF_EXPRESSION)(13,14) + PsiElement(\\n, \\r\\n)('\n')(13,14) + ElixirUnmatchedTwoOperationImpl(UNMATCHED_TWO_OPERATION)(14,29) + UNMATCHED_UNQUALIFIED_NO_ARGUMENTS_CALL(14,19) + ElixirIdentifierImpl(IDENTIFIER)(14,19) + PsiElement(identifier)('three')(14,19) + PsiWhiteSpace('\n\n')(19,21) + ElixirTwoInfixOperatorImpl(TWO_INFIX_OPERATOR)(21,23) + PsiElement(++, --, <>)('--')(21,23) + PsiWhiteSpace('\n\n')(23,25) + UNMATCHED_UNQUALIFIED_NO_ARGUMENTS_CALL(25,29) + ElixirIdentifierImpl(IDENTIFIER)(25,29) + PsiElement(identifier)('four')(25,29) + PsiWhiteSpace('\n')(29,30) + ElixirEndOfExpressionImpl(END_OF_EXPRESSION)(30,31) + PsiElement(\\n, \\r\\n)('\n')(30,31) + ElixirUnmatchedTwoOperationImpl(UNMATCHED_TWO_OPERATION)(31,44) + UNMATCHED_UNQUALIFIED_NO_ARGUMENTS_CALL(31,35) + ElixirIdentifierImpl(IDENTIFIER)(31,35) + PsiElement(identifier)('five')(31,35) + PsiWhiteSpace('\n\n')(35,37) + ElixirTwoInfixOperatorImpl(TWO_INFIX_OPERATOR)(37,39) + PsiElement(..)('..')(37,39) + PsiWhiteSpace('\n\n')(39,41) + UNMATCHED_UNQUALIFIED_NO_ARGUMENTS_CALL(41,44) + ElixirIdentifierImpl(IDENTIFIER)(41,44) + PsiElement(identifier)('six')(41,44) + PsiWhiteSpace('\n')(44,45) + ElixirEndOfExpressionImpl(END_OF_EXPRESSION)(45,46) + PsiElement(\\n, \\r\\n)('\n')(45,46) + ElixirUnmatchedTwoOperationImpl(UNMATCHED_TWO_OPERATION)(46,62) + UNMATCHED_UNQUALIFIED_NO_ARGUMENTS_CALL(46,51) + ElixirIdentifierImpl(IDENTIFIER)(46,51) + PsiElement(identifier)('seven')(46,51) + PsiWhiteSpace('\n\n')(51,53) + ElixirTwoInfixOperatorImpl(TWO_INFIX_OPERATOR)(53,55) + PsiElement(++, --, <>)('<>')(53,55) + PsiWhiteSpace('\n\n')(55,57) + UNMATCHED_UNQUALIFIED_NO_ARGUMENTS_CALL(57,62) + ElixirIdentifierImpl(IDENTIFIER)(57,62) + PsiElement(identifier)('eight')(57,62) diff --git a/testData/org/elixir_lang/parser_definition/eol_to_whitespace/Type.txt b/testData/org/elixir_lang/parser_definition/eol_to_whitespace/Type.txt new file mode 100644 index 000000000..a7f311032 --- /dev/null +++ b/testData/org/elixir_lang/parser_definition/eol_to_whitespace/Type.txt @@ -0,0 +1,39 @@ +Elixir File(0,55) + UNMATCHED_UNQUALIFIED_NO_PARENTHESES_CALL(0,55) + ElixirIdentifierImpl(IDENTIFIER)(0,9) + PsiElement(identifier)('defmodule')(0,9) + PsiWhiteSpace(' ')(9,10) + ElixirNoParenthesesOneArgumentImpl(NO_PARENTHESES_ONE_ARGUMENT)(10,11) + ElixirAccessExpressionImpl(ACCESS_EXPRESSION)(10,11) + ElixirAliasImpl(ALIAS)(10,11) + PsiElement(Alias)('A')(10,11) + PsiWhiteSpace(' ')(11,12) + ElixirDoBlockImpl(DO_BLOCK)(12,55) + PsiElement(do)('do')(12,14) + PsiWhiteSpace('\n ')(14,17) + ElixirStabImpl(STAB)(17,51) + ElixirStabBodyImpl(STAB_BODY)(17,51) + UNMATCHED_AT_UNQUALIFIED_NO_PARENTHESES_CALL(17,51) + ElixirAtIdentifierImpl(AT_IDENTIFIER)(17,22) + ElixirAtPrefixOperatorImpl(AT_PREFIX_OPERATOR)(17,18) + PsiElement(@)('@')(17,18) + PsiElement(identifier)('type')(18,22) + PsiWhiteSpace(' ')(22,23) + ElixirNoParenthesesOneArgumentImpl(NO_PARENTHESES_ONE_ARGUMENT)(23,51) + ElixirMatchedTypeOperationImpl(MATCHED_TYPE_OPERATION)(23,51) + MATCHED_UNQUALIFIED_NO_ARGUMENTS_CALL(23,24) + ElixirIdentifierImpl(IDENTIFIER)(23,24) + PsiElement(identifier)('t')(23,24) + PsiWhiteSpace('\n\n ')(24,34) + ElixirTypeInfixOperatorImpl(TYPE_INFIX_OPERATOR)(34,36) + PsiElement(::)('::')(34,36) + PsiWhiteSpace('\n\n ')(36,48) + ElixirAccessExpressionImpl(ACCESS_EXPRESSION)(48,51) + ElixirMapOperationImpl(MAP_OPERATION)(48,51) + ElixirMapPrefixOperatorImpl(MAP_PREFIX_OPERATOR)(48,49) + PsiElement(%)('%')(48,49) + ElixirMapArgumentsImpl(MAP_ARGUMENTS)(49,51) + PsiElement({)('{')(49,50) + PsiElement(})('}')(50,51) + PsiWhiteSpace('\n')(51,52) + PsiElement(end)('end')(52,55) diff --git a/testData/org/elixir_lang/parser_definition/eol_to_whitespace/When.txt b/testData/org/elixir_lang/parser_definition/eol_to_whitespace/When.txt new file mode 100644 index 000000000..33864aba4 --- /dev/null +++ b/testData/org/elixir_lang/parser_definition/eol_to_whitespace/When.txt @@ -0,0 +1,126 @@ +Elixir File(0,187) + UNMATCHED_UNQUALIFIED_NO_PARENTHESES_CALL(0,187) + ElixirIdentifierImpl(IDENTIFIER)(0,9) + PsiElement(identifier)('defmodule')(0,9) + PsiWhiteSpace(' ')(9,10) + ElixirNoParenthesesOneArgumentImpl(NO_PARENTHESES_ONE_ARGUMENT)(10,11) + ElixirAccessExpressionImpl(ACCESS_EXPRESSION)(10,11) + ElixirAliasImpl(ALIAS)(10,11) + PsiElement(Alias)('A')(10,11) + PsiWhiteSpace(' ')(11,12) + ElixirDoBlockImpl(DO_BLOCK)(12,187) + PsiElement(do)('do')(12,14) + PsiWhiteSpace('\n ')(14,17) + ElixirStabImpl(STAB)(17,183) + ElixirStabBodyImpl(STAB_BODY)(17,183) + UNMATCHED_AT_UNQUALIFIED_NO_PARENTHESES_CALL(17,63) + ElixirAtIdentifierImpl(AT_IDENTIFIER)(17,22) + ElixirAtPrefixOperatorImpl(AT_PREFIX_OPERATOR)(17,18) + PsiElement(@)('@')(17,18) + PsiElement(identifier)('spec')(18,22) + PsiWhiteSpace(' ')(22,23) + ElixirNoParenthesesOneArgumentImpl(NO_PARENTHESES_ONE_ARGUMENT)(23,63) + ElixirMatchedWhenOperationImpl(MATCHED_WHEN_OPERATION)(23,63) + MATCHED_UNQUALIFIED_PARENTHESES_CALL(23,27) + ElixirIdentifierImpl(IDENTIFIER)(23,24) + PsiElement(identifier)('a')(23,24) + ElixirMatchedParenthesesArgumentsImpl(MATCHED_PARENTHESES_ARGUMENTS)(24,27) + ElixirParenthesesArgumentsImpl(PARENTHESES_ARGUMENTS)(24,27) + PsiElement(()('(')(24,25) + UNMATCHED_UNQUALIFIED_NO_ARGUMENTS_CALL(25,26) + ElixirIdentifierImpl(IDENTIFIER)(25,26) + PsiElement(identifier)('b')(25,26) + PsiElement())(')')(26,27) + PsiWhiteSpace('\n\n ')(27,37) + ElixirWhenInfixOperatorImpl(WHEN_INFIX_OPERATOR)(37,41) + PsiElement(when)('when')(37,41) + PsiWhiteSpace('\n\n ')(41,53) + ElixirNoParenthesesKeywordsImpl(NO_PARENTHESES_KEYWORDS)(53,63) + ElixirNoParenthesesKeywordPairImpl(NO_PARENTHESES_KEYWORD_PAIR)(53,63) + ElixirKeywordKeyImpl(KEYWORD_KEY)(53,54) + PsiElement(A-Z, a-z, _, @, 0-9. ?, !)('b')(53,54) + PsiElement(Keyword Pair Colon (:))(':')(54,55) + PsiWhiteSpace(' ')(55,56) + MATCHED_UNQUALIFIED_NO_ARGUMENTS_CALL(56,63) + ElixirIdentifierImpl(IDENTIFIER)(56,63) + PsiElement(identifier)('integer')(56,63) + ElixirEndOfExpressionImpl(END_OF_EXPRESSION)(63,64) + PsiElement(\\n, \\r\\n)('\n')(63,64) + PsiWhiteSpace(' ')(64,66) + UNMATCHED_UNQUALIFIED_NO_PARENTHESES_CALL(66,183) + ElixirIdentifierImpl(IDENTIFIER)(66,69) + PsiElement(identifier)('def')(66,69) + PsiWhiteSpace(' ')(69,70) + ElixirNoParenthesesOneArgumentImpl(NO_PARENTHESES_ONE_ARGUMENT)(70,107) + ElixirMatchedWhenOperationImpl(MATCHED_WHEN_OPERATION)(70,107) + MATCHED_UNQUALIFIED_PARENTHESES_CALL(70,74) + ElixirIdentifierImpl(IDENTIFIER)(70,71) + PsiElement(identifier)('a')(70,71) + ElixirMatchedParenthesesArgumentsImpl(MATCHED_PARENTHESES_ARGUMENTS)(71,74) + ElixirParenthesesArgumentsImpl(PARENTHESES_ARGUMENTS)(71,74) + PsiElement(()('(')(71,72) + UNMATCHED_UNQUALIFIED_NO_ARGUMENTS_CALL(72,73) + ElixirIdentifierImpl(IDENTIFIER)(72,73) + PsiElement(identifier)('b')(72,73) + PsiElement())(')')(73,74) + PsiWhiteSpace('\n\n ')(74,82) + ElixirWhenInfixOperatorImpl(WHEN_INFIX_OPERATOR)(82,86) + PsiElement(when)('when')(82,86) + PsiWhiteSpace('\n\n ')(86,94) + MATCHED_UNQUALIFIED_PARENTHESES_CALL(94,107) + ElixirIdentifierImpl(IDENTIFIER)(94,104) + PsiElement(identifier)('is_integer')(94,104) + ElixirMatchedParenthesesArgumentsImpl(MATCHED_PARENTHESES_ARGUMENTS)(104,107) + ElixirParenthesesArgumentsImpl(PARENTHESES_ARGUMENTS)(104,107) + PsiElement(()('(')(104,105) + UNMATCHED_UNQUALIFIED_NO_ARGUMENTS_CALL(105,106) + ElixirIdentifierImpl(IDENTIFIER)(105,106) + PsiElement(identifier)('b')(105,106) + PsiElement())(')')(106,107) + PsiWhiteSpace(' ')(107,108) + ElixirDoBlockImpl(DO_BLOCK)(108,183) + PsiElement(do)('do')(108,110) + PsiWhiteSpace('\n ')(110,115) + ElixirStabImpl(STAB)(115,177) + ElixirStabBodyImpl(STAB_BODY)(115,177) + ElixirAccessExpressionImpl(ACCESS_EXPRESSION)(115,177) + ElixirAnonymousFunctionImpl(ANONYMOUS_FUNCTION)(115,177) + PsiElement(fn)('fn')(115,117) + PsiWhiteSpace('\n ')(117,124) + ElixirStabImpl(STAB)(124,169) + ElixirStabOperationImpl(STAB_OPERATION)(124,169) + ElixirStabNoParenthesesSignatureImpl(STAB_NO_PARENTHESES_SIGNATURE)(124,156) + ElixirNoParenthesesArgumentsImpl(NO_PARENTHESES_ARGUMENTS)(124,156) + ElixirNoParenthesesOneArgumentImpl(NO_PARENTHESES_ONE_ARGUMENT)(124,156) + ElixirMatchedWhenOperationImpl(MATCHED_WHEN_OPERATION)(124,156) + MATCHED_UNQUALIFIED_NO_ARGUMENTS_CALL(124,125) + ElixirIdentifierImpl(IDENTIFIER)(124,125) + PsiElement(identifier)('c')(124,125) + PsiWhiteSpace('\n\n ')(125,133) + ElixirWhenInfixOperatorImpl(WHEN_INFIX_OPERATOR)(133,137) + PsiElement(when)('when')(133,137) + PsiWhiteSpace('\n\n ')(137,147) + MATCHED_UNQUALIFIED_PARENTHESES_CALL(147,156) + ElixirIdentifierImpl(IDENTIFIER)(147,153) + PsiElement(identifier)('is_pid')(147,153) + ElixirMatchedParenthesesArgumentsImpl(MATCHED_PARENTHESES_ARGUMENTS)(153,156) + ElixirParenthesesArgumentsImpl(PARENTHESES_ARGUMENTS)(153,156) + PsiElement(()('(')(153,154) + UNMATCHED_UNQUALIFIED_NO_ARGUMENTS_CALL(154,155) + ElixirIdentifierImpl(IDENTIFIER)(154,155) + PsiElement(identifier)('c')(154,155) + PsiElement())(')')(155,156) + PsiWhiteSpace(' ')(156,157) + ElixirStabInfixOperatorImpl(STAB_INFIX_OPERATOR)(157,159) + PsiElement(->)('->')(157,159) + PsiWhiteSpace('\n ')(159,168) + ElixirStabBodyImpl(STAB_BODY)(168,169) + UNMATCHED_UNQUALIFIED_NO_ARGUMENTS_CALL(168,169) + ElixirIdentifierImpl(IDENTIFIER)(168,169) + PsiElement(identifier)('c')(168,169) + PsiWhiteSpace('\n ')(169,174) + PsiElement(end)('end')(174,177) + PsiWhiteSpace('\n ')(177,180) + PsiElement(end)('end')(180,183) + PsiWhiteSpace('\n')(183,184) + PsiElement(end)('end')(184,187) diff --git a/testData/org/elixir_lang/reference/callable/unquote_in_anonymous_fun_stab_body/unquote_in_anonymous_fun_stab_body.ex b/testData/org/elixir_lang/reference/callable/unquote_in_anonymous_fun_stab_body/unquote_in_anonymous_fun_stab_body.ex new file mode 100644 index 000000000..0d84c4912 --- /dev/null +++ b/testData/org/elixir_lang/reference/callable/unquote_in_anonymous_fun_stab_body/unquote_in_anonymous_fun_stab_body.ex @@ -0,0 +1,13 @@ +defmodule AnonymousFunStabBody do + @moduledoc false + + @callback docs_uri() :: binary() + defmacro __using__(opts) do + fn -> + quote do + unquote(docs_uri) + unquote(other_var) + end + end + end +end diff --git a/testData/org/elixir_lang/reference/callable/unquote_in_bracket_operation/unquote_in_bracket_operation.ex b/testData/org/elixir_lang/reference/callable/unquote_in_bracket_operation/unquote_in_bracket_operation.ex new file mode 100644 index 000000000..c0e7e2921 --- /dev/null +++ b/testData/org/elixir_lang/reference/callable/unquote_in_bracket_operation/unquote_in_bracket_operation.ex @@ -0,0 +1,12 @@ +defmodule BracketOp do + @moduledoc false + + @callback docs_uri() :: binary() + defmacro __using__(opts) do + quote do + # Unquote with bracket operation in one of the unquoted values + unquote(docs_uri) + unquote(opts[:other_key]) + end + end +end diff --git a/testData/org/elixir_lang/reference/callable/unquote_in_case_stab_body/unquote_in_case_stab_body.ex b/testData/org/elixir_lang/reference/callable/unquote_in_case_stab_body/unquote_in_case_stab_body.ex new file mode 100644 index 000000000..6b4370861 --- /dev/null +++ b/testData/org/elixir_lang/reference/callable/unquote_in_case_stab_body/unquote_in_case_stab_body.ex @@ -0,0 +1,14 @@ +defmodule CaseStabBody do + @moduledoc false + + @callback docs_uri() :: binary() + defmacro __using__(opts) do + case :ok do + :ok -> + quote do + unquote(docs_uri) + unquote(other_var) + end + end + end +end diff --git a/testData/org/elixir_lang/reference/callable/unquote_in_stab_body/unquote_in_stab_body.ex b/testData/org/elixir_lang/reference/callable/unquote_in_stab_body/unquote_in_stab_body.ex new file mode 100644 index 000000000..5a2802dee --- /dev/null +++ b/testData/org/elixir_lang/reference/callable/unquote_in_stab_body/unquote_in_stab_body.ex @@ -0,0 +1,16 @@ +defmodule StabBody do + @moduledoc false + + @callback docs_uri() :: binary() + defmacro __using__(opts) do + docs_uri = + quote do + unquote(opts[:docs_uri]) + end + + quote do + @moduledoc unquote(module_doc) + unquote(docs_uri) + end + end +end diff --git a/tests/mockito-core-2.0.7-beta-javadoc.jar b/tests/mockito-core-2.0.7-beta-javadoc.jar deleted file mode 100644 index eb4a8c390..000000000 Binary files a/tests/mockito-core-2.0.7-beta-javadoc.jar and /dev/null differ diff --git a/tests/mockito-core-2.0.7-beta-sources.jar b/tests/mockito-core-2.0.7-beta-sources.jar deleted file mode 100644 index 467648818..000000000 Binary files a/tests/mockito-core-2.0.7-beta-sources.jar and /dev/null differ diff --git a/tests/mockito-core-2.0.7-beta.jar b/tests/mockito-core-2.0.7-beta.jar deleted file mode 100644 index 053e72482..000000000 Binary files a/tests/mockito-core-2.0.7-beta.jar and /dev/null differ diff --git a/tests/objenesis-2.1-javadoc.jar b/tests/objenesis-2.1-javadoc.jar deleted file mode 100644 index 25e98439d..000000000 Binary files a/tests/objenesis-2.1-javadoc.jar and /dev/null differ diff --git a/tests/objenesis-2.1-sources.jar b/tests/objenesis-2.1-sources.jar deleted file mode 100644 index 4a41981e9..000000000 Binary files a/tests/objenesis-2.1-sources.jar and /dev/null differ diff --git a/tests/objenesis-2.1.jar b/tests/objenesis-2.1.jar deleted file mode 100644 index 92ab8ea74..000000000 Binary files a/tests/objenesis-2.1.jar and /dev/null differ diff --git a/tests/org/elixir_lang/FindUsagesTest.kt b/tests/org/elixir_lang/FindUsagesTest.kt index cd05fd3fb..3da6d30e4 100644 --- a/tests/org/elixir_lang/FindUsagesTest.kt +++ b/tests/org/elixir_lang/FindUsagesTest.kt @@ -12,8 +12,8 @@ class FindUsagesTest : PlatformTestCase() { } fun testFunctionRecursiveDeclaration() { - val usages = myFixture.testFindUsagesUsingAction("function_recursive_declaration.ex", "kernel.ex") - .map { it as UsageInfo2UsageAdapter } + val usages = myFixture.testFindUsages("function_recursive_declaration.ex", "kernel.ex") + .map { UsageInfo2UsageAdapter(it) } assertEquals(3, usages.size) assertContainsElements(usages.map { it.element!!.textOffset }, listOf(63, 93)) @@ -40,8 +40,8 @@ class FindUsagesTest : PlatformTestCase() { } fun testFunctionRecursiveUsage() { - val usages = myFixture.testFindUsagesUsingAction("function_recursive_usage.ex", "kernel.ex") - .map { it as UsageInfo2UsageAdapter } + val usages = myFixture.testFindUsages("function_recursive_usage.ex", "kernel.ex") + .map { UsageInfo2UsageAdapter(it) } assertEquals(3, usages.size) assertContainsElements(usages.map { it.element!!.textOffset }, listOf(63, 93)) @@ -68,8 +68,8 @@ class FindUsagesTest : PlatformTestCase() { } fun testFunctionSingleClauseUnused() { - val usages = myFixture.testFindUsagesUsingAction("function_single_clause_unused.ex", "kernel.ex") - .map { it as UsageInfo2UsageAdapter } + val usages = myFixture.testFindUsages("function_single_clause_unused.ex", "kernel.ex") + .map { UsageInfo2UsageAdapter(it) } assertEquals(1, usages.size) assertContainsElements(usages.map { it.element!!.textOffset }, listOf(26)) @@ -90,8 +90,8 @@ class FindUsagesTest : PlatformTestCase() { } fun testFunctionMultipleClausesUnused() { - val usages = myFixture.testFindUsagesUsingAction("function_multiple_clauses_unused.ex", "kernel.ex") - .map { it as UsageInfo2UsageAdapter } + val usages = myFixture.testFindUsages("function_multiple_clauses_unused.ex", "kernel.ex") + .map { UsageInfo2UsageAdapter(it) } assertEquals(2, usages.size) assertContainsElements(usages.map { it.element!!.textOffset }, listOf(26)) @@ -113,11 +113,11 @@ class FindUsagesTest : PlatformTestCase() { } fun testFunctionMultipleModulesDeclaration() { - val usages = myFixture.testFindUsagesUsingAction( + val usages = myFixture.testFindUsages( "function_multiple_modules_declaration_target.ex", "function_multiple_modules_declaration_usage.ex", "kernel.ex" - ).map { it as UsageInfo2UsageAdapter } + ).map { UsageInfo2UsageAdapter(it) } assertEquals(2, usages.size) assertContainsElements(usages.map { it.element!!.textOffset }, listOf(31, 50)) @@ -143,11 +143,11 @@ class FindUsagesTest : PlatformTestCase() { } fun testFunctionMultipleModulesUsage() { - val usages = myFixture.testFindUsagesUsingAction( + val usages = myFixture.testFindUsages( "function_multiple_modules_usage_target.ex", "function_multiple_modules_usage_declaration.ex", "kernel.ex" - ).map { it as UsageInfo2UsageAdapter } + ).map { UsageInfo2UsageAdapter(it) } assertEquals(2, usages.size) assertContainsElements(usages.map { it.element!!.textOffset }, listOf(31, 50)) @@ -173,11 +173,11 @@ class FindUsagesTest : PlatformTestCase() { } fun testFunctionImportDeclaration() { - val usages = myFixture.testFindUsagesUsingAction( + val usages = myFixture.testFindUsages( "function_import_declaration_target.ex", "function_import_declaration_usage.ex", "kernel.ex" - ).map { it as UsageInfo2UsageAdapter } + ).map { UsageInfo2UsageAdapter(it) } assertEquals(2, usages.size) assertContainsElements(usages.map { it.element!!.textOffset }, listOf(31, 60)) @@ -218,11 +218,11 @@ class FindUsagesTest : PlatformTestCase() { myFixture.findUsages(resolved[0].element!!) } else { - myFixture.testFindUsagesUsingAction( + myFixture.testFindUsages( "function_import_usage_target.ex", "function_import_usage_declaration.ex", "kernel.ex" - ).map { it.let { it as UsageInfo2UsageAdapter }.usageInfo } + ) } assertEquals(2, usageInfos.size) @@ -249,8 +249,8 @@ class FindUsagesTest : PlatformTestCase() { } fun testParameterDeclaration() { - val usages = myFixture.testFindUsagesUsingAction("parameter_declaration.ex", "kernel.ex") - .map { it as UsageInfo2UsageAdapter } + val usages = myFixture.testFindUsages("parameter_declaration.ex", "kernel.ex") + .map { UsageInfo2UsageAdapter(it) } assertEquals(2, usages.size) assertContainsElements(usages.map { it.element!!.textOffset }, listOf(39, 70)) @@ -276,8 +276,8 @@ class FindUsagesTest : PlatformTestCase() { } fun testModuleRecursiveDeclaration() { - val usages = myFixture.testFindUsagesUsingAction("module_recursive_declaration.ex", "kernel.ex") - .map { it as UsageInfo2UsageAdapter } + val usages = myFixture.testFindUsages("module_recursive_declaration.ex", "kernel.ex") + .map { UsageInfo2UsageAdapter(it) } assertEquals(2, usages.size) assertContainsElements(usages.map { it.element!!.textOffset }, listOf(10, 33)) @@ -303,10 +303,8 @@ class FindUsagesTest : PlatformTestCase() { } fun testModuleRecursiveUsage() { - val fileNames = arrayOf("module_recursive_usage.ex", "kernel.ex") - val usageInfos = if (AlreadyResolved.alreadyResolved) { - myFixture.configureByFiles(*fileNames) + myFixture.configureByFiles("module_recursive_usage.ex", "kernel.ex") val reference = myFixture.getReferenceAtCaretPositionWithAssertion() as PsiPolyVariantReference assertNotNull(reference) @@ -316,7 +314,7 @@ class FindUsagesTest : PlatformTestCase() { myFixture.findUsages(resolved[0].element!!) } else { - myFixture.testFindUsagesUsingAction(*fileNames).map { it.let { it as UsageInfo2UsageAdapter }.usageInfo } + myFixture.testFindUsages("module_recursive_usage.ex", "kernel.ex") } assertEquals(2, usageInfos.size) @@ -343,8 +341,8 @@ class FindUsagesTest : PlatformTestCase() { } fun testModuleNestedRecursiveDeclaration() { - val usages = myFixture.testFindUsagesUsingAction("module_nested_recursive_declaration.ex", "kernel.ex") - .map { it as UsageInfo2UsageAdapter } + val usages = myFixture.testFindUsages("module_nested_recursive_declaration.ex", "kernel.ex") + .map { UsageInfo2UsageAdapter(it) } assertEquals(3, usages.size) assertContainsElements(usages.map { it.element!!.textOffset }, listOf(10, 40, 75)) @@ -371,11 +369,11 @@ class FindUsagesTest : PlatformTestCase() { } fun testModuleMultipleModulesDeclaration() { - val usages = myFixture.testFindUsagesUsingAction( + val usages = myFixture.testFindUsages( "module_multiple_modules_declaration_target.ex", "module_multiple_modules_declaration_usage.ex", "kernel.ex" - ).map { it as UsageInfo2UsageAdapter } + ).map { UsageInfo2UsageAdapter(it) } assertEquals(2, usages.size) assertContainsElements(usages.map { it.element!!.textOffset }, listOf(10, 27)) @@ -418,7 +416,7 @@ class FindUsagesTest : PlatformTestCase() { myFixture.findUsages(resolved[0].element!!) } else { - myFixture.testFindUsagesUsingAction(*fileNames).map { it.let { it as UsageInfo2UsageAdapter }.usageInfo } + myFixture.testFindUsages(*fileNames) } assertEquals(2, usageInfos.size) @@ -446,7 +444,7 @@ class FindUsagesTest : PlatformTestCase() { fun testParameterUnused() { val usages = - myFixture.testFindUsagesUsingAction("parameter_unused.ex", "kernel.ex").map { it as UsageInfo2UsageAdapter } + myFixture.testFindUsages("parameter_unused.ex", "kernel.ex").map { UsageInfo2UsageAdapter(it) } assertEquals(1, usages.size) assertContainsElements(usages.map { it.element!!.textOffset }, listOf(39)) @@ -468,7 +466,7 @@ class FindUsagesTest : PlatformTestCase() { fun testParameterUsage() { val usages = - myFixture.testFindUsagesUsingAction("parameter_usage.ex", "kernel.ex").map { it as UsageInfo2UsageAdapter } + myFixture.testFindUsages("parameter_usage.ex", "kernel.ex").map { UsageInfo2UsageAdapter(it) } assertEquals(2, usages.size) assertContainsElements(usages.map { it.element!!.textOffset }, listOf(39)) @@ -494,8 +492,8 @@ class FindUsagesTest : PlatformTestCase() { } fun testVariableDeclaration() { - val usages = myFixture.testFindUsagesUsingAction("variable_declaration.ex", "kernel.ex") - .map { it as UsageInfo2UsageAdapter } + val usages = myFixture.testFindUsages("variable_declaration.ex", "kernel.ex") + .map { UsageInfo2UsageAdapter(it) } assertEquals(2, usages.size) assertContainsElements(usages.map { it.element!!.textOffset }, listOf(46, 99)) @@ -522,7 +520,7 @@ class FindUsagesTest : PlatformTestCase() { fun testVariableUnused() { val usages = - myFixture.testFindUsagesUsingAction("variable_unused.ex", "kernel.ex").map { it as UsageInfo2UsageAdapter } + myFixture.testFindUsages("variable_unused.ex", "kernel.ex").map { UsageInfo2UsageAdapter(it) } assertEquals(1, usages.size) @@ -545,7 +543,7 @@ class FindUsagesTest : PlatformTestCase() { fun testVariableUsage() { val usages = - myFixture.testFindUsagesUsingAction("variable_usage.ex", "kernel.ex").map { it as UsageInfo2UsageAdapter } + myFixture.testFindUsages("variable_usage.ex", "kernel.ex").map { UsageInfo2UsageAdapter(it) } assertEquals(2, usages.size) assertContainsElements(usages.map { it.element!!.textOffset }, listOf(46)) @@ -571,8 +569,8 @@ class FindUsagesTest : PlatformTestCase() { } fun testModuleAttributeDeclaration() { - val usages = myFixture.testFindUsagesUsingAction("module_attribute_declaration.ex", "kernel.ex") - .map { it as UsageInfo2UsageAdapter } + val usages = myFixture.testFindUsages("module_attribute_declaration.ex", "kernel.ex") + .map { UsageInfo2UsageAdapter(it) } assertEquals(2, usages.size) assertContainsElements(usages.map { it.element!!.textOffset }, listOf(31, 69)) @@ -598,8 +596,8 @@ class FindUsagesTest : PlatformTestCase() { } fun testModuleAttributeUsage() { - val usages = myFixture.testFindUsagesUsingAction("module_attribute_usage.ex", "kernel.ex") - .map { it as UsageInfo2UsageAdapter } + val usages = myFixture.testFindUsages("module_attribute_usage.ex", "kernel.ex") + .map { UsageInfo2UsageAdapter(it) } assertEquals(2, usages.size) assertContainsElements(usages.map { it.element!!.textOffset }, listOf(31, 69)) @@ -626,7 +624,7 @@ class FindUsagesTest : PlatformTestCase() { fun testIssue2374() { val usages = - myFixture.testFindUsagesUsingAction("issue_2374.ex", "kernel.ex").map { it as UsageInfo2UsageAdapter } + myFixture.testFindUsages("issue_2374.ex", "kernel.ex").map { UsageInfo2UsageAdapter(it) } assertEquals(3, usages.size) diff --git a/tests/org/elixir_lang/PlatformTestCase.kt b/tests/org/elixir_lang/PlatformTestCase.kt index a1a67ef58..8711b2cbf 100644 --- a/tests/org/elixir_lang/PlatformTestCase.kt +++ b/tests/org/elixir_lang/PlatformTestCase.kt @@ -1,16 +1,21 @@ package org.elixir_lang import com.intellij.openapi.util.registry.Registry +import com.intellij.openapi.vfs.newvfs.impl.VfsRootAccess import com.intellij.testFramework.fixtures.BasePlatformTestCase import org.elixir_lang.injection.markdown.Injector +import java.nio.file.Path -@org.junit.Ignore("abstract") -open class PlatformTestCase : BasePlatformTestCase() { +abstract class PlatformTestCase : BasePlatformTestCase() { @Throws(Exception::class) override fun setUp() { super.setUp() + // Allow access to testData directory in tests + val testDataPath = Path.of(myFixture.testDataPath).toAbsolutePath().toString() + VfsRootAccess.allowRootAccess(myFixture.testRootDisposable, testDataPath) + // Enable literal sigil injection for tests to maintain backward compatibility Registry.get(Injector.REG_KEY_ENABLE_LITERAL_SIGIL_INJECTION).setValue(true) } @@ -20,8 +25,10 @@ open class PlatformTestCase : BasePlatformTestCase() { try { // Reset registry to default Registry.get(Injector.REG_KEY_ENABLE_LITERAL_SIGIL_INJECTION).resetToDefault() - } catch (e: Exception) { + } catch (_: Exception) { + } finally { + super.tearDown() } } diff --git a/tests/org/elixir_lang/beam/DecompilerTest.java b/tests/org/elixir_lang/beam/DecompilerTest.java index b15804b1c..8c6b94ff2 100644 --- a/tests/org/elixir_lang/beam/DecompilerTest.java +++ b/tests/org/elixir_lang/beam/DecompilerTest.java @@ -9,8 +9,7 @@ import java.io.File; import java.io.IOException; - -import static java.nio.charset.StandardCharsets.UTF_8; +import java.nio.charset.Charset; public class DecompilerTest extends PlatformTestCase { /* @@ -514,7 +513,7 @@ private void assertDecompiled(String name) throws IOException { String prefix = testDataPath + "/" + name + "."; File expectedFile = new File(prefix + "ex"); - String expected = Files.toString(expectedFile, UTF_8); + String expected = Files.toString(expectedFile, Charset.forName("UTF-8")); VfsRootAccess.allowRootAccess(getTestRootDisposable(), testDataPath); diff --git a/tests/org/elixir_lang/eex/lexer/look_ahead/Test.java b/tests/org/elixir_lang/eex/lexer/look_ahead/Test.java index 8a7942e62..4d45ae785 100644 --- a/tests/org/elixir_lang/eex/lexer/look_ahead/Test.java +++ b/tests/org/elixir_lang/eex/lexer/look_ahead/Test.java @@ -2,19 +2,15 @@ import com.intellij.psi.tree.IElementType; import org.elixir_lang.eex.lexer.LookAhead; -import org.elixir_lang.eex.lexer.Flex; -import org.elixir_lang.eex.psi.TokenType; import org.jetbrains.annotations.NotNull; -import org.junit.Ignore; import java.util.Arrays; import java.util.stream.Collectors; import static org.junit.Assert.assertEquals; -@Ignore("abstract") -public class Test extends org.elixir_lang.flex_lexer.Test { - public static class Lex { +public abstract class Test extends org.elixir_lang.flex_lexer.Test { + static class Lex { @NotNull final CharSequence text; @NotNull @@ -37,8 +33,7 @@ public String toString() { return "\"" + text + "\" parses as " + tokenType + " and advances to state " + state; } } - - public static class Sequence { + static class Sequence { @NotNull final Lex[] lexes; @NotNull @@ -64,13 +59,13 @@ public String toString() { */ @NotNull - public final Sequence sequence; + final Sequence sequence; /* * Constructors */ - public Test(@NotNull Sequence sequence) { + Test(@NotNull Sequence sequence) { this.sequence = sequence; } diff --git a/tests/org/elixir_lang/elixir_flex_lexer/SigilTest.java b/tests/org/elixir_lang/elixir_flex_lexer/SigilTest.java index e3db81211..2a4520095 100644 --- a/tests/org/elixir_lang/elixir_flex_lexer/SigilTest.java +++ b/tests/org/elixir_lang/elixir_flex_lexer/SigilTest.java @@ -4,10 +4,10 @@ import com.intellij.psi.tree.IElementType; import org.elixir_lang.ElixirFlexLexer; import org.elixir_lang.psi.ElixirTypes; +import org.jetbrains.annotations.NotNull; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; -import java.io.IOException; import java.util.Arrays; import java.util.Collection; @@ -29,7 +29,7 @@ public SigilTest(CharSequence charSequence, IElementType tokenType, int lexicalS */ @Override - protected void start(CharSequence charSequence) { + protected void start(@NotNull CharSequence charSequence) { // start to trigger SIGIL state CharSequence fullCharSequence = "~" + charSequence; super.start(fullCharSequence); diff --git a/tests/org/elixir_lang/elixir_flex_lexer/Test.java b/tests/org/elixir_lang/elixir_flex_lexer/Test.java index 9759baf9f..3a320ee16 100644 --- a/tests/org/elixir_lang/elixir_flex_lexer/Test.java +++ b/tests/org/elixir_lang/elixir_flex_lexer/Test.java @@ -1,9 +1,7 @@ package org.elixir_lang.elixir_flex_lexer; import org.elixir_lang.ElixirLexer; -import org.junit.Ignore; -@Ignore("abstract") public abstract class Test extends org.elixir_lang.flex_lexer.Test { /* * Methods diff --git a/tests/org/elixir_lang/elixir_flex_lexer/TokenTest.java b/tests/org/elixir_lang/elixir_flex_lexer/TokenTest.java index ccd1f47d2..5f3179a15 100644 --- a/tests/org/elixir_lang/elixir_flex_lexer/TokenTest.java +++ b/tests/org/elixir_lang/elixir_flex_lexer/TokenTest.java @@ -1,17 +1,14 @@ package org.elixir_lang.elixir_flex_lexer; import com.intellij.psi.tree.IElementType; -import org.junit.Ignore; import java.io.IOException; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; /** * Created by luke.imhoff on 9/8/14. */ -@Ignore("abstract") public abstract class TokenTest extends Test { /* * Constants @@ -33,11 +30,11 @@ public abstract class TokenTest extends Test { public TokenTest() { } - public TokenTest(CharSequence charSequence, IElementType tokenType, int lexicalState) { + protected TokenTest(CharSequence charSequence, IElementType tokenType, int lexicalState) { this(charSequence, tokenType, lexicalState, true); } - public TokenTest(CharSequence charSequence, IElementType tokenType, int lexicalState, boolean consumeAll) { + protected TokenTest(CharSequence charSequence, IElementType tokenType, int lexicalState, boolean consumeAll) { this.charSequence = charSequence; this.lexicalState = lexicalState; this.tokenType = tokenType; @@ -57,9 +54,9 @@ public void token() throws IOException { lexer.advance(); if (consumeAll) { - assertTrue("Failure: expected all of \"" + charSequence + "\" to be consumed", lexer.getTokenType() == null); + assertNull("Failure: expected all of \"" + charSequence + "\" to be consumed", lexer.getTokenType()); } else { - assertTrue("Failure: expected all of \"" + charSequence + "\" not to be consumed", lexer.getTokenType() != null); + assertNotNull("Failure: expected all of \"" + charSequence + "\" not to be consumed", lexer.getTokenType()); } assertEquals(lexicalState, lexer.getState()); diff --git a/tests/org/elixir_lang/elixir_flex_lexer/group/Test.java b/tests/org/elixir_lang/elixir_flex_lexer/group/Test.java index 196bc03d8..7906f2813 100644 --- a/tests/org/elixir_lang/elixir_flex_lexer/group/Test.java +++ b/tests/org/elixir_lang/elixir_flex_lexer/group/Test.java @@ -3,12 +3,10 @@ import com.intellij.psi.tree.IElementType; import org.elixir_lang.elixir_flex_lexer.TokenTest; import org.jetbrains.annotations.NotNull; -import org.junit.Ignore; /** * Created by luke.imhoff on 9/6/14. */ -@Ignore("abstract") public abstract class Test extends TokenTest { /* * Constructors diff --git a/tests/org/elixir_lang/elixir_flex_lexer/group/quote/Test.java b/tests/org/elixir_lang/elixir_flex_lexer/group/quote/Test.java index 74e756179..875edca7d 100644 --- a/tests/org/elixir_lang/elixir_flex_lexer/group/quote/Test.java +++ b/tests/org/elixir_lang/elixir_flex_lexer/group/quote/Test.java @@ -2,7 +2,6 @@ import com.intellij.psi.tree.IElementType; import org.elixir_lang.ElixirFlexLexer; -import org.junit.Ignore; import java.util.Arrays; import java.util.Collection; @@ -11,7 +10,6 @@ /** * Created by luke.imhoff on 9/6/14. */ -@Ignore("abstract") public abstract class Test extends org.elixir_lang.elixir_flex_lexer.group.Test { /* * Constants @@ -47,7 +45,7 @@ public static Collection generateData(IElementType fragmentType, Colle } ); - Collection combinedData = new Vector(); + Collection combinedData = new Vector<>(); combinedData.addAll(commonData); combinedData.addAll(quoteData); diff --git a/tests/org/elixir_lang/elixir_flex_lexer/group/sigil/Test.java b/tests/org/elixir_lang/elixir_flex_lexer/group/sigil/Test.java index e2866d0ff..d17faec17 100644 --- a/tests/org/elixir_lang/elixir_flex_lexer/group/sigil/Test.java +++ b/tests/org/elixir_lang/elixir_flex_lexer/group/sigil/Test.java @@ -4,9 +4,7 @@ import org.elixir_lang.ElixirFlexLexer; import org.elixir_lang.psi.ElixirTypes; import org.jetbrains.annotations.NotNull; -import org.junit.Ignore; -import java.io.IOException; import java.util.Arrays; import java.util.Collection; import java.util.Vector; @@ -14,7 +12,6 @@ /** * Created by luke.imhoff on 9/6/14. */ -@Ignore("abstract") public abstract class Test extends org.elixir_lang.elixir_flex_lexer.group.Test { /* * CONSTANTS @@ -56,7 +53,7 @@ protected void start(@NotNull CharSequence charSequence) { protected abstract char promoter(); public static Collection generateData(Collection terminatorData) { - Collection combinedData = new Vector(); + Collection combinedData = new Vector<>(); combinedData.addAll(QUOTE_DATA); combinedData.addAll(terminatorData); diff --git a/tests/org/elixir_lang/elixir_flex_lexer/group_heredoc_end/quote/Test.java b/tests/org/elixir_lang/elixir_flex_lexer/group_heredoc_end/quote/Test.java index d33c40598..25198eefe 100644 --- a/tests/org/elixir_lang/elixir_flex_lexer/group_heredoc_end/quote/Test.java +++ b/tests/org/elixir_lang/elixir_flex_lexer/group_heredoc_end/quote/Test.java @@ -3,14 +3,10 @@ import com.intellij.psi.tree.IElementType; import org.elixir_lang.elixir_flex_lexer.TokenTest; import org.jetbrains.annotations.NotNull; -import org.junit.Ignore; - -import java.io.IOException; /** * Created by luke.imhoff on 9/12/14. */ -@Ignore("abstract") public abstract class Test extends TokenTest { /* * Constructors diff --git a/tests/org/elixir_lang/elixir_flex_lexer/group_heredoc_end/sigil/Test.java b/tests/org/elixir_lang/elixir_flex_lexer/group_heredoc_end/sigil/Test.java index b616a9347..637bfd5c2 100644 --- a/tests/org/elixir_lang/elixir_flex_lexer/group_heredoc_end/sigil/Test.java +++ b/tests/org/elixir_lang/elixir_flex_lexer/group_heredoc_end/sigil/Test.java @@ -2,14 +2,11 @@ import com.intellij.psi.tree.IElementType; import org.elixir_lang.elixir_flex_lexer.TokenTest; -import org.junit.Ignore; - -import java.io.IOException; +import org.jetbrains.annotations.NotNull; /** * Created by luke.imhoff on 9/12/14. */ -@Ignore("abstract") public abstract class Test extends TokenTest { /* * Constructors @@ -24,7 +21,7 @@ public Test(CharSequence charSequence, IElementType tokenType, int lexicalState, */ @Override - protected void start(CharSequence charSequence) { + protected void start(@NotNull CharSequence charSequence) { CharSequence fullCharSequence = sigilName() + promoter() + '\n' + charSequence; super.start(fullCharSequence); // consume sigilName diff --git a/tests/org/elixir_lang/elixir_flex_lexer/group_heredoc_end/sigil/char_list/Test.java b/tests/org/elixir_lang/elixir_flex_lexer/group_heredoc_end/sigil/char_list/Test.java index 58740ac8e..7dc9873bc 100644 --- a/tests/org/elixir_lang/elixir_flex_lexer/group_heredoc_end/sigil/char_list/Test.java +++ b/tests/org/elixir_lang/elixir_flex_lexer/group_heredoc_end/sigil/char_list/Test.java @@ -1,12 +1,9 @@ package org.elixir_lang.elixir_flex_lexer.group_heredoc_end.sigil.char_list; import com.intellij.psi.tree.IElementType; -import org.junit.Ignore; - /** * Created by luke.imhoff on 9/12/14. */ -@Ignore("abstract") public abstract class Test extends org.elixir_lang.elixir_flex_lexer.group_heredoc_end.sigil.Test { /* * Constructors diff --git a/tests/org/elixir_lang/elixir_flex_lexer/group_heredoc_end/sigil/custom/Test.java b/tests/org/elixir_lang/elixir_flex_lexer/group_heredoc_end/sigil/custom/Test.java index 67dadf250..47a4604ed 100644 --- a/tests/org/elixir_lang/elixir_flex_lexer/group_heredoc_end/sigil/custom/Test.java +++ b/tests/org/elixir_lang/elixir_flex_lexer/group_heredoc_end/sigil/custom/Test.java @@ -1,12 +1,10 @@ package org.elixir_lang.elixir_flex_lexer.group_heredoc_end.sigil.custom; import com.intellij.psi.tree.IElementType; -import org.junit.Ignore; /** * Created by luke.imhoff on 9/12/14. */ -@Ignore("abstract") public abstract class Test extends org.elixir_lang.elixir_flex_lexer.group_heredoc_end.sigil.Test { /* * Constructors diff --git a/tests/org/elixir_lang/elixir_flex_lexer/group_heredoc_end/sigil/regex/Test.java b/tests/org/elixir_lang/elixir_flex_lexer/group_heredoc_end/sigil/regex/Test.java index 3e4a9a373..10091a575 100644 --- a/tests/org/elixir_lang/elixir_flex_lexer/group_heredoc_end/sigil/regex/Test.java +++ b/tests/org/elixir_lang/elixir_flex_lexer/group_heredoc_end/sigil/regex/Test.java @@ -1,12 +1,10 @@ package org.elixir_lang.elixir_flex_lexer.group_heredoc_end.sigil.regex; import com.intellij.psi.tree.IElementType; -import org.junit.Ignore; /** * Created by luke.imhoff on 9/12/14. */ -@Ignore("abstract") public abstract class Test extends org.elixir_lang.elixir_flex_lexer.group_heredoc_end.sigil.Test { /* * Constructors diff --git a/tests/org/elixir_lang/elixir_flex_lexer/group_heredoc_end/sigil/string/Test.java b/tests/org/elixir_lang/elixir_flex_lexer/group_heredoc_end/sigil/string/Test.java index 7f9fa7d90..d4460e3f4 100644 --- a/tests/org/elixir_lang/elixir_flex_lexer/group_heredoc_end/sigil/string/Test.java +++ b/tests/org/elixir_lang/elixir_flex_lexer/group_heredoc_end/sigil/string/Test.java @@ -1,12 +1,10 @@ package org.elixir_lang.elixir_flex_lexer.group_heredoc_end.sigil.string; import com.intellij.psi.tree.IElementType; -import org.junit.Ignore; /** * Created by luke.imhoff on 9/12/14. */ -@Ignore("abstract") public abstract class Test extends org.elixir_lang.elixir_flex_lexer.group_heredoc_end.sigil.Test { /* * Constructors diff --git a/tests/org/elixir_lang/elixir_flex_lexer/group_heredoc_end/sigil/words/Test.java b/tests/org/elixir_lang/elixir_flex_lexer/group_heredoc_end/sigil/words/Test.java index 0a6c80679..90157b67e 100644 --- a/tests/org/elixir_lang/elixir_flex_lexer/group_heredoc_end/sigil/words/Test.java +++ b/tests/org/elixir_lang/elixir_flex_lexer/group_heredoc_end/sigil/words/Test.java @@ -1,12 +1,10 @@ package org.elixir_lang.elixir_flex_lexer.group_heredoc_end.sigil.words; import com.intellij.psi.tree.IElementType; -import org.junit.Ignore; /** * Created by luke.imhoff on 9/12/14. */ -@Ignore("abstract") public abstract class Test extends org.elixir_lang.elixir_flex_lexer.group_heredoc_end.sigil.Test { /* * Constructors diff --git a/tests/org/elixir_lang/elixir_flex_lexer/group_heredoc_line_body/quote/PromoterTest.java b/tests/org/elixir_lang/elixir_flex_lexer/group_heredoc_line_body/quote/PromoterTest.java index 25269ab2c..f7d4696bf 100644 --- a/tests/org/elixir_lang/elixir_flex_lexer/group_heredoc_line_body/quote/PromoterTest.java +++ b/tests/org/elixir_lang/elixir_flex_lexer/group_heredoc_line_body/quote/PromoterTest.java @@ -5,16 +5,13 @@ import org.elixir_lang.elixir_flex_lexer.TokenTest; import org.elixir_lang.psi.ElixirTypes; import org.jetbrains.annotations.NotNull; -import org.junit.Ignore; -import java.io.IOException; import java.util.Arrays; import java.util.Collection; /** * Created by luke.imhoff on 9/6/14. */ -@Ignore("abstract") public abstract class PromoterTest extends TokenTest { /* * Constructors diff --git a/tests/org/elixir_lang/elixir_flex_lexer/group_heredoc_line_body/sigil/PromoterTest.java b/tests/org/elixir_lang/elixir_flex_lexer/group_heredoc_line_body/sigil/PromoterTest.java index b2d195304..9b2f6191b 100644 --- a/tests/org/elixir_lang/elixir_flex_lexer/group_heredoc_line_body/sigil/PromoterTest.java +++ b/tests/org/elixir_lang/elixir_flex_lexer/group_heredoc_line_body/sigil/PromoterTest.java @@ -4,16 +4,14 @@ import org.elixir_lang.ElixirFlexLexer; import org.elixir_lang.elixir_flex_lexer.TokenTest; import org.elixir_lang.psi.ElixirTypes; -import org.junit.Ignore; +import org.jetbrains.annotations.NotNull; -import java.io.IOException; import java.util.Arrays; import java.util.Collection; /** * Created by luke.imhoff on 9/6/14. */ -@Ignore("abstract") public abstract class PromoterTest extends TokenTest { /* * Constructors @@ -39,7 +37,7 @@ public static Collection generateData(IElementType fragmentType) { } @Override - protected void start(CharSequence charSequence) { + protected void start(@NotNull CharSequence charSequence) { // start to trigger GROUP state CharSequence fullCharSequence = "~" + sigilName() + "'''\n" + charSequence; super.start(fullCharSequence); diff --git a/tests/org/elixir_lang/elixir_flex_lexer/named_sigil/Test.java b/tests/org/elixir_lang/elixir_flex_lexer/named_sigil/Test.java index 5a610b1f3..03235b49c 100644 --- a/tests/org/elixir_lang/elixir_flex_lexer/named_sigil/Test.java +++ b/tests/org/elixir_lang/elixir_flex_lexer/named_sigil/Test.java @@ -4,7 +4,6 @@ import org.elixir_lang.ElixirFlexLexer; import org.elixir_lang.elixir_flex_lexer.TokenTest; import org.jetbrains.annotations.NotNull; -import org.junit.Ignore; import java.util.Arrays; import java.util.Collection; @@ -15,7 +14,6 @@ /** * Created by luke.imhoff on 9/4/14. */ -@Ignore("abstract") public abstract class Test extends TokenTest { /* * Constructors diff --git a/tests/org/elixir_lang/elixir_flex_lexer/sigil_modifiers/group/Test.java b/tests/org/elixir_lang/elixir_flex_lexer/sigil_modifiers/group/Test.java index beb5915c4..917fa1aba 100644 --- a/tests/org/elixir_lang/elixir_flex_lexer/sigil_modifiers/group/Test.java +++ b/tests/org/elixir_lang/elixir_flex_lexer/sigil_modifiers/group/Test.java @@ -5,10 +5,9 @@ import org.elixir_lang.ElixirFlexLexer; import org.elixir_lang.elixir_flex_lexer.TokenTest; import org.elixir_lang.psi.ElixirTypes; -import org.junit.Ignore; +import org.jetbrains.annotations.NotNull; import org.junit.runners.Parameterized; -import java.io.IOException; import java.util.Arrays; import java.util.Collection; @@ -17,7 +16,6 @@ /** * Created by luke.imhoff on 10/23/14. */ -@Ignore("abstract") public abstract class Test extends TokenTest { /* * Constructors @@ -50,7 +48,7 @@ public static Collection generateData() { protected abstract char terminator(); @Override - protected void start(CharSequence charSequence) { + protected void start(@NotNull CharSequence charSequence) { // start to trigger SIGIL_MODIFIERS after GROUP CharSequence fullCharSequence = "~r" + promoter() + terminator() + charSequence; super.start(fullCharSequence); diff --git a/tests/org/elixir_lang/elixir_flex_lexer/sigil_modifiers/group_heredoc_end/Test.java b/tests/org/elixir_lang/elixir_flex_lexer/sigil_modifiers/group_heredoc_end/Test.java index d23fc3771..d589eaedc 100644 --- a/tests/org/elixir_lang/elixir_flex_lexer/sigil_modifiers/group_heredoc_end/Test.java +++ b/tests/org/elixir_lang/elixir_flex_lexer/sigil_modifiers/group_heredoc_end/Test.java @@ -6,17 +6,14 @@ import org.elixir_lang.elixir_flex_lexer.TokenTest; import org.elixir_lang.psi.ElixirTypes; import org.jetbrains.annotations.NotNull; -import org.junit.Ignore; import org.junit.runners.Parameterized; -import java.io.IOException; import java.util.Arrays; import java.util.Collection; /** * Created by luke.imhoff on 10/23/14. */ -@Ignore("abstract") public abstract class Test extends TokenTest { /* * Constructors diff --git a/tests/org/elixir_lang/flex_lexer/Test.java b/tests/org/elixir_lang/flex_lexer/Test.java index a441ea153..5477aaab1 100644 --- a/tests/org/elixir_lang/flex_lexer/Test.java +++ b/tests/org/elixir_lang/flex_lexer/Test.java @@ -3,12 +3,8 @@ import com.intellij.lexer.Lexer; import org.jetbrains.annotations.NotNull; import org.junit.Before; -import org.junit.Ignore; -import java.io.IOException; - -@Ignore("abstract") public abstract class Test { /* * Fields diff --git a/tests/org/elixir_lang/parser_definition/EOLtoWhitespace.java b/tests/org/elixir_lang/parser_definition/EOLtoWhitespaceTestCase.java similarity index 97% rename from tests/org/elixir_lang/parser_definition/EOLtoWhitespace.java rename to tests/org/elixir_lang/parser_definition/EOLtoWhitespaceTestCase.java index 248c32d99..fa309f6ad 100644 --- a/tests/org/elixir_lang/parser_definition/EOLtoWhitespace.java +++ b/tests/org/elixir_lang/parser_definition/EOLtoWhitespaceTestCase.java @@ -1,6 +1,6 @@ package org.elixir_lang.parser_definition; -public class EOLtoWhitespace extends ParsingTestCase { +public class EOLtoWhitespaceTestCase extends ParsingTestCase { public void testAnd() { assertParsedAndQuotedCorrectly(); } diff --git a/tests/org/elixir_lang/parser_definition/MatchedArrowOperationParsingTestCase.java b/tests/org/elixir_lang/parser_definition/MatchedArrowOperationParsingTestCase.java index e4539f5da..d48476de2 100644 --- a/tests/org/elixir_lang/parser_definition/MatchedArrowOperationParsingTestCase.java +++ b/tests/org/elixir_lang/parser_definition/MatchedArrowOperationParsingTestCase.java @@ -5,7 +5,7 @@ */ public class MatchedArrowOperationParsingTestCase extends ParsingTestCase { public void testAssociativity() { - if (!isTravis()) { + if (isNotTravis()) { assertParsedAndQuotedCorrectly(); } } @@ -19,7 +19,7 @@ public void testCaptureNonNumericOperation() { } public void testMatchedInOperation() { - if (!isTravis()) { + if (isNotTravis()) { assertParsedAndQuotedCorrectly(); } } diff --git a/tests/org/elixir_lang/parser_definition/MatchedThreeOperationParsingTestCase.java b/tests/org/elixir_lang/parser_definition/MatchedThreeOperationParsingTestCase.java index fdd9f47dc..4e78655fd 100644 --- a/tests/org/elixir_lang/parser_definition/MatchedThreeOperationParsingTestCase.java +++ b/tests/org/elixir_lang/parser_definition/MatchedThreeOperationParsingTestCase.java @@ -5,7 +5,7 @@ */ public class MatchedThreeOperationParsingTestCase extends ParsingTestCase { public void testAssociativity() { - if (!isTravis()) { + if (isNotTravis()) { assertParsedAndQuotedCorrectly(); } } @@ -19,7 +19,7 @@ public void testCaptureNonNumericOperation() { } public void testMatchedInOperation() { - if (!isTravis()) { + if (isNotTravis()) { assertParsedAndQuotedCorrectly(); } } diff --git a/tests/org/elixir_lang/parser_definition/ParsingTestCase.java b/tests/org/elixir_lang/parser_definition/ParsingTestCase.java index 203d90fa5..8449c3c14 100644 --- a/tests/org/elixir_lang/parser_definition/ParsingTestCase.java +++ b/tests/org/elixir_lang/parser_definition/ParsingTestCase.java @@ -1,7 +1,6 @@ package org.elixir_lang.parser_definition; import com.ericsson.otp.erlang.OtpErlangObject; -import com.intellij.lang.Language; import com.intellij.lang.ParserDefinition; import com.intellij.psi.*; import org.elixir_lang.ElixirLanguage; @@ -16,7 +15,6 @@ /** * Created by luke.imhoff on 8/7/14. */ -@org.junit.Ignore("abstract") public abstract class ParsingTestCase extends com.intellij.testFramework.ParsingTestCase { public ParsingTestCase() { this("ex", new ElixirParserDefinition()); @@ -35,11 +33,6 @@ protected void assertParsedAndQuotedAroundError(boolean checkResult) { assertQuotedAroundError(); } - protected void assertParsedAndQuotedAroundExit() { - doTest(true); - assertQuotedAroundExit(); - } - protected void assertParsedAndQuotedCorrectly() { assertParsedAndQuotedCorrectly(true); } @@ -61,15 +54,15 @@ protected void assertParsedWithErrors(boolean checkResult) { Quoter.assertError(myFile); } - private List localErrors(@NotNull Language language) { + private List localErrors() { final FileViewProvider fileViewProvider = myFile.getViewProvider(); - PsiFile root = fileViewProvider.getPsi(language); + PsiFile root = fileViewProvider.getPsi(ElixirLanguage.INSTANCE); final List errorElementList = new LinkedList<>(); root.accept( new PsiRecursiveElementWalkingVisitor() { @Override - public void visitElement(PsiElement element) { + public void visitElement(@NotNull PsiElement element) { if (element instanceof PsiErrorElement) { errorElementList.add(element); } @@ -83,21 +76,15 @@ public void visitElement(PsiElement element) { } protected void assertWithLocalError() { - assertWithLocalError(ElixirLanguage.INSTANCE); - } - protected void assertWithLocalError(@NotNull Language language) { - List errorElementList = localErrors(language); + List errorElementList = localErrors(); - assertTrue("No PsiErrorElements found in parsed file PSI", !errorElementList.isEmpty()); + assertFalse("No PsiErrorElements found in parsed file PSI", errorElementList.isEmpty()); } protected void assertWithoutLocalError() { - assertWithoutLocalError(ElixirLanguage.INSTANCE); - } - protected void assertWithoutLocalError(@NotNull Language language) { - List errorElementList = localErrors(language); + List errorElementList = localErrors(); assertTrue("PsiErrorElements found in parsed file PSI", errorElementList.isEmpty()); } @@ -107,11 +94,6 @@ protected void assertQuotedAroundError() { Quoter.assertError(myFile); } - protected void assertQuotedAroundExit() { - assertInstanceOf(ElixirPsiImplUtil.quote(myFile), OtpErlangObject.class); - Quoter.assertExit(myFile); - } - protected void assertQuotedCorrectly() { Quoter.assertQuotedCorrectly(myFile); } @@ -126,15 +108,10 @@ protected String getTestDataPath() { * * @return {@code true} if on Travis CI; {@code false} otherwise */ - protected boolean isTravis() { + protected boolean isNotTravis() { String travis = System.getenv("TRAVIS"); - return travis != null && travis.equals("true"); - } - - @Override - protected boolean skipSpaces() { - return false; + return travis == null || !travis.equals("true"); } @Override diff --git a/tests/org/elixir_lang/parser_definition/matched_call_operation/ParsingTestCase.java b/tests/org/elixir_lang/parser_definition/matched_call_operation/ParsingTestCase.java index 01d60d6fb..2c8fa66b4 100644 --- a/tests/org/elixir_lang/parser_definition/matched_call_operation/ParsingTestCase.java +++ b/tests/org/elixir_lang/parser_definition/matched_call_operation/ParsingTestCase.java @@ -3,7 +3,6 @@ /** * Created by luke.imhoff on 8/7/14. */ -@org.junit.Ignore("abstract") public abstract class ParsingTestCase extends org.elixir_lang.parser_definition.ParsingTestCase { @Override protected String getTestDataPath() { diff --git a/tests/org/elixir_lang/parser_definition/matched_dot_operator_call_operation/ParsingTestCase.java b/tests/org/elixir_lang/parser_definition/matched_dot_operator_call_operation/ParsingTestCase.java index 4c033e14a..aa0eb08c5 100644 --- a/tests/org/elixir_lang/parser_definition/matched_dot_operator_call_operation/ParsingTestCase.java +++ b/tests/org/elixir_lang/parser_definition/matched_dot_operator_call_operation/ParsingTestCase.java @@ -3,7 +3,6 @@ /** * Created by luke.imhoff on 8/7/14. */ -@org.junit.Ignore("abstract") public abstract class ParsingTestCase extends org.elixir_lang.parser_definition.ParsingTestCase { @Override protected String getTestDataPath() { diff --git a/tests/org/elixir_lang/reference/callable/UnquoteCallbackNamedVariableTest.kt b/tests/org/elixir_lang/reference/callable/UnquoteCallbackNamedVariableTest.kt new file mode 100644 index 000000000..a3fde8e48 --- /dev/null +++ b/tests/org/elixir_lang/reference/callable/UnquoteCallbackNamedVariableTest.kt @@ -0,0 +1,109 @@ +package org.elixir_lang.reference.callable + +import com.intellij.testFramework.LoggedErrorProcessor +import org.elixir_lang.PlatformTestCase +import java.io.File + +/** + * Regression tests for handling PSI parent elements when walking unquoted variables that match @callback names. + * + * Bug pattern discovered: When a variable name matches a @callback name (e.g., docs_uri) + * and appears in unquote() alongside another unquote(), the reference resolver attempts + * to resolve the callback-named variable, triggering treeWalkUpUnquotedVariable(). This + * walks up the PSI tree through parent elements, which must include ElixirStabBody and + * other transparent wrapper types (Call, ElixirStab, QuotableArguments, QuotableKeywordList). + * + * Original issue: Credo's check.ex file + * See: https://github.com/rrrene/credo/blob/9ba02a636f0ef22b0ad965b2c710c727d1a73902/lib/credo/check.ex + */ +class UnquoteCallbackNamedVariableTest : PlatformTestCase() { + + private fun testNoErrorLogged(fixtureDir: String, fixtureFile: String, displayName: String) { + var errorLogged = false + LoggedErrorProcessor.executeWith(object : LoggedErrorProcessor() { + override fun processError( + category: String, + message: String, + details: Array, + t: Throwable? + ): Set { + if (t?.message?.contains("Don't know how to walk unquoted variable parent") == true) { + errorLogged = true + } + return Action.ALL + } + }) { + val testFile = File("testData/org/elixir_lang/reference/callable/$fixtureDir", fixtureFile) + val content = testFile.readText() + val psiFile = myFixture.configureByText(fixtureFile, content) + + psiFile.accept(object : com.intellij.psi.PsiRecursiveElementVisitor() { + override fun visitElement(element: com.intellij.psi.PsiElement) { + super.visitElement(element) + if (element is com.intellij.psi.PsiNamedElement) { + element.references.forEach { ref -> + try { + ref.resolve() + } catch (_: Exception) { + // Ignore resolution errors + } + } + } + } + }) + } + + assertFalse( + "Logger.error should not be called for unquoted variable parent in $displayName", + errorLogged + ) + } + + /** + * Tests basic case: unquote of callback-named variable in a quote block within macro. + * Tests that ElixirStabBody is handled as a transparent wrapper. + */ + fun testUnquoteInStabBody() { + testNoErrorLogged( + "unquote_in_stab_body", + "unquote_in_stab_body.ex", + "stab body" + ) + } + + /** + * Tests unquote of callback-named variable inside a quote block within a case statement. + * Tests that ElixirStab and ElixirStabBody are handled as transparent wrappers. + */ + fun testUnquoteInCaseStabBody() { + testNoErrorLogged( + "unquote_in_case_stab_body", + "unquote_in_case_stab_body.ex", + "case stab body" + ) + } + + /** + * Tests unquote of callback-named variable inside a quote block within an anonymous function. + * Tests that Call (anonymous function) and ElixirStabBody are handled as transparent wrappers. + */ + fun testUnquoteInAnonymousFunStabBody() { + testNoErrorLogged( + "unquote_in_anonymous_fun_stab_body", + "unquote_in_anonymous_fun_stab_body.ex", + "anonymous function stab body" + ) + } + + /** + * Tests unquote of callback-named variable alongside bracket operation access. + * Tests that ElixirStabBody and bracket operations work together correctly. + */ + fun testUnquoteInBracketOperation() { + testNoErrorLogged( + "unquote_in_bracket_operation", + "unquote_in_bracket_operation.ex", + "bracket operation" + ) + } +}