[
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Describe the bug**\nA clear and concise description of what the bug is.\n\n**To Reproduce**\nSteps to reproduce the behavior:\n1. Go to '...'\n2. Click on '....'\n3. Scroll down to '....'\n4. See error\n\n**Expected behavior**\nA clear and concise description of what you expected to happen.\n\n**Screenshots**\nIf applicable, add screenshots to help explain your problem.\n\n**Desktop (please complete the following information):**\n - OS: [e.g. iOS]\n - Browser [e.g. chrome, safari]\n - Version [e.g. 22]\n\n**Smartphone (please complete the following information):**\n - Device: [e.g. iPhone6]\n - OS: [e.g. iOS8.1]\n - Browser [e.g. stock browser, safari]\n - Version [e.g. 22]\n\n**Additional context**\nAdd any other context about the problem here.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/custom.md",
    "content": "---\nname: Custom issue template\nabout: Describe this issue template's purpose here.\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "content": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Is your feature request related to a problem? Please describe.**\nA clear and concise description of what the problem is. Ex. I'm always frustrated when [...]\n\n**Describe the solution you'd like**\nA clear and concise description of what you want to happen.\n\n**Describe alternatives you've considered**\nA clear and concise description of any alternative solutions or features you've considered.\n\n**Additional context**\nAdd any other context or screenshots about the feature request here.\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "# Dependabot configuration:\n# https://docs.github.com/en/free-pro-team@latest/github/administering-a-repository/configuration-options-for-dependency-updates\n\nversion: 2\nupdates:\n  # Maintain dependencies for Gradle dependencies\n  - package-ecosystem: \"gradle\"\n    directory: \"/\"\n    schedule:\n      interval: \"daily\"\n  # Maintain dependencies for GitHub Actions\n  - package-ecosystem: \"github-actions\"\n    directory: \"/\"\n    schedule:\n      interval: \"daily\"\n"
  },
  {
    "path": ".github/workflows/build.yml",
    "content": "# GitHub Actions Workflow is created for testing and preparing the plugin release in the following steps:\n# - Validate Gradle Wrapper.\n# - Run 'test' and 'verifyPlugin' tasks.\n# - Run Qodana inspections.\n# - Run the 'buildPlugin' task and prepare artifact for further tests.\n# - Run the 'runPluginVerifier' task.\n# - Create a draft release.\n#\n# The workflow is triggered on push and pull_request events.\n#\n# GitHub Actions reference: https://help.github.com/en/actions\n#\n## JBIJPPTPL\n\nname: Build\non:\n  # Trigger the workflow on pushes to only the 'main' branch (this avoids duplicate checks being run e.g., for dependabot pull requests)\n  push:\n    branches: [ main ]\n  # Trigger the workflow on any pull request\n  pull_request:\n\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}\n  cancel-in-progress: true\n\njobs:\n\n  # Prepare environment and build the plugin\n  build:\n    name: Build\n    runs-on: ubuntu-latest\n    outputs:\n      version: ${{ steps.properties.outputs.version }}\n      changelog: ${{ steps.properties.outputs.changelog }}\n      pluginVerifierHomeDir: ${{ steps.properties.outputs.pluginVerifierHomeDir }}\n    steps:\n      # Free GitHub Actions Environment Disk Space\n      - name: Maximize Build Space\n        uses: jlumbroso/free-disk-space@main\n        with:\n          tool-cache: false\n          large-packages: false\n\n      # Check out the current repository\n      - name: Fetch Sources\n        uses: actions/checkout@v4\n\n      # Validate wrapper\n      - name: Gradle Wrapper Validation\n        uses: gradle/actions/wrapper-validation@v4\n\n      # Set up Java environment for the next steps\n      - name: Setup Java\n        uses: actions/setup-java@v4\n        with:\n          distribution: jetbrains\n          java-version: 17\n\n      # Setup Gradle\n      - name: Setup Gradle\n        uses: gradle/actions/setup-gradle@v4\n\n      # Set environment variables\n      - name: Export Properties\n        id: properties\n        shell: bash\n        run: |\n          PROPERTIES=\"$(./gradlew properties --console=plain -q)\"\n          VERSION=\"$(echo \"$PROPERTIES\" | grep \"^version:\" | cut -f2- -d ' ')\"\n          CHANGELOG=\"$(./gradlew getChangelog --unreleased --no-header --console=plain -q)\"\n\n          echo \"version=$VERSION\" >> $GITHUB_OUTPUT\n          echo \"pluginVerifierHomeDir=~/.pluginVerifier\" >> $GITHUB_OUTPUT\n          \n          echo \"changelog<<EOF\" >> $GITHUB_OUTPUT\n          echo \"$CHANGELOG\" >> $GITHUB_OUTPUT\n          echo \"EOF\" >> $GITHUB_OUTPUT\n\n      # Build plugin\n      - name: Build plugin\n        run: ./gradlew buildPlugin\n\n      # Prepare plugin archive content for creating artifact\n      - name: Prepare Plugin Artifact\n        id: artifact\n        shell: bash\n        run: |\n          cd ${{ github.workspace }}/build/distributions\n          FILENAME=`ls *.zip`\n          unzip \"$FILENAME\" -d content\n\n          echo \"filename=${FILENAME:0:-4}\" >> $GITHUB_OUTPUT\n\n      # Store already-built plugin as an artifact for downloading\n      - name: Upload artifact\n        uses: actions/upload-artifact@v4\n        with:\n          name: ${{ steps.artifact.outputs.filename }}\n          path: ./build/distributions/content/*/*\n\n  # Run tests and upload a code coverage report\n  test:\n    name: Test\n    needs: [ build ]\n    runs-on: ubuntu-latest\n    steps:\n      # Check out the current repository\n      - name: Fetch Sources\n        uses: actions/checkout@v4\n\n      # Set up Java environment for the next steps\n      - name: Setup Java\n        uses: actions/setup-java@v4\n        with:\n          distribution: jetbrains\n          java-version: 17\n\n      # Setup Gradle\n      - name: Setup Gradle\n        uses: gradle/actions/setup-gradle@v4\n        with:\n          gradle-home-cache-cleanup: true\n\n      # Run tests\n      - name: Run Tests\n        run: ./gradlew check\n\n      # Collect Tests Result of failed tests\n      - name: Collect Tests Result\n        if: ${{ failure() }}\n        uses: actions/upload-artifact@v4\n        with:\n          name: tests-result\n          path: ${{ github.workspace }}/build/reports/tests\n\n      # Upload the Kover report to CodeCov\n      - name: Upload Code Coverage Report\n        uses: codecov/codecov-action@v4\n        with:\n          files: ${{ github.workspace }}/build/reports/kover/report.xml\n\n  # Run plugin structure verification along with IntelliJ Plugin Verifier\n  verify:\n    name: Verify plugin\n    needs: [ build ]\n    runs-on: ubuntu-latest\n    steps:\n      # Free GitHub Actions Environment Disk Space\n      - name: Maximize Build Space\n        uses: jlumbroso/free-disk-space@main\n        with:\n          tool-cache: false\n          large-packages: false\n\n      # Check out the current repository\n      - name: Fetch Sources\n        uses: actions/checkout@v4\n\n      # Set up Java environment for the next steps\n      - name: Setup Java\n        uses: actions/setup-java@v4\n        with:\n          distribution: zulu\n          java-version: 17\n\n      # Setup Gradle\n      - name: Setup Gradle\n        uses: gradle/actions/setup-gradle@v4\n        with:\n          gradle-home-cache-cleanup: true\n\n      # Cache Plugin Verifier IDEs\n      - name: Setup Plugin Verifier IDEs Cache\n        uses: actions/cache@v4\n        with:\n          path: ${{ needs.build.outputs.pluginVerifierHomeDir }}/ides\n          key: plugin-verifier-${{ hashFiles('build/listProductsReleases.txt') }}\n\n      # Run Verify Plugin task and IntelliJ Plugin Verifier tool\n      - name: Run Plugin Verification tasks\n        run: ./gradlew verifyPlugin -Dplugin.verifier.home.dir=${{ needs.build.outputs.pluginVerifierHomeDir }}\n\n      # Collect Plugin Verifier Result\n      - name: Collect Plugin Verifier Result\n        if: ${{ always() }}\n        uses: actions/upload-artifact@v4\n        with:\n          name: pluginVerifier-result\n          path: ${{ github.workspace }}/build/reports/pluginVerifier\n\n  # Prepare a draft release for GitHub Releases page for the manual verification\n  # If accepted and published, release workflow would be triggered\n  releaseDraft:\n    name: Release draft\n    if: github.event_name != 'pull_request'\n    needs: [ build, test, verify ]\n    runs-on: ubuntu-latest\n    permissions:\n      contents: write\n    steps:\n\n      # Check out the current repository\n      - name: Fetch Sources\n        uses: actions/checkout@v4\n\n      # Remove old release drafts by using the curl request for the available releases with a draft flag\n      - name: Remove Old Release Drafts\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        run: |\n          gh api repos/{owner}/{repo}/releases \\\n            --jq '.[] | select(.draft == true) | .id' \\\n            | xargs -I '{}' gh api -X DELETE repos/{owner}/{repo}/releases/{}\n\n      # Create a new release draft which is not publicly visible and requires manual acceptance\n      - name: Create Release Draft\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        run: |\n          gh release create \"v${{ needs.build.outputs.version }}\" \\\n            --draft \\\n            --title \"v${{ needs.build.outputs.version }}\" \\\n            --notes \"$(cat << 'EOM'\n          ${{ needs.build.outputs.changelog }}\n          EOM\n          )\"\n"
  },
  {
    "path": ".github/workflows/release.yml",
    "content": "# GitHub Actions Workflow created for handling the release process based on the draft release prepared with the Build workflow.\n# Running the publishPlugin task requires all following secrets to be provided: PUBLISH_TOKEN, PRIVATE_KEY, PRIVATE_KEY_PASSWORD, CERTIFICATE_CHAIN.\n# See https://plugins.jetbrains.com/docs/intellij/plugin-signing.html for more information.\n\nname: Publish JetBrains\non:\n  release:\n    types: [prereleased, released]\n\njobs:\n\n  # Prepare and publish the plugin to JetBrains Marketplace repository\n  release:\n    name: Publish Plugin\n    runs-on: ubuntu-latest\n    strategy:\n      fail-fast: true\n      matrix:\n        platform-version: [ 241 ]\n    env:\n      ORG_GRADLE_PROJECT_platformVersion: ${{ matrix.platform-version }}\n\n    permissions:\n      contents: write\n      pull-requests: write\n    steps:\n      # Free GitHub Actions Environment Disk Space\n      - name: Maximize Build Space\n        uses: jlumbroso/free-disk-space@main\n        with:\n          tool-cache: false\n          large-packages: false\n\n      # Check out current repository\n      - name: Fetch Sources\n        uses: actions/checkout@v4\n        with:\n          submodules: 'true'\n          ref: ${{ github.event.release.tag_name }}\n\n      # Set up Java environment for the next steps\n      - name: Setup Java\n        uses: actions/setup-java@v4\n        with:\n          distribution: zulu\n          java-version: 17\n\n      # Setup Gradle\n      - name: Setup Gradle\n        uses: gradle/actions/setup-gradle@v4\n        with:\n          gradle-home-cache-cleanup: true\n\n      # Set environment variables\n      - name: Export Properties\n        id: properties\n        shell: bash\n        run: |\n          CHANGELOG=\"$(cat << 'EOM' | sed -e 's/^[[:space:]]*$//g' -e '/./,$!d'\n          ${{ github.event.release.body }}\n          EOM\n          )\"\n          \n          echo \"changelog<<EOF\" >> $GITHUB_OUTPUT\n          echo \"$CHANGELOG\" >> $GITHUB_OUTPUT\n          echo \"EOF\" >> $GITHUB_OUTPUT\n\n      # Update Unreleased section with the current release note\n      - name: Patch Changelog\n        if: ${{ steps.properties.outputs.changelog != '' }}\n        env:\n          CHANGELOG: ${{ steps.properties.outputs.changelog }}\n        run: |\n          ./gradlew patchChangelog --release-note=\"$CHANGELOG\"\n\n      # Publish the plugin to JetBrains Marketplace\n      - name: Publish Plugin\n        env:\n          PUBLISH_TOKEN: ${{ secrets.PUBLISH_TOKEN }}\n          CERTIFICATE_CHAIN: ${{ secrets.CERTIFICATE_CHAIN }}\n          PRIVATE_KEY: ${{ secrets.PRIVATE_KEY }}\n          PRIVATE_KEY_PASSWORD: ${{ secrets.PRIVATE_KEY_PASSWORD }}\n        run: ./gradlew :publishPlugin\n\n      # Upload artifact as a release asset\n      - name: Upload Release Asset\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        run: gh release upload ${{ github.event.release.tag_name }} ./build/distributions/*\n"
  },
  {
    "path": ".github/workflows/run-ui-tests.yml",
    "content": "# GitHub Actions Workflow for launching UI tests on Linux, Windows, and Mac in the following steps:\n# - Prepare and launch IDE with your plugin and robot-server plugin, which is needed to interact with the UI.\n# - Wait for IDE to start.\n# - Run UI tests with a separate Gradle task.\n#\n# Please check https://github.com/JetBrains/intellij-ui-test-robot for information about UI tests with IntelliJ Platform.\n#\n# Workflow is triggered manually.\n\nname: Run UI Tests\non:\n  workflow_dispatch\n\njobs:\n\n  testUI:\n    runs-on: ${{ matrix.os }}\n    strategy:\n      fail-fast: false\n      matrix:\n        include:\n          - os: ubuntu-latest\n            runIde: |\n              export DISPLAY=:99.0\n              Xvfb -ac :99 -screen 0 1920x1080x16 &\n              gradle runIdeForUiTests &\n          - os: windows-latest\n            runIde: start gradlew.bat :plugin:runIdeForUiTests\n          - os: macos-latest\n            runIde: ./gradlew :plugin:runIdeForUiTests &\n\n    steps:\n\n      # Check out current repository\n      - name: Fetch Sources\n        uses: actions/checkout@v4\n\n      # Set up Java environment for the next steps\n      - name: Setup Java\n        uses: actions/setup-java@v4\n        with:\n          distribution: zulu\n          java-version: 17\n\n      # Setup Gradle\n      - name: Setup Gradle\n        uses: gradle/actions/setup-gradle@v4\n        with:\n          gradle-home-cache-cleanup: true\n\n      # Run IDEA prepared for UI testing\n      - name: Run IDE\n        run: ${{ matrix.runIde }}\n\n      # Wait for IDEA to be started\n      - name: Health Check\n        uses: jtalk/url-health-check-action@v4\n        with:\n          url: http://127.0.0.1:8082\n          max-attempts: 15\n          retry-delay: 30s\n\n      # Run tests\n      - name: Tests\n        run: ./gradlew test\n"
  },
  {
    "path": ".gitignore",
    "content": ".gradle\n.idea\n.qodana\nbuild\n.kotlin/\n.shire-output\n.intellijPlatform\nrecording.jsonl\n**/bin/"
  },
  {
    "path": ".run/Build Plugin.run.xml",
    "content": "<component name=\"ProjectRunConfigurationManager\">\n  <configuration default=\"false\" name=\"Build Plugin\" type=\"GradleRunConfiguration\" factoryName=\"Gradle\">\n    <ExternalSystemSettings>\n      <option name=\"executionName\" />\n      <option name=\"externalProjectPath\" value=\"$PROJECT_DIR$\" />\n      <option name=\"externalSystemIdString\" value=\"GRADLE\" />\n      <option name=\"scriptParameters\" value=\"\" />\n      <option name=\"taskDescriptions\">\n        <list />\n      </option>\n      <option name=\"taskNames\">\n        <list>\n          <option value=\"buildPlugin\" />\n        </list>\n      </option>\n      <option name=\"vmOptions\" />\n    </ExternalSystemSettings>\n    <ExternalSystemDebugServerProcess>false</ExternalSystemDebugServerProcess>\n    <ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>\n    <DebugAllEnabled>false</DebugAllEnabled>\n    <RunAsTest>false</RunAsTest>\n    <method v=\"2\" />\n  </configuration>\n</component>"
  },
  {
    "path": ".run/PublishPlugin.run.xml",
    "content": "<component name=\"ProjectRunConfigurationManager\">\n  <configuration default=\"false\" name=\"PublishPlugin\" type=\"GradleRunConfiguration\" factoryName=\"Gradle\">\n    <ExternalSystemSettings>\n      <option name=\"executionName\" />\n      <option name=\"externalProjectPath\" value=\"$PROJECT_DIR$\" />\n      <option name=\"externalSystemIdString\" value=\"GRADLE\" />\n      <option name=\"scriptParameters\" value=\"\" />\n      <option name=\"taskDescriptions\">\n        <list />\n      </option>\n      <option name=\"taskNames\">\n        <list>\n          <option value=\"publishPlugin\" />\n        </list>\n      </option>\n      <option name=\"vmOptions\" />\n    </ExternalSystemSettings>\n    <ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>\n    <ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>\n    <DebugAllEnabled>false</DebugAllEnabled>\n    <RunAsTest>false</RunAsTest>\n    <method v=\"2\" />\n  </configuration>\n</component>"
  },
  {
    "path": ".run/Run IDE for UI Tests.run.xml",
    "content": "<component name=\"ProjectRunConfigurationManager\">\n  <configuration default=\"false\" name=\"Run IDE for UI Tests\" type=\"GradleRunConfiguration\" factoryName=\"Gradle\">\n    <log_file alias=\"idea.log\" path=\"$PROJECT_DIR$/build/idea-sandbox/system/log/idea.log\" />\n    <ExternalSystemSettings>\n      <option name=\"executionName\" />\n      <option name=\"externalProjectPath\" value=\"$PROJECT_DIR$\" />\n      <option name=\"externalSystemIdString\" value=\"GRADLE\" />\n      <option name=\"scriptParameters\" value=\"\" />\n      <option name=\"taskDescriptions\">\n        <list />\n      </option>\n      <option name=\"taskNames\">\n        <list>\n          <option value=\"runIdeForUiTests\" />\n        </list>\n      </option>\n      <option name=\"vmOptions\" />\n    </ExternalSystemSettings>\n    <ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>\n    <ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>\n    <DebugAllEnabled>false</DebugAllEnabled>\n    <RunAsTest>false</RunAsTest>\n    <method v=\"2\" />\n  </configuration>\n</component>"
  },
  {
    "path": ".run/Run Plugin.run.xml",
    "content": "<component name=\"ProjectRunConfigurationManager\">\n  <configuration default=\"false\" name=\"Run Plugin\" type=\"GradleRunConfiguration\" factoryName=\"Gradle\">\n    <log_file alias=\"idea.log\" path=\"$PROJECT_DIR$/build/idea-sandbox/system/log/idea.log\" />\n    <ExternalSystemSettings>\n      <option name=\"executionName\" />\n      <option name=\"externalProjectPath\" value=\"$PROJECT_DIR$\" />\n      <option name=\"externalSystemIdString\" value=\"GRADLE\" />\n      <option name=\"scriptParameters\" value=\"\" />\n      <option name=\"taskDescriptions\">\n        <list />\n      </option>\n      <option name=\"taskNames\">\n        <list>\n          <option value=\"runIde\" />\n        </list>\n      </option>\n      <option name=\"vmOptions\" value=\"\" />\n    </ExternalSystemSettings>\n    <ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>\n    <ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>\n    <DebugAllEnabled>false</DebugAllEnabled>\n    <RunAsTest>false</RunAsTest>\n    <method v=\"2\" />\n  </configuration>\n</component>"
  },
  {
    "path": ".run/Run Qodana.run.xml",
    "content": "<component name=\"ProjectRunConfigurationManager\">\n  <configuration default=\"false\" name=\"Run Qodana\" type=\"GradleRunConfiguration\" factoryName=\"Gradle\">\n    <ExternalSystemSettings>\n      <option name=\"executionName\" />\n      <option name=\"externalProjectPath\" value=\"$PROJECT_DIR$\" />\n      <option name=\"externalSystemIdString\" value=\"GRADLE\" />\n      <option name=\"scriptParameters\" value=\"\" />\n      <option name=\"taskDescriptions\">\n        <list />\n      </option>\n      <option name=\"taskNames\">\n        <list>\n          <option value=\"qodanaScan\" />\n        </list>\n      </option>\n      <option name=\"vmOptions\" />\n    </ExternalSystemSettings>\n    <ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>\n    <ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>\n    <DebugAllEnabled>false</DebugAllEnabled>\n    <RunAsTest>false</RunAsTest>\n    <method v=\"2\" />\n  </configuration>\n</component>"
  },
  {
    "path": ".run/Run Tests.run.xml",
    "content": "<component name=\"ProjectRunConfigurationManager\">\n  <configuration default=\"false\" name=\"Run Tests\" type=\"GradleRunConfiguration\" factoryName=\"Gradle\">\n    <log_file alias=\"idea.log\" path=\"$PROJECT_DIR$/build/idea-sandbox/system/log/idea.log\" />\n    <ExternalSystemSettings>\n      <option name=\"executionName\" />\n      <option name=\"externalProjectPath\" value=\"$PROJECT_DIR$\" />\n      <option name=\"externalSystemIdString\" value=\"GRADLE\" />\n      <option name=\"scriptParameters\" value=\"\" />\n      <option name=\"taskDescriptions\">\n        <list />\n      </option>\n      <option name=\"taskNames\">\n        <list>\n          <option value=\"check\" />\n        </list>\n      </option>\n      <option name=\"vmOptions\" value=\"\" />\n    </ExternalSystemSettings>\n    <ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>\n    <ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>\n    <DebugAllEnabled>false</DebugAllEnabled>\n    <RunAsTest>true</RunAsTest>\n    <method v=\"2\" />\n  </configuration>\n</component>\n"
  },
  {
    "path": ".run/Run Verifications.run.xml",
    "content": "<component name=\"ProjectRunConfigurationManager\">\n  <configuration default=\"false\" name=\"Run Verifications\" type=\"GradleRunConfiguration\" factoryName=\"Gradle\">\n    <log_file alias=\"idea.log\" path=\"$PROJECT_DIR$/build/idea-sandbox/system/log/idea.log\" />\n    <ExternalSystemSettings>\n      <option name=\"executionName\" />\n      <option name=\"externalProjectPath\" value=\"$PROJECT_DIR$\" />\n      <option name=\"externalSystemIdString\" value=\"GRADLE\" />\n      <option name=\"scriptParameters\" value=\"\" />\n      <option name=\"taskDescriptions\">\n        <list />\n      </option>\n      <option name=\"taskNames\">\n        <list>\n          <option value=\"runPluginVerifier\" />\n        </list>\n      </option>\n      <option name=\"vmOptions\" value=\"\" />\n    </ExternalSystemSettings>\n    <ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>\n    <ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>\n    <DebugAllEnabled>false</DebugAllEnabled>\n    <method v=\"2\">\n      <option name=\"Gradle.BeforeRunTask\" enabled=\"true\" tasks=\"clean\" externalProjectPath=\"$PROJECT_DIR$\" vmOptions=\"\" scriptParameters=\"\" />\n    </method>\n  </configuration>\n</component>"
  },
  {
    "path": ".shire/docs/context_variable.shire",
    "content": "---\nname: \"Context Variable\"\ndescription: \"Here is a description of the action.\"\ninteraction:  RunPanel\nvariables:\n  \"contextVariable\": /ContextVariable\\.kt/ { cat }\n  \"psiContextVariable\": /PsiContextVariable\\.kt/ { cat }\nonStreamingEnd: { parseCode | saveFile(\"docs/shire/shire-builtin-variable.md\") }\n---\n\n根据如下的信息，编写对应的 ContextVariable 相关信息的 markdown 文档。\n\n你所需要包含的 ContextVariable 信息如下：\n\n```\n$contextVariable\n```\n\n你所需要包含的 PsiContextVariable 信息如下：\n\n```\n$psiContextVariable\n```\n\n要求 1. Variable 信息使用表格展示，表格的第一列是变量名，第二列是描述，表格的第一行是表头，表头的第一列是 `变量名`，第二列是 `描述`。\n要求 2. 变量名应该取自 `variableName` field，即采用 camelCase 命名法。\n要求 3. 你应该输出的格式是 markdown 格式，包含 front matter 和正文。\n\n返回示例格式如下：\n\n```markdown\n\\-\\-\\-\nlayout: default\ntitle: Shire Context Variable\nparent: Shire Language\nnav_order: 5\n\\-\\-\\-\n\n## Context Variable\n\n\n## PsiContextVariable\n```\n\n请将内容放到上述的 markdown 模板中 。\n"
  },
  {
    "path": ".shire/docs/hobbit-hole.shire",
    "content": "---\nname: \"Hobbit Hole\"\ndescription: \"Here is a description of the action.\"\ninteraction:  RunPanel\nvariables:\n  \"currentCode\": /HobbitHole\\.kt/ { cat }\n  \"testCode\": /ShireCompileTest\\.kt/ { cat }\nonStreamingEnd: { saveFile(\"docs/shire/shire-hobbit-hole.md\")  }\n---\n\n我有一份用户手册写得不好，需要你从用户容易阅读的角度，重新写一份。\n\n根据如下的代码用例、文档，编写对应的 HobbitHole 相关信息的 markdown 文档。\n\n现有代码：\n\n$currentCode\n\n代码用例如下：\n\n$testCode\n\n要求：\n\n1. 尽详细介绍 HobbitHole 的相关信息和示例。\n2. 请按现有的文档 Heading 方式编写，并去除非必要的代码。\n"
  },
  {
    "path": ".shire/docs/on-streamin-done.shire",
    "content": "---\nname: \"On Streaming Done\"\ndescription: \"Here is a description of the action.\"\ninteraction:  RunPanel\nvariables:\n  \"currentDoc\": /on-streaming-done.md/ { cat }\n  \"currentCode\": /BuiltinPostHandler\\.kt/ { cat }\nonStreamingEnd: { saveFile(\"docs/lifecycle/on-streaming-done.md\")  }\n---\n\n根据如下当前的最新代码，更新现有的文档，请返回更新后的文档内容。\n\n最新的代码如下：\n\n$currentCode\n\n你需要更新的文档如下：\n\n$currentDoc\n\n要求：\n\n1. 使用表格展示内置的后处理器\n2. 粘贴必要的代码（如果有的话）\n\n请重新整理文档内容，使其符合最新的代码。\n\n\n"
  },
  {
    "path": ".shire/docs/shire-command.shire",
    "content": "---\nname: \"Shire Command\"\ndescription: \"Here is a description of the action.\"\ninteraction:  RunPanel\nvariables:\n  \"currentDoc\": /shire-command-template\\.md/ { cat }\n  \"currentCode\": /BuiltinCommand\\.kt/ { cat }\nonStreamingEnd: { saveFile(\"docs/shire/shire-command-template.md\")  }\n---\n\n根据如下当前的最新代码，更新现有的文档，请返回更新后的文档内容。\n\n最新的代码如下：\n\n$currentCode\n\n你需要更新的文档如下：\n\n$currentDoc\n\n请重新整理文档内容，使其符合最新的代码。\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# [](https://github.com/phodal/shire/compare/v1.3.4...v) (2025-02-05)\n\n## [Unreleased]\n\n## [1.3.4](https://github.com/phodal/shire/compare/v1.3.3...v[1.3.4]) (2025-02-05)\n\n### Features\n\n* **command:** add isApplicable method to ShireCommand ([4857af8](https://github.com/phodal/shire/commit/4857af8de1afb8301881ecaf71c5f0fed3145bd5))\n* **commands:** add commandName property to all ShireCommand implementations ([08fc7f3](https://github.com/phodal/shire/commit/08fc7f38a14151d2b9d8444829d21065812758b7))\n* **commands:** enhance command descriptions and add enableInSketch flag ([0f88b46](https://github.com/phodal/shire/commit/0f88b4661598efb0a8577d47a45cc827e251e958))\n* **database:** add schema function and simplify toolchain provider ([05acdae](https://github.com/phodal/shire/commit/05acdaedd0312df00d08a99c7a8180208af0f3b7))\n* **search:** add ripgrep search command ([bafff1b](https://github.com/phodal/shire/commit/bafff1b3747ef7d9cc4cc9073d3987a36adeff6c))\n* **terminal:** add terminal sketch provider for bash support ([1148dc8](https://github.com/phodal/shire/commit/1148dc868a3c8cc71ac98be4caca00eac6500a28))\n\n## [1.3.3](https://github.com/phodal/shire/compare/v1.3.2...v[1.3.3]) (2025-01-14)\n\n### Bug Fixes\n\n* **compiler:** update file validation in LocalSearchInsCommand ([fb26400](https://github.com/phodal/shire/commit/fb264003c10e968d8177a1c11d2543e651c9d1f8))\n* **run-code:** wrap scratch file deletion in runWriteAction ([5271cd7](https://github.com/phodal/shire/commit/5271cd7072d1ac33535c07ecaeda5fb229e1edfd))\n\n### Features\n\n* **commands:** add local search, related symbol, and open file commands ([7e7de3b](https://github.com/phodal/shire/commit/7e7de3b5174e8b6f9959ad5ceff28684a37d426e))\n* **editor:** Add file selection support in preview editor ([07140f3](https://github.com/phodal/shire/commit/07140f3597d46a62a08dcf8feb1e7fe7361fe507))\n* **kotlin:** add KotlinPsiContextVariableProvider ([512cb3a](https://github.com/phodal/shire/commit/512cb3a11c088ac9dee1791ef28afc8e2dc75792))\n* **markdown:** add support for Shire code fence parsing ([b72753c](https://github.com/phodal/shire/commit/b72753c50581c4eb0cb24acefecda3907d0dad39))\n* **parser:** add support for `DIR` command to list directory contents ([8d0ddc9](https://github.com/phodal/shire/commit/8d0ddc940e36245f6e049e60a5e34afe9a87a3dc))\n* **streaming:** integrate OnStreamingService into ShireInlineChat ([23858b1](https://github.com/phodal/shire/commit/23858b103a00d3b11abb62741ebb3c045a5a45b6))\n\n## [1.3.2](https://github.com/phodal/shire/compare/v1.3.1...v[1.3.2]) (2025-01-09)\n\n### Bug Fixes\n\n* **debugger:** restrict debug/run execution based on executor ID [#183](https://github.com/phodal/shire/issues/183) ([47d2b71](https://github.com/phodal/shire/commit/47d2b7106a3b94d2cffcc94428b23ce8cb62d885))\n\n### Features\n\n* **debugger:** add debug tab layout and session listener [#183](https://github.com/phodal/shire/issues/183) ([2053470](https://github.com/phodal/shire/commit/20534702c693ba2a72e3e8d22ab68b5672658fbd))\n* **debugger:** add Shire debugger support [#183](https://github.com/phodal/shire/issues/183) ([3b1fd51](https://github.com/phodal/shire/commit/3b1fd51d6de03ba8e41b60201e6e7b2d00596b78))\n* **debugger:** enhance debug process with variable snapshot support [#183](https://github.com/phodal/shire/issues/183) ([184e691](https://github.com/phodal/shire/commit/184e69186e765f727bcd1bbfe71c8fa29c71e034))\n* **debugger:** enhance debugger with breakpoint handling and UI improvements [#183](https://github.com/phodal/shire/issues/183) ([743f4ca](https://github.com/phodal/shire/commit/743f4ca1c2f8181e7edb6271265c051b0faca580))\n* **debugger:** enhance stack frame presentation with custom variables [#183](https://github.com/phodal/shire/issues/183) ([bfc1f10](https://github.com/phodal/shire/commit/bfc1f10ce65ced92b28e821c97316e24cb0e378d))\n\n### Reverts\n\n* Revert \"refactor(debugger): remove unused ShireDebugSettings file #183\" ([c7f6f14](https://github.com/phodal/shire/commit/c7f6f1424298d5d0c2ae0f54a5ceb670a2110698)), closes [#183](https://github.com/phodal/shire/issues/183)\n* Revert \"refactor(debugger): remove unused ShireDebugSettings configuration\" ([358a343](https://github.com/phodal/shire/commit/358a34383d75ed7706d2d49cfbe9a6e4a650ad83))\n\n## [1.3.1](https://github.com/phodal/shire/compare/v1.3.0...v[1.3.1]) (2025-01-06)\n\n### Bug Fixes\n\n* **build:** disable prepareTestSandbox task ([0140675](https://github.com/phodal/shire/commit/014067564ce8801c1b5a020c9200e160e7c3d4b2))\n* **code-highlight:** normalize line separators in text replacement [#157](https://github.com/phodal/shire/issues/157) ([1a4ddd4](https://github.com/phodal/shire/commit/1a4ddd4f458e2c6d36d1e6b23f82ac993dfd5379))\n* **compatibility:** refactor database session handling ([0d4885e](https://github.com/phodal/shire/commit/0d4885e701d7c1c0da99d4bacb174f8da50c6eb6))\n* **compatibility:** remove deprecated client property setting ([f0b2774](https://github.com/phodal/shire/commit/f0b27741779d7e93195304928a88dda4f38ab466))\n* **editor:** add missing row for highlight sketch [#178](https://github.com/phodal/shire/issues/178) ([3d9f6cb](https://github.com/phodal/shire/commit/3d9f6cb17496f8fd0322f7c957da186b0ff4a7a8))\n* **editor:** set PlainTextLanguage for CodeHighlightSketch [#157](https://github.com/phodal/shire/issues/157) ([6fd404f](https://github.com/phodal/shire/commit/6fd404f04b53c5a844352a6c9f8bf60141f3cf72))\n* **editor:** update sample file label format [#178](https://github.com/phodal/shire/issues/178) ([8c68528](https://github.com/phodal/shire/commit/8c68528ce1f3b93e7345247552ce3d12ccfafb5d))\n* **injection:** change error log to warn for language injection ([6cf0cda](https://github.com/phodal/shire/commit/6cf0cda4048f31d6e1f4a768148509fc7e250704))\n* **input:** ensure safe removal of elements from listModel ([a433c42](https://github.com/phodal/shire/commit/a433c428b260cf4cdab3bd173e6d95030758f48b))\n* **input:** Resolved close button functionality in chat box ([5d66d68](https://github.com/phodal/shire/commit/5d66d688d1574cbfbdbe919095b3f022909dbdc4))\n* **input:** skip invalid psiFile in ShireInput ([c3c49a8](https://github.com/phodal/shire/commit/c3c49a8137503a21998ae687d79700c7b5c54603))\n* **input:** skip invalid psiFile in ShireInput ([0b0f011](https://github.com/phodal/shire/commit/0b0f0110d2cdd84dca09a8c6445833902d51699f))\n* **python:** restore PythonCore plugin and simplify PsiUtil check ([63f73e5](https://github.com/phodal/shire/commit/63f73e5102eb05def5b85c88a670cad7481b0bde))\n* **tests:** update file extension and add error handling ([eae3a8c](https://github.com/phodal/shire/commit/eae3a8c0d7eeba6f540e3f19576a88ef9038e9c8))\n* **ui:** execute table update on UI thread ([9bec8f3](https://github.com/phodal/shire/commit/9bec8f363a11ef824d7516168a8739b92bfe072d))\n\n### Features\n\n* **chat:** add inline chat provider support [#157](https://github.com/phodal/shire/issues/157) ([594d985](https://github.com/phodal/shire/commit/594d985c19388b716f6133163cd4e07e24e396a6))\n* **chat:** add prompt method to inline chat service [#157](https://github.com/phodal/shire/issues/157) ([ab09b82](https://github.com/phodal/shire/commit/ab09b822be0af908d5441731d2a19a9a4c56d32d))\n* **chat:** enhance inline chat UI and interaction [#157](https://github.com/phodal/shire/issues/157) ([c85ff46](https://github.com/phodal/shire/commit/c85ff467ada569b28ef0c6acb434130cd1665736))\n* **compiler:** add database command support [#271](https://github.com/phodal/shire/issues/271) ([6f079b4](https://github.com/phodal/shire/commit/6f079b4b6cebc53d1064f0d4f6dbf1cb02ed427d))\n* **compiler:** add ResolvableVariableSnapshot for variable tracking [#178](https://github.com/phodal/shire/issues/178) ([22131dd](https://github.com/phodal/shire/commit/22131dd3a890e54d62763b02653eb6c30215ec62))\n* **compiler:** add ResolvableVariableSnapshot for variable tracking [#183](https://github.com/phodal/shire/issues/183) ([c6f319a](https://github.com/phodal/shire/commit/c6f319a06817d9cf512d4511cc3e299e82362984))\n* **completion:** enhance custom agent completion with auto-insert ([146ecc4](https://github.com/phodal/shire/commit/146ecc4222bf0dfcc59eb933a93cb13877d4d9a0))\n* **database:** add SQL query execution support [#171](https://github.com/phodal/shire/issues/171) ([660eb67](https://github.com/phodal/shire/commit/660eb674116ec5b73a1347871977f2a8271d270a))\n* **editor:** add document listener for real-time updates [#157](https://github.com/phodal/shire/issues/157) ([68c88ba](https://github.com/phodal/shire/commit/68c88ba638628c14876503f43c35da746d4a22f5))\n* **editor:** add gutter icon handler for editor selections [#157](https://github.com/phodal/shire/issues/157) ([bf85b08](https://github.com/phodal/shire/commit/bf85b0802a7604ddce409dc1f67affc88e213f58))\n* **editor:** add ShireFileEditorProvider for custom file editing [#178](https://github.com/phodal/shire/issues/178) ([a309063](https://github.com/phodal/shire/commit/a3090638501c360afeebaf505222328d8d9c1959))\n* **editor:** add ShirePreviewEditorProvider and enhance editor creation [#157](https://github.com/phodal/shire/issues/157) ([99058b6](https://github.com/phodal/shire/commit/99058b6b03e6749addc3823c1224381c75de4419))\n* **editor:** add ShireVariablePanel for variable display [#157](https://github.com/phodal/shire/issues/157) ([ff245f5](https://github.com/phodal/shire/commit/ff245f5f927e14aee99d5574aafec2ed8c159947))\n* **editor:** enhance variable debug panel UI and functionality [#157](https://github.com/phodal/shire/issues/157) ([c51b388](https://github.com/phodal/shire/commit/c51b388178b370be375ea9d6e048c4dbe2ad5bdb))\n* **editor:** improve UI and async handling ([42e350f](https://github.com/phodal/shire/commit/42e350fdfe3147c73ffab7216490b8a16e0bc7a8)), closes [#178](https://github.com/phodal/shire/issues/178)\n* **editor:** replace ShireFileEditor with ShireSplitEditor [#157](https://github.com/phodal/shire/issues/157) ([4b51084](https://github.com/phodal/shire/commit/4b51084becc30eb0c2235cda3f1ab2d9bb1336b0))\n* **editor:** replace variable panel with table and add refresh action [#157](https://github.com/phodal/shire/issues/157) ([1dca503](https://github.com/phodal/shire/commit/1dca503ed88d91bc6fd0b816ef350f17cf163eb0))\n* **inline-chat:** add inline chat service and integrate with gutter icon [#157](https://github.com/phodal/shire/issues/157) ([933e8d7](https://github.com/phodal/shire/commit/933e8d79e00eca2eced45f419b757381ad5cac17))\n* **inline-chat:** add LLM integration and input handling [#157](https://github.com/phodal/shire/issues/157) ([13b49e2](https://github.com/phodal/shire/commit/13b49e2f59da1d4538d16d065daba19702b983cd))\n* **inline-chat:** enhance inline chat panel with improved UI and state management [#157](https://github.com/phodal/shire/issues/157) ([a9cfd60](https://github.com/phodal/shire/commit/a9cfd60982a105c0995bb6f1cff73f6005e2b837))\n* **inline-chat:** enhance prompt handling with dynamic actions [#157](https://github.com/phodal/shire/issues/157) ([97ce772](https://github.com/phodal/shire/commit/97ce772b52f0f81cf42532c2b179a7537340dcbf))\n* **java:** add test file lookup for related classes ([680c804](https://github.com/phodal/shire/commit/680c8040b1947b40dfcac7e367da20cd1a19397b))\n* **kotlin:** add Kotlin language support for testing, refactoring, and related classes ([9f55993](https://github.com/phodal/shire/commit/9f5599354f8f34fb13ae3245dc81b49f51524e4c))\n* **parser:** add DATABASE command to code block checks [#171](https://github.com/phodal/shire/issues/171) ([8339428](https://github.com/phodal/shire/commit/83394289f9ae052d815bda627cf6b89911369f4b))\n* **prompt:** add ShirePromptBuilder for dynamic prompt generation [#157](https://github.com/phodal/shire/issues/157) ([df07fcc](https://github.com/phodal/shire/commit/df07fcc4109dbd4506c5c913f111b3e1db837e54))\n* **runner:** add streaming service support in ShireRunner [#157](https://github.com/phodal/shire/issues/157) ([58124a3](https://github.com/phodal/shire/commit/58124a3061f64f292c7e40182d651bd80383639c))\n* **runner:** add support for custom editor in ShireRunner [#178](https://github.com/phodal/shire/issues/178) ([dd139d2](https://github.com/phodal/shire/commit/dd139d25ee5ba75193010cc7c5da82fd6ab2b874))\n* **sonarlint:** move SonarLint listener to thirdparty package [#157](https://github.com/phodal/shire/issues/157) ([b3c1530](https://github.com/phodal/shire/commit/b3c15304acd7ba22fe335dac8c096b9945cc985e))\n* **sse:** enhance custom request body handling ([5a424fb](https://github.com/phodal/shire/commit/5a424fbf5dfc65cc78a718b38bc4089c7669591c))\n* **ui:** adjust editor and panel border styles [#157](https://github.com/phodal/shire/issues/157) ([ae76259](https://github.com/phodal/shire/commit/ae762595c719dca22456ac5349a54b1c86e604cf))\n* **ui:** enhance border styling and add submit button [#157](https://github.com/phodal/shire/issues/157) ([982db35](https://github.com/phodal/shire/commit/982db35ba49ff2032cefb3b24ea506c363128f06))\n\n### Reverts\n\n* Revert \"chore(gradle): update Gradle wrapper  to 8.12 and try to resolve GitHub Action issue and add SPDX license\" ([fcad6ba](https://github.com/phodal/shire/commit/fcad6ba533cbb0bdce9e07fb334ea826df5f3e74))\n\n## [1.2.4](https://github.com/phodal/shire/compare/v1.2.3...v[1.2.4]) (2024-12-26)\n\n### Bug Fixes\n\n* **input:** skip unsupported languages in selectionChanged [#170](https://github.com/phodal/shire/issues/170) ([cd182d3](https://github.com/phodal/shire/commit/cd182d3fd91a3ae945024e11c420561bf337d548))\n\n### Features\n\n* **compiler:** add file path comment in code block output [#170](https://github.com/phodal/shire/issues/170) ([81a93dd](https://github.com/phodal/shire/commit/81a93dd295f9f9d53663d90549e55ffe06b7e253))\n* **completion:** add priority to file reference completion elements ([c97be93](https://github.com/phodal/shire/commit/c97be93ef4fe596214ab3ab8d7fced8e927407e2))\n* **completion:** add ShireCompletionLookupActionProvider and ShireLookupElement ([8ff9ee3](https://github.com/phodal/shire/commit/8ff9ee3669eed108d497338719a23fad3c79f9e5))\n* **completion:** enhance lookup functionality and refactor dependencies ([38b2f3d](https://github.com/phodal/shire/commit/38b2f3d13a8036a5d0e51efb782b48c29c96896f))\n* **core:** add related classes provider and lookup functionality ([e97e828](https://github.com/phodal/shire/commit/e97e82831925c5daece6688a8a3c16813731c0af))\n* **core:** add relative file path and caching for related classes [#170](https://github.com/phodal/shire/issues/170) ([58858b9](https://github.com/phodal/shire/commit/58858b913c056397a175437c862fb9ca38b47c15))\n* **diff:** add openDiffView method and refactor diff panel creation ([acf2245](https://github.com/phodal/shire/commit/acf22455d8e99be3ba43873cc57d87aa54b88c1d))\n* **diff:** enhance diff view and prevent duplicate entries ([61795f2](https://github.com/phodal/shire/commit/61795f2dd02b5ebb49fdcb592050deba11c354f6))\n* **diff:** try to add for local compare view ([8d6e4c1](https://github.com/phodal/shire/commit/8d6e4c1e39427c0230c9c780e0222d14d59a0d4c))\n* **folding:** add file command support to folding builder ([b502605](https://github.com/phodal/shire/commit/b5026055ca824d493a542f552739e6557e595485))\n* **input:** add editor and related listeners to ShireInput [#170](https://github.com/phodal/shire/issues/170) ([9e71d1e](https://github.com/phodal/shire/commit/9e71d1e6a7cc774e8714d20adac8920376449704))\n* **shirelang:** add support for STRUCTURE command [#170](https://github.com/phodal/shire/issues/170) ([be8084e](https://github.com/phodal/shire/commit/be8084ef487fc403d547d540b0fbc6e2dd24e087))\n* **ui:** add element list and append text functionality ([fb5c61d](https://github.com/phodal/shire/commit/fb5c61dca54d2c374836035df2f153d988ece929))\n* **ui:** extract LookupManagerListener to separate file [#170](https://github.com/phodal/shire/issues/170) ([6514ce9](https://github.com/phodal/shire/commit/6514ce9fe1b72ce17f892f939917432f7233188d))\n\n## [1.2.3](https://github.com/phodal/shire/compare/v1.2.2...v[1.2.3]) (2024-12-23)\n\n### Bug Fixes\n\n* **core:** handle missing Shire language and FileCreateService ([dcece27](https://github.com/phodal/shire/commit/dcece27bdbd97cbdb05bee4637d4fbe3cc146538))\n* fix Compatibility issue for 243 ([81b5d5f](https://github.com/phodal/shire/commit/81b5d5f002c206d349525e0ff241b64ec538c58a))\n\n### Features\n\n* **shire:** add FileCreateService and integrate with ShireInput ([50e698e](https://github.com/phodal/shire/commit/50e698ecd49c057dc5c3d86d870e14e84292e433))\n* **ui:** add stop button and refactor document listener ([c544688](https://github.com/phodal/shire/commit/c544688ad3b8a148d26a89959d49954aec70ce2c))\n\n## [1.2.2](https://github.com/phodal/shire/compare/v1.2.1...v[1.2.2]) (2024-12-23)\n\n### Bug Fixes\n\n* **core:** Unregister the shortcut to ensure it works the second time ([5381851](https://github.com/phodal/shire/commit/5381851ed60e406ad9a8ed657a9a067986beb6cd))\n* **llm:** NPE in streamSSE ([07a900c](https://github.com/phodal/shire/commit/07a900c4009d5b5eae122f31d9b5577222e87f7f))\n* **run:** 修复ShireProgramRunner的ID获取问题并优化代码结构 ([b4aaabe](https://github.com/phodal/shire/commit/b4aaabe49c3bc235ddd471e4e6641bc4acb25d1a))\n\n### Features\n\n* **CodeHighlight:** enhance editor with line number handling [#159](https://github.com/phodal/shire/issues/159) ([4a416f4](https://github.com/phodal/shire/commit/4a416f43203b3ce78a22a4ed60ce6c9fe5b7c891))\n* **compiler:** add line number functionality [#159](https://github.com/phodal/shire/issues/159) ([471d710](https://github.com/phodal/shire/commit/471d7107d6dfd6b3f7b9f2652488bf35e26d3770))\n* **lifycycle:** Add TimingStreamingService and refactor provider interface [#160](https://github.com/phodal/shire/issues/160) ([656915e](https://github.com/phodal/shire/commit/656915e5031368436035cad803fe496cd4999adf))\n* **marketplace:** replace LightVirtualFile with ScratchRootType for temp files [#165](https://github.com/phodal/shire/issues/165) ([11c1082](https://github.com/phodal/shire/commit/11c1082117b71f620fa190e29efb1d1615abfd48))\n* **run:** add ShireConsoleView and ShireProcessAdapter ([617ae40](https://github.com/phodal/shire/commit/617ae40dcc0be7d584bf835ab5527b831a9cfd1f))\n* **runner:** add ShireFileRunService and enhance run configuration ([e08c831](https://github.com/phodal/shire/commit/e08c83140c2f40185cd36b5f16ef3d99fdc462ca)), closes [#165](https://github.com/phodal/shire/issues/165)\n* **shire:** add chatbox functionality and update file references ([d045cd3](https://github.com/phodal/shire/commit/d045cd319108d2361a3ab20631a13091e6a5b6ca))\n* **streaming:** add logging streaming service and refactor streaming API [#160](https://github.com/phodal/shire/issues/160) ([ecbd342](https://github.com/phodal/shire/commit/ecbd342ab3da934c9f2b12d01f423a171c4a2505))\n* **streaming:** Add profiling capability and enhance documentation [#160](https://github.com/phodal/shire/issues/160) ([8f299e2](https://github.com/phodal/shire/commit/8f299e2a5bd1f185632648883b43aaffd4d5da86))\n* **streaming:** add StreamingServiceProvider interface and OnStreamingService [#160](https://github.com/phodal/shire/issues/160) ([8997e96](https://github.com/phodal/shire/commit/8997e966299f8b181bbaeab008894e30a66f76d1))\n* **streaming:** enhance OnStreamingService with registration and notification [#160](https://github.com/phodal/shire/issues/160) ([b7cc22e](https://github.com/phodal/shire/commit/b7cc22e539c55a21ef808e59b70b75e7b449233b))\n* **ui:** add custom progress bar and input section components [#165](https://github.com/phodal/shire/issues/165) ([6531f4c](https://github.com/phodal/shire/commit/6531f4c882e54ca9235a9308c2651b934894885a))\n* **ui:** add scratch file management to ShireInput ([510965e](https://github.com/phodal/shire/commit/510965eda304107ab0d87a588da76b88d139784c))\n* **ui:** add ShireInputTextField component and integrate with ShireInputSection [#165](https://github.com/phodal/shire/issues/165) ([7f71c0d](https://github.com/phodal/shire/commit/7f71c0d55d698a646b195f9e9466af99aa1751f4))\n* **utils:** add PostCodeProcessor for markdown code formatting ([11cd109](https://github.com/phodal/shire/commit/11cd109ba816f0210a3582e68cd7b9608ae06f6e))\n* **variable-resolver:** add support for selection with line numbers [#159](https://github.com/phodal/shire/issues/159) ([cdf84c4](https://github.com/phodal/shire/commit/cdf84c4262dc2928de8e57de50b0b91f65fc62cf))\n* **viewer:** Add cancel functionality to progress bar ([d947425](https://github.com/phodal/shire/commit/d9474257b9a5cf8b1846ca4ebdce66eb591b2cf5))\n\n## [1.2.1](https://github.com/phodal/shire/compare/v1.2.0...v[1.2.1]) (2024-12-12)\n\n### Bug Fixes\n\n* **diff:** correct line selection logic in DiffStreamHandler [#153](https://github.com/phodal/shire/issues/153) ([511f7f1](https://github.com/phodal/shire/commit/511f7f1d63591cd1b74684fcb78ca6ffb665ef00))\n* **diff:** optimize StreamDiff handling and add DiffLineType conversion [#153](https://github.com/phodal/shire/issues/153) ([b103f58](https://github.com/phodal/shire/commit/b103f58bf282c6da8b13639ba2d413312dcbafe4))\n* **openrewrite:** resolve missing OpenRewriteType class ([46f4084](https://github.com/phodal/shire/commit/46f408436fc31f53e9307446b61d5a46a8ca1dee))\n* **ui:** remove redundant revalidate and repaint calls ([78374cd](https://github.com/phodal/shire/commit/78374cdbf6ebdb6a9e2f3177be06e9cb50ab0605))\n* **utils:** prevent adding empty new lines to CodeFence content ([449efdf](https://github.com/phodal/shire/commit/449efdf5b35972c8bdd04b4078a2e8f4dec297d0)), closes [#153](https://github.com/phodal/shire/issues/153)\n\n### Features\n\n* **action:** Add RunCode action and refactor execution flow ([11d48c0](https://github.com/phodal/shire/commit/11d48c089cef5c4aeba7b27144de9fd5e9fa6c1c))\n* **code-highlight:** enhance setupActionBar and add Mermaid support [#153](https://github.com/phodal/shire/issues/153) ([f8f69a2](https://github.com/phodal/shire/commit/f8f69a26fb10ceef4810db9e431ab1bbf3f09432))\n* **core:** add DiffLangSketchProvider for diff language support [#153](https://github.com/phodal/shire/issues/153) ([61b592b](https://github.com/phodal/shire/commit/61b592bfeb0cab4b4dd59132c6bd0f12d14c1d72))\n* **diff-ui:** implement apply patch from clipboard dialog and enhance SingleFileDiffLangSketch [#153](https://github.com/phodal/shire/issues/153) ([a1e89d2](https://github.com/phodal/shire/commit/a1e89d29fde66eacf29b538992ed3a353dbdfb97))\n* **diff:** add diff streaming support for editors [#153](https://github.com/phodal/shire/issues/153) ([f2014bc](https://github.com/phodal/shire/commit/f2014bc60fc29f77f5472bb6fe87d1bc70582fc0))\n* **diff:** enhance diff view and context handling ([ea610a4](https://github.com/phodal/shire/commit/ea610a4e3febb39a9dd76376e8f6ce5e77027e4a))\n* **diff:** enhance SingleFileDiffLangSketch with new actions and dialog ([72a667f](https://github.com/phodal/shire/commit/72a667f856bf8d62d3e67ac9b7fe03fbcbd33545)), closes [#153](https://github.com/phodal/shire/issues/153)\n* **DiffPatchViewer:** enhance patch application and UI [#153](https://github.com/phodal/shire/issues/153) ([a2099cf](https://github.com/phodal/shire/commit/a2099cfc3da4e472332bdddd3c7abe272b223da8))\n* **diffs:** add StreamDiff feature and improve UI [#153](https://github.com/phodal/shire/issues/153) ([b260b49](https://github.com/phodal/shire/commit/b260b497adfd9f36b73dcd337448a0342dadbf79))\n* **extensions:** add DiffStreamService project service [#153](https://github.com/phodal/shire/issues/153) ([d8cb1b0](https://github.com/phodal/shire/commit/d8cb1b055507d9062137dbe386e192504ae78b07))\n* **patch:** add clipboard patch application dialog ([7ea22c5](https://github.com/phodal/shire/commit/7ea22c542d895e75c94bf84c329455a67860b4b1))\n* **shirelang:** implement `/goto` command for file navigation [#153](https://github.com/phodal/shire/issues/153) ([bd37fda](https://github.com/phodal/shire/commit/bd37fda786e3e6f1b6e131a8dc51b9c1eba95094))\n* **stream-diff:** implement stream diff functionality [#153](https://github.com/phodal/shire/issues/153) ([57ee622](https://github.com/phodal/shire/commit/57ee622fd0c470e561e733d17a55a6ced481e0a0))\n* **toolbar:** add ShireDiffCodeAction [#153](https://github.com/phodal/shire/issues/153) ([74d995a](https://github.com/phodal/shire/commit/74d995a13caca4580a3ed95ff47785f578d5dabe))\n* **toolsets:** add mermaid support and integration plugin [#153](https://github.com/phodal/shire/issues/153) ([55e33d0](https://github.com/phodal/shire/commit/55e33d0b34a8644c7f6a92990dba06ea0a044b7f))\n* **ui:** add diff viewer and toolbar actions [#153](https://github.com/phodal/shire/issues/153) ([ed903cd](https://github.com/phodal/shire/commit/ed903cd1bb8d05ca86361291b0977d470db796d4))\n* **ui:** add doneUpdateText method and optimize component handling [#153](https://github.com/phodal/shire/issues/153) ([5622123](https://github.com/phodal/shire/commit/56221238217a4fddfa655f0c9358eea3b5c1f55a))\n* **ui:** add patch display and action buttons to ShireMarketplaceTableView [#153](https://github.com/phodal/shire/issues/153) ([17eb9ff](https://github.com/phodal/shire/commit/17eb9ffbdd7960fb5d29b97a80f29d352127dbf8))\n* **ui:** enhance diff viewer with preview dialog [#153](https://github.com/phodal/shire/issues/153) ([b9a62db](https://github.com/phodal/shire/commit/b9a62db07c2dde01b228015d478ecbc3538b6c91))\n* **ui:** enhance DiffPatchViewer with project context and diff viewing [#153](https://github.com/phodal/shire/issues/153) ([7b88f2a](https://github.com/phodal/shire/commit/7b88f2a1efb2b491d4a39d8c8954e846b9b09bcc))\n* **ui:** enhance DiffPatchViewer with project context and diff viewing [#153](https://github.com/phodal/shire/issues/153) ([7cd80ea](https://github.com/phodal/shire/commit/7cd80ea8d5971aa0d514e93bef0e487241ec4e71))\n* **ui:** implement LangSketch and add PlantUML support [#153](https://github.com/phodal/shire/issues/153) ([6aa5b75](https://github.com/phodal/shire/commit/6aa5b755d9eed391597a3a02cc2e83225ee115c7))\n* **view:** Implement ShirePanelView for enhanced UI representation ([676df38](https://github.com/phodal/shire/commit/676df3895f62522e33a51c99360a6f4123ac3e82))\n\n### Reverts\n\n* revert change of delete file for MKT place ([1adbd5a](https://github.com/phodal/shire/commit/1adbd5a0cc12adb3bae2b4cb8dabbd00a7886fb7))\n\n## [1.1.1](https://github.com/phodal/shire/compare/v1.1.0...v[1.1.1]) (2024-12-02)\n\n### Bug Fixes\n\n* **core:** correctly assign stdout and stderr in RunServiceTask ([c320749](https://github.com/phodal/shire/commit/c32074984d9e757d95625a9103ef16ca46fe4795))\n* **core:** resolve compatibility issues with editor highlighter service ([175e6c9](https://github.com/phodal/shire/commit/175e6c96b86bd7cd5ef54ce13583dcaa5b15ad63))\n* set default python execute to python 3 and update && closed [#146](https://github.com/phodal/shire/issues/146) ([3f57680](https://github.com/phodal/shire/commit/3f57680f93fd8ee35b7352fa0470f94b457f4349))\n* **shire-javascript:** remove deprecated JSXHarmonyFileType reference ([9760487](https://github.com/phodal/shire/commit/9760487a3ead0449dd6297acf1a45edfdeb4d36d))\n\n### Features\n\n* **intellij-plugin:** Add soft wrap and scroll to end actions to console toolbar ([8453f57](https://github.com/phodal/shire/commit/8453f57a38874750a504df56f025f4710a3ae683))\n* **shell:** improve temp file cleanup in shell command execution ([8a5493d](https://github.com/phodal/shire/commit/8a5493d2320e502775b256e680859bcc39869516))\n* **ui:** add CodeView component for code preview ([bc8e07a](https://github.com/phodal/shire/commit/bc8e07a063d42cd26d0f10b52b7726e4842ae437))\n* **ui:** add toolwindow actions for copy, insert code, and language label ([b42962c](https://github.com/phodal/shire/commit/b42962c4e9de0bcfab63a76705935b1340a6444b))\n* **ui:** scroll to end of text on insert and add interaction type check ([4b3ecf8](https://github.com/phodal/shire/commit/4b3ecf8f88ba89ea5b777b35b65644e0131c7500))\n* **variables:** add Component data class and ReactPsiUtil utility ([0300680](https://github.com/phodal/shire/commit/03006808a73caf24d34feb9c9c4686027889d6ed))\n\n## [1.0.6](https://github.com/phodal/shire/compare/v1.0.4-SNAPSHOT...v[1.0.6]) (2024-11-24)\n\n### Bug Fixes\n\n* **core:** return complexity count as String instead of Int ([4cea791](https://github.com/phodal/shire/commit/4cea7917ed2bfc9553ed77c918b26df9a7efa0a6))\n* **runner:** Run fails due to duplicate variables ([430e57a](https://github.com/phodal/shire/commit/430e57a7b375c3709beeb6af6be8b7dbcf0dbe4d))\n\n### Features\n\n* **actions:** add ShireConsoleAction and update related configurations ([8ee1a18](https://github.com/phodal/shire/commit/8ee1a18bb43d0762549064b37377c1e9708052ad))\n* **actions:** add ShireDatabaseAction to Database panel menu bar ([19d02df](https://github.com/phodal/shire/commit/19d02dfeefce3b4e6d8fa630a6207a8adfae7c77))\n* **compiler:** Improve the logic of the if expression to handle variables in velocityBlock ([61e3aab](https://github.com/phodal/shire/commit/61e3aab3f0e408d883f4cd90e947e31d751ac0ec))\n* **kotlin:** add complexity analysis for Kotlin language ([412a90d](https://github.com/phodal/shire/commit/412a90d55a2144162a9d49aa3cfb28b5d0d55253)), closes [#139](https://github.com/phodal/shire/issues/139)\n* **provider:** add change count, line count, and complexity count variables [#143](https://github.com/phodal/shire/issues/143) and closed [#139](https://github.com/phodal/shire/issues/139) ([e23687c](https://github.com/phodal/shire/commit/e23687c8a0c31f4b23ea09483e482dd9b3fb53b0))\n* **provider:** add process method to ComplexityProvider interface [#139](https://github.com/phodal/shire/issues/139) ([61a6cad](https://github.com/phodal/shire/commit/61a6cad3a26d1c6f884fe1059d6da98ba93d2f15))\n* **provider:** implement complexity count calculation for PsiVariableProvider [#139](https://github.com/phodal/shire/issues/139) ([8a8f1e8](https://github.com/phodal/shire/commit/8a8f1e8df6171714ab0f6660fd91474b3fb7b769))\n* **shirelang:** add Shire Vcs Log Action and update menu text ([b90f450](https://github.com/phodal/shire/commit/b90f45013defc16685bbe5483af2a71c6c798c0e))\n* **shirelang:** add SonarLint action and tool window integration ([20e7f13](https://github.com/phodal/shire/commit/20e7f1308220d4f5ad24f34614ff8783aac0d9ad))\n* **shirelang:** add Vcs.Log action to ShireActionStartupActivity ([d34a8da](https://github.com/phodal/shire/commit/d34a8da8f1b2eefef80c3789f52e64af93f5b16c))\n* **vcs:** add support for Diff variable in VcsToolchainVariable ([d448e33](https://github.com/phodal/shire/commit/d448e33ee43fd34fb12f7653d84e4421de8f32d7))\n\n## [1.0.4-SNAPSHOT](https://github.com/phodal/shire/compare/v1.0.2...v[1.0.4-SNAPSHOT]) (2024-11-13)\n\n### Features\n\n* **core:** add ActionableWebView class for enhanced web interaction [#132](https://github.com/phodal/shire/issues/132) ([b65fdc0](https://github.com/phodal/shire/commit/b65fdc02d34cc1065bfcc614fd05993822ac2a39))\n* **grammar:** Add support for if expressions ([d536289](https://github.com/phodal/shire/commit/d536289b7518fbef89f6c41dc08cb3ee4eab8be4))\n\n## [1.0.2](https://github.com/phodal/shire/compare/v1.0.1...v[1.0.2]) (2024-11-03)\n\n### Bug Fixes\n\n* **hobbit:** refine key bindings for approval processor ([d402baa](https://github.com/phodal/shire/commit/d402baa9192e7f16a7bb3b45a3a0cc1c38be5bd4))\n* **openrewrite:** improve run configuration setup [#119](https://github.com/phodal/shire/issues/119) ([eecd294](https://github.com/phodal/shire/commit/eecd294277565db875ec21c8f756b5f1d80219e2))\n* **openrewrite:** update working directory for configuration [#119](https://github.com/phodal/shire/issues/119) ([35600e3](https://github.com/phodal/shire/commit/35600e368289e06bb52f68a3db4fbac0b8987913))\n\n### Features\n\n* **brace-matching:** enhance ShireBraceMatcher with improved pairs and conditions ([65f500e](https://github.com/phodal/shire/commit/65f500e1d1cdf72b71bf6235edd55691f1664d84))\n* **navigation:** add APPROVAL_EXECUTE pattern and refactor goto declaration handling ([f856236](https://github.com/phodal/shire/commit/f856236edf01eda761d4de1ef396a976e3cbbc18))\n* **openwrite:** try to add OpenRewrite support and integration [#119](https://github.com/phodal/shire/issues/119) ([b1cc797](https://github.com/phodal/shire/commit/b1cc7978be1a0fbec4e03ae340f18f1431b6acc0))\n* **shirelang:** add formatter support for Shire language files ([a37bf9d](https://github.com/phodal/shire/commit/a37bf9dad6ec86f3a11a5c7c7b683f306e87feaa))\n* **shirelang:** add highlight error filter for improved syntax highlighting ([c743528](https://github.com/phodal/shire/commit/c743528ddd01b1900fd63e52985f4e533ec533a6))\n* **shirelang:** implement beforeCharTyped override for custom handling ([ee12c7f](https://github.com/phodal/shire/commit/ee12c7f9cd1eb6dda9df4ec5790f1182e3841b89))\n* **shirelang:** implement highlighting, brace matching, and quote handling ([ced541f](https://github.com/phodal/shire/commit/ced541fa5bba0b55a8d532984928546afbddbd9a))\n\n## [0.9.1](https://github.com/phodal/shire/compare/v0.9.0...v[0.9.1]) (2024-10-10)\n\n### Bug Fixes\n\n* **actions:** The shire file downloaded from the marketplace has not been dynamically updated ([4a0aa6f](https://github.com/phodal/shire/commit/4a0aa6f57306d693cf78a7098e826f87de1179cc))\n* **build:** comment out failureLevel in pluginVerification [#100](https://github.com/phodal/shire/issues/100) ([e260a10](https://github.com/phodal/shire/commit/e260a1055c6b6a93acd0fa938e5e06c400aa0843))\n* fix for json module outside project issue [#112](https://github.com/phodal/shire/issues/112) ([ed12724](https://github.com/phodal/shire/commit/ed12724e2fc460e2a511a9becfa0b8f402ed3fb6))\n* fix plugin group error issue ([acbdeca](https://github.com/phodal/shire/commit/acbdeca7cd2bc22953cea47eaac37b7c44e06506))\n* **github-actions:** correct gradle task paths in release workflow [#100](https://github.com/phodal/shire/issues/100) ([4d5a39d](https://github.com/phodal/shire/commit/4d5a39dbe22ea76845a34ccbadf61c994045643f))\n* **javascript:** fix for siganture issue ([94ebee5](https://github.com/phodal/shire/commit/94ebee5af25aa07864175a351780595ffc08df34))\n* **javascript:** update variable resolution in JSPsiContextVariableProvider ([8e2a7e9](https://github.com/phodal/shire/commit/8e2a7e91b2344bef5ab9f57aae432f72fdd833ac))\n* **lexer:** make sure front-matter parse success [#112](https://github.com/phodal/shire/issues/112) ([a9c3770](https://github.com/phodal/shire/commit/a9c3770fdcf0d52ad412e3a1a157de83e36f13be))\n* **logging:** improve error messages for PatternActionFunc arguments ([844dcd8](https://github.com/phodal/shire/commit/844dcd813d069be62d7f6cddb057f1e2d4ef0a58))\n* **settings:** Test connection failed without applying settings ([e883e38](https://github.com/phodal/shire/commit/e883e383a540368f26295ce3504d5253f637b294))\n* **shirelang:** correct typo in ShireFileModificationListener class name ([8baad36](https://github.com/phodal/shire/commit/8baad367cae909fa19ce542f64041c1be05e65b7))\n\n### Features\n\n* **build.gradle.kts:** import Changelog and update PatchPluginXmlTask ([cd7df2c](https://github.com/phodal/shire/commit/cd7df2c4ea3769aec3681e5418a683015558b37b))\n* **build:** configure specific subprojects in build.gradle.kts [#100](https://github.com/phodal/shire/issues/100) ([e7db461](https://github.com/phodal/shire/commit/e7db4611e96e523bc774040857c40d3b04345f71))\n* **build:** optimize GitHub Actions build space ([0f3a6f9](https://github.com/phodal/shire/commit/0f3a6f9f6c991619c3fa3be990d0ea0f5211b820))\n* **build:** try to resize verify range for reduce size of disk in GitHub Action [#100](https://github.com/phodal/shire/issues/100) ([e127f88](https://github.com/phodal/shire/commit/e127f88b628448dea3d80cbf3befa951c9011e73))\n* **build:** update Gradle setup and plugin verification tasks ([b779cd0](https://github.com/phodal/shire/commit/b779cd0be31599f8e05e45ae634fdec0689fbe04))\n* **build:** update Gradle version and enable PatchPluginXmlTask [#100](https://github.com/phodal/shire/issues/100) ([8044084](https://github.com/phodal/shire/commit/804408437ab7b3e6fb9c6e6d15d7dc566b4a4e90))\n* **compiler:** enable support for custom foreign functions [#116](https://github.com/phodal/shire/issues/116) ([c4e51fc](https://github.com/phodal/shire/commit/c4e51fcc4a64356910723673c2a3c52cdd51b57f))\n* **compiler:** implement foreign function support [#116](https://github.com/phodal/shire/issues/116) ([61059cd](https://github.com/phodal/shire/commit/61059cd8982c6383b6a09584d5caf78fd4769724))\n* **compiler:** refine input type rules in PatternActionProcessor [#112](https://github.com/phodal/shire/issues/112) ([db58a87](https://github.com/phodal/shire/commit/db58a874035ef8c5a202e22f85959e146093f091))\n* **docs, shirelang:** update function parameters and syntax highlighting [#116](https://github.com/phodal/shire/issues/116) ([b6c6e9d](https://github.com/phodal/shire/commit/b6c6e9d98f58c11cedb0f1164f86dd43be777a52))\n* **gradle:** update IntelliJ IDEA version and add plugin verification [#100](https://github.com/phodal/shire/issues/100) ([c5b58a1](https://github.com/phodal/shire/commit/c5b58a1d4d9fbb96788b72634ca216caa10b8448))\n* **gradle:** update IntelliJ platform version and add shire-json language support [#100](https://github.com/phodal/shire/issues/100) ([da4f120](https://github.com/phodal/shire/commit/da4f1206e70922bfd10960981123e9d4d1c8771b))\n* **IDEA-243:** add deps plugins [#100](https://github.com/phodal/shire/issues/100) ([276f391](https://github.com/phodal/shire/commit/276f391c617265d67e562616787322db3c3fa123))\n* init tokenizer function for [#104](https://github.com/phodal/shire/issues/104) ([1ae89ed](https://github.com/phodal/shire/commit/1ae89ed7c39a4f4421f6946fb24e14d232596f59))\n* **javascript:** enhance JavaScriptLanguageToolchainProvider for detailed context collection ([d4341e4](https://github.com/phodal/shire/commit/d4341e44abd263c0c932ff86e729c26c9aa1c747))\n* **javascript:** fix for import issues ([c646e4c](https://github.com/phodal/shire/commit/c646e4c568803e3e7b34ed3dff4904a059e3c0b7))\n* **json-plugin:** add dependency on IntelliJ JSON plugin [#100](https://github.com/phodal/shire/issues/100) ([ce0bbad](https://github.com/phodal/shire/commit/ce0bbadaebd310307f29409c324803ad2d8e9a7d))\n* **markdown:** add support for markdown headers in ShireCompileTest [#112](https://github.com/phodal/shire/issues/112) ([b1d96f6](https://github.com/phodal/shire/commit/b1d96f6c18568766c1213911944a7a41929ccd2f))\n* **parser:** add process and output for parser [#116](https://github.com/phodal/shire/issues/116) ([5b9bbd9](https://github.com/phodal/shire/commit/5b9bbd995ee4f10259eb3198458e6e74843c9a50))\n* **parser:** Support Markdown headers in ShireParser [#112](https://github.com/phodal/shire/issues/112) ([f3e2e09](https://github.com/phodal/shire/commit/f3e2e0936e8a2dc9cdddf5e211f4065ec359d33f))\n* **patternaction:** add tokenizer function and refactor PatternActionFuncType to PatternActionFuncDef ([37ece32](https://github.com/phodal/shire/commit/37ece322421625f46b90ed96615bced0792ca36e))\n* **patternaction:** enhance function documentation and formatting [#112](https://github.com/phodal/shire/issues/112) ([1dfd2a9](https://github.com/phodal/shire/commit/1dfd2a9d553e8ff905249c1ab994bb3a8b57c080))\n* **plugin:** update plugin name and improve markdown table formatting ([05aa38a](https://github.com/phodal/shire/commit/05aa38a5a5440514429c08da1fcb1c878d7be2a3))\n* **post-processors:** Add descriptions and list view for processors [#112](https://github.com/phodal/shire/issues/112) ([4535863](https://github.com/phodal/shire/commit/4535863463b322b2c9619432d4180250c9e43f3b))\n* **shire-json:** move JSON related functionalities from core to shire-json module [#100](https://github.com/phodal/shire/issues/100) ([3f3a77a](https://github.com/phodal/shire/commit/3f3a77a4399a6e66c7b400d452cb1509f2fb674d))\n* **ShireFileListener:** ignore directories and LightVirtualFiles in onUpdated method ([086ed79](https://github.com/phodal/shire/commit/086ed79e9064c56d4eef15b2dee41110fa886c2f))\n* **shirelang:** add example option to ShireToolchainFunctionProvider [#112](https://github.com/phodal/shire/issues/112) ([4c16e36](https://github.com/phodal/shire/commit/4c16e3668540b95a544e00c55583e83f66602645))\n* **shirelang:** add ShireToolchainFunctionProvider and update GitFunctionProvider [#112](https://github.com/phodal/shire/issues/112) ([90a962f](https://github.com/phodal/shire/commit/90a962f762f81537416aba7abe310e72864193dc))\n* **shirelang:** add support for custom functions and update lexer and parser [#116](https://github.com/phodal/shire/issues/116) ([14ef5a0](https://github.com/phodal/shire/commit/14ef5a0ee6e4c30355d8833233b098cc666c475c))\n* **shirelang:** enhance foreign function execution with args support ([f9658ea](https://github.com/phodal/shire/commit/f9658ea65d4a7453985c7327d6f43422c4c227f4)), closes [#119](https://github.com/phodal/shire/issues/119)\n* timeout ([c02f7b2](https://github.com/phodal/shire/commit/c02f7b29ef0f0389b3a9e5adac72ff7a6415bfb5))\n* **tokenizer:** add Jieba Chinese tokenizer support [#112](https://github.com/phodal/shire/issues/112) ([2f73edf](https://github.com/phodal/shire/commit/2f73edf38b6ce0124d37ab99f26de8a931d0098c))\n* **tokenizer:** add multiple tokenizer types support in TokenizerProcessor [#100](https://github.com/phodal/shire/issues/100) ([9575b14](https://github.com/phodal/shire/commit/9575b141109b81ad87157bbe2546908960e53868))\n* **tokenizer:** add variable substitution in tokenizer and related test [#104](https://github.com/phodal/shire/issues/104) ([30c40c5](https://github.com/phodal/shire/commit/30c40c52b1cef465d49339aeb3b9c1b59764cb45))\n* **tokenizer:** ensure distinct tokenization results ([09746ca](https://github.com/phodal/shire/commit/09746cafce7766ffe2a8a6d8b9f866def8a98d21))\n\n## [0.8.2](https://github.com/phodal/shire/compare/v0.8.1...v[0.8.2]) (2024-09-23)\n\n### Bug Fixes\n\n* **middleware:** use <html to replace <html> ([483ab87](https://github.com/phodal/shire/commit/483ab8781e146ba483906394d285c02a217db1bb))\n\n### Features\n\n* add OpenWebpage processor ([24c2bf2](https://github.com/phodal/shire/commit/24c2bf217e601f99bd4628e9438f3edbc0e5d14a))\n* **core:** add ShowWebviewProcessor and WebViewWindow ([6a3ddb4](https://github.com/phodal/shire/commit/6a3ddb47110621558fdc46feaed2e27246bcd116))\n* **core:** allow OpenFileProcessor to accept arguments ([f54e0c0](https://github.com/phodal/shire/commit/f54e0c0ed14292018ae9c99b894cb95299019ea8))\n* **core:** enhance error message in RunCodeProcessor ([87d65cd](https://github.com/phodal/shire/commit/87d65cdc3868efffc3ee91277a3aff07590df85e))\n* **database:** add Excel toolchain functions ([868510c](https://github.com/phodal/shire/commit/868510c6cf949efc813d2e0d7dfd64ed1ae1e3e2))\n* **docs:** update documentation and add WebView functionality ([15b5a4c](https://github.com/phodal/shire/commit/15b5a4c280c91173cbc34d33e2ebffc02155ef64))\n* **gradle:** update pluginUntilBuild version in gradle.properties ([a219ce4](https://github.com/phodal/shire/commit/a219ce41edd44aee7f44c261b27967a3eb0c227a))\n* **httpclient:** add log display in console for HTTP requests ([d181831](https://github.com/phodal/shire/commit/d18183196a3e77845db1dd538d350078b7f4f54e))\n* **httpclient:** improve request logging format in CUrlHttpHandler ([3f7740e](https://github.com/phodal/shire/commit/3f7740e7ecdf0e30935f605c16e86ea743ec7c1f))\n* **httpclient:** refactor CUrlHttpHandler and CUrlConverter for better variable handling ([a039679](https://github.com/phodal/shire/commit/a03967985a5156c8abfc56bb96a3b699495b6e7e))\n* **json-path:** enable for parse start for `data:` ([d5a2d71](https://github.com/phodal/shire/commit/d5a2d71b8eba81c7ecd42e787870552cd59d9d54))\n* **PatternFuncProcessor:** add newline join for Array and List results ([57ea7e6](https://github.com/phodal/shire/commit/57ea7e6e609173f6c67bcfd32eafa71cb17710aa))\n* **ShireFileModifier:** wrap file processing in runReadAction ([e8cc6f0](https://github.com/phodal/shire/commit/e8cc6f06fd8238ea7679dc0e31a1c3f976d2f1d0))\n* **UI:** enable off-screen rendering and improve webview popup ([96f3791](https://github.com/phodal/shire/commit/96f37913f889de070c608313481361cb73badce4))\n\n## [0.8.1](https://github.com/phodal/shire/compare/v0.8.0...v[0.8.1]) (2024-09-18)\n\n### Bug Fixes\n\n* **core:** update file path validation regex and related tests ([3ab4e9f](https://github.com/phodal/shire/commit/3ab4e9f4e8d42643170141ecc4d0538612907def))\n* **database:** update rror messages ([8b4e932](https://github.com/phodal/shire/commit/8b4e932e12cef7a6a2df78b8c76303ceaff5c15a))\n* **llm:** add configuration update from state in OpenAILikeProvider [#85](https://github.com/phodal/shire/issues/85) ([56a1961](https://github.com/phodal/shire/commit/56a19612a4e4fb94471d4a63b876d7b1b6b7d562))\n* **middleware:** 修正文件保存处理器，避免非法文件名 ([35785bd](https://github.com/phodal/shire/commit/35785bdb980f28ba61d30c7827a29d5f33658275))\n* **patternaction:** 修复PatternActionFunc中的空指针异常 ([e048ab9](https://github.com/phodal/shire/commit/e048ab9c058393ad6a9aa509ceeda52c896357ea))\n* **PatternFuncProcessor:** enhance path resolution and refactor tests [#83](https://github.com/phodal/shire/issues/83) ([a81c603](https://github.com/phodal/shire/commit/a81c6032b36f295c2c828010546bae61d64d600f))\n* **PatternFuncProcessor:** remove joinToString from array operations [#83](https://github.com/phodal/shire/issues/83) ([9e251f7](https://github.com/phodal/shire/commit/9e251f7734affa2cc24c1d8be59be97a5bd48f60))\n* **runner:** adjust execution subscription order in ConfigurationRunner ([8ba7447](https://github.com/phodal/shire/commit/8ba7447e5e5570281d370a38917a089cdd67c5d3))\n* **runner:** LlmProvider is still working after canceling the shire process ([d458e1f](https://github.com/phodal/shire/commit/d458e1f88e0ea621b77e593b6b1dc66aba7e7974))\n* **runner:** 简化输出LLM模型名 ([06ddd76](https://github.com/phodal/shire/commit/06ddd76fca69ca6782ed3bc09ed4d2e196c27295))\n* **shirelang:** add empty intentions check and change error level in ShireIntentionHelper ([badc42c](https://github.com/phodal/shire/commit/badc42cb9e6f5dc09326ee0ffdb773fa4a76fe15))\n* **shirelang:** refactor array handling and add regression test [#83](https://github.com/phodal/shire/issues/83) ([1f4c8fe](https://github.com/phodal/shire/commit/1f4c8fe8d96e65959f3c0d61cb6899febb87edf6))\n* **Wiremock:** 添加文件路径到 Wiremock 错误信息中，以便于调试 ([2a888cd](https://github.com/phodal/shire/commit/2a888cd75703c161bb009e0569276557862eae5e))\n* 在ShireProcessHandler中添加异常日志记录 ([28d6dea](https://github.com/phodal/shire/commit/28d6dea0c4650b6254079ed2fee5af6b78ccfe70))\n\n### Features\n\n* add basic batch processor of content ([ec4fa1c](https://github.com/phodal/shire/commit/ec4fa1c58d2053fd22bfdea8c87a318473879d96))\n* add more psiUtil for helper [#89](https://github.com/phodal/shire/issues/89) ([b4fa3eb](https://github.com/phodal/shire/commit/b4fa3eb59c6ab2b64430392bbf4aa5090f283796))\n* **batch-processing:** implement batch processing functionality ([f38dbc2](https://github.com/phodal/shire/commit/f38dbc2915891945fae99f245e2d3425142bd5cc))\n* **batch:** add custom variables support to ShireTemplateCompiler ([6ce8430](https://github.com/phodal/shire/commit/6ce8430ca9a35e272824f43e9ad2b159b7acb9e9))\n* **batch:** add goto decl ([9412afb](https://github.com/phodal/shire/commit/9412afbdf5ff6c38999c603bcd12d4a0055442be))\n* **beforeStreaming:** refactor function naming and add coroutine support ([1a1bd30](https://github.com/phodal/shire/commit/1a1bd30964bf477dcdd038211f2b4b3e13625fc1))\n* **codemodel:** enhance class name extraction in DirectoryStructure [#89](https://github.com/phodal/shire/issues/89) ([dfb02b3](https://github.com/phodal/shire/commit/dfb02b320cfc3fe28528fb513053b47cd2b555f4))\n* **compiler:** add Batch and Destroy functions to PatternActionFunc ([e32fc61](https://github.com/phodal/shire/commit/e32fc61cebd44597c09beba059c32e904c5592fe))\n* **compiler:** refactor action classes and add beforeStreaming functionality ([461361d](https://github.com/phodal/shire/commit/461361df471d668cf4d6748cf53d150306501319))\n* **execute:** enable for gradle run support ([9521375](https://github.com/phodal/shire/commit/95213753eaeaf10ab4da3f17e72a4e0d92deec7d))\n* **git:** add git commit function support ([820ec89](https://github.com/phodal/shire/commit/820ec89991e313caf5d9aa77a7039114c3a4e726))\n* **git:** enhance commitChanges function in GitFunctionProvider ([fb7c286](https://github.com/phodal/shire/commit/fb7c286f31bee87565ac83388ac932becfee7daa))\n* **go:** add golang tool context provider [#89](https://github.com/phodal/shire/issues/89) ([6058a5a](https://github.com/phodal/shire/commit/6058a5a680a2bb5886a810884e90beb6f47ef921))\n* **GoLanguageProvider:** add method to get Go version [#89](https://github.com/phodal/shire/issues/89) ([56478b5](https://github.com/phodal/shire/commit/56478b52b0d367acef7bb984cf3db3f743b3ae75))\n* **GoPsiContextVariableProvider:** map related types to text [#89](https://github.com/phodal/shire/issues/89) ([cce91ed](https://github.com/phodal/shire/commit/cce91ed81c4be2b9a80412e8fa9d8bac10b5f28e))\n* init downloader for marketplace [#86](https://github.com/phodal/shire/issues/86) ([9aaa430](https://github.com/phodal/shire/commit/9aaa43010dca2b55655e32d3ae46961851367658))\n* **javascript:** add variable provider and utility functions ([1e5ff4e](https://github.com/phodal/shire/commit/1e5ff4e2a7084720c8a9ba3b40a841b418140a81))\n* **lifecycle:** add 'beforeStreaming' and 'mock' functions ([293d361](https://github.com/phodal/shire/commit/293d3616296451a8e892a9bb41fe082a8c10cdef))\n* **marketplace:** add download notifications and refresh functionality [#86](https://github.com/phodal/shire/issues/86) ([97482aa](https://github.com/phodal/shire/commit/97482aa964ee633224ca58e55bea44b66178dc4b))\n* **marketplace:** add MarketplacePanel UI and functionality [#86](https://github.com/phodal/shire/issues/86) ([ece56e0](https://github.com/phodal/shire/commit/ece56e07f267f4ea1f7fe85c9165549e21621ab7))\n* **marketplace:** add MarketplaceToolWindowFactory and ShireIdeaIcons [#86](https://github.com/phodal/shire/issues/86) ([70ef324](https://github.com/phodal/shire/commit/70ef324d15432ab190d25b8ebe8304f65281c5c4))\n* **marketplace:** add refresh button and refactor UI layout [#86](https://github.com/phodal/shire/issues/86) ([f996913](https://github.com/phodal/shire/commit/f99691388694d64c8ecac7e652b3f1f18c50af63))\n* **marketplace:** change UI anchor and update error messages [#86](https://github.com/phodal/shire/issues/86) ([a34fd1c](https://github.com/phodal/shire/commit/a34fd1c1cf7c9ccc56a981a49d5c7e9ef7503f8d))\n* **marketplace:** enhance ShirePackage table with download functionality [#86](https://github.com/phodal/shire/issues/86) ([e868010](https://github.com/phodal/shire/commit/e868010c94e958df6b465e28b12a027212cef31a))\n* **marketplace:** refactor MarketplacePanel and add table view [#86](https://github.com/phodal/shire/issues/86) ([7088492](https://github.com/phodal/shire/commit/70884927f878b2ca193c45e5781a9ea86a7e894a))\n* **marketplace:** refactor table component and add install action [#86](https://github.com/phodal/shire/issues/86) ([fc18040](https://github.com/phodal/shire/commit/fc18040664e54dce91189b57c81ecad45739e7e7))\n* **marketplace:** update ShireMarketplaceTable to fetch data from API [#86](https://github.com/phodal/shire/issues/86) ([bcecadb](https://github.com/phodal/shire/commit/bcecadbe2c19e227b42ee6dc858b1610146fa1c7))\n* **mock:** init for run mock serivce ([ec41dfb](https://github.com/phodal/shire/commit/ec41dfbe31dd8dafa2c026a8d26226d20f503369))\n* **mock:** init mock server for testing apis ([6813876](https://github.com/phodal/shire/commit/6813876203fdb4c313d07895cae9d63939666846))\n* **mock:** update Wiremock provider path and improve error handling ([2389f53](https://github.com/phodal/shire/commit/2389f53b53c7db0a3b47826fae412574637955dd))\n* **patternaction:** refactor pattern action function parsing ([4b6e13a](https://github.com/phodal/shire/commit/4b6e13a9f0c5ed7bc5243ba0a6ffef0ff0e89bf7))\n* **PatternFuncProcessor:** change argument addition order and improve error message ([9d2c1bc](https://github.com/phodal/shire/commit/9d2c1bc22fcde20caf3ad34ed52dd24ae6dbc797))\n* **python:** init py psi util ([c441f62](https://github.com/phodal/shire/commit/c441f626132224d34a375bbcb5fb19e0bb68ca84))\n* **run-service:** wrap operations in runReadAction in ShirePythonRunService.kt ([478a3d9](https://github.com/phodal/shire/commit/478a3d990c23cb5d30520cf876fe886c3cd2499f))\n* **search:** disable semantic embedding functionality ([3560a23](https://github.com/phodal/shire/commit/3560a23c1481f9aeddc71bbc86c8739094387345))\n* **shire-go:** add GoPsiContextVariableProvider for Go language support [#89](https://github.com/phodal/shire/issues/89) ([aefdadb](https://github.com/phodal/shire/commit/aefdadbb28e8fa65799445d796f082b19db91c99))\n* **shire-go:** add iota detection in Go expressions and constants [#89](https://github.com/phodal/shire/issues/89) ([33216d7](https://github.com/phodal/shire/commit/33216d77c9e2d4fb510d5f8f8f74ebfeae92e32c))\n* **shire-go:** add support for Go language [#89](https://github.com/phodal/shire/issues/89) ([ab09a6f](https://github.com/phodal/shire/commit/ab09a6fe57586d1ac65893346e18373e6bac8af1))\n* **shire-go:** enhance related classes and code smell handling in GoPsiContextVariableProvider [#89](https://github.com/phodal/shire/issues/89) ([8d91a88](https://github.com/phodal/shire/commit/8d91a88943e350e5365ceb6705f6ceeee42bf5e2))\n* **variable-provider:** enhance context variable handling in JS and Go ([d344944](https://github.com/phodal/shire/commit/d34494437238f58c06c2e4c6c403be02735faf9a))\n* 添加 LLM 提供者未找到的错误信息 ([fde5a88](https://github.com/phodal/shire/commit/fde5a88f24ce99392ccc67335bc7f81c25d20e6e))\n\n## [0.7.4](https://github.com/phodal/shire/compare/v0.7.3...v[0.7.4]) (2024-09-09)\n\n### Bug Fixes\n\n* **db:** fix toolchain call issue ([99eb680](https://github.com/phodal/shire/commit/99eb680b20d806967df871b64a0c752e844f6e76))\n* **runner:** An unexpected exception occurred, causing the shire process cannot be canceled ([7eba18c](https://github.com/phodal/shire/commit/7eba18c8b1adaccf226fad1362a239cb60d19da9))\n* **runner:** The consoleView is not the original consoleView when processing the exit code of the script ([474b681](https://github.com/phodal/shire/commit/474b6813565e790a832c37f74e7ac4acd6db7696))\n* **runner:** The messageFilter of the console view appends extra data ([a47db5d](https://github.com/phodal/shire/commit/a47db5dd1fe51408409339a4711c7d16a23922d1))\n* **shirelang:** ensure null safety in ShireVcsSingleAction [#78](https://github.com/phodal/shire/issues/78) ([0c4665b](https://github.com/phodal/shire/commit/0c4665b9faf4573b2fb66abb1fabccc484fc3d51))\n\n### Features\n\n* **actions:** add support for enabling/disabling actions and improve action config handling [#78](https://github.com/phodal/shire/issues/78) ([045c962](https://github.com/phodal/shire/commit/045c962be11e55139c83cdb241d3a8a13f749b52))\n* **codemodel:** Add JavaScript and TypeScript structure providers ([ce936bd](https://github.com/phodal/shire/commit/ce936bda74be3e1aa07bc47ffde133fefa867f8e))\n* **javascript:** add JavaScript support and build system integration ([c09e3d3](https://github.com/phodal/shire/commit/c09e3d36483be5e85c3cead1dbec88f361f16d71))\n* **javascript:** add JestCodeModifier and JSAutoTestingService ([fde85ad](https://github.com/phodal/shire/commit/fde85ad1e959a18d102e4ce44c6197e0914f3bc8))\n* **javascript:** implement TypeScript refactoring tool and language support ([f46a3f8](https://github.com/phodal/shire/commit/f46a3f877a2aeed6fdce703a9b3008e617cf6625))\n* **llm:** add LlmConfig class for LLM configuration management [#78](https://github.com/phodal/shire/issues/78) ([a824eda](https://github.com/phodal/shire/commit/a824eda7e6bc15b7e81c8fc088484582c1bd4276))\n* **llm:** add maxTokens parameter to CustomFields and LlmConfig [#78](https://github.com/phodal/shire/issues/78) ([c47ae75](https://github.com/phodal/shire/commit/c47ae75e062a65b7b2ca4479efa16346c05dd644))\n* **model:** enable for custom model in project && closed [#78](https://github.com/phodal/shire/issues/78) ([d3e4859](https://github.com/phodal/shire/commit/d3e4859b28f60d7b85e8188ae50eaf5a46ba1abb))\n* **navigation:** implement GotoDeclarationHandler for Shire language ([2c7d744](https://github.com/phodal/shire/commit/2c7d7444759e81e9c3661f5ec5633ae7a086f810))\n* **pattern-action:** refactor to use PatternActionFuncType enum ([445b2ac](https://github.com/phodal/shire/commit/445b2ac9e9539cf41468a7c9a0a819b98d63aa7f))\n\n## [0.7.2](https://github.com/phodal/shire/compare/v0.7.1...v[0.7.2]) (2024-09-05)\n\n### Bug Fixes\n\n* **runtime:** switch to workerThread for terminal UI tasks execution [#72](https://github.com/phodal/shire/issues/72) ([53672d3](https://github.com/phodal/shire/commit/53672d3def0a373de2358a0754a3798c398e8934))\n\n### Features\n\n* **database:** add function to retrieve and display database info ([c30dd13](https://github.com/phodal/shire/commit/c30dd130809290e1a5495ab5960f098945b02f73))\n* **httpclient:** enable pass variable table value to curl.sh file ([615b280](https://github.com/phodal/shire/commit/615b280ec54f4b76dfd1b1823f815373865a6747))\n* **middleware:** add DiffProcessor support [#66](https://github.com/phodal/shire/issues/66) ([b3110f6](https://github.com/phodal/shire/commit/b3110f634fe71be5c11170ccfae155558fd714de))\n* **middleware:** add Patch processor for applying code patches ([425fdb4](https://github.com/phodal/shire/commit/425fdb4ab28aa6f06ee645339d70289b9ae3acf8))\n* **parser:** enable regex pattern function support ([8f88ddd](https://github.com/phodal/shire/commit/8f88ddd0e21d5326f9b6a20c53b43f0c438749c9))\n* **parser:** implement custom ShireGrepFuncCall and refactor related components ([4a319ec](https://github.com/phodal/shire/commit/4a319ec9295b9b82e851c8bd1cdbacb822bd240e))\n* **parser:** implement sed function call and improve injection handling ([00f3aca](https://github.com/phodal/shire/commit/00f3acaf0724eaeea8ce0535ee15bad3d5016f70))\n* **shirelang:** implement regex pattern support for 'grep' function ([e3bb682](https://github.com/phodal/shire/commit/e3bb6828bfa72e9fba2bb39f954097487c8fa1bb))\n* **testing:** add Shire language annotation to test cases and implement shell script runner ([9c53db9](https://github.com/phodal/shire/commit/9c53db9d4609cb78c026a93fb6b719c4b294a05f))\n\n## [0.7.1](https://github.com/phodal/shire/compare/v0.7.0...v[0.7.1]) (2024-09-02)\n\n### Features\n\n* **browse:** add useragent generator [#60](https://github.com/phodal/shire/issues/60) ([df595f4](https://github.com/phodal/shire/commit/df595f4d983a794a41840be289bd6ca119fe35bb))\n* **compiler:** add JsonPath support for pattern actions and closed [#11](https://github.com/phodal/shire/issues/11) ([14bed16](https://github.com/phodal/shire/commit/14bed168ec74647072184b344bb5eb3e70b2a97c))\n* **compiler:** add support for 'capture' and 'thread' pattern actions [#11](https://github.com/phodal/shire/issues/11) ([dc50586](https://github.com/phodal/shire/commit/dc50586aec094172179e469c6faf380d2bae876d))\n* **core:** add cURL execution support and HTTP handler extension point [#11](https://github.com/phodal/shire/issues/11) ([2717014](https://github.com/phodal/shire/commit/271701435325c6856791ce17e81f64c8ff0119bb))\n* **httpclient:** add functionality to convert cURL to HTTP request scratch file [#11](https://github.com/phodal/shire/issues/11) ([079968e](https://github.com/phodal/shire/commit/079968e4ff354e03d139751aaa2d6f5df28669be))\n* **httpclient:** enhance CUrlConverter with variable support and testing adjustments [#11](https://github.com/phodal/shire/issues/11) ([9e97423](https://github.com/phodal/shire/commit/9e97423f583b1e4ec7becb75864945d5e88a4d70))\n* **httpclient:** implement buildFullUrl function for RestClientRequest [#11](https://github.com/phodal/shire/issues/11) ([b49049b](https://github.com/phodal/shire/commit/b49049be5cb56d6214389f03d20fbc120f477e80))\n* **httpclient:** Implement URL builder and scratch file creation [#11](https://github.com/phodal/shire/issues/11) ([9a2162e](https://github.com/phodal/shire/commit/9a2162e29d32f7c167649bf93aec7eff66551df9))\n* **index:** add ShireEnvironmentIndex for indexing environment variables [#11](https://github.com/phodal/shire/issues/11) ([d43eb96](https://github.com/phodal/shire/commit/d43eb96eb51507b6bc8ea2b2e2bec9d57fdac31a))\n* **kotlin-refactor:** implement Kotlin refactoring tool support [#58](https://github.com/phodal/shire/issues/58) ([ad6d1b2](https://github.com/phodal/shire/commit/ad6d1b253670b2d8a77eb901c0fb50830dccebc6))\n* **kotlin:** implement structure providers for Kotlin plugin [#58](https://github.com/phodal/shire/issues/58) ([393b747](https://github.com/phodal/shire/commit/393b74799db5d56d09b769bb5b1f5862a5e33e73))\n* **languages:** add shire-markdown module and update dependencies [#59](https://github.com/phodal/shire/issues/59) ([26fd92a](https://github.com/phodal/shire/commit/26fd92adf599b97fc599aa3d4c5be7678ddff000))\n* **markdown:** add MarkdownPsiCapture for URL extraction [#59](https://github.com/phodal/shire/issues/59) ([ab9739b](https://github.com/phodal/shire/commit/ab9739bf0607d00d914d1265a56c94c490ce92b6))\n* **runner:** Add LLM output to runFinish method and process handling [#60](https://github.com/phodal/shire/issues/60) ([5870ef8](https://github.com/phodal/shire/commit/5870ef8e00231abb12be061cf4c1a89964da01d1))\n* **shire-kotlin:** add Java support for KotlinLanguageToolchainProvider [#58](https://github.com/phodal/shire/issues/58) ([e026929](https://github.com/phodal/shire/commit/e0269299c57e99f3dc7b15608657544148901004))\n* **shirelang:** add crawl functionality and processor support [#59](https://github.com/phodal/shire/issues/59) ([ee5a3ea](https://github.com/phodal/shire/commit/ee5a3ea42b3b2b7feef4974002d5bb8b92817037))\n* **shirelang:** add support for threading function execution [#60](https://github.com/phodal/shire/issues/60) ([0d31f9e](https://github.com/phodal/shire/commit/0d31f9e8636d9fa169eb6359dd9fff1c4ebc61ad))\n\n### Bug Fixes\n\n* **executor:** handle exceptions in ShireDefaultLlmExecutor [#60](https://github.com/phodal/shire/issues/60) ([da44e85](https://github.com/phodal/shire/commit/da44e858f32ea9bdb1c68b7b2b5d9f1db671676a))\n* **plugin:** add Kotlin module support ([c3c1ecb](https://github.com/phodal/shire/commit/c3c1ecb98b03418b88415e26eb9742db82da3806))\n* **shirelang:** add exception handling for LlmProvider streaming output [#60](https://github.com/phodal/shire/issues/60) ([a8272b2](https://github.com/phodal/shire/commit/a8272b2341e7770d2109ac666036e02d3d4bf103))\n\n## [0.5.2](https://github.com/phodal/shire/compare/v0.5.0...v[0.5.2]) (2024-08-15)\n\n### Bug Fixes\n\n* add lost files ([e6f524d](https://github.com/phodal/shire/commit/e6f524dbf661b128270591bf92fe6a24795a9ae6))\n\n### Features\n\n* **compiler:** enhance query processor to lookup elements [#41](https://github.com/phodal/shire/issues/41) ([dbcdc6b](https://github.com/phodal/shire/commit/dbcdc6b89a8ab913245b2eed8858197c447bd96b))\n* **git-plugin:** add Git4Idea plugin dependency [#41](https://github.com/phodal/shire/issues/41) ([f59cc83](https://github.com/phodal/shire/commit/f59cc83a6cb449f7de4f70d8e5cbe01a2064299e))\n* **search:** add LLM reranker and reranker interface [#46](https://github.com/phodal/shire/issues/46) ([6a7b599](https://github.com/phodal/shire/commit/6a7b599be6ed44ac3e540865d3af6d995bebcf1d))\n* **search:** add new ranking algorithm and update reranking methods [#46](https://github.com/phodal/shire/issues/46) ([b0b92b5](https://github.com/phodal/shire/commit/b0b92b555118543f5a10bd7187788b0db6e4de5a))\n* **search:** enhance search functionality in SemanticService [#46](https://github.com/phodal/shire/issues/46) ([995331c](https://github.com/phodal/shire/commit/995331c76efafc9bf1deb5224c61044951a576c6))\n* **search:** replace IndexEntry with ScoredEntry and add reranking functionality [#46](https://github.com/phodal/shire/issues/46) ([6a84387](https://github.com/phodal/shire/commit/6a843872b0e185028ae7f92f5aa0226efd3bd92b))\n\n### Reverts\n\n* Revert \"refactor(core): update SecretPattern and SecretPatterns classes #47\" ([1e1d556](https://github.com/phodal/shire/commit/1e1d5560303bd59dae2b73567c03b853041c33a7)), closes [#47](https://github.com/phodal/shire/issues/47)\n\n## [0.4.8](https://github.com/phodal/shire/compare/v0.4.7...v[0.4.8]) (2024-08-02)\n\n### Bug Fixes\n\n* **guard:** improve regex pattern validation and update UK phone pattern [#47](https://github.com/phodal/shire/issues/47) ([bcc7e16](https://github.com/phodal/shire/commit/bcc7e162ad328445c707d4df8a0041ac23d391ad))\n\n### Features\n\n* **core:** add GuardScanner interface and ScanResult data class [#47](https://github.com/phodal/shire/issues/47) ([b32b7d8](https://github.com/phodal/shire/commit/b32b7d8e53dd52129ea2a2280fe4682bba3591b8))\n* **guard:** add matching and masking functions, remove Joni dependency [#47](https://github.com/phodal/shire/issues/47) ([6a12233](https://github.com/phodal/shire/commit/6a12233170625ed147694167b8c71690649f0266))\n* **scanner:** refactor scanner classes and update documentation [#47](https://github.com/phodal/shire/issues/47) ([a223428](https://github.com/phodal/shire/commit/a223428131c757fdf1bf2cde8a0877895020d13c))\n* **schema-provider:** add CustomAgentSchemaFileProvider to factory list [#47](https://github.com/phodal/shire/issues/47) ([c8999e5](https://github.com/phodal/shire/commit/c8999e59d6ea9fac58a59e2864680d89ec163253))\n* **search:** add dimensions parameter to embedInternal function ([494a8b5](https://github.com/phodal/shire/commit/494a8b56f27d8e0f90338457e3276321fac724dc))\n* **secrets-detection:** rename secretType to description and add LocalModelBasedScanner [#47](https://github.com/phodal/shire/issues/47) ([091bacd](https://github.com/phodal/shire/commit/091bacdaea7fe70542b1016840670998e2649d38))\n* **secrets-guard:** implement regex-based secret detection [#47](https://github.com/phodal/shire/issues/47) ([1eb2517](https://github.com/phodal/shire/commit/1eb2517e69f2b46066d41e13fc07539eb021ddb2))\n* **secrets:** implement regex patterns for PII detection ([a718ac7](https://github.com/phodal/shire/commit/a718ac7c248c7de043f7c2b7c641ed162b7bd7a4))\n* **security:** implement BanKeywordsScanner and Replacer interface [#47](https://github.com/phodal/shire/issues/47) ([47f2b69](https://github.com/phodal/shire/commit/47f2b697b857c39d76d85f1338b7128288fb4b99))\n* **shirelang:** implement redact function for data masking [#47](https://github.com/phodal/shire/issues/47) ([ed28585](https://github.com/phodal/shire/commit/ed285856f36e443687fc8bc75f3a70084d75723f))\n\n## [0.4.7](https://github.com/phodal/shire/compare/v0.4.6...v[0.4.7]) (2024-07-30)\n\n### Bug Fixes\n\n* **core:** enhance InsertUtil to validate offset range ([022d8e2](https://github.com/phodal/shire/commit/022d8e249da9b5aaa7e7166cc69de2269ff05e34))\n\n### Features\n\n* **core:** add UpdateEditorTextProcessor ([1d8b566](https://github.com/phodal/shire/commit/1d8b566e2b2b0da7ba98f88fecb9ec8be89214c2))\n* **docs:** update lifecycle documentation and examples ([9ca64fe](https://github.com/phodal/shire/commit/9ca64fee7870c5b9365541e4d4a67391758fd83f))\n* **git-provider:** implement lookupGitData and ShireVcsCommit updates [#41](https://github.com/phodal/shire/issues/41) ([2d56eaf](https://github.com/phodal/shire/commit/2d56eafbdbd15c6824da891366529da3e6282d49))\n* **llm:** enhance OpenAILikeProvider to include temperature setting ([cbb6f1d](https://github.com/phodal/shire/commit/cbb6f1d15041520c19f8f2ffde54b8ae7aa1bbeb))\n* **provider:** add GitQLDataProvider and update ShireQLDataProvider interface [#41](https://github.com/phodal/shire/issues/41) ([5af617e](https://github.com/phodal/shire/commit/5af617e2ebbabd0908c0ef9d9c82eead4886a8f7))\n* **settings:** add temperature setting and UI component ([264211f](https://github.com/phodal/shire/commit/264211f5937a0d42f10d90a8dfee92aae29035ac))\n* **shirelang:** add VcsStatementProcessor for commit info handling [#41](https://github.com/phodal/shire/issues/41) ([c71fe57](https://github.com/phodal/shire/commit/c71fe57d5dcf73a37298139a2ac1dee34b4f5c91))\n\n## [0.4.6](https://github.com/phodal/shire/compare/v0.4.5...v[0.4.6]) (2024-07-24)\n\n### Bug Fixes\n\n* **compiler:** handle exceptions when finding files ([e85eb79](https://github.com/phodal/shire/commit/e85eb79923442db4a519c62fa78242862cd16d76))\n\n### Features\n\n* **compiler:** add support for preserving last output in function execution ([73739f5](https://github.com/phodal/shire/commit/73739f5bf2bd1ab4c1fa2b71979690cfd53fd64f))\n* **compiler:** enhance afterStreaming execution flow ([c2b7227](https://github.com/phodal/shire/commit/c2b7227abe7f129cbad9f553c2f759928709ba77))\n* **core:** add MarkdownPsiContextVariableProvider ([5418957](https://github.com/phodal/shire/commit/5418957719f848ac0e28aac345c8b0a2e95c3dd4))\n* **core:** allow null values in compiledVariables and refactor file execution ([20eed68](https://github.com/phodal/shire/commit/20eed683f0d265bcbbef2e710afe0784f320134b))\n* **core:** enhance MarkdownPsiContextVariableProvider for file context ([33b97da](https://github.com/phodal/shire/commit/33b97da7fbb7db91ae34c397f8caf1165266f819))\n* **core:** enhance MarkdownPsiContextVariableProvider for HTML conversion ([451e61d](https://github.com/phodal/shire/commit/451e61d73e370601437af95c12ce6364e8efa094))\n* **custom:** add custom SSE handling ([5735a58](https://github.com/phodal/shire/commit/5735a589b4f7e45b3a750dfc736135b578eb0e08))\n* **docs:** update response routing for Java and dynamic input ([0bd650d](https://github.com/phodal/shire/commit/0bd650dc75d39a4a966d8873551cd33b29c8ef11))\n* **runner:** enhance Shire runner to handle last output ([4e2804b](https://github.com/phodal/shire/commit/4e2804b6501a7086c3e0437e1bff6e9fcaa6f309))\n* **searching:** add similarity threshold to search function ([dd8cd73](https://github.com/phodal/shire/commit/dd8cd73c9c9466f14551650a685fb2d5f0538f03))\n* **search:** normalize embeddings and update search methods ([ad907ec](https://github.com/phodal/shire/commit/ad907ec2b27c822d3c8b07467f550618133a6ebc))\n* **Shirelang:** enhance HobbitHole and ShireRunFileAction for dynamic interaction ([18f3679](https://github.com/phodal/shire/commit/18f3679711dea6dcc1588a1d921dc9339ad03279))\n* **shirelang:** improve file not found error logging ([043488e](https://github.com/phodal/shire/commit/043488e6c1a81066d2dda95bcc661d932af7fff1))\n* **ShireRunner:** enhance error handling for detachProcess ([d221082](https://github.com/phodal/shire/commit/d22108282ac9e2fedba2442986708b70b19b35cd))\n* **testing:** add new test case for afterStreamingOnly functionality ([a91be0d](https://github.com/phodal/shire/commit/a91be0d2532bc7540ac3bdfefbcdfc707513622c))\n\n## [0.4.5](https://github.com/phodal/shire/compare/v0.4.3...v[0.4.5]) (2024-07-19)\n\n### Features\n\n* **core:** implement equals and hashCode for IndexEntry ([2543805](https://github.com/phodal/shire/commit/25438059ca7ee50a2c47fba29a7e0cc8ef8ed677))\n* **search:** add interface for similarity algorithms ([27c65c1](https://github.com/phodal/shire/commit/27c65c1c0dd85c7c91ddcfd72ba90470c03136f8))\n* **search:** implement BM25 similarity algorithm and refactor SimilarChunkSearcher ([ef32475](https://github.com/phodal/shire/commit/ef3247552446f1218bd6a0ea30857cb952160933))\n\n## [0.4.3](https://github.com/phodal/shire/compare/v0.4.2...v[0.4.3]) (2024-07-14)\n\n### Bug Fixes\n\n* **actions:** handle null hole in context menu actions ([4a04f35](https://github.com/phodal/shire/commit/4a04f356b16689514bdc4af10df30b8d136b8b6a))\n\n### Features\n\n* **compiler:** add Tee class for writing to files [#36](https://github.com/phodal/shire/issues/36) ([153cc93](https://github.com/phodal/shire/commit/153cc93097f162e96b2f37aa1011f3caf6178886))\n* **interaction:** add PasteBoard interaction type ([33afb37](https://github.com/phodal/shire/commit/33afb37775288360865cb3e47b6a943bee1895bd))\n* **middleware:** add append functionality and AppendProcessor [#36](https://github.com/phodal/shire/issues/36) ([7413368](https://github.com/phodal/shire/commit/74133686a19869e25ff84a5c0392c081c9df787d))\n* **middleware:** expose and update compiledVariables across components [#36](https://github.com/phodal/shire/issues/36) ([c83ffbe](https://github.com/phodal/shire/commit/c83ffbe27659d96139ee3b7165aa6982495c1187))\n* **provider:** add method support for JavaPsiQLInterpreter ([ad83803](https://github.com/phodal/shire/commit/ad838038fefac63ad64c4e24351e34db50319f89))\n* **provider:** add PsiQLMethodCallInterpreter interface ([c9b6606](https://github.com/phodal/shire/commit/c9b6606d127d0a4e4f6df2d1c0229569272f5e25))\n* **shirelang:** extend pipelineArg syntax and add contentTee test [#36](https://github.com/phodal/shire/issues/36) ([42031b7](https://github.com/phodal/shire/commit/42031b7065886b9e399b07497e5157c2d2cd5652))\n\n## [0.4.2](https://github.com/phodal/shire/compare/v0.4.1...v[0.4.2]) (2024-07-09)\n\n### Bug Fixes\n\n* **git:** handle null data context in GitToolchainVariableProvider ([69c48cd](https://github.com/phodal/shire/commit/69c48cd74e444cebdbeadae31bcf84b71c11a0ed))\n\n### Features\n\n* **build:** add kotlinx-coroutines-core dependency ([836332b](https://github.com/phodal/shire/commit/836332bfdca2481f4762a331b7d82b207a4f374b))\n* **run:** add console message before running configuration ([a27e899](https://github.com/phodal/shire/commit/a27e8993cd2090293d42764fb9215a7955853c84))\n* **search:** add caching support to semantic search ([7ef7287](https://github.com/phodal/shire/commit/7ef7287ecd15b27726d456587942204ccb184005))\n\n## [0.4.1](https://github.com/phodal/shire/compare/v0.3.0...v[0.4.1]) (2024-07-07)\n\n### Bug Fixes\n\n* **compiler:** handle null defaultTask in TaskRoutes.kt ([10cfc67](https://github.com/phodal/shire/commit/10cfc67464c249822a1abdc400bbadd32726537b))\n* **completion:** update HobbitHoleValueCompletion to display action location with descriptions. ([71153b4](https://github.com/phodal/shire/commit/71153b4a8aed7d8c617b1ec37cd39762a04ca1cf))\n* **config:** handle nullable Flow<String> in EditorInteractionProvider ([6086166](https://github.com/phodal/shire/commit/6086166d6e95bf7c1d24e13fba5e6587ed74d349))\n* **core:** ensure thread safety in BaseCodeGenTask.kt ([4479e8a](https://github.com/phodal/shire/commit/4479e8aef4356f49ac6bb2ee7c5674415e3c6359))\n* **run:** handle line markers for leaf elements only ([62f09f6](https://github.com/phodal/shire/commit/62f09f6399e03b5e14500b268341e99ad5ab80f1))\n* **runner:** detach process handler in ShireRunner ([041c356](https://github.com/phodal/shire/commit/041c35682330af2257c80b8bb63377b0f5b72317))\n* **shirelang:** check for null before adding action to toolsMenu ([289f578](https://github.com/phodal/shire/commit/289f57876eecea204acdabde87dec7016b5d4857))\n* **shirelang:** update line marker provider to support front matter entries ([2e843a9](https://github.com/phodal/shire/commit/2e843a913b19fb201190212f5d35244064495274))\n\n### Features\n\n* **actions:** add method to set keymap shortcut ([f99686a](https://github.com/phodal/shire/commit/f99686a6017f9a50a506aa31a77f6902ff96adae))\n* **code-completion:** refactor code completion task and add InsertUtil [#29](https://github.com/phodal/shire/issues/29) ([b02987e](https://github.com/phodal/shire/commit/b02987edb180f8dc4db36a607651d512fc3b6d1e))\n* **compiler:** add CaseMatch functionality in PatternActionFunc [#29](https://github.com/phodal/shire/issues/29) ([0521198](https://github.com/phodal/shire/commit/05211984004d3e0b96e77c76a6a252e168eafd87))\n* **compiler:** add save file functionality ([c6bbde6](https://github.com/phodal/shire/commit/c6bbde6e87f9aa065e7daa9832f6bacec1197878))\n* **compiler:** add support for WHEN condition and VARIABLES ([593abd5](https://github.com/phodal/shire/commit/593abd56c897d527891ae0e365f177aad0809b18))\n* **completion:** add PostProcessor completion provider ([4968586](https://github.com/phodal/shire/commit/49685869b416679d0c050d06d2bfa523e3266c6d))\n* **completion:** add PSI context variables to VariableCompletionProvider [#29](https://github.com/phodal/shire/issues/29) ([564b0fe](https://github.com/phodal/shire/commit/564b0fe36aa318bb852b08a5e000edc05d315be5))\n* **completion:** add QueryStatementCompletion provider ([696a306](https://github.com/phodal/shire/commit/696a3069f8e4302f94fdfed4c5b0d87e56511580))\n* **core:** add code completion task and related changes [#29](https://github.com/phodal/shire/issues/29) ([34eb6fe](https://github.com/phodal/shire/commit/34eb6fec9f5d70b99068870382f7fe8f1cc7154c))\n* **core:** add GitActionLocationEditor for commit menu ([48db3c4](https://github.com/phodal/shire/commit/48db3c4e1325ab93201fcde518cd4924cc58f2f8))\n* **core:** add postExecute callback to code execution tasks [#29](https://github.com/phodal/shire/issues/29) ([af6567b](https://github.com/phodal/shire/commit/af6567bfdf418643d44af590ce1a266b422b8a92))\n* **core:** add postExecute invocation and update interactionType in ShireDefaultRunner [#29](https://github.com/phodal/shire/issues/29) ([8bc4b8b](https://github.com/phodal/shire/commit/8bc4b8bf2b9ee1397286b1769db504b953e72c29))\n* **core:** add reflection support for ToolchainVariable ([72ec463](https://github.com/phodal/shire/commit/72ec463441f50c0a65adc174c65e8a7fcdc86700))\n* **docs:** add agent examples and documentation provider ([8d7aafe](https://github.com/phodal/shire/commit/8d7aafeb9107503fa3797700a214db19b37939de))\n* **docs:** add examples for code comments, refactoring, CLI copilot, and commit message generation ([1e07f68](https://github.com/phodal/shire/commit/1e07f689fc50d0eaa4eff7c72339765d6b646dff))\n* **EditorInteractionProvider:** enhance task creation and error handling [#29](https://github.com/phodal/shire/issues/29) ([e0f34a3](https://github.com/phodal/shire/commit/e0f34a381cc19847330881966d02956f891786f8))\n* **git:** add commit message UI retrieval improvement in ShireVcsSingleAction ([a8f11de](https://github.com/phodal/shire/commit/a8f11de61dcc51fa55a128f05140a8e83223b73d))\n* **input:** add custom input box action ([8ec40a4](https://github.com/phodal/shire/commit/8ec40a41517b17f5644268a9eb2ea885233377c6))\n* **interaction:** add support for running code in Run panel ([7c4e8d5](https://github.com/phodal/shire/commit/7c4e8d5f4f798e73b01fa91a473afb66c3e1ec5c))\n* **interaction:** improve code completion and generation tasks [#29](https://github.com/phodal/shire/issues/29) ([957e75d](https://github.com/phodal/shire/commit/957e75dbef245538472313595882c872093c1309))\n* **interaction:** refactor code generation tasks and add BaseCodeGenTask [#29](https://github.com/phodal/shire/issues/29) ([0f84b3b](https://github.com/phodal/shire/commit/0f84b3bd80af33b07c5831ad5441b9df1ee9a7a8))\n* **java:** add class structure representation and data builder [#29](https://github.com/phodal/shire/issues/29) ([90f2364](https://github.com/phodal/shire/commit/90f2364ffbbb45f73a6e09e801461080d8a9010a))\n* **java:** add method caller and called method lookup [#29](https://github.com/phodal/shire/issues/29) ([6b46c00](https://github.com/phodal/shire/commit/6b46c001b5f0a209b8cfd65418205c5206698852))\n* **java:** add methods to retrieve containing class and method ([b4c26ea](https://github.com/phodal/shire/commit/b4c26eaf868a1dfb9845d377b330b933d97eda61))\n* **keyboard:** add support for setting keymap shortcuts ([4eda080](https://github.com/phodal/shire/commit/4eda0800f6b168c315b1ce72231a66508ef625ef))\n* **lints:** add duplicate agent inspection ([7947d3c](https://github.com/phodal/shire/commit/7947d3cd92329d9e81a241682646dfdb566e0002))\n* **logging:** improve error handling and logging in ShireActionStartupActivity ([e661e16](https://github.com/phodal/shire/commit/e661e161f51a7837cbdb88387bbc3b7533eb306e))\n* **middleware:** add InsertNewlineProcessor ([73cebef](https://github.com/phodal/shire/commit/73cebef93bff5e71558938e8d6153f906276a895))\n* **middleware:** add ParseCommentProcessor ([aafb938](https://github.com/phodal/shire/commit/aafb9386c5e9e55a54577c531cd770c83a557f5f))\n* **provider:** add terminal location executor ([99c93a0](https://github.com/phodal/shire/commit/99c93a0da396940ddd1f2fe1c186474d609fbcce))\n* **runner:** add support for user input in Shire configuration ([6ffebd6](https://github.com/phodal/shire/commit/6ffebd6a2fd0ab5004dde65526b2eb11de395dd1))\n* **runner:** refactor ShireRunner to improve terminal task execution and error handling ([b236ad2](https://github.com/phodal/shire/commit/b236ad2f5c7b238c26e7e021d17991aaf312bc0f))\n* **shirelang:** add icon support and improve line marker provider [#29](https://github.com/phodal/shire/issues/29) ([f12c199](https://github.com/phodal/shire/commit/f12c19900f307ff84e057b304564e0b5d37e5454))\n* **shirelang:** add ShirePsiExprLineMarkerProvider for line marker support [#29](https://github.com/phodal/shire/issues/29) ([1a5c3ca](https://github.com/phodal/shire/commit/1a5c3ca56752179586baf390c85a4f21f2c4bd07))\n* **shirelang:** refactor and improve pattern action processing [#29](https://github.com/phodal/shire/issues/29) ([57689a2](https://github.com/phodal/shire/commit/57689a2ae0e8c803bbeb6e646e95f39ec095a8e9))\n* **shirelang:** update line marker provider for ShirePsiExpr ([6448aa1](https://github.com/phodal/shire/commit/6448aa154ccc9a6e02213cf664dc7d6aa84aa05d))\n* **shirelang:** update line marker provider to support front matter entries ([1d4bb4b](https://github.com/phodal/shire/commit/1d4bb4b4c9403b88ede31b2ca38b92e1ce4d8fef))\n* **terminal:** add input box popup for terminal action ([03dce26](https://github.com/phodal/shire/commit/03dce2625011eea61cd3b2b5e0860527a904229f))\n* **terminal:** add shell command suggestion action ([958340c](https://github.com/phodal/shire/commit/958340c89a7dcc8e6e1eef190d31f409356be83c))\n* **terminal:** add ShireTerminalAction for custom assistants ([01023de](https://github.com/phodal/shire/commit/01023de953d69115760d0b6c29a085f344a2f067))\n* **terminal:** add TerminalToolchainVariableProvider ([8b13dc3](https://github.com/phodal/shire/commit/8b13dc39d88636f34196e7807c700d94f957feb3))\n* **variable:** add BuiltinVariable and resolver ([f88af49](https://github.com/phodal/shire/commit/f88af49fe0bc197eb9d1443c0addafb1d7157667))\n* **variable:** add SystemInfoVariable and resolver ([c1a54eb](https://github.com/phodal/shire/commit/c1a54eb4cdc1f06cbb0b47d7ce58e639d56404bd))\n* **VariableCompletionProvider:** add icon to variable lookup elements [#29](https://github.com/phodal/shire/issues/29) ([7ae12c5](https://github.com/phodal/shire/commit/7ae12c5f071c1ec494f7410f482a4375a1eba01b))\n* **variables:** add code smell detection and test data generation [#29](https://github.com/phodal/shire/issues/29) ([3763184](https://github.com/phodal/shire/commit/3763184da20ade0d3df8a9916730855438502f41))\n* **variables:** add support for similar code search ([5fe7f8f](https://github.com/phodal/shire/commit/5fe7f8f2db96727312d56101e77abff9726e9138))\n* **vcs): add Shirefeat VCS single(vcs:** action ([e982cec](https://github.com/phodal/shire/commit/e982cecd524b786fd0c30d12770587286082e3f4))\n\n## [0.0.8](https://github.com/phodal/shire/compare/v0.0.7...v[0.0.8]) (2024-07-01)\n\n### Bug Fixes\n\n* **compiler:** wrap parsing operations in read actions ([97c8d15](https://github.com/phodal/shire/commit/97c8d156b78971f787bcfd202eb6f6aa3f030a06))\n* **compiler:** wrap parsing operations in read actions ([76e1700](https://github.com/phodal/shire/commit/76e1700b7b213d73de07f53053df28c96bba7905))\n* **completion:** fix code fence insertion in completion ([7beb240](https://github.com/phodal/shire/commit/7beb240a2eaafd997e85d82e52330ea5f5fb3cf2))\n* **shirelang:** refine action body parsing in FrontmatterParser kt file ([32776cd](https://github.com/phodal/shire/commit/32776cdf389710c941010a3d117bd5076202d1b1))\n* **shirelang:** update when condition syntax and test evaluation ([2ee436c](https://github.com/phodal/shire/commit/2ee436c73362d06ecb7dd1858c42d094f0205a34))\n\n### Features\n\n* **actions:** refactor action groups and context menu action ([900d613](https://github.com/phodal/shire/commit/900d613900ed6dd9785b8ea3a1b082065db18f54))\n* **chat:** add ChatRole enum and FileGenerateTask for file output ([9bf98d5](https://github.com/phodal/shire/commit/9bf98d590d0c9a5e6726969009809fdefe02d4a8))\n* **compiler:** add 'afterStreaming' feature and enhance pattern action processing [#24](https://github.com/phodal/shire/issues/24) ([bb21198](https://github.com/phodal/shire/commit/bb2119819f27bdc966dc0949f4b79681989bccaf))\n* **compiler:** add FunctionStatementProcessor and refactor related classes [#24](https://github.com/phodal/shire/issues/24) ([1179c5e](https://github.com/phodal/shire/commit/1179c5e2089a1bd821e17dcd8d37a54c3a3977ca))\n* **compiler:** add jsonpath support and modify error condition [#24](https://github.com/phodal/shire/issues/24) ([1913e4f](https://github.com/phodal/shire/commit/1913e4f994c208d624dbed7f70caff4e888e297e))\n* **compiler:** enhance function statement processing and add new pattern actions [#24](https://github.com/phodal/shire/issues/24) ([054fec5](https://github.com/phodal/shire/commit/054fec5ae136a0507a715bd5c222351616e81c73))\n* **compiler:** enhance statement processing in FunctionStatementProcessor [#24](https://github.com/phodal/shire/issues/24) ([56a551c](https://github.com/phodal/shire/commit/56a551c2b40ec86c59ee4ca2226040f5e0ef9a57))\n* **compiler:** refactor function execution and improve logging [#24](https://github.com/phodal/shire/issues/24) ([8944045](https://github.com/phodal/shire/commit/8944045043e5b6b4b00f18e4c49c27f97aade484))\n* **compiler:** refactor method invocation in FunctionStatementProcessor ([5b4911b](https://github.com/phodal/shire/commit/5b4911b38c8a9f1808a6028e8ea596c86c21d041))\n* **compiler:** update function execution and case matching logic [#24](https://github.com/phodal/shire/issues/24) ([ae44272](https://github.com/phodal/shire/commit/ae44272ddab12bedfa89cd1efd6cc7dc53253809))\n* **core:** enhance code parsing and saving [#24](https://github.com/phodal/shire/issues/24) ([0e95539](https://github.com/phodal/shire/commit/0e95539119bddbf5c1ffd5f95179fa28f6da3213))\n* **core:** modify execute method to return string [#27](https://github.com/phodal/shire/issues/27) ([aeaf5d4](https://github.com/phodal/shire/commit/aeaf5d488b98da2db5adccc1d8159f6f8912dff8))\n* **core:** refactor LlmProvider and related classes to shirecore package [#27](https://github.com/phodal/shire/issues/27) ([92e4026](https://github.com/phodal/shire/commit/92e4026c5407c9fa59e1854e24f7b5712797dcb3))\n* **core:** update InteractionType and improve coroutine handling ([fffda5c](https://github.com/phodal/shire/commit/fffda5cc122eb48f60dc1063648e9aa81f22e7ec))\n* **custom:** add custom SSE processor and JSON response callback [#10](https://github.com/phodal/shire/issues/10) ([6f75068](https://github.com/phodal/shire/commit/6f750687997d7660c73be3aa38781871da0f9a27))\n* **docs:** add custom AI agent quickstart guide [#10](https://github.com/phodal/shire/issues/10) ([03f777b](https://github.com/phodal/shire/commit/03f777bf3437316259e9b932c0c01aada1592881))\n* **docs:** add IDE note and update GitToolchainVariableProvider [#27](https://github.com/phodal/shire/issues/27) ([d59fcb8](https://github.com/phodal/shire/commit/d59fcb804185a9eb94a71b297b741ecf7e18f7e2))\n* **editor:** add smart code insertion feature [#24](https://github.com/phodal/shire/issues/24) ([788051f](https://github.com/phodal/shire/commit/788051f45f39712844536c4b8460c440a403b799))\n* **folding:** add support for query statements and block comments ([32e1da8](https://github.com/phodal/shire/commit/32e1da8e9287509d7e561aeda12fa081eaa2ee21))\n* **grammar:** add support for QUOTE_STRING in agentId ([3fdf55c](https://github.com/phodal/shire/commit/3fdf55c59a306e3884a8fd37aecee94c610b0a68))\n* **InteractionType:** add new interaction type and change defaults ([b944f93](https://github.com/phodal/shire/commit/b944f935b4a891297d03933fbf7517e0eced93f3))\n* **java:** add smart insert method in JavaCodeModifier ([42d8326](https://github.com/phodal/shire/commit/42d8326c95c5df03d34beb11b8b9a08374fc6884))\n* **lexer:** add brace level tracking and state transitions [#16](https://github.com/phodal/shire/issues/16) ([882444a](https://github.com/phodal/shire/commit/882444a62f0053e44705a058632a4c73abd784ef))\n* **lexer:** add support for multiple front matter variables [#16](https://github.com/phodal/shire/issues/16) ([9eb9f8d](https://github.com/phodal/shire/commit/9eb9f8d6ed8761f33f3f3736e51b285667004867))\n* **middleware:** Add console parameter to PostProcessor execute methods [#24](https://github.com/phodal/shire/issues/24) ([ebcbd94](https://github.com/phodal/shire/commit/ebcbd944182804dfa44050f46ef38e42f658eb96))\n* **middleware:** add FormatCode functionality ([6905f4f](https://github.com/phodal/shire/commit/6905f4f25dea2a7757811a8b14076f5afd0e045c))\n* **middleware:** add OpenFileProcessor to handle file opening [#24](https://github.com/phodal/shire/issues/24) ([99a2ec2](https://github.com/phodal/shire/commit/99a2ec2efb2f04194c927ccab4ef767466f9867c))\n* **middleware:** add RunCodeProcessor and ExtensionPoint for file execution [#24](https://github.com/phodal/shire/issues/24) ([0b9f065](https://github.com/phodal/shire/commit/0b9f065f7b4925158429f43152429e38b38dcd21))\n* **middleware:** enhance code execution, file saving and verification [#24](https://github.com/phodal/shire/issues/24) ([ed7ca3b](https://github.com/phodal/shire/commit/ed7ca3baaa7af894af532499d33c86b704af4d9f))\n* **parser:** add parentheses detection in method calls [#16](https://github.com/phodal/shire/issues/16) ([29abe86](https://github.com/phodal/shire/commit/29abe86737527e152a686bcf32c89e74849aa574))\n* **plugin:** add Python support to Shirelang plugin [#24](https://github.com/phodal/shire/issues/24) ([7da1a1f](https://github.com/phodal/shire/commit/7da1a1ff6e798613740924fefb21cbb58cc6e92e))\n* **QueryStatementProcessor:** enhance error logging for method or field not found [#16](https://github.com/phodal/shire/issues/16) ([9dd696f](https://github.com/phodal/shire/commit/9dd696fac416c077712e5a998242af664e700436))\n* **run-code:** add CLI execution support for Python, JavaScript, and Ruby files [#24](https://github.com/phodal/shire/issues/24) ([0619ecb](https://github.com/phodal/shire/commit/0619ecb511d8976a5b4535df1f6debbf035388b1))\n* **runFile:** wrap file search in runReadAction for thread safety ([d8324c3](https://github.com/phodal/shire/commit/d8324c350f2e7580d613cde6e90e2bada8e1bc7a))\n* **runner:** refactor interaction handling in IDE locations [#27](https://github.com/phodal/shire/issues/27) ([e51dc98](https://github.com/phodal/shire/commit/e51dc98a938b291b67dc68fcd9e47b05ba25486b))\n* **runners:** add HobbitHole to ShireRunner classes [#24](https://github.com/phodal/shire/issues/24) ([9e55bd1](https://github.com/phodal/shire/commit/9e55bd1d97de1c5ce2e9151a65c05cb7950ab29f))\n* **schema:** add Shire Custom Agent schema provider factory [#16](https://github.com/phodal/shire/issues/16) ([dc03d0c](https://github.com/phodal/shire/commit/dc03d0c08d50c146ecd9539730db84c497f734a8))\n* **shire-core:** implement EditorInteractionProvider and add Shire Toolchain Variable doc [#27](https://github.com/phodal/shire/issues/27) ([aff5380](https://github.com/phodal/shire/commit/aff5380c05edf560a202de52947812b1d58994fa))\n* **shirelang:** add default condition and new test for afterStreaming [#24](https://github.com/phodal/shire/issues/24) ([e5a28cb](https://github.com/phodal/shire/commit/e5a28cbe8e1a5036dd24080883b8c3ce71590224))\n* **shirelang:** add file execution support and improve condition handling ([d37e306](https://github.com/phodal/shire/commit/d37e3060f47d5b8e3ff290d18ac2cc59d7beebd8)), closes [#24](https://github.com/phodal/shire/issues/24)\n* **shirelang:** add new lifecycle keywords and update grammar [#24](https://github.com/phodal/shire/issues/24) ([c957282](https://github.com/phodal/shire/commit/c9572825a23a9c2c3ded709427784d3f736f7532))\n* **shirelang:** add new lifecycle keywords to syntax highlighter ([3e43d38](https://github.com/phodal/shire/commit/3e43d38282bc35e46ff6785f03f0371f8ee9f75b))\n* **shirelang:** add support for finish flags in output control flow [#24](https://github.com/phodal/shire/issues/24) ([99f1023](https://github.com/phodal/shire/commit/99f10230c6c856d6fe429c5c8c8b996d835525ee))\n* **shirelang:** enhance pattern action processing and test handling [#24](https://github.com/phodal/shire/issues/24) ([cb59bfb](https://github.com/phodal/shire/commit/cb59bfb8233c027ace91680c750ffe38a63a778b))\n* **TaskRoutes:** make defaultTask optional and refactor execution logic [#24](https://github.com/phodal/shire/issues/24) ([5787cca](https://github.com/phodal/shire/commit/5787cca2ad6fad9e00b59e5108e701a267d5f5e0))\n* **testing:** add success condition and improve function execution [#24](https://github.com/phodal/shire/issues/24) ([6eadfda](https://github.com/phodal/shire/commit/6eadfdaae2deb177158c134f4b72ddee51397f69))\n* **variable resolver:** add project context to resolveAll method [#27](https://github.com/phodal/shire/issues/27) ([b5f05f9](https://github.com/phodal/shire/commit/b5f05f940c3619db1c5ee38e9ca235ce4fdc6ffb))\n* **variable-resolver:** add support for toolchain variables [#27](https://github.com/phodal/shire/issues/27) ([fdbb4c9](https://github.com/phodal/shire/commit/fdbb4c91b01f9ef78b0a0752d545c70f801fab02))\n* **variables:** add ToolsetVariable and ToolsetVariableProvider [#27](https://github.com/phodal/shire/issues/27) ([c1da8a5](https://github.com/phodal/shire/commit/c1da8a5d3ac7be9c8452f2f9c34bd5aa49a5dd86))\n* **vcs:** add dynamic actions to VCS action group [#24](https://github.com/phodal/shire/issues/24) ([9018da5](https://github.com/phodal/shire/commit/9018da5056323b148a9c5d797c9f994efb1efd93))\n* **vcs:** add ShireVcsActionGroup for dynamic actions ([902bc2a](https://github.com/phodal/shire/commit/902bc2ab24df996bfc1fd7bd47eb5204b5dc916c))\n* wrap code blocks with appropriate application run actions [#24](https://github.com/phodal/shire/issues/24) ([55f7495](https://github.com/phodal/shire/commit/55f7495db0c4a23adb3e736545a61dc2d6555ee4))\n\n## [0.0.7](https://github.com/phodal/shire/compare/v0.0.6...v[0.0.7]) (2024-06-24)\n\n### Bug Fixes\n\n* **grammar:** update frontMatterArray syntax in ShireParser.bnf [#16](https://github.com/phodal/shire/issues/16) ([428033c](https://github.com/phodal/shire/commit/428033cb0b365e23e2e6fd77981ac38208c732ad))\n* **pattern-searcher:** handle invalid regex and refactor code [#18](https://github.com/phodal/shire/issues/18) ([b00cd54](https://github.com/phodal/shire/commit/b00cd54bc1d9eb82a2dd259e2617f89b33ff6831))\n\n### Features\n\n* **codemodel:** add FileStructure and VariableStructure classes [#14](https://github.com/phodal/shire/issues/14) ([322e897](https://github.com/phodal/shire/commit/322e89770f2ef3b93804f514705c0d5f220a4111))\n* **codemodel:** add MethodStructureProvider and related modifications [#14](https://github.com/phodal/shire/issues/14) ([29eedb9](https://github.com/phodal/shire/commit/29eedb9f5c00bcdcb78554f2c0c4e42d74dd78e2))\n* **codemodel:** add VariableStructureProvider and FileStructureProvider, refactor MethodStructureProvider [#14](https://github.com/phodal/shire/issues/14) ([4be010b](https://github.com/phodal/shire/commit/4be010b8ca205aba73159d8436b1ab0d9049daa8))\n* **codemodel:** enhance ClassStructure and MethodStructure formatting [#14](https://github.com/phodal/shire/issues/14) ([07488b0](https://github.com/phodal/shire/commit/07488b029ba653957738d226eb464f1270b64ebc))\n* **codemodel:** update FileStructure and add DirectoryStructure [#14](https://github.com/phodal/shire/issues/14) ([076368e](https://github.com/phodal/shire/commit/076368ed87806d32bbfe4453ccdca613ad9a8f22))\n* **compiler:** add error handling for function arguments ([376691f](https://github.com/phodal/shire/commit/376691f8ad251658282c69e62525cdcfc8ccee3d))\n* **compiler:** add execution logic for variable pattern functions [#16](https://github.com/phodal/shire/issues/16) ([3542461](https://github.com/phodal/shire/commit/354246175027b6ff0cd8df14c1af0e3cd53edb0f))\n* **compiler:** add operator handling in QueryStatementProcessor [#16](https://github.com/phodal/shire/issues/16) ([fc9f89d](https://github.com/phodal/shire/commit/fc9f89d6664e475f85227724eebf2496d8f26c16))\n* **compiler:** add PatternSearcher for file matching by regex [#18](https://github.com/phodal/shire/issues/18) ([d129e40](https://github.com/phodal/shire/commit/d129e406478cfaba891b8ea57a608149da7796f9))\n* **compiler:** add query statement support in pattern action [#16](https://github.com/phodal/shire/issues/16) ([12fd4fd](https://github.com/phodal/shire/commit/12fd4fda5671881128059ccdb844f12fa210f8dc))\n* **compiler:** add regex support and array handling in PatternActionFunc [#18](https://github.com/phodal/shire/issues/18) ([aebf478](https://github.com/phodal/shire/commit/aebf478ba9b04a9ac4240809b7699a6edd6ddc73))\n* **compiler:** add support for query statements in Shire language [#16](https://github.com/phodal/shire/issues/16) ([824c9f0](https://github.com/phodal/shire/commit/824c9f009ca0e428779703d30daf65ed7db65137))\n* **compiler:** add Value class and evaluate function in ShireExpression [#16](https://github.com/phodal/shire/issues/16) ([8b04548](https://github.com/phodal/shire/commit/8b0454857ef01d31e669f69488d7ba277005413c))\n* **compiler:** enhance QueryStatementProcessor functionality [#16](https://github.com/phodal/shire/issues/16) ([ddc2ff5](https://github.com/phodal/shire/commit/ddc2ff51e6b38d17d4de7db9a31de2366537e4b3))\n* **compiler:** handle null arguments and improve error handling [#16](https://github.com/phodal/shire/issues/16) ([59502be](https://github.com/phodal/shire/commit/59502be32bcd45294fc884be19728eecdbe2327a))\n* **compiler:** improve pattern action execution and testing [#18](https://github.com/phodal/shire/issues/18) ([4cada42](https://github.com/phodal/shire/commit/4cada4234799def70e7fc64534d8a7b50bd6acc2))\n* **compiler:** improve pattern handling and file loading in ShireCompiler [#18](https://github.com/phodal/shire/issues/18) ([4f3062e](https://github.com/phodal/shire/commit/4f3062e4e3b4ca5e95db03a5c48c9f4f12ed424b))\n* **compiler:** refactor built-in methods to enum class [#18](https://github.com/phodal/shire/issues/18) ([07e0475](https://github.com/phodal/shire/commit/07e0475851e59c0778254d6522666705ba78f58a))\n* **compiler:** refactor pattern action classes and move to ast package [#18](https://github.com/phodal/shire/issues/18) ([35a5515](https://github.com/phodal/shire/commit/35a55153df29b82c715d010237ef0bf6ab916349))\n* **compiler:** refactor query statement parsing and improve documentation [#16](https://github.com/phodal/shire/issues/16) ([6e385d5](https://github.com/phodal/shire/commit/6e385d58b2055f8d8faa0c37a308bd00d8f53f5d))\n* **compiler:** refactor template compilation and variable resolution ([93d7536](https://github.com/phodal/shire/commit/93d75369b19e13938ab604e3be7d08f42c31db90)), closes [#18](https://github.com/phodal/shire/issues/18)\n* **compiler:** refactor VariableStatement to VariableElement and add new PatternActionFunc subclasses [#16](https://github.com/phodal/shire/issues/16) ([6c9c9f3](https://github.com/phodal/shire/commit/6c9c9f3eddc5f33130fb6401250ff54d00571495))\n* **console:** add console output for Shirelang execution and error handling [#18](https://github.com/phodal/shire/issues/18) ([b9e4bb1](https://github.com/phodal/shire/commit/b9e4bb19e8b04b828513ae5066ee4169e73b8b2d))\n* **core:** add comment and refactor code in CustomAgent, ClassStructure, ShireExpression ([d5c0573](https://github.com/phodal/shire/commit/d5c05735dc7680c9cfc1060986f29c683a7da425))\n* **docs, core, languages:** add design samples and method context variables ([bf99f08](https://github.com/phodal/shire/commit/bf99f086f14796d42a82b92d801a8968eac958e7))\n* **FrontMatterType:** add QUERY_STATEMENT subclass [#16](https://github.com/phodal/shire/issues/16) ([a7ccc1a](https://github.com/phodal/shire/commit/a7ccc1a2629762fb11e0507bdc5188790610c997))\n* **java-toolchain:** add Maven support and refactor tech stack detection [#18](https://github.com/phodal/shire/issues/18) ([9c14272](https://github.com/phodal/shire/commit/9c1427233e41d329add38933c399eb1110b80385))\n* **java-toolchain:** refactor Maven build tool functionality into separate class [#18](https://github.com/phodal/shire/issues/18) ([39f4783](https://github.com/phodal/shire/commit/39f47837a79b159c1db04c2c7badfd1dd2863264))\n* **java-toolchain:** replace JavaTasksUtil with GradleTasksUtil [#18](https://github.com/phodal/shire/issues/18) ([83c5e00](https://github.com/phodal/shire/commit/83c5e00f642226c5a0fb2b16d9e9d303bc666afe))\n* **java:** enhance RelatedClassesProvider to support PsiClass [#14](https://github.com/phodal/shire/issues/14) ([f39aa4e](https://github.com/phodal/shire/commit/f39aa4e59f8472e7989c240c94ec061f5fdb36be))\n* **java:** refactor method name and add JavaCodeModifier [#14](https://github.com/phodal/shire/issues/14) ([319cd74](https://github.com/phodal/shire/commit/319cd740b96e5217d27f92495dc7d9023b1f9410))\n* **parser:** add 'and' operator and improve comment handling [#16](https://github.com/phodal/shire/issues/16) ([153c5a4](https://github.com/phodal/shire/commit/153c5a447fbe6ec575f04a5951fb917ce9a3a083))\n* **parser:** add support for comments in ShireParserDefinition [#16](https://github.com/phodal/shire/issues/16) ([dbdf410](https://github.com/phodal/shire/commit/dbdf4108077c40d6440b71dd1710acf8e5858ba7))\n* **parser:** remove whitespace support in query and from clauses [#16](https://github.com/phodal/shire/issues/16) ([9ac9d82](https://github.com/phodal/shire/commit/9ac9d8210727d584a77a72ff2806ce3730aa2c84))\n* **parser:** update grammar and lexer for query expressions [#16](https://github.com/phodal/shire/issues/16) ([7fd6746](https://github.com/phodal/shire/commit/7fd67469b351e12af06d32770ecf0a43b64e00ec))\n* **parsing:** update grammar and lexer for query expressions [#16](https://github.com/phodal/shire/issues/16) ([383e1ac](https://github.com/phodal/shire/commit/383e1ac48807f9b7befbfaa0f47344eb953e84d0))\n* **pattern-action:** add array handling in Grep function [#18](https://github.com/phodal/shire/issues/18) ([9a76f1c](https://github.com/phodal/shire/commit/9a76f1cd3f5237534bae63d69937e7d1444e1f40))\n* **plugin:** add Shire plugin configuration and enhance symbol lookup [#16](https://github.com/phodal/shire/issues/16) ([d25e77b](https://github.com/phodal/shire/commit/d25e77bac3f17cd0a9bb0964929b906dbe6ceb4b))\n* **QueryStatementProcessor:** implement Expression and String cases ([915dd81](https://github.com/phodal/shire/commit/915dd81ddb7c710263d8e0f38f82514239d1007e))\n* **QueryStatementProcessor:** implement operator and type evaluation [#16](https://github.com/phodal/shire/issues/16) ([4369408](https://github.com/phodal/shire/commit/4369408a74328b8a46418122f9e5a4e944f71e8e))\n* remove saql project from build ([53d4d1f](https://github.com/phodal/shire/commit/53d4d1f919cf2219a9fbba6c053cc55b6af39047))\n* **runner:** add system info variable resolution [#16](https://github.com/phodal/shire/issues/16) ([9f5d86b](https://github.com/phodal/shire/commit/9f5d86b91c29da377bcfe9eda39f402f84fa581d))\n* **runner:** enhance data fetching methods in SystemInfoVariable ([411ebf4](https://github.com/phodal/shire/commit/411ebf4840152624f15a6800478bcf5715fdbf74))\n* **saql:** add lexer and parser for Shire SQL [#16](https://github.com/phodal/shire/issues/16) ([a4479c3](https://github.com/phodal/shire/commit/a4479c32b9c72a6f6c57e8be490d825e8bf7fcfb))\n* **saql:** add saql project and related files [#16](https://github.com/phodal/shire/issues/16) ([572694e](https://github.com/phodal/shire/commit/572694e1a129f2786a8e1e0fd15ff5584f2cea43))\n* **saql:** enable getSqlTable methods in SAQLParser ([16d1c3c](https://github.com/phodal/shire/commit/16d1c3c93e45304038ae5d4a726097f167dd425c))\n* **saql:** refactor Saql language support and add new classes [#16](https://github.com/phodal/shire/issues/16) ([cad778d](https://github.com/phodal/shire/commit/cad778dd28a51b8d5d931af54378189ec7df031f))\n* **saql:** refactor Saql language support and add new classes [#16](https://github.com/phodal/shire/issues/16) ([0b00f60](https://github.com/phodal/shire/commit/0b00f60c65bac04b4bf80b095da22817ae638825))\n* **shirelang:** add ShireCommenter and update lexer and parser [#16](https://github.com/phodal/shire/issues/16) ([916a7d7](https://github.com/phodal/shire/commit/916a7d758e09a77615f9f34f894914f882df6be4))\n* **shirelang:** enhance FrontMatterType subclasses and refactor pattern action processing [#18](https://github.com/phodal/shire/issues/18) ([4777ee3](https://github.com/phodal/shire/commit/4777ee34b2cb65c26c7d506a25d694bb67c90b8b))\n* **shirelang:** improve query expression and statement processing [#16](https://github.com/phodal/shire/issues/16) ([62cd708](https://github.com/phodal/shire/commit/62cd708d59179e40407c0bda184eb82baf7968a2))\n* **shirelang:** refactor methods and improve error handling [#18](https://github.com/phodal/shire/issues/18) ([9b170ee](https://github.com/phodal/shire/commit/9b170ee80e973f6fe5f6b3c93c684290826210b5))\n* **ShireLang:** update grammar rules and lexer definitions [#16](https://github.com/phodal/shire/issues/16) ([29cc1b1](https://github.com/phodal/shire/commit/29cc1b1364981f6548c08b4a7933ffc63d96fa09))\n* **syntax-highlighter:** add new keywords to ShireSyntaxHighlighter [#16](https://github.com/phodal/shire/issues/16) ([9d937ec](https://github.com/phodal/shire/commit/9d937ec7dd5d7b1424f5ef6814af09f711cc9c68))\n* **testing:** add DefaultShireSymbolProvider and update ShireQueryExpressionTest ([e756f77](https://github.com/phodal/shire/commit/e756f77af2a8b23b6b3ac136982b5a234b82d148)), closes [#16](https://github.com/phodal/shire/issues/16)\n* **variable-resolver:** add date and time to SystemInfoVariableResolver ([bc3356b](https://github.com/phodal/shire/commit/bc3356b694f63036fb2b06860b3631097a87966f))\n* **variable-resolver:** refactor variable resolution system [#18](https://github.com/phodal/shire/issues/18) ([a96c00e](https://github.com/phodal/shire/commit/a96c00ee704f545c270ef1a2333ffab33ab5904c))\n* **VariablePatternActionExecutor:** add project, editor, and hole as class properties [#18](https://github.com/phodal/shire/issues/18) ([54da7a6](https://github.com/phodal/shire/commit/54da7a6d47c60b4c1cce75a9d833049b745c24c1))\n* **variable:** refactor PsiVariable to PsiContextVariable [#18](https://github.com/phodal/shire/issues/18) ([6e5e176](https://github.com/phodal/shire/commit/6e5e176ef30e4ae83a93bfba07386dfb4773812d))\n\n### Performance Improvements\n\n* **build:** optimize Gradle build performance in GitHub Actions ([f09254d](https://github.com/phodal/shire/commit/f09254d1bd81a47a47794545d5abb9e0988b78bc))\n\n## [0.0.6](https://github.com/phodal/shire/compare/v0.0.4...v[0.0.6]) (2024-06-16)\n\n### Bug Fixes\n\n* **java:** fix null check for JavaSdkType in JavaToolchainProvider ([fb8ad5f](https://github.com/phodal/shire/commit/fb8ad5f88d980f14505f8af1eb798de733438b81))\n* **java:** handle null psiElement in resolveVariableValue ([ba34428](https://github.com/phodal/shire/commit/ba34428e95ca67e1373853be6e590d5ed4cf1eb9))\n* **test:** update file patterns in test data ([bfbe463](https://github.com/phodal/shire/commit/bfbe46370b41a4c7f2c10f8d48f3446c97866b47))\n\n### Features\n\n* **actions:** add WhenConditionValidator for dynamic actions ([2e1c06a](https://github.com/phodal/shire/commit/2e1c06a1088c0c69cb471e4ae90af020f9147c36))\n* **compiler:** add support for method call with arguments ([1cb74e3](https://github.com/phodal/shire/commit/1cb74e3771f265266480fa35daa18053630883ff))\n* **compiler:** update HobbitHole variables to use list of conditions ([dd9dc0a](https://github.com/phodal/shire/commit/dd9dc0ae5638f0693dd3c651705d5da6e17f0dce))\n* **completion:** add support for when condition functions ([35e13aa](https://github.com/phodal/shire/commit/35e13aa45c33886252ed31f9cde1af2accd154ff))\n* **core:** add CodeStructVariableProvider for code struct generation [#14](https://github.com/phodal/shire/issues/14) ([9a6c573](https://github.com/phodal/shire/commit/9a6c5739066fd4cd2f0a4f61e0683919d89bb6b8))\n* **frontmatter:** add support for logical OR expressions in frontmatter parsing ([3c0f5dc](https://github.com/phodal/shire/commit/3c0f5dc9e48aa7c4ec296cfab95cd5a5e59d3f29))\n* **grammar:** add support for velocity expressions ([c71fb07](https://github.com/phodal/shire/commit/c71fb07f638afa52279cbc4921733c17c52d6cc7))\n* **highlight:** add 'WHEN' keyword support ([abdcb6c](https://github.com/phodal/shire/commit/abdcb6cbfc46d5a12f81ff631304b47cac36a392))\n* **java:** add JavaCodeStructVariableProvider for code struct generation [#15](https://github.com/phodal/shire/issues/15) ([378f44e](https://github.com/phodal/shire/commit/378f44ec4a4f4779cbdcc0329568257e1a519adb))\n* **runner:** add SymbolResolver for variable resolution [#14](https://github.com/phodal/shire/issues/14) ([ed03e25](https://github.com/phodal/shire/commit/ed03e25211dc222a92dca99269150af5a4fbf351))\n* **search:** add TfIdf class for text analysis [#14](https://github.com/phodal/shire/issues/14) ([55e94a6](https://github.com/phodal/shire/commit/55e94a6f109dd4677f985f605119d133045c75c6))\n* **shire:** add PatternFun Cat subclass ([ca2b531](https://github.com/phodal/shire/commit/ca2b53117d2ca0c512bf182d8d83fb55922ee8bb))\n* **template:** add Shire Action template and action [#14](https://github.com/phodal/shire/issues/14) ([0493d23](https://github.com/phodal/shire/commit/0493d2363006ae25073272d8035d0851aee15824))\n* **tokenizer:** add TermSplitter and StopwordsBasedTokenizer [#14](https://github.com/phodal/shire/issues/14) ([a774f48](https://github.com/phodal/shire/commit/a774f48a17c64553ab417763511b230dfaf73b18))\n\n## [0.0.4](https://github.com/phodal/shire/compare/2b4a6f06733149d0cd9763e2d4719a048fa37ce3...v[0.0.4]) (2024-06-11)\n\n### Bug Fixes\n\n* **build:** remove unnecessary project dependency ([4628e8b](https://github.com/phodal/shire/commit/4628e8b8b992b9904f68fd46d5c0117af9c51418))\n* **build:** update paths for plugin directory in workflows ([a0b6570](https://github.com/phodal/shire/commit/a0b657083ec8d49d65dff87f4efd003848aac7ed))\n* **parser:** fix filenameRules regex in ShireFmObject test ([85de5bd](https://github.com/phodal/shire/commit/85de5bdf5964886d6568aee24b61a1ff2e873da2))\n* **parser:** fix filenameRules regex in ShireFmObject test ([74783a1](https://github.com/phodal/shire/commit/74783a1d7dfb15b956132f68e5316ba9775c0512))\n* **release:** update gradle task path for patching changelog ([201c458](https://github.com/phodal/shire/commit/201c458e09399435039ade13fac81a0d43d26754))\n\n### Features\n\n* **action:** add ShireAction and ShireActionRegister interfaces ([e5ed7b8](https://github.com/phodal/shire/commit/e5ed7b8577e577717c26bb44520e4099dc57cb37))\n* **actions:** add context and intent action retrieval ([1be1fda](https://github.com/phodal/shire/commit/1be1fda1b58f6d1e8f7a9e535ab1fedff0c17a15))\n* **agent:** add custom agent response actions and configurations ([d6b7501](https://github.com/phodal/shire/commit/d6b7501a707c0c0172c6fc2a44db8dfc51a03569))\n* **codeedit:** add CodeModifier interface for code editing ([3bcdf44](https://github.com/phodal/shire/commit/3bcdf44b35840e90af31045c0a387c93fbf2f38a))\n* **codemodel:** add ClassStructureBuilder, ClassStructureProvider, and FormatableElement ([5504468](https://github.com/phodal/shire/commit/55044689e759e1ed7de6f4c1ca9c71ae19cee83e))\n* **compiler:** add ElementStrategy for auto selecting parent block element ([4f5118b](https://github.com/phodal/shire/commit/4f5118b68a75af91262a2d5540e83c9dd3a1e920))\n* **compiler:** add FrontmatterParser for dynamic action configuration ([12bb82a](https://github.com/phodal/shire/commit/12bb82a23275910b9195655ae9cdee16462a585c))\n* **compiler:** add ShellRunService for running shell scripts ([63ab840](https://github.com/phodal/shire/commit/63ab840a6844d7a7a608aa11126da80863603a10))\n* **compiler:** add support for browsing URLs ([3b63e52](https://github.com/phodal/shire/commit/3b63e52e2ff4debc066c6ae17de5ae4218bc9edd))\n* **compiler:** add support for DATE type in front matter ([3062445](https://github.com/phodal/shire/commit/3062445a58cba099b1c3031a72bf8ec7247132f4))\n* **compiler:** add support for filename rules ([c9e860c](https://github.com/phodal/shire/commit/c9e860c9bd14f7b8dc88b0f24792c1aacba19c56))\n* **compiler:** add support for front matter configuration ([aada8af](https://github.com/phodal/shire/commit/aada8afdd3efaeb25504e3f7aa81350cb1c11080))\n* **compiler:** add support for frontmatter parsing ([b80cee1](https://github.com/phodal/shire/commit/b80cee1da7c9e1a629777abcd4e9e202799e6898))\n* **compiler:** add support for head and tail functions ([b3f0aa0](https://github.com/phodal/shire/commit/b3f0aa024408ff8add6091b8f2f2204d5b538aaf))\n* **compiler:** add support for loading custom agents ([62357fe](https://github.com/phodal/shire/commit/62357feadc08f20ccfc3665e3b8498b65d4a3383))\n* **compiler:** add support for print function ([511cdde](https://github.com/phodal/shire/commit/511cddef30a3d4169cb1679a0ecd2623da082d02))\n* **compiler:** add support for replace pattern function ([9d4160f](https://github.com/phodal/shire/commit/9d4160f75700f130370a5112c4e1e3f1f43f6b1c))\n* **compiler:** add support for variables in HobbitHole ([0178647](https://github.com/phodal/shire/commit/01786479ab1b3c524489afcbd12cc2bd6cc8ed47))\n* **completion:** add basic completion for project run tasks ([e8c6578](https://github.com/phodal/shire/commit/e8c6578c4ce9a06a567bf4301b50041792286f61))\n* **completion:** add completion providers for code fence languages, file references, variables, agents, commands, and more ([4ef7ab4](https://github.com/phodal/shire/commit/4ef7ab49a09f58fdf9dbd379e41f64f16dad7aaa))\n* **completion:** add completion support for Git revisions ([c977564](https://github.com/phodal/shire/commit/c9775640d40ae7818a2b8c339195a22e50dbc7af))\n* **completion:** add Hobbit Hole code completion ([4fd6e41](https://github.com/phodal/shire/commit/4fd6e419b9ece791a1194a6dd17a256d56e944b2))\n* **core:** add commit functionality to RevisionProvider ([7be17f1](https://github.com/phodal/shire/commit/7be17f1cb0136bb177a478a5bc1a51e3cf91d543))\n* **core:** add DAG support with topological sort and cycle detection ([c01ada5](https://github.com/phodal/shire/commit/c01ada5760574a31ada3b6b9c8598a4840ff5cf7))\n* **core:** add file filtering functionality ([8864237](https://github.com/phodal/shire/commit/88642371187671d02f3fc95335a8b1fd88073d88))\n* **core:** add Java build system provider ([5afd1ba](https://github.com/phodal/shire/commit/5afd1ba4c2c81722a488f454130d13fca996f2c4))\n* **core:** add ProjectRunService interface and extension point ([71d02e1](https://github.com/phodal/shire/commit/71d02e1fa5e7f5ed01c243f02fd68859dd5485b2))\n* **core:** add ToolchainProvider extension point ([6a6d1d8](https://github.com/phodal/shire/commit/6a6d1d8a679e15694618b5043311714027dda14d))\n* **docs:** add roadmap section to README ([e2def4d](https://github.com/phodal/shire/commit/e2def4d965b5dace4c3978dce269426df39dd401))\n* **docs:** add support for frontmatter in code highlighting ([dbf5765](https://github.com/phodal/shire/commit/dbf5765e5eb9b4b76f2b27e625065af063dfb78f))\n* **frontmatter:** add filename and file content filters ([be33598](https://github.com/phodal/shire/commit/be3359814375937a125fbe25efd7f01582dfc8c0))\n* **git:** add GitQuery and VcsPrompting classes ([f5d5fe7](https://github.com/phodal/shire/commit/f5d5fe743edcef610e14b5ffb902856035faecb7))\n* **git:** add GitQuery and VcsPrompting classes ([eddfe37](https://github.com/phodal/shire/commit/eddfe372741acc6fac73c39f78ad9b22763806cf))\n* **grammar:** add qualRefExpr to ShireParser.bnf ([e5bf261](https://github.com/phodal/shire/commit/e5bf26123704d8ade8f2302925aa60cb18854a82))\n* **grammar:** add support for 'when' keyword in condition expressions ([ee3dc7e](https://github.com/phodal/shire/commit/ee3dc7ee33568d8265bfc07d368c9ee6a5148930))\n* **grammar:** add support for pattern actions ([9471994](https://github.com/phodal/shire/commit/947199424eb5f6c5354d278500c65e7dfa132343))\n* **grammar:** extend grammar for expressions and operators ([0aa661f](https://github.com/phodal/shire/commit/0aa661fc31934df8661f288c2efbbc300c7a5a6d))\n* **hobbit:** add support for action location in HobbitHole ([3c530dd](https://github.com/phodal/shire/commit/3c530ddd20b938454e77edc22c069e2d1aec6dea))\n* **hobbit:** add support for selection strategy ([0b4e090](https://github.com/phodal/shire/commit/0b4e090472f418023342460f4905bdcba9fb3326))\n* **httpclient:** add HttpClientRunService for HTTP requests ([8a33137](https://github.com/phodal/shire/commit/8a33137a00783c4c110b00e810f8a08b1925be9e))\n* **httpclient:** add support for REST client plugin ([994ff0e](https://github.com/phodal/shire/commit/994ff0e142419d33951036e9bce3300b57b94fc8))\n* **index:** add ShireIdentifierIndex for file content ([62891a0](https://github.com/phodal/shire/commit/62891a0d0c5ffc8888515ff2471fdcab41c6b930))\n* **intention:** add Shire Assistant with AI AutoAction ([0b23d35](https://github.com/phodal/shire/commit/0b23d3584dbb87b193aedd14a8d93bd4288051f5))\n* **intention:** add Shire Hobbit AI action support ([c1122e1](https://github.com/phodal/shire/commit/c1122e182d5deb5da52f061177e5f1a34f60511d))\n* **java:** add Java element strategy builder ([a4d571a](https://github.com/phodal/shire/commit/a4d571ac2a3b6bb41cb6af23c80955ff7d1e1b46))\n* **java:** add Java symbol provider implementation ([76c2042](https://github.com/phodal/shire/commit/76c2042268e2e5f92b7ff261dbe8c793269891ed))\n* **java:** add Java toolchain provider ([864c1cb](https://github.com/phodal/shire/commit/864c1cb6ca6af4088dcdbb586954af8d72174f70))\n* **java:** add method to find nearest target element ([3cf769c](https://github.com/phodal/shire/commit/3cf769c0c41efd8fe001cce91e5bc4a97e3d1df3))\n* **java:** add method to get run task name ([4e4ab8a](https://github.com/phodal/shire/commit/4e4ab8a1b107a0d0eeb5c02dbd67d39272affaab))\n* **java:** add MvcContextService and ControllerContext ([b5edce5](https://github.com/phodal/shire/commit/b5edce576ef24eae8892a8e1b3b09ff1acfa8b91))\n* **language:** add Shire language injector, folding builder, and completion contributor ([8f5ca99](https://github.com/phodal/shire/commit/8f5ca99d51554dd0d93da3a62fe50e9ee8324bef))\n* **language:** add Shire language support ([8332a3c](https://github.com/phodal/shire/commit/8332a3c93dd0c922e118694089264b68d3d312a8))\n* **language:** add Shire language support ([f351552](https://github.com/phodal/shire/commit/f3515520c86f99c8b0387614f86cc93a2e87bfa5))\n* **llm:** add MockProvider for testing ([089d496](https://github.com/phodal/shire/commit/089d4968d9fa6c44314a0f21c393de96808ce863))\n* **llm:** add OpenAI LLM provider and settings ([8518bd3](https://github.com/phodal/shire/commit/8518bd3360b2dd1d40bc688c3409419b4acc4aa6))\n* **middleware:** add CodeVerifyProcessor for syntax error checking ([1bec4c3](https://github.com/phodal/shire/commit/1bec4c33f07be7cbc3f516e4ce57cb216d9b76d1))\n* **middleware:** add post processor support ([0a2aeea](https://github.com/phodal/shire/commit/0a2aeeaf5ec9407cbf95620a3303373e3a886beb))\n* **middleware:** add PostCodeHandler interface and PostCodeHandle enum ([9d734c5](https://github.com/phodal/shire/commit/9d734c54d475a887df34ebb2ade8a429857a1d28))\n* **middleware:** add TimeMetricProcessor for measuring code execution time ([2c3b1a6](https://github.com/phodal/shire/commit/2c3b1a6c9589aea733666bc533dc130747fea5da))\n* **modify:** add ShireModificationListener for file modification ([c61f973](https://github.com/phodal/shire/commit/c61f97347945ed6d2610608289c74537909bdf43))\n* **modify:** add ShireModificationListener for file modification ([230b00e](https://github.com/phodal/shire/commit/230b00e9d0b36c82a36da3b3bf861845faeb439c))\n* **parser:** add new test data files and refactor code ([b666fc6](https://github.com/phodal/shire/commit/b666fc6d3a9d51ab17972082227eeee6a5ba32d6))\n* **parser:** add support for front matter parsing ([01ef64e](https://github.com/phodal/shire/commit/01ef64ebaed4951e32892b8d0bf0704b0974c994))\n* **parser:** add support for pattern actions ([bb30004](https://github.com/phodal/shire/commit/bb30004e14684c9127eb138167336e5b4fd93d87))\n* **parser:** add support for pattern element ([12fb06e](https://github.com/phodal/shire/commit/12fb06e83338c143b8614a0ad27485c1c0c1d2c7))\n* **pattern:** add pattern actions for filtering, sorting, and executing commands ([be08b28](https://github.com/phodal/shire/commit/be08b28353dfbd940c3c585bd1a2f1fe1f7563f6))\n* **project:** add core and language modules ([2b4a6f0](https://github.com/phodal/shire/commit/2b4a6f06733149d0cd9763e2d4719a048fa37ce3))\n* **provider:** add AutoTesting provider for unit testing ([2fb10e2](https://github.com/phodal/shire/commit/2fb10e26c27dbd4d2d2f2090c84827dae5a37680))\n* **provider:** add PsiElementStrategyBuilder interface ([c5ca72b](https://github.com/phodal/shire/commit/c5ca72b86b4fa84417bf7646500b1cb4ddf52da4))\n* **provider:** add RevisionProvider interface and implementation ([fa05221](https://github.com/phodal/shire/commit/fa05221654c53c7821e5de8ff148d62c6c0a9b3c))\n* **provider:** add RunService interface and implementation ([a509717](https://github.com/phodal/shire/commit/a5097170adb1efc848aa027761613b213f0c39b8))\n* **psi:** add method to get relative PsiElement with PsiComment ([ba3b521](https://github.com/phodal/shire/commit/ba3b5210647242802c6c66b0fb968344345f674c))\n* **run:** add Shire program runner and process processor ([554c39f](https://github.com/phodal/shire/commit/554c39f8784773a53063e5eab9246e4b4d87a8aa))\n* **runner:** add support for running tasks in projects ([586a9d6](https://github.com/phodal/shire/commit/586a9d6bcfc5979fd12d5b2089eb339ae03238f5))\n* **settings:** add LlmCoroutineScope, CustomAgent loadFromProject, and TestConnection ([efdc2f9](https://github.com/phodal/shire/commit/efdc2f99ff0739edb0a9d40a62404a68defece37))\n* **settings:** add Shire settings configurable UI ([dba5d68](https://github.com/phodal/shire/commit/dba5d68ec0f969ebce518ab573d3471de3b2fe10))\n* **shell:** add shell language support plugin file ([bdc1c90](https://github.com/phodal/shire/commit/bdc1c90f10d7c2c4fb8f7157db343dd756c11352))\n* **shire:** add Shire context action group and location support ([e3df86d](https://github.com/phodal/shire/commit/e3df86d16d0c9985fec787a8e76ce553be9a9a45))\n\n[Unreleased]: https://github.com/phodal/shire/compare/v1.3.4...HEAD\n[1.3.4]: https://github.com/phodal/shire/compare/v1.3.3...v1.3.4\n[1.3.3]: https://github.com/phodal/shire/compare/v1.3.2...v1.3.3\n[1.3.2]: https://github.com/phodal/shire/compare/v1.3.1...v1.3.2\n[1.3.1]: https://github.com/phodal/shire/compare/v1.2.4...v1.3.1\n[1.2.4]: https://github.com/phodal/shire/compare/v1.2.3...v1.2.4\n[1.2.3]: https://github.com/phodal/shire/compare/v1.2.2...v1.2.3\n[1.2.2]: https://github.com/phodal/shire/compare/v1.2.1...v1.2.2\n[1.2.1]: https://github.com/phodal/shire/compare/v1.1.1...v1.2.1\n[1.1.1]: https://github.com/phodal/shire/compare/v1.0.6...v1.1.1\n[1.0.6]: https://github.com/phodal/shire/compare/v1.0.4-SNAPSHOT...v1.0.6\n[1.0.4-SNAPSHOT]: https://github.com/phodal/shire/compare/v1.0.2...v1.0.4-SNAPSHOT\n[1.0.2]: https://github.com/phodal/shire/compare/v0.9.1...v1.0.2\n[0.9.1]: https://github.com/phodal/shire/compare/v0.8.2...v0.9.1\n[0.8.2]: https://github.com/phodal/shire/compare/v0.8.1...v0.8.2\n[0.8.1]: https://github.com/phodal/shire/compare/v0.7.4...v0.8.1\n[0.7.4]: https://github.com/phodal/shire/compare/v0.7.2...v0.7.4\n[0.7.2]: https://github.com/phodal/shire/compare/v0.7.1...v0.7.2\n[0.7.1]: https://github.com/phodal/shire/compare/v0.5.2...v0.7.1\n[0.5.2]: https://github.com/phodal/shire/compare/v0.4.8...v0.5.2\n[0.4.8]: https://github.com/phodal/shire/compare/v0.4.7...v0.4.8\n[0.4.7]: https://github.com/phodal/shire/compare/v0.4.6...v0.4.7\n[0.4.6]: https://github.com/phodal/shire/compare/v0.4.5...v0.4.6\n[0.4.5]: https://github.com/phodal/shire/compare/v0.4.3...v0.4.5\n[0.4.3]: https://github.com/phodal/shire/compare/v0.4.2...v0.4.3\n[0.4.2]: https://github.com/phodal/shire/compare/v0.4.1...v0.4.2\n[0.4.1]: https://github.com/phodal/shire/compare/v0.0.8...v0.4.1\n[0.0.8]: https://github.com/phodal/shire/compare/v0.0.7...v0.0.8\n[0.0.7]: https://github.com/phodal/shire/compare/v0.0.6...v0.0.7\n[0.0.6]: https://github.com/phodal/shire/compare/v0.0.4...v0.0.6\n[0.0.4]: https://github.com/phodal/shire/commits/v0.0.4\n"
  },
  {
    "path": "LICENSE",
    "content": "Mozilla Public License Version 2.0\n==================================\n\n1. Definitions\n--------------\n\n1.1. \"Contributor\"\n    means each individual or legal entity that creates, contributes to\n    the creation of, or owns Covered Software.\n\n1.2. \"Contributor Version\"\n    means the combination of the Contributions of others (if any) used\n    by a Contributor and that particular Contributor's Contribution.\n\n1.3. \"Contribution\"\n    means Covered Software of a particular Contributor.\n\n1.4. \"Covered Software\"\n    means Source Code Form to which the initial Contributor has attached\n    the notice in Exhibit A, the Executable Form of such Source Code\n    Form, and Modifications of such Source Code Form, in each case\n    including portions thereof.\n\n1.5. \"Incompatible With Secondary Licenses\"\n    means\n\n    (a) that the initial Contributor has attached the notice described\n        in Exhibit B to the Covered Software; or\n\n    (b) that the Covered Software was made available under the terms of\n        version 1.1 or earlier of the License, but not also under the\n        terms of a Secondary License.\n\n1.6. \"Executable Form\"\n    means any form of the work other than Source Code Form.\n\n1.7. \"Larger Work\"\n    means a work that combines Covered Software with other material, in\n    a separate file or files, that is not Covered Software.\n\n1.8. \"License\"\n    means this document.\n\n1.9. \"Licensable\"\n    means having the right to grant, to the maximum extent possible,\n    whether at the time of the initial grant or subsequently, any and\n    all of the rights conveyed by this License.\n\n1.10. \"Modifications\"\n    means any of the following:\n\n    (a) any file in Source Code Form that results from an addition to,\n        deletion from, or modification of the contents of Covered\n        Software; or\n\n    (b) any new file in Source Code Form that contains any Covered\n        Software.\n\n1.11. \"Patent Claims\" of a Contributor\n    means any patent claim(s), including without limitation, method,\n    process, and apparatus claims, in any patent Licensable by such\n    Contributor that would be infringed, but for the grant of the\n    License, by the making, using, selling, offering for sale, having\n    made, import, or transfer of either its Contributions or its\n    Contributor Version.\n\n1.12. \"Secondary License\"\n    means either the GNU General Public License, Version 2.0, the GNU\n    Lesser General Public License, Version 2.1, the GNU Affero General\n    Public License, Version 3.0, or any later versions of those\n    licenses.\n\n1.13. \"Source Code Form\"\n    means the form of the work preferred for making modifications.\n\n1.14. \"You\" (or \"Your\")\n    means an individual or a legal entity exercising rights under this\n    License. For legal entities, \"You\" includes any entity that\n    controls, is controlled by, or is under common control with You. For\n    purposes of this definition, \"control\" means (a) the power, direct\n    or indirect, to cause the direction or management of such entity,\n    whether by contract or otherwise, or (b) ownership of more than\n    fifty percent (50%) of the outstanding shares or beneficial\n    ownership of such entity.\n\n2. License Grants and Conditions\n--------------------------------\n\n2.1. Grants\n\nEach Contributor hereby grants You a world-wide, royalty-free,\nnon-exclusive license:\n\n(a) under intellectual property rights (other than patent or trademark)\n    Licensable by such Contributor to use, reproduce, make available,\n    modify, display, perform, distribute, and otherwise exploit its\n    Contributions, either on an unmodified basis, with Modifications, or\n    as part of a Larger Work; and\n\n(b) under Patent Claims of such Contributor to make, use, sell, offer\n    for sale, have made, import, and otherwise transfer either its\n    Contributions or its Contributor Version.\n\n2.2. Effective Date\n\nThe licenses granted in Section 2.1 with respect to any Contribution\nbecome effective for each Contribution on the date the Contributor first\ndistributes such Contribution.\n\n2.3. Limitations on Grant Scope\n\nThe licenses granted in this Section 2 are the only rights granted under\nthis License. No additional rights or licenses will be implied from the\ndistribution or licensing of Covered Software under this License.\nNotwithstanding Section 2.1(b) above, no patent license is granted by a\nContributor:\n\n(a) for any code that a Contributor has removed from Covered Software;\n    or\n\n(b) for infringements caused by: (i) Your and any other third party's\n    modifications of Covered Software, or (ii) the combination of its\n    Contributions with other software (except as part of its Contributor\n    Version); or\n\n(c) under Patent Claims infringed by Covered Software in the absence of\n    its Contributions.\n\nThis License does not grant any rights in the trademarks, service marks,\nor logos of any Contributor (except as may be necessary to comply with\nthe notice requirements in Section 3.4).\n\n2.4. Subsequent Licenses\n\nNo Contributor makes additional grants as a result of Your choice to\ndistribute the Covered Software under a subsequent version of this\nLicense (see Section 10.2) or under the terms of a Secondary License (if\npermitted under the terms of Section 3.3).\n\n2.5. Representation\n\nEach Contributor represents that the Contributor believes its\nContributions are its original creation(s) or it has sufficient rights\nto grant the rights to its Contributions conveyed by this License.\n\n2.6. Fair Use\n\nThis License is not intended to limit any rights You have under\napplicable copyright doctrines of fair use, fair dealing, or other\nequivalents.\n\n2.7. Conditions\n\nSections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted\nin Section 2.1.\n\n3. Responsibilities\n-------------------\n\n3.1. Distribution of Source Form\n\nAll distribution of Covered Software in Source Code Form, including any\nModifications that You create or to which You contribute, must be under\nthe terms of this License. You must inform recipients that the Source\nCode Form of the Covered Software is governed by the terms of this\nLicense, and how they can obtain a copy of this License. You may not\nattempt to alter or restrict the recipients' rights in the Source Code\nForm.\n\n3.2. Distribution of Executable Form\n\nIf You distribute Covered Software in Executable Form then:\n\n(a) such Covered Software must also be made available in Source Code\n    Form, as described in Section 3.1, and You must inform recipients of\n    the Executable Form how they can obtain a copy of such Source Code\n    Form by reasonable means in a timely manner, at a charge no more\n    than the cost of distribution to the recipient; and\n\n(b) You may distribute such Executable Form under the terms of this\n    License, or sublicense it under different terms, provided that the\n    license for the Executable Form does not attempt to limit or alter\n    the recipients' rights in the Source Code Form under this License.\n\n3.3. Distribution of a Larger Work\n\nYou may create and distribute a Larger Work under terms of Your choice,\nprovided that You also comply with the requirements of this License for\nthe Covered Software. If the Larger Work is a combination of Covered\nSoftware with a work governed by one or more Secondary Licenses, and the\nCovered Software is not Incompatible With Secondary Licenses, this\nLicense permits You to additionally distribute such Covered Software\nunder the terms of such Secondary License(s), so that the recipient of\nthe Larger Work may, at their option, further distribute the Covered\nSoftware under the terms of either this License or such Secondary\nLicense(s).\n\n3.4. Notices\n\nYou may not remove or alter the substance of any license notices\n(including copyright notices, patent notices, disclaimers of warranty,\nor limitations of liability) contained within the Source Code Form of\nthe Covered Software, except that You may alter any license notices to\nthe extent required to remedy known factual inaccuracies.\n\n3.5. Application of Additional Terms\n\nYou may choose to offer, and to charge a fee for, warranty, support,\nindemnity or liability obligations to one or more recipients of Covered\nSoftware. However, You may do so only on Your own behalf, and not on\nbehalf of any Contributor. You must make it absolutely clear that any\nsuch warranty, support, indemnity, or liability obligation is offered by\nYou alone, and You hereby agree to indemnify every Contributor for any\nliability incurred by such Contributor as a result of warranty, support,\nindemnity or liability terms You offer. You may include additional\ndisclaimers of warranty and limitations of liability specific to any\njurisdiction.\n\n4. Inability to Comply Due to Statute or Regulation\n---------------------------------------------------\n\nIf it is impossible for You to comply with any of the terms of this\nLicense with respect to some or all of the Covered Software due to\nstatute, judicial order, or regulation then You must: (a) comply with\nthe terms of this License to the maximum extent possible; and (b)\ndescribe the limitations and the code they affect. Such description must\nbe placed in a text file included with all distributions of the Covered\nSoftware under this License. Except to the extent prohibited by statute\nor regulation, such description must be sufficiently detailed for a\nrecipient of ordinary skill to be able to understand it.\n\n5. Termination\n--------------\n\n5.1. The rights granted under this License will terminate automatically\nif You fail to comply with any of its terms. However, if You become\ncompliant, then the rights granted under this License from a particular\nContributor are reinstated (a) provisionally, unless and until such\nContributor explicitly and finally terminates Your grants, and (b) on an\nongoing basis, if such Contributor fails to notify You of the\nnon-compliance by some reasonable means prior to 60 days after You have\ncome back into compliance. Moreover, Your grants from a particular\nContributor are reinstated on an ongoing basis if such Contributor\nnotifies You of the non-compliance by some reasonable means, this is the\nfirst time You have received notice of non-compliance with this License\nfrom such Contributor, and You become compliant prior to 30 days after\nYour receipt of the notice.\n\n5.2. If You initiate litigation against any entity by asserting a patent\ninfringement claim (excluding declaratory judgment actions,\ncounter-claims, and cross-claims) alleging that a Contributor Version\ndirectly or indirectly infringes any patent, then the rights granted to\nYou by any and all Contributors for the Covered Software under Section\n2.1 of this License shall terminate.\n\n5.3. In the event of termination under Sections 5.1 or 5.2 above, all\nend user license agreements (excluding distributors and resellers) which\nhave been validly granted by You or Your distributors under this License\nprior to termination shall survive termination.\n\n************************************************************************\n*                                                                      *\n*  6. Disclaimer of Warranty                                           *\n*  -------------------------                                           *\n*                                                                      *\n*  Covered Software is provided under this License on an \"as is\"       *\n*  basis, without warranty of any kind, either expressed, implied, or  *\n*  statutory, including, without limitation, warranties that the       *\n*  Covered Software is free of defects, merchantable, fit for a        *\n*  particular purpose or non-infringing. The entire risk as to the     *\n*  quality and performance of the Covered Software is with You.        *\n*  Should any Covered Software prove defective in any respect, You     *\n*  (not any Contributor) assume the cost of any necessary servicing,   *\n*  repair, or correction. This disclaimer of warranty constitutes an   *\n*  essential part of this License. No use of any Covered Software is   *\n*  authorized under this License except under this disclaimer.         *\n*                                                                      *\n************************************************************************\n\n************************************************************************\n*                                                                      *\n*  7. Limitation of Liability                                          *\n*  --------------------------                                          *\n*                                                                      *\n*  Under no circumstances and under no legal theory, whether tort      *\n*  (including negligence), contract, or otherwise, shall any           *\n*  Contributor, or anyone who distributes Covered Software as          *\n*  permitted above, be liable to You for any direct, indirect,         *\n*  special, incidental, or consequential damages of any character      *\n*  including, without limitation, damages for lost profits, loss of    *\n*  goodwill, work stoppage, computer failure or malfunction, or any    *\n*  and all other commercial damages or losses, even if such party      *\n*  shall have been informed of the possibility of such damages. This   *\n*  limitation of liability shall not apply to liability for death or   *\n*  personal injury resulting from such party's negligence to the       *\n*  extent applicable law prohibits such limitation. Some               *\n*  jurisdictions do not allow the exclusion or limitation of           *\n*  incidental or consequential damages, so this exclusion and          *\n*  limitation may not apply to You.                                    *\n*                                                                      *\n************************************************************************\n\n8. Litigation\n-------------\n\nAny litigation relating to this License may be brought only in the\ncourts of a jurisdiction where the defendant maintains its principal\nplace of business and such litigation shall be governed by laws of that\njurisdiction, without reference to its conflict-of-law provisions.\nNothing in this Section shall prevent a party's ability to bring\ncross-claims or counter-claims.\n\n9. Miscellaneous\n----------------\n\nThis License represents the complete agreement concerning the subject\nmatter hereof. If any provision of this License is held to be\nunenforceable, such provision shall be reformed only to the extent\nnecessary to make it enforceable. Any law or regulation which provides\nthat the language of a contract shall be construed against the drafter\nshall not be used to construe this License against a Contributor.\n\n10. Versions of the License\n---------------------------\n\n10.1. New Versions\n\nMozilla Foundation is the license steward. Except as provided in Section\n10.3, no one other than the license steward has the right to modify or\npublish new versions of this License. Each version will be given a\ndistinguishing version number.\n\n10.2. Effect of New Versions\n\nYou may distribute the Covered Software under the terms of the version\nof the License under which You originally received the Covered Software,\nor under the terms of any subsequent version published by the license\nsteward.\n\n10.3. Modified Versions\n\nIf you create software not governed by this License, and you want to\ncreate a new license for such software, you may create and use a\nmodified version of this License if you rename the license and remove\nany references to the name of the license steward (except to note that\nsuch modified license differs from this License).\n\n10.4. Distributing Source Code Form that is Incompatible With Secondary\nLicenses\n\nIf You choose to distribute Source Code Form that is Incompatible With\nSecondary Licenses under the terms of this version of the License, the\nnotice described in Exhibit B of this License must be attached.\n\nExhibit A - Source Code Form License Notice\n-------------------------------------------\n\n  This Source Code Form is subject to the terms of the Mozilla Public\n  License, v. 2.0. If a copy of the MPL was not distributed with this\n  file, You can obtain one at http://mozilla.org/MPL/2.0/.\n\nIf it is not possible or desirable to put the notice in a particular\nfile, then You may include the notice in a location (such as a LICENSE\nfile in a relevant directory) where a recipient would be likely to look\nfor such a notice.\n\nYou may add additional accurate notices of copyright ownership.\n\nExhibit B - \"Incompatible With Secondary Licenses\" Notice\n---------------------------------------------------------\n\n  This Source Code Form is \"Incompatible With Secondary Licenses\", as\n  defined by the Mozilla Public License, v. 2.0.\n"
  },
  {
    "path": "README.md",
    "content": "<p align=\"center\">\n  <img src=\"src/main/resources/META-INF/pluginIcon.svg\" width=\"160px\" height=\"160px\"  alt=\"logo\" />\n</p>\n<h1 align=\"center\">Shire - AI Coding Agent Language</h1>\n<p align=\"center\">\n  <a href=\"https://github.com/phodal/shire/actions/workflows/build.yml\">\n    <img src=\"https://github.com/phodal/shire/workflows/Build/badge.svg\" alt=\"Build\" />\n  </a>\n  <a href=\"https://plugins.jetbrains.com/plugin/24549\">\n    <img src=\"https://img.shields.io/jetbrains/plugin/v/24549.svg\" alt=\"Version\" />\n  </a>\n  <a href=\"https://plugins.jetbrains.com/plugin/24549\">\n    <img src=\"https://img.shields.io/jetbrains/plugin/d/24549.svg\" alt=\"Downloads\" />\n  </a>\n</p>\n\nShire offers a straightforward AI Coding Agent Language\nthat enables communication between an LLM and control IDE for automated programming.\n\n[Quick Start →](https://shire.phodal.com/) (Documentation)\n\n> The concept of Shire originated from [AutoDev](https://github.com/unit-mesh/auto-dev), a subproject\n> of [UnitMesh](https://unitmesh.cc/). In AutoDev, we designed an AI-driven IDE for developers that includes DevIns, the\n> precursor to Shire. DevIns aims to enable users to create AI agents tailored to their own IDEs, allowing them to build\n> their customized AI-driven development environments.\n\n---\n\n## Unite Your Dev Ecosystem, Create Your AI Copilot\n\n![Inline Chat](https://shire.run/images/shire-ecology-system.png)\n\n### Agentic with Tool Ecosystem, Reshaping the SDLC\n\n不论是组织内部的 DevOps 工具链：Jira、Confluence、SonarQube、Jenkins、GitLab、GitHub，还是各种内部 LLM 模型平台。\n\n| 又或者在代码编辑器、终端、数据库、版本控制等等，Shire 都可以帮助你快速实现自动化编程。 | ![Shire Command](https://shire.run/images/shire-command.png) |\n|------------------------------------------------|--------------------------------------------------------------|\n\n### Customize your AI Copilot with Your IDE\n\n我们内置了多种交互方式，以快速将你的 IDE 变为你的专属 AI Copilot。。\n\n| ![Shire Customize Menu](https://shire.run/images/shire-customize-menu.png) | 右键菜单、Alt+Enter、终端菜单、提交菜单、运行面板、输入框、数据库菜单、控制台菜单、VCS 日志菜单、聊天框、内联聊天等等。 |\n|----------------------------------------------------------------------------|--------------------------------------------------------------------|\n\n### Follow Leading Community Practices\n\n结合我们在行业的最佳洞见（[https://aise.phodal.com/](https://aise.phodal.com/)），你可以在 Shire 上体验到最佳的编程实践。\n\n| StreamDiff、多文件编辑、FastApply、InlineChat 等 | <img src=\"https://shire.run/images/shire-industry-best-practise.png\" alt=\"Shire Best Practice\" width=\"350\"> |\n|-----------------------------------------|-------------------------------------------------------------------------------------------------------------|\n\n## Shire\n\nShire example Project: [Java example](https://github.com/shire-lang/shire-spring-java-demo)\n\n### SDLC\n\n- [Code change analysis](https://github.com/shire-lang/shire-demo/blob/master/.shire/requirement/crud/analysis-requirements.shire) use LLM to analysis requirements, then choose the best files to change.\n- [Requirement + AutoCRUD](https://github.com/shire-lang/shire-demo/blob/master/.shire/requirement/analysis-requirements.shire) analysis requirements, then auto generate CRUD code.\n- [Dify + OpenAPI/Swagger](https://github.com/shire-lang/shire-demo/blob/master/.shire/api/design/design-rest-api.shire) interactive with Dify agent to design REST API\n- [Add Spring doc to project](https://github.com/shire-lang/shire-demo/blob/master/.shire/api/setup-dep/setup-spring-doc-openapi.shire) add Spring doc to project.\n- [Generate RestAssured Test](https://github.com/shire-lang/shire-demo/blob/master/.shire/api/verify/rest-assure.shire) AI to generate RestAssured test code.\n- [Generate JavaDoc](https://github.com/shire-lang/shire-demo/blob/master/.shire/documentation/javadoc.shire) use LLM to generate JavaDoc.\n- [Complexity Analysis](https://github.com/shire-lang/shire-demo/blob/master/.shire/git/complexity.shire) calculate code complexity.\n- [PlantUML: fetch Github issue for analysis](https://github.com/shire-lang/shire-demo/blob/master/.shire/requirement/visual/mindmap.shire) fetch GitHub issue to generate mindmap.\n\nFrontEnd:\n\n- [Frontend + HTML mockup](https://github.com/shire-lang/shire-demo/blob/master/.shire/frontend/html-mock-up.shire) use LLM to generate HTML mockup and show in WebView.\n- [Mobile + Ionic](https://github.com/shire-lang/shire-demo/blob/master/.shire/frontend/mobile-mock-up.shire) use LLM to generate mobile mockup with Ionic, show in WebView.\n- [Mobile + React](https://github.com/shire-lang/shire-demo/blob/master/.shire/frontend/react-mock-up.shire) use LLM to generate mobile mockup with React, show in WebView.\n- [JavaScript Auto Unittest](https://github.com/shire-lang/shire-demo/blob/master/.shire/frontend/js-test.shire) use LLM to generate JavaScript test code.\n\nTest:\n\n- [E2E Test: Playwright](https://github.com/shire-lang/shire-demo/blob/master/.shire/api/e2e/playwright.shire) AI to use Playwright to test the API and auto execute test.\n- [API Test: Java](https://github.com/shire-lang/shire-demo/blob/master/.shire/test/java/api-test.shire) use LLM to generate Java API test code.\n- [Unit Test: Java](https://github.com/shire-lang/shire-demo/blob/master/.shire/test/java/autotest.shire) use LLM to generate Java unit test code.\n- [Unit Test: Python](https://github.com/shire-lang/shire-demo/blob/master/.shire/test/python/AutoTest.shire) use LLM to generate Python unit test code.\n- [Unit Test: Golang](https://github.com/shire-lang/shire-demo/blob/master/.shire/test/go/AutoTest.shire) use LLM to generate Golang unit test code.\n\n### Workflow & IDE Integration\n\n- [Capture web pages and generate report](https://github.com/shire-lang/shire-demo/blob/master/.shire/research/research.shire) capture web pages and generate report.\n- [approvalExecute](https://github.com/shire-lang/shire-demo/blob/master/.shire/approve/approve.shire) waiting for approval to execute next shire code\n- [Custom InlineChat](https://github.com/shire-lang/shire-demo/blob/master/.shire/chatbox/inline-chat.shire)  custom inline chat\n- [Custom ChatBox](https://github.com/shire-lang/shire-demo/blob/master/.shire/chatbox/wrapper-chat.shire) custom prompt to use right panel chat box\n- [Python as Foreign Function Interface](https://github.com/shire-lang/shire-demo/blob/master/.shire/ffi/python-shell-thread.shire) use Python to run shell command in thread.\n- [Quick Input](https://github.com/shire-lang/shire-demo/blob/master/.shire/miscs/quick-input.shire) show quick input dialog.\n- [Terminal Agent](https://github.com/shire-lang/shire-demo/blob/master/.shire/miscs/terminal.shire) use terminal agent to run shell command.\n\n### EcoSystem\n\n- [Git: Auto push code](https://github.com/shire-lang/shire-demo/blob/master/.shire/git/auto-push.shire) auto commit and push code to server.\n- [Git: diff AI changed code](https://github.com/shire-lang/shire-demo/blob/master/.shire/git/diff-example.shire) diff AI changed code.\n- [Git: Commit message](https://github.com/shire-lang/shire-demo/blob/master/.shire/git/login-commit-message.shire) generate commit message.\n- [Git: Commit ID with Jira](https://github.com/shire-lang/shire-demo/blob/master/.shire/git/commit-message.shire) generate commit message with Jira ID.\n- [Database: GitHub issue + Design Database Schema](https://github.com/shire-lang/shire-demo/blob/master/.shire/database/design-db.shire) fetch GitHub issue as context to design database schema\n- [Database: Run SQL in Database](https://github.com/shire-lang/shire-demo/blob/master/.shire/database/command.shire) run SQL with `/database` command.\n- [OpenRewrite: generate refactoring code](https://github.com/shire-lang/shire-demo/blob/master/.shire/refactor/openRewrite.shire) use OpenRewrite to generate refactoring code.\n- [MockServer: WireMock](https://github.com/shire-lang/shire-demo/blob/master/.shire/api/mock/gen-mock.shire) AI to generate mock server with WireMock and auto start mock server.\n- [PlantUML: with remote Agent](https://github.com/shire-lang/shire-demo/blob/master/.shire/toolchain/puml/plantuml-remote.shire) use remote agent to generate PlantUML code.\n- [Mermaid: with remote Agent](https://github.com/shire-lang/shire-demo/blob/master/.shire/toolchain/mermaid.shire) use remote agent to generate Mermaid code.\n- [Sonarlint: fix issue](https://github.com/shire-lang/shire-demo/blob/master/.shire/toolchain/sonarfix.shire) use Sonarlint to fix issue.\n\n## Shire Resources\n\nShire Cheatsheet\n\n![Shire Cheatsheet](docs/images/shire-sheet.svg)\n\nShire Data Architecture:\n\n![Shire Data Architecture](docs/images/shire-data-flow.svg)\n\nShire Resources\n    \n- Documentation: [Shire AI Coding Agent Language](https://shire.phodal.com/)\n- [Shire Book: AI for software-engineering](https://aise.phodal.com/) (Chinese only)\n- [Shire.Run - the shareable AI coding agent](https://shire.run/)\n\n## Demo Video\n\nYoutube:\n\n[![Shire AI Coding Agent Language](https://img.youtube.com/vi/z1ijWOL1rFY/0.jpg)](https://www.youtube.com/watch?v=z1ijWOL1rFY)\n\nBilibili\n\n[![Shire AI Coding Agent Language](https://img.youtube.com/vi/z1ijWOL1rFY/0.jpg)](https://www.bilibili.com/video/BV1Lf421q7S7/)\n\n## Thanks\n\n感谢智谱 AI 赞助的 GLM 4 Air\n资源包。[【加入Z计划，和智谱AI一起创业】（点击跳转👇）](https://zhipu-ai.feishu.cn/share/base/form/shrcntPu1mUMhoapEseCJpmUUuf)\n\n<a href=\"https://zhipu-ai.feishu.cn/share/base/form/shrcntPu1mUMhoapEseCJpmUUuf\" target=\"_blank\">\n    <img src=\"https://aise.phodal.com/images/zhipu-z-plan.svg\" width=\"256px\" height=\"auto\"  alt=\"logo\" />\n</a>\n\n## LICENSE\n\nNotes:\n\nStreamDiff based on Continue Dev, Inc, which is licensed under the Apache License, Version 2.0. See `LICENSE-APACHE` in this directory.\n\nThis code is distributed under the MPL 2.0 license. See `LICENSE` in this directory.\n"
  },
  {
    "path": "build.gradle.kts",
    "content": "import groovy.util.Node\nimport groovy.xml.XmlParser\nimport org.jetbrains.changelog.Changelog\nimport org.jetbrains.intellij.platform.gradle.IntelliJPlatformType\nimport org.jetbrains.intellij.platform.gradle.IntelliJPlatformType.*\nimport org.jetbrains.intellij.platform.gradle.TestFrameworkType\nimport org.jetbrains.intellij.platform.gradle.extensions.IntelliJPlatformDependenciesExtension\nimport org.jetbrains.intellij.platform.gradle.extensions.IntelliJPlatformTestingExtension\nimport org.jetbrains.intellij.platform.gradle.tasks.RunIdeTask\nimport org.jetbrains.intellij.platform.gradle.tasks.VerifyPluginTask\nimport org.jetbrains.intellij.platform.gradle.utils.extensionProvider\nimport org.jetbrains.kotlin.gradle.tasks.KotlinCompile\nimport java.util.*\n\nfun properties(key: String) = providers.gradleProperty(key)\nfun environment(key: String) = providers.environmentVariable(key)\n\n// Configure Gradle Changelog Plugin - read more: https://github.com/JetBrains/gradle-changelog-plugin\nchangelog {\n    version.set(properties(\"pluginVersion\"))\n    groups.empty()\n    path.set(rootProject.file(\"CHANGELOG.md\").toString())\n    repositoryUrl.set(properties(\"pluginRepositoryUrl\"))\n    itemPrefix.set(\"*\")\n}\n\n/// maybe refs: https://github.com/HaxeFoundation/intellij-haxe/blob/develop/build.gradle.kts\n/// and https://github.com/JetBrains/educational-plugin\n@Suppress(\"DSL_SCOPE_VIOLATION\")\nplugins {\n    id(\"java\") // Java support\n    alias(libs.plugins.kotlin) // Kotlin support\n    alias(libs.plugins.gradleIntelliJPlugin) // Gradle IntelliJ Plugin\n    alias(libs.plugins.changelog) // Gradle Changelog Plugin\n    alias(libs.plugins.qodana) // Gradle Qodana Plugin\n    alias(libs.plugins.kover) // Gradle Kover Plugin\n    alias(libs.plugins.serialization)\n\n    id(\"org.jetbrains.grammarkit\") version \"2022.3.2.2\"\n\n    id(\"net.saliman.properties\") version \"1.5.2\"\n}\n\nval ideaPlatformVersion = prop(\"ideaPlatformVersion\").toInt()\nval pluginProjects: List<Project> get() = rootProject.allprojects.toList()\nval basePluginArchiveName = \"intellij-shire\"\nval ideaPlugins = listOf(\n    \"org.jetbrains.plugins.terminal\",\n    \"com.intellij.java\",\n    \"org.jetbrains.plugins.gradle\",\n    \"org.jetbrains.idea.maven\",\n    \"JavaScript\",\n    \"org.jetbrains.kotlin\",\n    \"com.jetbrains.restClient\"\n) + if (ideaPlatformVersion == 243) {\n    listOf(prop(\"jsonPlugin\"))\n} else {\n    emptyList()\n} + prop(\"platformPlugins\").split(\",\")\n\nrepositories {\n    intellijPlatform {\n        defaultRepositories()\n        jetbrainsRuntime()\n    }\n}\n\nconfigure(\n    subprojects\n            - project(\":languages\")\n            - project(\":toolsets\")\n) {\n    apply {\n        plugin(\"org.jetbrains.intellij.platform.module\")\n    }\n\n    intellijPlatform {\n        instrumentCode = false\n        buildSearchableOptions = false\n    }\n\n    tasks {\n        prepareSandbox { enabled = false }\n//        prepareTestSandbox { enabled = false }\n    }\n\n    val testOutput = configurations.create(\"testOutput\")\n\n    dependencies {\n        testOutput(sourceSets.test.get().output.classesDirs)\n\n        if (ideaPlatformVersion == 243) {\n            testImplementation(\"junit:junit:4.13.2\")\n            testImplementation(\"org.opentest4j:opentest4j:1.3.0\")\n        }\n\n        intellijPlatform {\n            testFramework(TestFrameworkType.Bundled)\n        }\n    }\n}\n\nconfigure(\n    allprojects\n            - project(\":languages\")\n            - project(\":toolsets\")\n) {\n    apply {\n        plugin(\"idea\")\n        plugin(\"kotlin\")\n        plugin(\"org.jetbrains.kotlinx.kover\")\n        plugin(\"org.jetbrains.intellij.platform.module\")\n    }\n\n    repositories {\n        mavenCentral()\n\n        intellijPlatform {\n            defaultRepositories()\n        }\n    }\n\n    dependencies {\n//        compileOnly(kotlin(\"stdlib-jdk8\"))\n        implementation(\"org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2\") {\n            excludeKotlinDeps()\n        }\n\n        implementation(\"org.jetbrains.kotlinx:kotlinx-serialization-json:1.8.1\") {\n            excludeKotlinDeps()\n        }\n\n        intellijPlatform {\n        }\n\n//        slf4j-simple\n        implementation(\"org.slf4j:slf4j-simple:1.7.36\")\n    }\n\n    configurations.all {\n        resolutionStrategy {\n            eachDependency {\n                if (requested.group == \"org.slf4j\" && requested.name == \"slf4j-api\") {\n                    useVersion(\"1.7.36\")\n                }\n                // \"org.apache.velocity:velocity-engine-core:2.4.1\"\n                if (requested.group == \"org.apache.velocity\" && requested.name == \"velocity-engine-core\") {\n                    useVersion(\"2.4.1\")\n                }\n            }\n        }\n    }\n\n\n    idea {\n        module {\n            generatedSourceDirs.add(file(\"src/gen\"))\n            isDownloadJavadoc = true\n            isDownloadSources = true\n        }\n    }\n}\n\nproject(\":core\") {\n    apply {\n        plugin(\"org.jetbrains.kotlin.plugin.serialization\")\n    }\n\n    intellijPlatform {\n        instrumentCode = false\n    }\n\n    dependencies {\n        intellijPlatform {\n            intellijIde(prop(\"ideaVersion\"))\n            intellijPlugins(ideaPlugins)\n\n            pluginModule(implementation(project(\":languages:shire-json\")))\n\n            testFramework(TestFrameworkType.Platform)\n        }\n\n        implementation(project(\":languages:shire-json\"))\n\n        implementation(\"com.charleskorn.kaml:kaml:0.98.0\")\n        implementation(\"org.reflections:reflections:0.10.2\") {\n            exclude(group = \"org.slf4j\", module = \"slf4j-api\")\n        }\n\n        // chocolate factory\n        // follow: https://onnxruntime.ai/docs/get-started/with-java.html\n//        implementation(\"com.microsoft.onnxruntime:onnxruntime:1.19.2\")\n//        implementation(\"ai.djl.huggingface:tokenizers:0.29.0\")\n\n        implementation(\"cc.unitmesh:document:1.0.0\")\n        implementation(\"cc.unitmesh:cocoa-core:1.0.0\") {\n            exclude(group = \"org.jetbrains.kotlinx\", module = \"kotlinx-coroutines-core\")\n            excludeKotlinDeps()\n        }\n\n        // custom agent deps\n        implementation(\"com.nfeld.jsonpathkt:jsonpathkt:2.0.1\")\n        implementation(\"com.squareup.okhttp3:okhttp:4.4.1\")\n        implementation(\"com.squareup.okhttp3:okhttp-sse:4.12.0\")\n        // open ai deps\n        implementation(\"io.reactivex.rxjava3:rxjava:3.1.10\")\n        implementation(\"com.fasterxml.jackson.module:jackson-module-kotlin:2.19.0\")\n        implementation(\"com.fasterxml.jackson.core:jackson-databind:2.18.3\")\n    }\n}\n\nproject(\":languages:shire-java\") {\n    dependencies {\n        intellijPlatform {\n            intellijIde(prop(\"ideaVersion\"))\n            intellijPlugins(ideaPlugins)\n        }\n\n        implementation(project(\":core\"))\n    }\n}\n\nproject(\":languages:shire-javascript\") {\n    dependencies {\n        intellijPlatform {\n            intellijIde(prop(\"ideaVersion\"))\n            intellijPlugins(ideaPlugins)\n            intellijPlugins(prop(\"nodejsPlugin\"))\n\n            testFramework(TestFrameworkType.Plugin.JavaScript)\n        }\n\n        implementation(project(\":core\"))\n    }\n}\n\nproject(\":languages:shire-kotlin\") {\n    dependencies {\n        intellijPlatform {\n            intellijIde(prop(\"ideaVersion\"))\n            intellijPlugins(ideaPlugins)\n        }\n\n        implementation(project(\":core\"))\n        implementation(project(\":languages:shire-java\"))\n    }\n}\n\nproject(\":languages:shire-markdown\") {\n    dependencies {\n        intellijPlatform {\n            intellijIde(prop(\"ideaVersion\"))\n            intellijPlugins(ideaPlugins)\n        }\n\n        implementation(project(\":core\"))\n    }\n}\n\nproject(\":languages:shire-python\") {\n    dependencies {\n        intellijPlatform {\n            intellijIde(prop(\"ideaVersion\"))\n            intellijPlugins(ideaPlugins + prop(\"pythonPlugins\"))\n        }\n\n        implementation(project(\":core\"))\n    }\n}\n\nproject(\":languages:shire-go\") {\n    dependencies {\n        intellijPlatform {\n            intellijIde(prop(\"ideaVersion\"))\n            intellijPlugins(ideaPlugins)\n            intellijPlugins(prop(\"goPlugin\"))\n        }\n\n        implementation(project(\":core\"))\n    }\n}\n\nproject(\":languages:shire-proto\") {\n    dependencies {\n        intellijPlatform {\n            intellijIde(prop(\"ideaVersion\"))\n            intellijPlugins(ideaPlugins)\n            intellijPlugins(prop(\"protoPlugin\"))\n        }\n\n        implementation(project(\":core\"))\n    }\n}\n\nproject(\":languages:shire-json\") {\n    dependencies {\n        intellijPlatform {\n            intellijIde(prop(\"ideaVersion\"))\n            if (ideaPlatformVersion == 243) {\n                plugins(prop(\"jsonPlugin\"))\n            }\n\n            intellijPlugins(ideaPlugins + if (ideaPlatformVersion == 243) \"com.intellij.modules.json\" else \"\")\n        }\n    }\n\n    sourceSets {\n        main {\n            resources.srcDirs(\"src/$ideaPlatformVersion/main/resources\")\n        }\n        test {\n            resources.srcDirs(\"src/$ideaPlatformVersion/test/resources\")\n        }\n    }\n    kotlin {\n        sourceSets {\n            main {\n                kotlin.srcDirs(\"src/main/kotlin\", \"src/$ideaPlatformVersion/main/kotlin\")\n            }\n            test {\n                kotlin.srcDirs(\"src/$ideaPlatformVersion/test/kotlin\")\n            }\n        }\n    }\n}\n\nproject(\":toolsets:git\") {\n    dependencies {\n        intellijPlatform {\n            intellijIde(prop(\"ideaVersion\"))\n            intellijPlugins(ideaPlugins + \"Git4Idea\")\n        }\n\n        implementation(project(\":core\"))\n\n        implementation(\"org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2\")\n    }\n}\n\nproject(\":toolsets:httpclient\") {\n    dependencies {\n        intellijPlatform {\n            intellijIde(prop(\"ideaVersion\"))\n            intellijPlugins(ideaPlugins)\n        }\n\n        implementation(project(\":core\"))\n        implementation(project(\":languages:shire-json\"))\n\n        // custom agent deps\n        implementation(\"com.nfeld.jsonpathkt:jsonpathkt:2.0.1\")\n        implementation(\"com.squareup.okhttp3:okhttp:4.4.1\")\n        implementation(\"com.squareup.okhttp3:okhttp-sse:4.12.0\")\n        // open ai deps\n        implementation(\"io.reactivex.rxjava3:rxjava:3.1.10\")\n        implementation(\"com.fasterxml.jackson.module:jackson-module-kotlin:2.19.0\")\n        implementation(\"com.fasterxml.jackson.core:jackson-databind:2.19.0\")\n    }\n}\n\nproject(\":toolsets:terminal\") {\n    dependencies {\n        intellijPlatform {\n            intellijIde(prop(\"ideaVersion\"))\n            intellijPlugins(ideaPlugins)\n        }\n\n        implementation(project(\":core\"))\n    }\n}\n\nproject(\":toolsets:sonarqube\") {\n    dependencies {\n        intellijPlatform {\n            intellijIde(prop(\"ideaVersion\"))\n            intellijPlugins(ideaPlugins + prop(\"sonarPlugin\"))\n        }\n\n        implementation(project(\":core\"))\n    }\n}\n\nproject(\":toolsets:plantuml\") {\n    dependencies {\n        intellijPlatform {\n            intellijIde(prop(\"ideaVersion\"))\n            intellijPlugins(ideaPlugins + prop(\"plantUmlPlugin\"))\n        }\n\n        implementation(project(\":core\"))\n    }\n}\n\nproject(\":toolsets:mock\") {\n    dependencies {\n        intellijPlatform {\n            intellijIde(prop(\"ideaVersion\"))\n            intellijPlugins(ideaPlugins + prop(\"wireMockPlugin\"))\n        }\n\n        implementation(project(\":core\"))\n    }\n}\n\nproject(\":toolsets:database\") {\n    dependencies {\n        intellijPlatform {\n            intellijIde(prop(\"ideaVersion\"))\n            intellijPlugins(ideaPlugins + \"com.intellij.database\")\n        }\n\n        implementation(project(\":core\"))\n    }\n}\n\nproject(\":toolsets:openrewrite\") {\n    dependencies {\n        intellijPlatform {\n            intellijIde(prop(\"ideaVersion\"))\n            intellijPlugins(ideaPlugins + \"com.intellij.openRewrite\")\n        }\n\n        implementation(project(\":core\"))\n    }\n}\n\nproject(\":toolsets:mermaid\") {\n    dependencies {\n        intellijPlatform {\n            intellijIde(prop(\"ideaVersion\"))\n            intellijPlugins(ideaPlugins + prop(\"mermaidPlugin\"))\n        }\n\n        implementation(project(\":core\"))\n    }\n}\n\nproject(\":toolsets:docker\") {\n    dependencies {\n        intellijPlatform {\n            intellijIde(prop(\"ideaVersion\"))\n            intellijPlugins(ideaPlugins + \"Docker\")\n        }\n\n        implementation(project(\":core\"))\n    }\n}\n\nproject(\":shirelang\") {\n    apply {\n        plugin(\"org.jetbrains.grammarkit\")\n    }\n\n    dependencies {\n        intellijPlatform {\n            intellijIde(prop(\"ideaVersion\"))\n            intellijPlugins(ideaPlugins + \"org.intellij.plugins.markdown\" + \"com.jetbrains.sh\" + \"Git4Idea\")\n        }\n\n        implementation(\"com.nfeld.jsonpathkt:jsonpathkt:2.0.1\")\n        implementation(\"org.apache.velocity:velocity-engine-core:2.4.1\") {\n            exclude(group = \"org.slf4j\", module = \"slf4j-api\")\n        }\n\n        // https://mvnrepository.com/artifact/com.huaban/jieba-analysis\n        implementation(\"com.huaban:jieba-analysis:1.0.2\")\n\n        implementation(\"cc.unitmesh:cocoa-core:1.0.0\") {\n            excludeKotlinDeps()\n        }\n\n        // for ShireQL Schema\n        implementation(\"org.jetbrains.kotlinx:kotlinx-datetime:0.6.2\")\n\n        implementation(kotlin(\"reflect\"))\n        implementation(project(\":core\"))\n        implementation(project(\":languages:shire-json\"))\n    }\n\n    tasks {\n        generateLexer {\n            sourceFile.set(file(\"src/main/grammar/ShireLexer.flex\"))\n            targetOutputDir.set(file(\"src/gen/com/phodal/shirelang/lexer\"))\n            purgeOldFiles.set(true)\n        }\n\n        generateParser {\n            sourceFile.set(file(\"src/main/grammar/ShireParser.bnf\"))\n            targetRootOutputDir.set(file(\"src/gen\"))\n            pathToParser.set(\"com/phodal/shirelang/parser/ShireParser.java\")\n            pathToPsiRoot.set(\"com/phodal/shirelang/psi\")\n            purgeOldFiles.set(true)\n        }\n\n        withType<KotlinCompile> {\n            dependsOn(generateLexer, generateParser)\n        }\n    }\n\n    sourceSets {\n        main {\n            java.srcDirs(\"src/gen\")\n        }\n    }\n}\n\n/**\n * Creates `run$[baseTaskName]` Gradle task to run IDE of given [type]\n * via `runIde` task with plugins according to [ideToPlugins] map\n */\nfun IntelliJPlatformTestingExtension.customRunIdeTask(\n    type: IntelliJPlatformType,\n    versionWithCode: String? = null,\n    baseTaskName: String = type.name,\n) {\n    runIde.register(\"run$baseTaskName\") {\n        useInstaller = false\n\n        if (versionWithCode != null) {\n            val version = versionWithCode.toTypeWithVersion().version\n\n            this.type = type\n            this.version = version\n        } else {\n            val pathProperty = baseTaskName.replaceFirstChar { it.lowercaseChar() } + \"Path\"\n            // Avoid throwing exception during property calculation.\n            // Some IDE tooling (for example, Package Search plugin) may try to calculate properties during `Sync` phase for all tasks.\n            // In our case, some `run*` task may not have `pathProperty` in your `gradle.properties`,\n            // and as a result, the `Sync` tool window will show you the error thrown by `prop` function.\n            //\n            // The following solution just moves throwing the corresponding error to task execution,\n            // i.e., only when a task is actually invoked\n            if (hasProp(pathProperty)) {\n                localPath.convention(layout.dir(provider { file(prop(pathProperty)) }))\n            } else {\n                task {\n                    doFirst {\n                        throw GradleException(\"Property `$pathProperty` is not defined in gradle.properties\")\n                    }\n                }\n            }\n        }\n\n        // Specify custom sandbox directory to have a stable path to log file\n        sandboxDirectory =\n            intellijPlatform.sandboxContainer.dir(\"${baseTaskName.lowercase()}-sandbox-${prop(\"ideaPlatformVersion\")}\")\n\n        plugins {\n            plugins(ideaPlugins)\n        }\n    }\n}\n\nproject(\":\") {\n    apply {\n        plugin(\"org.jetbrains.changelog\")\n        plugin(\"org.jetbrains.intellij.platform\")\n    }\n\n    repositories {\n        intellijPlatform {\n            defaultRepositories()\n            jetbrainsRuntime()\n        }\n    }\n\n    intellijPlatform {\n        projectName = basePluginArchiveName\n        pluginConfiguration {\n            id = \"com.phodal.shire\"\n            name = \"Shire - AI Coding Agent Language\"\n            version = prop(\"pluginVersion\")\n\n            ideaVersion {\n                sinceBuild = prop(\"pluginSinceBuild\")\n                untilBuild = prop(\"pluginUntilBuild\")\n            }\n\n            vendor {\n                name = \"Phodal Huang\"\n            }\n\n            val changelog = project.changelog // local variable for configuration cache compatibility\n            // Get the latest available change notes from the changelog file\n            changeNotes = providers.gradleProperty(\"pluginVersion\").map { pluginVersion ->\n                with(changelog) {\n                    renderItem(\n                        (getOrNull(pluginVersion) ?: getUnreleased())\n                            .withHeader(false)\n                            .withEmptySections(false),\n                        Changelog.OutputType.HTML,\n                    )\n                }\n            }\n        }\n\n        pluginVerification {\n            freeArgs = listOf(\"-mute\", \"TemplateWordInPluginId,ForbiddenPluginIdPrefix\")\n            failureLevel = listOf(VerifyPluginTask.FailureLevel.MISSING_DEPENDENCIES)\n            ides {\n                select {\n                    sinceBuild = \"242\"\n                    untilBuild = \"243\"\n////                    sinceBuild = prop(\"pluginSinceBuild\")\n////                    untilBuild = prop(\"pluginUntilBuild\")\n                }\n            }\n        }\n\n        instrumentCode = false\n        buildSearchableOptions = false\n\n        signing {\n            certificateChain = providers.environmentVariable(\"CERTIFICATE_CHAIN\")\n            privateKey = providers.environmentVariable(\"PRIVATE_KEY\")\n            password = providers.environmentVariable(\"PRIVATE_KEY_PASSWORD\")\n        }\n\n        publishing {\n            token = providers.environmentVariable(\"PUBLISH_TOKEN\")\n            channels.set(properties(\"pluginVersion\").map {\n                listOf(it.split('-').getOrElse(1) { \"default\" }.split('.').first())\n            })\n        }\n    }\n\n    dependencies {\n        intellijPlatform {\n            pluginVerifier()\n            intellijIde(prop(\"ideaVersion\"))\n            intellijPlugins(ideaPlugins)\n\n            if (hasProp(\"jbrVersion\")) {\n                jetbrainsRuntime(prop(\"jbrVersion\"))\n            } else {\n                jetbrainsRuntime()\n            }\n\n            plugin(prop(\"jsonPlugin\"))\n\n            pluginModule(implementation(project(\":core\")))\n            pluginModule(implementation(project(\":shirelang\")))\n            pluginModule(implementation(project(\":languages:shire-java\")))\n            pluginModule(implementation(project(\":languages:shire-javascript\")))\n            pluginModule(implementation(project(\":languages:shire-python\")))\n            pluginModule(implementation(project(\":languages:shire-kotlin\")))\n            pluginModule(implementation(project(\":languages:shire-go\")))\n            pluginModule(implementation(project(\":languages:shire-markdown\")))\n            pluginModule(implementation(project(\":languages:shire-proto\")))\n\n//            pluginModule(implementation(project(\":languages:shire-json\")))\n            pluginModule(implementation(project(\":toolsets:git\")))\n            pluginModule(implementation(project(\":toolsets:httpclient\")))\n            pluginModule(implementation(project(\":toolsets:terminal\")))\n            pluginModule(implementation(project(\":toolsets:sonarqube\")))\n            pluginModule(implementation(project(\":toolsets:database\")))\n            pluginModule(implementation(project(\":toolsets:mock\")))\n            pluginModule(implementation(project(\":toolsets:openrewrite\")))\n            pluginModule(implementation(project(\":toolsets:plantuml\")))\n            pluginModule(implementation(project(\":toolsets:mermaid\")))\n            pluginModule(implementation(project(\":toolsets:docker\")))\n\n            testFramework(TestFrameworkType.Bundled)\n        }\n\n        // custom agent deps\n        implementation(\"com.nfeld.jsonpathkt:jsonpathkt:2.0.1\")\n        implementation(\"com.squareup.okhttp3:okhttp:4.4.1\")\n        implementation(\"com.squareup.okhttp3:okhttp-sse:4.12.0\")\n        // open ai deps\n        implementation(\"io.reactivex.rxjava3:rxjava:3.1.10\")\n        implementation(\"com.fasterxml.jackson.module:jackson-module-kotlin:2.19.0\")\n        implementation(\"com.fasterxml.jackson.core:jackson-databind:2.19.0\")\n    }\n\n//    kotlin {\n//        sourceSets {\n//            main {\n//                resources.srcDirs(\"src/$ideaPlatformVersion/main/resources\")\n//            }\n//        }\n//    }\n\n    tasks {\n        val projectName = project.extensionProvider.flatMap { it.projectName }\n\n        composedJar {\n            archiveBaseName.convention(projectName)\n        }\n\n        withType<RunIdeTask> {\n            // Disable auto plugin reloading. See `com.intellij.ide.plugins.DynamicPluginVfsListener`\n            // To enable dynamic reloading, change value to `true` and disable `EduDynamicPluginListener`\n            autoReload = false\n            jvmArgs(\"-Xmx2g\")\n            jvmArgs(\"-Dide.experimental.ui=true\")\n\n            // Uncomment to show localized messages\n            // jvmArgs(\"-Didea.l10n=true\")\n\n            // Uncomment to enable memory dump creation if plugin cannot be unloaded by the platform\n            // jvmArgs(\"-Dide.plugins.snapshot.on.unload.fail=true\")\n\n            // Uncomment to enable FUS testing mode\n            // jvmArgs(\"-Dfus.internal.test.mode=true\")\n        }\n\n        patchPluginXml {\n            pluginDescription.set(provider { file(\"src/description.html\").readText() })\n\n            changelog {\n                version.set(properties(\"pluginVersion\"))\n                groups.empty()\n                path.set(rootProject.file(\"CHANGELOG.md\").toString())\n                repositoryUrl.set(properties(\"pluginRepositoryUrl\"))\n            }\n\n            val changelog = project.changelog\n            // Get the latest available change notes from the changelog file\n            changeNotes.set(properties(\"pluginVersion\").map { pluginVersion ->\n                with(changelog) {\n                    renderItem(\n                        (getOrNull(pluginVersion) ?: getUnreleased())\n                            .withHeader(false)\n                            .withEmptySections(false),\n\n                        Changelog.OutputType.HTML,\n                    )\n                }\n            })\n        }\n\n        val newName = \"intellij-shire-\" + prop(\"ideaVersion\") + \"-\" + prop(\"pluginVersion\")\n\n        publishPlugin {\n            dependsOn(\"patchChangelog\")\n            archiveFile = file(\"build/distributions/$newName.zip\")\n        }\n\n        buildPlugin {\n            archiveBaseName.convention(newName)\n        }\n\n        intellijPlatformTesting {\n            // Generates event scheme for JetBrains Academy plugin FUS events to `build/eventScheme.json`\n            runIde.register(\"buildEventsScheme\") {\n                task {\n                    args(\n                        \"buildEventsScheme\",\n                        \"--outputFile=${buildDir()}/eventScheme.json\",\n                        \"--pluginId=com.jetbrains.edu\"\n                    )\n                    // Force headless mode to be able to run command on CI\n                    systemProperty(\"java.awt.headless\", \"true\")\n                    // BACKCOMPAT: 2024.1. Update value to 242 and this comment\n                    // `IDEA_BUILD_NUMBER` variable is used by `buildEventsScheme` task to write `buildNumber` to output json.\n                    // It will be used by TeamCity automation to set minimal IDE version for new events\n                    environment(\"IDEA_BUILD_NUMBER\", \"241\")\n                }\n            }\n\n            runIde.register(\"runInSplitMode\") {\n                splitMode = true\n\n                // Specify custom sandbox directory to have a stable path to log file\n                sandboxDirectory = intellijPlatform.sandboxContainer.dir(\"split-mode-sandbox-$ideaPlatformVersion\")\n\n                plugins {\n                    plugins(ideaPlugins)\n                }\n            }\n\n            customRunIdeTask(IntellijIdeaUltimate, prop(\"ideaVersion\"), baseTaskName = \"Idea\")\n        }\n    }\n}\n\nfun File.isPluginJar(): Boolean {\n    if (!isFile) return false\n    if (extension != \"jar\") return false\n    return zipTree(this).files.any { it.isManifestFile() }\n}\n\nfun File.isManifestFile(): Boolean {\n    if (extension != \"xml\") return false\n    val rootNode = try {\n        val parser = XmlParser()\n        parser.parse(this)\n    } catch (e: Exception) {\n        logger.error(\"Failed to parse $path\", e)\n        return false\n    }\n    return rootNode.name() == \"idea-plugin\"\n}\n\ndata class TypeWithVersion(val type: IntelliJPlatformType, val version: String)\n\nfun String.toTypeWithVersion(): TypeWithVersion {\n    val (code, version) = split(\"-\", limit = 2)\n    return TypeWithVersion(IntelliJPlatformType.fromCode(code), version)\n}\n\nfun IntelliJPlatformDependenciesExtension.intellijIde(versionWithCode: String) {\n    val (type, version) = versionWithCode.toTypeWithVersion()\n    create(type, version, useInstaller = false)\n}\n\nfun IntelliJPlatformDependenciesExtension.intellijPlugins(vararg notations: String) {\n    for (notation in notations) {\n        if (notation.contains(\":\")) {\n            plugin(notation)\n        } else {\n            bundledPlugin(notation)\n        }\n    }\n}\n\nfun IntelliJPlatformDependenciesExtension.intellijPlugins(notations: List<String>) {\n    intellijPlugins(*notations.toTypedArray())\n}\n\nfun hasProp(name: String): Boolean = extra.has(name)\n\nfun prop(name: String): String =\n    extra.properties[name] as? String ?: error(\"Property `$name` is not defined in gradle.properties\")\n\nfun withProp(name: String, action: (String) -> Unit) {\n    if (hasProp(name)) {\n        action(prop(name))\n    }\n}\n\nfun withProp(filePath: String, name: String, action: (String) -> Unit) {\n    if (!file(filePath).exists()) {\n        println(\"$filePath doesn't exist\")\n        return\n    }\n    val properties = loadProperties(filePath)\n    val value = properties.getProperty(name) ?: return\n    action(value)\n}\n\nfun buildDir(): String {\n    return project.layout.buildDirectory.get().asFile.absolutePath\n}\n\nfun <T : ModuleDependency> T.excludeKotlinDeps() {\n    exclude(module = \"kotlin-runtime\")\n    exclude(module = \"kotlin-reflect\")\n    exclude(module = \"kotlin-stdlib\")\n    exclude(module = \"kotlin-stdlib-common\")\n    exclude(module = \"kotlin-stdlib-jdk8\")\n}\n\nfun loadProperties(path: String): Properties {\n    val properties = Properties()\n    file(path).bufferedReader().use { properties.load(it) }\n    return properties\n}\n\nfun parseManifest(file: File): Node {\n    val node = XmlParser().parse(file)\n    check(node.name() == \"idea-plugin\") {\n        \"Manifest file `$file` doesn't contain top-level `idea-plugin` attribute\"\n    }\n    return node\n}\n\nfun manifestFile(project: Project): File? {\n    var filePath: String? = null\n\n    val mainOutput = project.sourceSets.main.get().output\n    val resourcesDir = mainOutput.resourcesDir ?: error(\"Failed to find resources dir for ${project.name}\")\n\n    if (filePath != null) {\n        return resourcesDir.resolve(filePath).takeIf { it.exists() }\n            ?: error(\"Failed to find manifest file for ${project.name} module\")\n    }\n    val rootManifestFile =\n        manifestFile(project(\":intellij-plugin\")) ?: error(\"Failed to find manifest file for :intellij-plugin module\")\n    val rootManifest = parseManifest(rootManifestFile)\n    val children = ((rootManifest[\"content\"] as? List<*>)?.single() as? Node)?.children()\n        ?: error(\"Failed to find module declarations in root manifest\")\n    return children.filterIsInstance<Node>()\n        .flatMap { node ->\n            if (node.name() != \"module\") return@flatMap emptyList()\n            val name = node.attribute(\"name\") as? String ?: return@flatMap emptyList()\n            listOfNotNull(resourcesDir.resolve(\"$name.xml\").takeIf { it.exists() })\n        }.firstOrNull() ?: error(\"Failed to find manifest file for ${project.name} module\")\n}\n\nfun findModulePackage(project: Project): String? {\n    val moduleManifest = manifestFile(project) ?: return null\n    val node = parseManifest(moduleManifest)\n    return node.attribute(\"package\") as? String ?: error(\"Failed to find package for ${project.name}\")\n}\n\nfun verifyClasses(project: Project) {\n    val pkg = findModulePackage(project) ?: return\n    val expectedDir = pkg.replace('.', '/')\n\n    var hasErrors = false\n    for (classesDir in project.sourceSets.main.get().output.classesDirs) {\n        val basePath = classesDir.toPath()\n        for (file in classesDir.walk()) {\n            if (file.isFile && file.extension == \"class\") {\n                val relativePath = basePath.relativize(file.toPath())\n                if (!relativePath.startsWith(expectedDir)) {\n                    logger.error(\n                        \"Wrong package of `${\n                            relativePath.joinToString(\".\").removeSuffix(\".class\")\n                        }` class. Expected `$pkg`\"\n                    )\n                    hasErrors = true\n                }\n            }\n        }\n    }\n\n    if (hasErrors) {\n        throw GradleException(\"Classes with wrong package were found. See https://docs.google.com/document/d/1pOy-qNlGOJe6wftHVYHkH8sZOoAfav1fdGDPJgkQWJo\")\n    }\n}\n\nfun DependencyHandler.implementationWithoutKotlin(dependencyNotation: Provider<*>) {\n    implementation(dependencyNotation) {\n        excludeKotlinDeps()\n    }\n}\n\nfun DependencyHandler.testImplementationWithoutKotlin(dependencyNotation: Provider<*>) {\n    testImplementation(dependencyNotation) {\n        excludeKotlinDeps()\n    }\n}\n"
  },
  {
    "path": "core/README.md",
    "content": "# MultiplePlatform with Cross IDE APIs\n\n\n"
  },
  {
    "path": "core/src/embeddings/LocalEmbedding.kt",
    "content": "package com.phodal.shirecore.search.function\n\nimport ai.djl.huggingface.tokenizers.HuggingFaceTokenizer\nimport ai.onnxruntime.OnnxTensor\nimport ai.onnxruntime.OrtEnvironment\nimport ai.onnxruntime.OrtSession\nimport ai.onnxruntime.OrtUtil\nimport com.intellij.util.concurrency.annotations.RequiresBackgroundThread\nimport kotlin.collections.indices\nimport kotlin.collections.slice\nimport kotlin.collections.toLongArray\nimport kotlin.ranges.until\nimport kotlin.to\nimport kotlinx.coroutines.*\n\nclass LocalEmbedding(\n    private val tokenizer: HuggingFaceTokenizer,\n    private val session: OrtSession,\n    private val env: OrtEnvironment,\n) {\n    @OptIn(ExperimentalCoroutinesApi::class)\n    private val embeddingContext = Dispatchers.Default.limitedParallelism(1)\n\n    @RequiresBackgroundThread\n    suspend fun embed(input: String): FloatArray {\n        return withContext(embeddingContext) {\n            embedInternal(input, 512)\n        }\n    }\n\n    fun embedInternal(input: String, dimensions: Int = 512): FloatArray {\n        val tokenized = tokenizer.encode(input, true, true)\n\n        var inputIds = tokenized.ids\n        var attentionMask = tokenized.attentionMask\n        var typeIds = tokenized.typeIds\n\n        if (tokenized.ids.size >= dimensions) {\n            inputIds = inputIds.slice(0 until dimensions).toLongArray()\n            attentionMask = attentionMask.slice(0 until dimensions).toLongArray()\n            typeIds = typeIds.slice(0 until dimensions).toLongArray()\n        }\n\n        val tensorInput = OrtUtil.reshape(inputIds, longArrayOf(1, inputIds.size.toLong()))\n        val tensorAttentionMask = OrtUtil.reshape(attentionMask, longArrayOf(1, attentionMask.size.toLong()))\n        val tensorTypeIds = OrtUtil.reshape(typeIds, longArrayOf(1, typeIds.size.toLong()))\n\n        val result = session.run(\n            mapOf(\n                \"input_ids\" to OnnxTensor.createTensor(env, tensorInput),\n                \"attention_mask\" to OnnxTensor.createTensor(env, tensorAttentionMask),\n                \"token_type_ids\" to OnnxTensor.createTensor(env, tensorTypeIds),\n            ),\n        )\n\n        val outputTensor: OnnxTensor = result.get(0) as OnnxTensor\n\n        val floatArray = outputTensor.floatBuffer.array()\n        // floatArray is an inputIds.size * 384 array, we need to mean it to 384 * 1\n        // 1, shape, shape.length\n        val shapeSize = outputTensor.info.shape[2].toInt()\n        val meanArray = FloatArray(shapeSize)\n        for (i in 0 until shapeSize) {\n            var sum = 0f\n            for (j in inputIds.indices) {\n                sum += floatArray[j * shapeSize + i]\n            }\n\n            meanArray[i] = sum / inputIds.size\n        }\n\n        return meanArray\n    }\n\n    companion object {\n        /**\n         * Create a new instance of [LocalEmbedding] with default model.\n         * We use official model: [all-MiniLM-L6-v2](https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2)\n         * We can use [optimum](https://github.com/huggingface/optimum) to transform the model to onnx.\n         */\n        fun create(): LocalEmbedding? {\n            val currentThread = Thread.currentThread()\n            val originalClassLoader = currentThread.contextClassLoader\n            val pluginClassLoader = Companion::class.java.classLoader\n\n            return try {\n                currentThread.setContextClassLoader(pluginClassLoader);\n\n                val tokenizerStream = pluginClassLoader.getResourceAsStream(\"model/tokenizer.json\")\n                val tokenizer = HuggingFaceTokenizer.newInstance(tokenizerStream, null)\n\n                val ortEnv = OrtEnvironment.getEnvironment()\n                val sessionOptions = OrtSession.SessionOptions()\n\n                val onnxStream = pluginClassLoader.getResourceAsStream(\"model/model.onnx\")!!\n                // load onnxPath as byte[]\n                val onnxPathAsByteArray = onnxStream.readAllBytes()\n                val session = ortEnv.createSession(onnxPathAsByteArray, sessionOptions)\n\n                return LocalEmbedding(tokenizer, session, ortEnv)\n            } catch (e: Exception) {\n                e.printStackTrace()\n                currentThread.setContextClassLoader(originalClassLoader)\n                null\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/ProjectUtil.kt",
    "content": "package com.phodal.shirecore\n\nimport com.intellij.openapi.application.ApplicationManager\nimport com.intellij.openapi.application.runReadAction\nimport com.intellij.openapi.fileTypes.FileTypeManager\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.project.guessProjectDir\nimport com.intellij.openapi.util.io.FileUtil\nimport com.intellij.openapi.util.io.FileUtilRt\nimport com.intellij.openapi.vcs.changes.VcsIgnoreManager\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.intellij.openapi.vfs.VirtualFileManager\nimport com.intellij.psi.search.FilenameIndex\nimport com.intellij.psi.search.ProjectScope\n\nfun Project.lookupFile(path: String): VirtualFile? {\n    val projectPath = this.guessProjectDir()?.toNioPath()\n    val realpath = projectPath?.resolve(path)\n    return VirtualFileManager.getInstance().findFileByUrl(\"file://${realpath?.toAbsolutePath()}\")\n}\n\nfun Project.findFile(filename: String, caseSensitively: Boolean = true): VirtualFile? {\n    ApplicationManager.getApplication().assertReadAccessAllowed()\n    val currentTask = ApplicationManager.getApplication().executeOnPooledThread<VirtualFile?> {\n        val searchedFiles = runReadAction {\n            FilenameIndex.getVirtualFilesByName(filename, caseSensitively, ProjectScope.getProjectScope(this))\n        }\n        return@executeOnPooledThread searchedFiles.firstOrNull()\n    }\n\n    return currentTask.get()\n}\n\nfun VirtualFile.canBeAdded(project: Project): Boolean {\n    if (!this.isValid || this.isDirectory) return false\n    if (this.fileType.isBinary || FileUtilRt.isTooLarge(this.length)) return false\n    if (FileTypeManager.getInstance().isFileIgnored(this)) return false\n    if (isIgnoredByVcs(project, this)) return false\n\n    return true\n}\n\nfun VirtualFile.relativePath(project: Project): String {\n    val projectDir = project.guessProjectDir()!!.toNioPath().toFile()\n    val relativePath = FileUtil.getRelativePath(projectDir, this.toNioPath().toFile())\n    return relativePath ?: this.path\n}\n\nfun isIgnoredByVcs(project: Project?, file: VirtualFile?): Boolean {\n    val ignoreManager = VcsIgnoreManager.getInstance(project!!)\n    return ignoreManager.isPotentiallyIgnoredFile(file!!)\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/ShireConstants.kt",
    "content": "package com.phodal.shirecore\n\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.project.guessProjectDir\nimport com.intellij.openapi.vfs.VirtualFile\n\npublic const val SHIRE_CHAT_BOX_FILE = \"shire-chatbox.default.shire\"\npublic const val SHIRE_TEMP_OUTPUT = \".shire-output\"\npublic const val LLM_LOGGING_JSONL = \"logging.jsonl\"\npublic const val LLM_LOGGING = \"logging.log\"\npublic const val SHIRE_MKT_HOST = \"https://shire.run/packages.json\"\n\nobject ShireConstants {\n    fun outputDir(project: Project): VirtualFile? {\n        val baseDir = project.guessProjectDir() ?: throw IllegalStateException(\"Project directory not found\")\n        val virtualFile = baseDir.findFileByRelativePath(SHIRE_TEMP_OUTPUT)\n        if (virtualFile == null) {\n            baseDir.createChildDirectory(this, SHIRE_TEMP_OUTPUT)\n        }\n\n        return virtualFile\n    }\n}\n\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/ShireCoreBundle.kt",
    "content": "package com.phodal.shirecore\n\nimport com.intellij.DynamicBundle\nimport org.jetbrains.annotations.NonNls\nimport org.jetbrains.annotations.PropertyKey\n\n@NonNls\nprivate const val BUNDLE = \"messages.ShireCoreBundle\"\n\nobject ShireCoreBundle : DynamicBundle(BUNDLE) {\n    @Suppress(\"SpreadOperator\")\n    @JvmStatic\n    fun message(@PropertyKey(resourceBundle = BUNDLE) key: String, vararg params: Any) = getMessage(key, *params)\n\n    @Suppress(\"SpreadOperator\", \"unused\")\n    @JvmStatic\n    fun messagePointer(@PropertyKey(resourceBundle = BUNDLE) key: String, vararg params: Any) =\n        getLazyMessage(key, *params)\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/ShireCoroutineScope.kt",
    "content": "package com.phodal.shirecore\n\nimport com.intellij.openapi.components.Service\nimport com.intellij.openapi.diagnostic.Logger\nimport com.intellij.openapi.project.Project\nimport com.intellij.util.concurrency.AppExecutorUtil\nimport kotlinx.coroutines.CoroutineExceptionHandler\nimport kotlinx.coroutines.CoroutineScope\nimport kotlinx.coroutines.SupervisorJob\nimport kotlinx.coroutines.asCoroutineDispatcher\n\n\npublic val workerThread = AppExecutorUtil.getAppExecutorService().asCoroutineDispatcher()\n\n\n@Service(Service.Level.PROJECT)\nclass ShireCoroutineScope {\n    val coroutineExceptionHandler = CoroutineExceptionHandler { _, throwable ->\n        Logger.getInstance(ShireCoroutineScope::class.java).error(throwable)\n    }\n\n    val coroutineScope: CoroutineScope = CoroutineScope(\n        SupervisorJob() + workerThread + coroutineExceptionHandler\n    )\n\n    companion object {\n        fun scope(project: Project): CoroutineScope =\n            project.getService(ShireCoroutineScope::class.java).coroutineScope\n\n        fun workerThread(): CoroutineScope = CoroutineScope(SupervisorJob() + workerThread)\n    }\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/ShirelangNotifications.kt",
    "content": "package com.phodal.shirecore\n\nimport com.intellij.notification.NotificationGroup\nimport com.intellij.notification.NotificationGroupManager\nimport com.intellij.notification.NotificationType\nimport com.intellij.openapi.project.Project\n\nobject ShirelangNotifications {\n    private fun group(): NotificationGroup? {\n        return NotificationGroupManager.getInstance().getNotificationGroup(\"Shirelang.notification.group\")\n    }\n\n    fun info(project: Project, msg: String) {\n        group()?.createNotification(msg, NotificationType.INFORMATION)?.notify(project)\n    }\n\n    fun error(project: Project, msg: String) {\n        group()?.createNotification(msg, NotificationType.ERROR)?.notify(project)\n    }\n\n    fun warn(project: Project, msg: String) {\n        group()?.createNotification(msg, NotificationType.WARNING)?.notify(project)\n    }\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/agent/CustomAgent.kt",
    "content": "package com.phodal.shirecore.agent\n\nimport com.intellij.openapi.diagnostic.logger\nimport com.intellij.openapi.project.Project\nimport com.intellij.psi.search.FilenameIndex\nimport com.phodal.shirecore.schema.CUSTOM_AGENT_JSON_EXTENSION\nimport com.phodal.shirecore.config.InteractionType\nimport kotlinx.serialization.Serializable\nimport kotlinx.serialization.json.Json\n\n@Serializable\ndata class CustomFlowTransition(\n    /**\n     * will be JsonPath\n     */\n    val source: String,\n    /**\n     * will be JsonPath too\n     */\n    val target: String,\n)\n\n/**\n * Basic configuration for a custom agent.\n * For Example:\n * ```json\n * {\n *   \"name\": \"CustomAgent\",\n *   \"description\": \"This is a custom agent configuration example.\",\n *   \"url\": \"https://custom-agent.example.com\",\n *   \"icon\": \"https://custom-agent.example.com/icon.png\",\n *   \"responseAction\": \"Direct\",\n *   \"transition\": [\n *     {\n *       \"source\": \"$.from\",\n *       \"target\": \"$.to\"\n *     }\n *   ],\n *   \"interactive\": \"ChatPanel\",\n *   \"auth\": {\n *     \"type\": \"Bearer\",\n *     \"token\": \"<PASSWORD>\"\n *   }\n * }\n * ```\n */\n@Serializable\ndata class CustomAgent(\n    val name: String,\n    val description: String = \"\",\n    val url: String = \"\",\n    val icon: String = \"\",\n    val connector: ConnectorConfig? = null,\n    val responseAction: CustomAgentResponseAction = CustomAgentResponseAction.Direct,\n    val transition: List<CustomFlowTransition> = emptyList(),\n    val interactive: InteractionType = InteractionType.AppendCursor,\n    val auth: CustomAgentAuth? = null,\n    /**\n     * Default to 10 minutes\n     */\n    val defaultTimeout: Long = 10,\n    val enabled: Boolean = true,\n    val isLocalAgent: Boolean = false,\n) {\n    var state: CustomAgentState = CustomAgentState.START\n\n    companion object {\n        private val cacheForVersion: MutableMap<String, List<CustomAgent>> = mutableMapOf()\n\n        fun loadFromProject(project: Project): List<CustomAgent> {\n            val configFiles =\n                FilenameIndex.getAllFilesByExt(project, CUSTOM_AGENT_JSON_EXTENSION)\n\n            if (configFiles.isEmpty()) return emptyList()\n\n            val firstFile = configFiles.first()\n\n            val content = firstFile.inputStream.reader().readText()\n\n            if (cacheForVersion.containsKey(content)) {\n                return cacheForVersion[content]!!\n            }\n\n            val configs: List<CustomAgent> = try {\n                Json.decodeFromString(content)\n            } catch (e: Exception) {\n                logger<CustomAgent>().error(\"Failed to load custom agent configuration\", e)\n                emptyList()\n            }\n\n            /**\n             * Only return enabled agents\n             */\n            return configs.filter { it.enabled }\n        }\n    }\n}\n\n@Serializable\ndata class ConnectorConfig(\n    /**\n     * will be Json Config\n     */\n    val requestFormat: String = \"\",\n    /**\n     * will be JsonPath\n     */\n    val responseFormat: String = \"\",\n)\n\n/**\n * CustomAgentState is an enumeration that defines the possible states of a custom agent.\n *\n * - [CustomAgentState.START] indicates that the agent has just been initialized and is ready to receive commands.\n * - [CustomAgentState.HANDLING] means that the agent is currently processing a command.\n * - [CustomAgentState.FINISHED] signifies that the agent has completed its execution and is no longer operational.\n *\n * This enum is used to track the state of the agent and make decisions accordingly in the agent's lifecycle.\n */\n@Serializable\nenum class CustomAgentState {\n    START,\n    HANDLING,\n    FINISHED\n}\n\n@Serializable\ndata class CustomAgentAuth(\n    val type: AuthType = AuthType.Bearer,\n    val token: String = \"\",\n)\n\n@Serializable\nenum class AuthType {\n    Bearer,\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/agent/CustomAgentResponseAction.kt",
    "content": "package com.phodal.shirecore.agent\n\n/**\n * CustomAgentResponseAction is an enumeration of possible response actions that can be taken by the\n * CustomAgent when processing user input.\n *\n * @property Direct Direct display result - The CustomAgent will directly display the result to the user.\n * @property TextChunk Text splitting result - The CustomAgent will split the result into text chunks for easier consumption.\n * @property Flow Will be handled by the client - The response will be handled by the client application.\n * @property Stream Stream response - The CustomAgent will stream the response to the user.\n * @property WebView Display result in WebView - The CustomAgent will display the result in a WebView.\n * @property Shire Handle by Shire language compile and run in code block.\n */\nenum class CustomAgentResponseAction {\n    /**\n     * Direct display result\n     */\n    Direct,\n\n    /**\n     * Stream response\n     */\n    Stream,\n\n    /**\n     * Text splitting result\n     */\n    TextChunk,\n\n    /**\n     * will be handled by the client\n     */\n    Flow,\n\n    /**\n     * Display result in WebView\n     */\n    WebView,\n\n    /**\n     * Handle by Shire language compile and run in code block.\n     * @since: AutoDev@1.8.2\n     */\n    Shire\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/agent/agenttool/AgentToolContext.kt",
    "content": "package com.phodal.shirecore.agent.agenttool\n\nimport com.intellij.openapi.project.Project\n\nclass AgentToolContext(\n    val project: Project,\n    val argument: String\n) {\n\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/agent/agenttool/AgentToolResult.kt",
    "content": "package com.phodal.shirecore.agent.agenttool\n\ndata class AgentToolResult(\n    val isSuccess: Boolean,\n    val output: String? = null\n)"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/agent/agenttool/browse/BrowseTool.kt",
    "content": "package com.phodal.shirecore.agent.agenttool.browse\n\nimport com.phodal.shirecore.agent.agenttool.AgentToolContext\nimport com.phodal.shirecore.provider.agent.AgentTool\nimport com.phodal.shirecore.agent.agenttool.AgentToolResult\nimport com.phodal.shirecore.agent.agenttool.ua.RandomUserAgent\nimport org.jsoup.Jsoup\nimport org.jsoup.nodes.Document\n\nclass BrowseTool : AgentTool {\n    override val name: String get() = \"Browse\"\n    override val description: String = \"Get the content of a given URL.\"\n\n    override fun execute(context: AgentToolContext): AgentToolResult {\n        return AgentToolResult(\n            isSuccess = true,\n            output = parse(context.argument).body\n        )\n    }\n\n    companion object {\n        /**\n         * Doc for parseHtml\n         *\n         * Intellij API: [com.intellij.inspectopedia.extractor.utils.HtmlUtils.cleanupHtml]\n         */\n        fun parse(url: String): DocumentContent {\n            val doc: Document = Jsoup.connect(url)\n                .ignoreContentType(true)\n                .userAgent(RandomUserAgent.random())\n                .followRedirects(true)\n                .get()\n\n            return DocumentCleaner().cleanHtml(doc)\n        }\n    }\n}\n\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/agent/agenttool/browse/DocumentCleaner.kt",
    "content": "package com.phodal.shirecore.agent.agenttool.browse\n\nimport org.jsoup.Jsoup\nimport org.jsoup.nodes.Document\nimport org.jsoup.nodes.Element\n\nclass DocumentCleaner {\n    fun cleanHtml(html: String): DocumentContent {\n        val doc = Jsoup.parse(html)\n        return cleanHtml(doc)\n    }\n\n    fun cleanHtml(doc: Document): DocumentContent {\n        return DocumentContent(\n            title = doc.title(),\n            language = metaContent(doc, \"http-equiv\", \"Content-Language\"),\n            description = metaDescription(doc),\n            body = articleNode(doc)\n        )\n    }\n\n    private fun metaDescription(doc: Document): String? {\n        val attributes = arrayOf(arrayOf(\"property\", \"description\"), arrayOf(\"name\", \"description\"))\n        return attributes\n            .asSequence()\n            .mapNotNull { (key, value) -> metaContent(doc, key, value) }\n            .firstOrNull()\n    }\n\n    private fun metaContent(doc: Document, key: String, value: String): String? {\n        val metaElements = doc.select(\"head meta[$key=$value]\")\n        return metaElements\n            .map { it.attr(\"content\").trim() }\n            .firstOrNull { it.isNotEmpty() }\n    }\n\n    private val ARTICLE_BODY_ATTR: Array<Pair<String, String>> = arrayOf(\n        Pair(\"itemprop\", \"articleBody\"),\n        Pair(\"data-testid\", \"article-body\"),\n        Pair(\"name\", \"articleBody\")\n    )\n\n    private fun articleNode(doc: Document): String? {\n        var bodyElement: Element? = doc.select(\"html\").select(\"body\").first()\n        val firstBodyElement = bodyElement ?: return null\n        // the Microdata\n        for ((attr, value) in ARTICLE_BODY_ATTR) {\n            bodyElement = doc.selectFirst(\"[$attr=$value]\")\n            if (bodyElement != null) {\n                return bodyElement.text()\n            }\n        }\n\n        return trySelectBestCode(firstBodyElement)\n    }\n\n    private fun trySelectBestCode(doc: Element): String {\n        val commonBestNodes = doc.select(\"article, main, #main, #content, #doc-content, #contents, .book-body\")\n        if (commonBestNodes.isNotEmpty()) {\n            return commonBestNodes.first()?.text() ?: \"\"\n        }\n\n        return doc.text()\n    }\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/agent/agenttool/browse/DocumentContent.kt",
    "content": "package com.phodal.shirecore.agent.agenttool.browse\n\ndata class DocumentContent(\n    val title: String?,\n    val language: String?,\n    val description: String?,\n    val body: String?\n) {\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/agent/agenttool/ua/BrowserType.kt",
    "content": "/**\n * This is free and unencumbered software released into the public domain.\n * GitHub: https://github.com/jonaskahn/user-agents\n */\npackage com.phodal.shirecore.agent.agenttool.ua\n\nenum class BrowserType {\n    CHROME,\n    FIREFOX,\n    SAFARI;\n\n    companion object {\n        fun all(): List<BrowserType> {\n            return arrayListOf(CHROME, FIREFOX, SAFARI)\n        }\n\n        fun cross(): List<BrowserType> {\n            return arrayListOf(CHROME, FIREFOX)\n        }\n    }\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/agent/agenttool/ua/DeviceType.kt",
    "content": "/**\n * This is free and unencumbered software released into the public domain.\n * GitHub: https://github.com/jonaskahn/user-agents\n */\npackage com.phodal.shirecore.agent.agenttool.ua\n\nenum class DeviceType {\n    MACOS,\n    LINUX,\n    WINDOWS,\n    IOS,\n    ANDROID;\n\n    companion object {\n        fun mobile(): List<DeviceType> {\n            return arrayListOf(IOS, ANDROID)\n        }\n\n        fun desktop(): List<DeviceType> {\n            return arrayListOf(MACOS, LINUX, WINDOWS)\n        }\n    }\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/agent/agenttool/ua/Pattern.kt",
    "content": "/**\n * This is free and unencumbered software released into the public domain.\n * GitHub: https://github.com/jonaskahn/user-agents\n */\npackage com.phodal.shirecore.agent.agenttool.ua\n\nobject Pattern {\n    const val WINDOWS_CHROME_AGENT = \"Mozilla/5.0 (%s) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/%s Safari/537.36\"\n    const val WINDOWS_FIREFOX_AGENT = \"Mozilla/5.0 (%s; rv:%s) Gecko/20100101 Firefox/%s\"\n\n    const val LINUX_CHROME_AGENT = WINDOWS_CHROME_AGENT\n    const val LINUX_FIREFOX_AGENT = WINDOWS_FIREFOX_AGENT\n\n    const val MACOS_FIREFOX_AGENT = WINDOWS_FIREFOX_AGENT\n    const val MACOS_CHROME_AGENT = WINDOWS_CHROME_AGENT\n    const val MACOS_SAFARI_AGENT = \"Mozilla/5.0 (%s) AppleWebKit/%s (KHTML, like Gecko) Version/%s Safari/%s\"\n\n    const val IOS_FIREFOX_AGENT =\n        \"Mozilla/5.0 (%s) AppleWebKit/%s (KHTML, like Gecko) FxiOS/%s Mobile/%s Safari/%s\"\n    const val IOS_CHROME_AGENT =\n        \"Mozilla/5.0 (%s) AppleWebKit/%s (KHTML, like Gecko) CriOS/%s Mobile/%s Safari/%s\"\n    const val IOS_SAFARI_AGENT =\n        \"Mozilla/5.0 (%s) AppleWebKit/%s (KHTML, like Gecko) Version/%s Mobile/%s Safari/%s\"\n\n    const val ANDROID_FIREFOX_AGENT =\n        \"Mozilla/5.0 (%s; rv:%s) Gecko/%s Firefox/%s\"\n    const val ANDROID_CHROME_AGENT =\n        \"Mozilla/5.0 (%s) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/%s Mobile Safari/537.36\"\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/agent/agenttool/ua/RandomUserAgent.kt",
    "content": "/**\n * This is free and unencumbered software released into the public domain.\n * GitHub: https://github.com/jonaskahn/user-agents\n */\npackage com.phodal.shirecore.agent.agenttool.ua\n\nimport java.util.*\nimport java.util.concurrent.ThreadLocalRandom\n\nobject RandomUserAgent {\n\n    private val random = ThreadLocalRandom.current()\n\n    /**\n     * Random a user-agent, result can be a {@link #desktop(DeviceType, BrowserType) desktop}\n     * or {@link #mobile(DeviceType, BrowserType) mobile} user-agent type\n     *\n     */\n    fun random(): String {\n        return if (random.nextBoolean()) desktop() else mobile()\n    }\n\n    /**\n     * Generate a desktop(MacOS, Linux, Windows) user-agent\n     * @param deviceType DeviceType must be a valid type or null. If device type is mobile, an exception will be thrown.\n     * @param browserType BrowserType must be a valid type or null.\n     * @return {@link String}\n     * @see one.ifelse.tools.useragent.types.DeviceType\n     * @see one.ifelse.tools.useragent.types.BrowserType\n     */\n    fun desktop(\n        deviceType: DeviceType? = null,\n        browserType: BrowserType? = null\n    ): String {\n        val device = deviceType ?: DeviceType.desktop().random()\n        return when (device) {\n            DeviceType.MACOS -> generateMacOS(browserType)\n            DeviceType.LINUX -> generateLinux(browserType)\n            DeviceType.WINDOWS -> generateWindowsAgent(browserType)\n            DeviceType.IOS -> throw UserAgentException(\"Require desktop device type like: ${DeviceType.desktop()}\")\n            DeviceType.ANDROID -> throw UserAgentException(\"Require desktop device type like: ${DeviceType.desktop()}\")\n        }\n    }\n\n    private fun generateWindowsAgent(browserType: BrowserType?): String {\n        val windowsVersion = Seeds.WINDOWS_DEVICES.random()\n        val type = browserType ?: BrowserType.cross().random()\n        return when (type) {\n            BrowserType.CHROME -> {\n                String.format(\n                    Pattern.WINDOWS_CHROME_AGENT,\n                    windowsVersion,\n                    Seeds.CHROME_VERSIONS.random()\n                )\n            }\n\n            BrowserType.FIREFOX -> {\n                val firefoxVersion = Seeds.FIREFOX_VERSIONS.random()\n                String.format(\n                    Pattern.WINDOWS_FIREFOX_AGENT,\n                    windowsVersion,\n                    firefoxVersion,\n                    firefoxVersion\n                )\n            }\n\n            BrowserType.SAFARI -> {\n                throw UserAgentException(\"Cannot generate user-gent for Windows and Safari\")\n            }\n        }\n    }\n\n    private fun generateLinux(browserType: BrowserType?): String {\n        val linuxVersion = Seeds.LINUX_DEVICES.random()\n        val type = browserType ?: BrowserType.cross().random()\n        return when (type) {\n            BrowserType.CHROME -> {\n                String.format(\n                    Pattern.LINUX_CHROME_AGENT,\n                    linuxVersion,\n                    Seeds.CHROME_VERSIONS.random()\n                )\n            }\n\n            BrowserType.FIREFOX -> {\n                val firefoxVersion = Seeds.FIREFOX_VERSIONS.random()\n                return String.format(\n                    Pattern.LINUX_FIREFOX_AGENT,\n                    linuxVersion,\n                    firefoxVersion,\n                    firefoxVersion\n                )\n            }\n\n            BrowserType.SAFARI -> {\n                throw UserAgentException(\"Cannot generate user-gent for Linux and Safari\")\n            }\n        }\n    }\n\n    private fun generateMacOS(browserType: BrowserType?): String {\n        val type = browserType ?: BrowserType.all().random()\n        val arch = Seeds.MAC_ARCHS.random()\n        val version = Seeds.MAC_VERSIONS.random()\n        val device = \"$arch $version\"\n        return when (type) {\n            BrowserType.CHROME -> {\n                String.format(\n                    Pattern.MACOS_CHROME_AGENT,\n                    device,\n                    Seeds.CHROME_VERSIONS.random(),\n                )\n            }\n\n            BrowserType.FIREFOX -> {\n                val firefoxVersion = Seeds.FIREFOX_VERSIONS.random()\n                return String.format(\n                    Pattern.MACOS_FIREFOX_AGENT,\n                    device,\n                    firefoxVersion,\n                    firefoxVersion\n                )\n            }\n\n            BrowserType.SAFARI -> {\n                val safariVersion = Seeds.SAFARI_VERSIONS.random()\n                return String.format(\n                    Pattern.MACOS_SAFARI_AGENT,\n                    device,\n                    safariVersion,\n                    version.replace(\"_\", \".\"),\n                    safariVersion\n                )\n            }\n        }\n    }\n\n\n    /**\n     * Generate a mobile(IOS, Android) user-agent\n     * @param deviceType DeviceType must be a valid type or null. If device type is desktop, an exception will be thrown.\n     * @param browserType BrowserType must be a valid type or null.\n     * @return {@link String}\n     * @see one.ifelse.tools.useragent.types.DeviceType\n     * @see one.ifelse.tools.useragent.types.BrowserType\n     */\n    fun mobile(\n        deviceType: DeviceType? = null,\n        browserType: BrowserType? = null\n    ): String {\n        val device = deviceType ?: DeviceType.mobile().random()\n        return when (device) {\n            DeviceType.MACOS -> throw UserAgentException(\"Require mobile device type like: ${DeviceType.mobile()}\")\n            DeviceType.LINUX -> throw UserAgentException(\"Require mobile device type like: ${DeviceType.mobile()}\")\n            DeviceType.WINDOWS -> throw UserAgentException(\"Require mobile device type like: ${DeviceType.mobile()}\")\n            DeviceType.IOS -> generateIOSAgent(browserType)\n            DeviceType.ANDROID -> generateAndroidAgent(browserType)\n        }\n    }\n\n    private fun generateIOSAgent(browserType: BrowserType?): String {\n        val type = browserType ?: BrowserType.all().random()\n        val version = Seeds.IOS_VERSIONS.entries.random()\n        val arch = Seeds.IOS_ARCHS.random()\n        val device = String.format(arch, version.key)\n        val safariVersion = Seeds.SAFARI_VERSIONS.random()\n        return when (type) {\n            BrowserType.CHROME -> {\n                String.format(\n                    Pattern.IOS_CHROME_AGENT,\n                    device,\n                    safariVersion,\n                    Seeds.CHROME_VERSIONS.random(),\n                    version.value,\n                    safariVersion\n                )\n            }\n\n            BrowserType.FIREFOX -> {\n                String.format(\n                    Pattern.IOS_FIREFOX_AGENT,\n                    device,\n                    safariVersion,\n                    Seeds.FIREFOX_VERSIONS.random(),\n                    version.value,\n                    safariVersion\n                )\n            }\n\n            BrowserType.SAFARI -> {\n                String.format(\n                    Pattern.IOS_SAFARI_AGENT,\n                    device,\n                    safariVersion,\n                    safariVersion,\n                    version.value,\n                    safariVersion\n                )\n            }\n        }\n    }\n\n    private fun generateAndroidAgent(browserType: BrowserType?): String {\n        val type = browserType ?: BrowserType.cross().random()\n        val device = Seeds.ANDROID_DEVICES.random()\n        return when (type) {\n            BrowserType.CHROME -> {\n                val chromeVersion = Seeds.CHROME_VERSIONS.random()\n                String.format(\n                    Pattern.ANDROID_CHROME_AGENT,\n                    device,\n                    chromeVersion,\n                )\n            }\n\n            BrowserType.FIREFOX -> {\n                val firefoxVersion = Seeds.FIREFOX_VERSIONS.random()\n                String.format(\n                    Pattern.ANDROID_FIREFOX_AGENT,\n                    device,\n                    firefoxVersion,\n                    firefoxVersion,\n                    firefoxVersion\n                )\n            }\n\n            BrowserType.SAFARI -> throw UserAgentException(\"Cannot generate user-gent for Android and Safari\")\n        }\n    }\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/agent/agenttool/ua/Seeds.kt",
    "content": "/**\n * This is free and unencumbered software released into the public domain.\n * GitHub: https://github.com/jonaskahn/user-agents\n */\npackage com.phodal.shirecore.agent.agenttool.ua\n\nobject Seeds {\n\n    val CHROME_VERSIONS = arrayListOf(\n        \"117.0.5938.104\",\n        \"116.0.5845.177\",\n        \"116.0.5845.146\",\n        \"116.0.5845.118\",\n        \"116.0.5845.103\",\n        \"115.0.5790.160\",\n        \"115.0.5790.130\",\n        \"115.0.5790.84\",\n        \"114.0.5735.124\",\n        \"114.0.5735.99\",\n        \"114.0.5735.50\",\n        \"113.0.5672.121\",\n        \"113.0.5672.109\",\n        \"113.0.5672.69\",\n        \"112.0.5615.167\",\n        \"117.0.5938.104\",\n        \"112.0.5615.70\",\n        \"112.0.5615.46\",\n        \"111.0.5563.101\",\n        \"111.0.5563.72\",\n        \"110.0.5481.114\",\n        \"110.0.5481.83\",\n        \"109.0.5414.112\",\n        \"109.0.5414.83\",\n        \"108.0.5359.112\",\n        \"108.0.5359.52\",\n        \"107.0.5304.101\",\n        \"107.0.5304.66\",\n        \"106.0.5249.92\",\n        \"106.0.5249.70\",\n        \"117.0.5938.104\",\n        \"105.0.5195.147\",\n        \"105.0.5195.129\",\n        \"105.0.5195.100\",\n        \"105.0.5195.98\",\n        \"104.0.5112.99\",\n        \"104.0.5112.88\",\n        \"104.0.5112.71\",\n        \"103.0.5060.63\",\n        \"103.0.5060.54\",\n        \"102.0.5005.87\",\n        \"102.0.5005.67\",\n        \"101.0.4951.58\",\n        \"101.0.4951.44\",\n        \"100.0.4896.85\"\n    )\n\n    val FIREFOX_VERSIONS = arrayListOf(\n        \"117.3\",\n        \"117.2\",\n        \"117.0\",\n        \"116.4\",\n        \"116.2\",\n        \"116.1\",\n        \"116.0\",\n        \"115.1\",\n        \"115.0\",\n        \"114.4\",\n        \"114.3\",\n        \"114.2\",\n        \"114.1\",\n        \"114.0\",\n        \"113.2\",\n        \"113.1\",\n        \"113.0\",\n        \"112.2\",\n        \"112.1\",\n        \"112.0\",\n        \"111.2\",\n        \"111.1\",\n        \"111.0\",\n        \"110.2\",\n        \"110.1\",\n        \"110.0\",\n        \"109\",\n        \"108.1\",\n        \"107.3\",\n        \"107.2\",\n        \"107.1\",\n        \"107.0\",\n        \"106.2\",\n        \"106.1\",\n        \"106.0\",\n        \"105.1\",\n        \"105\",\n        \"104.2\",\n        \"104.1\",\n        \"104\",\n    )\n\n    val SAFARI_VERSIONS = arrayListOf(\n        \"600.7.11\",\n        \"610.15.6\",\n        \"604.1.5\",\n        \"602.1.39\",\n        \"604.3.2\",\n        \"604.8.6\",\n        \"604.3.4\",\n        \"602.1.40\",\n        \"600.2.2\",\n        \"602.4.8\",\n        \"603.6.6\",\n        \"603.1.10\",\n        \"601.4.4\",\n        \"602.7.7\",\n        \"603.7.8\",\n        \"601.1.1\",\n        \"600.3.17\",\n        \"605.1.33\",\n        \"600.4.10\",\n        \"602.1.25\",\n        \"600.7.10\",\n        \"601.3.8\",\n        \"602.1.32\",\n        \"602.4.2\",\n        \"603.1.30\",\n        \"603.2.5\",\n        \"603.2.1\",\n        \"605.1.13\",\n        \"604.1.6\",\n        \"607.3.10\",\n        \"612.4.9\",\n        \"602.1.35\",\n        \"612.2.9\",\n        \"600.6.16\",\n        \"611.1.21\",\n        \"604.4.5\",\n        \"605.4.4\",\n        \"603.2.7\",\n        \"611.2.7\",\n        \"602.1.33\",\n        \"603.1.11\",\n        \"600.1.17\",\n        \"603.1.13\",\n        \"600.3.5\",\n        \"600.5.17\",\n        \"602.3.3\",\n        \"601.4.3\",\n        \"600.1.8\",\n        \"600.8.2\",\n        \"604.3.1\",\n        \"600.7.7\",\n        \"604.7.8\",\n        \"604.5.3\",\n        \"602.3.7\",\n        \"604.6.3\",\n        \"604.1.17\",\n        \"602.1.21\",\n        \"601.4.2\",\n        \"601.5.2\",\n        \"603.3.1\",\n        \"602.1.28\",\n        \"612.1.1\",\n        \"601.1.32\",\n        \"604.7.3\",\n        \"604.7.6\",\n        \"611.2.11\",\n        \"605.1.15\",\n        \"601.7.2\",\n        \"600.4.8\",\n        \"600.3.10\",\n        \"602.1.27\",\n        \"603.1.20\",\n        \"610.3.7\",\n        \"600.4.22\",\n        \"600.5.6\",\n        \"600.5.16\",\n        \"604.6.8\",\n        \"612.1.18\",\n        \"601.1.27\",\n        \"605.1.12\",\n        \"604.1.19\",\n        \"601.7.6\",\n        \"601.2.4\",\n        \"6033.1.15\",\n        \"602.1.37\",\n        \"612.1.26\",\n        \"600.2.5\",\n        \"603.3.6\",\n        \"600.5.15\",\n        \"605.1.11\",\n        \"600.6.17\",\n        \"604.1.31\",\n        \"603.2.4\",\n        \"604.2.4\",\n        \"603.1.23\",\n        \"601.5.17\",\n        \"601.2.5\",\n        \"602.3.10\",\n        \"612.1.13\",\n        \"611.3.10\",\n        \"601.7.1\",\n        \"602.3.6\",\n        \"601.7.5\",\n        \"608.5.12\",\n        \"600.1.3\",\n        \"609.4.1\",\n        \"601.6.17\",\n        \"603.3.8\",\n        \"601.2.7\",\n        \"601.1.3\",\n        \"604.3.8\",\n        \"604.4.6\",\n        \"601.2.8\",\n        \"604.1.8\",\n        \"604.0.13\",\n        \"603.7.5\",\n        \"602.4.3\",\n        \"601.5.18\",\n        \"601.1.46\",\n        \"604.1.25\",\n        \"604.1.16\",\n        \"601.6.7\",\n        \"601.7.7\",\n        \"604.1.22\",\n        \"603.3.7\",\n        \"603.3.3\",\n        \"602.2.7\",\n        \"601.6.16\",\n        \"602.5.1\",\n        \"602.1.41\",\n        \"600.6.24\",\n        \"604.2.7\",\n        \"600.7.12\",\n        \"604.1.28\",\n        \"604.4.3\",\n        \"603.3.4\",\n        \"614.2.8\",\n        \"603.1.6\",\n        \"609.1.20\",\n        \"604.1.34\",\n        \"600.5.9\",\n        \"600.1.4\",\n        \"600.2.14\",\n        \"601.3.4\",\n        \"610.4.3\",\n        \"606.1.36\",\n        \"604.1.32\",\n        \"602.1.38\",\n        \"612.3.6\",\n        \"603.1.29\",\n        \"603.1.12\",\n        \"605.1.4\",\n        \"610.2.11\",\n        \"600.1.25\",\n        \"600.3.14\",\n        \"600.8.9\",\n        \"605.4.2\",\n        \"603.2.8\",\n        \"601.5.3\",\n        \"600.5.22\",\n        \"602.1.1\",\n        \"605.7.4\",\n        \"600.8.13\",\n        \"605.1.2\",\n        \"632.5.12\",\n        \"603.1.1\",\n        \"601.1.56\",\n        \"606.1.15\",\n        \"601.5.8\",\n        \"604.1.21\",\n        \"601.1.43\",\n        \"602.1.31\",\n        \"601.3.5\",\n        \"600.2.27\",\n        \"604.1.15\",\n        \"603.29.12\",\n        \"601.6.14\",\n        \"600.4.5\",\n        \"605.5.4\",\n        \"603.3.5\",\n        \"600.5.8\",\n        \"601.1.35\",\n        \"602.1.50\",\n        \"602.4.6\",\n        \"604.1.23\",\n        \"612.1.15\",\n        \"607.3.9\",\n        \"601.5.4\",\n        \"612.1.8\",\n        \"602.2.3\",\n        \"602.3.12\",\n        \"601.3.9\",\n        \"605.1.3\",\n        \"602.1.7\",\n        \"605.6.5\",\n        \"601.1.33\",\n        \"600.1.22\",\n        \"603.1.3\",\n        \"602.7.8\",\n        \"604.5.6\",\n        \"604.1.27\",\n        \"604.1.35\",\n        \"605.2.1\",\n        \"612.1.4\",\n        \"603.5.1\",\n        \"605.1.10\",\n        \"608.2.11\",\n        \"601.5.10\",\n        \"609.3.5\",\n        \"605.6.4\",\n        \"6050.1.15\",\n        \"604.3.5\",\n        \"601.1.50\",\n        \"603.2.2\",\n        \"602.4.4\",\n        \"603.1.5\",\n        \"601.3.2\",\n        \"640.3.18\",\n        \"627.9.17\",\n        \"605.2.6\",\n        \"612.1.12\",\n        \"605.4.7\",\n        \"602.2.11\",\n        \"602.7.2\",\n        \"602.7.1\",\n        \"600.6.3\",\n        \"605.4.8\",\n        \"601.1.39\",\n        \"602.1.43\",\n        \"601.3.6\",\n        \"601.4.8\",\n        \"600.1.15\",\n        \"600.8.7\",\n        \"600.8.22\",\n        \"604.5.100\",\n        \"604.4.7\",\n        \"604.5.2\",\n        \"602.2.14\",\n        \"604.5.5\",\n        \"600.3.8\",\n        \"604.1.38\",\n        \"602.1.18\",\n        \"601.1.37\",\n        \"600.3.18\",\n        \"603.2.3\",\n        \"607.1.15\",\n        \"601.1.52\",\n        \"601.1.41\",\n        \"603.1.8\",\n        \"600.5.3\",\n        \"605.7.3\",\n        \"601.5.13\",\n        \"601.7.8\",\n        \"605.3.8\",\n        \"612.1.7\",\n        \"612.1.27\",\n        \"607.1.40\",\n    )\n\n    val WINDOWS_DEVICES = arrayListOf(\n        \"Windows NT 11.0; Win64; x64\",\n        \"Windows NT 11.0; Win32; x32\",\n        \"Windows NT 10.0; Win64; x64\",\n        \"Windows NT 10.0; Win32; x32\",\n        \"Windows NT 10; WOW64\",\n        \"Windows NT 10; WOW32\",\n        \"Windows NT 6.1; WOW64\",\n        \"Windows NT 6.1; WOW32\",\n        \"Windows NT 6.3; WOW64\",\n        \"Windows NT 6.3; WOW32\",\n        \"Windows NT 6.2; WOW64\",\n        \"Windows NT 6.2; WOW32\",\n    )\n\n    val LINUX_DEVICES = arrayListOf(\n        \"X11; Linux x86_64\",\n        \"X11; Ubuntu; Linux x86_64\",\n        \"X11; CrOS x86_64 7077.111.0\",\n        \"X11; CrOS x86_64 6946.86.0\",\n        \"X11; CrOS armv7l 7262.52.0\",\n        \"X11; FC Linux i686\"\n    )\n\n    val ANDROID_DEVICES = arrayListOf(\n        \"Linux; Android 13; SM-S911U\",\n        \"Linux; Android 13 QPR5; SM-S918U1\",\n        \"Linux; Android 13; SM-S918B Build/TP1A.220624.014; wv\",\n        \"Linux; Android 13; SAMSUNG SM-S918W\",\n        \"Linux; Android 13; SM-S911B\",\n        \"Linux; Android 13; SAMSUNG SM-S9110\",\n        \"Linux; Android 13; SAMSUNG SM-S916\",\n        \"Linux; Android 13; SAMSUNG SM-S9160\",\n        \"Linux; Android 13; SM-S908E Build/TP1A.220624.014; wv\",\n        \"Linux; Android 13; SM-F936U Build/TP1A.220624.014; wv\",\n        \"Linux; Android 13; SM-F936B Build/TP1A.220624.014; wv)\",\n        \"Linux; Android 13; SM-F936U1 Build/TP1A.220624.014; wv\",\n        \"Linux; Android 13; Samsung Galaxy Tab S9 Ultra\",\n        \"Linux; Android 13; SM-G980F Build/TP1A.220624.014; wv\",\n        \"Linux; Android 13; SM-G980F Build/TP1A.220624.014; wv\",\n        \"Linux; Android 13; SM-G980F\",\n        \"Linux; Android 13; SAMSUNG SM-G980F\",\n        \"Linux; Android 13; SM-G996B Build/TP1A.220624.014\",\n        \"Linux; Android 12; SM-G975F Build/SP1A.210812.016; wv\",\n        \"Linux; Android 10; SM-G986B/DS\",\n        \"Linux; Android 13; SAMSUNG SM-A526W\",\n        \"Linux; Android 13; SM-A526B Build/TP1A.220624.014; wv\",\n        \"Linux; Android 10; CDY-AN95 Build/HUAWEICDY-AN95; wv)\",\n        \"Linux; Android 10; MAR-LX3Bm Build/HUAWEIMAR-L03B; wv\",\n        \"(Linux; Android 11; BE2026 Build/RKQ1.201217.002; wv\",\n        \"Linux; Android 11; BE2029\",\n        \"Linux; Android 11; BE2025 Build/RKQ1.201217.002; wv\",\n        \"Linux; Android 12; LE2123\",\n        \"Linux; Android 12; LE2120\",\n        \"Linux; Android 12; LE2125\",\n        \"Linux; Android 13; 22071212AG\",\n        \"Linux; Android 13; 2201122G\",\n        \"Linux; Android 13; 2201122G Build/TKQ1.220807.001; wv\",\n        \"Linux; Android 7.1.2; Xiaomi 10 Pro Build/MBFMIEK\",\n        \"Linux; Android 12; 2210129SG\"\n    )\n\n    val MAC_ARCHS = arrayListOf(\n        \"Macintosh; Intel Mac OS X\",\n        \"Macintosh; M1 Mac OS X\",\n        \"Macintosh; M2 Mac OS X\"\n    )\n\n    val MAC_VERSIONS = arrayListOf(\n        \"12.0\",\n        \"12.0.1\",\n        \"12.1\",\n        \"12.2\",\n        \"12.2.1\",\n        \"12.3\",\n        \"12.3.1\",\n        \"12.4\",\n        \"12.5\",\n        \"12.5.1\",\n        \"12.6\",\n        \"12.6.1\",\n        \"12.6.2\",\n        \"12.6.3\",\n        \"12.6.4\",\n        \"12.6.5\",\n        \"12.6.6\",\n        \"12.6.7\",\n        \"12.6.8\",\n        \"12.6.9\",\n        \"13\",\n        \"13.0.1\",\n        \"13.1\",\n        \"13.2\",\n        \"13.2.1\",\n        \"13.3\",\n        \"13.3.1\",\n        \"13.4\",\n        \"13.4.1\",\n        \"13.5\",\n        \"13.5.1\",\n        \"13.5.2\"\n\n    )\n\n    val IOS_ARCHS = arrayListOf(\n        \"iPhone; CPU iPhone OS %s like Mac OS X\",\n        \"iPad; CPU OS %s like Mac OS X\"\n    )\n\n    val IOS_VERSIONS = mapOf(\n        \"13.2.2\" to \"17B102\",\n        \"13.2.3\" to \"17B111\",\n        \"13.3\" to \"17C54\",\n        \"13.3.1\" to \"17D50\",\n        \"13.6\" to \"17G68\",\n        \"13.6.1\" to \"17G80\",\n        \"13.7\" to \"17H35\",\n        \"14\" to \"18A373\",\n        \"14.0.1\" to \"18A393\",\n        \"14.1\" to \"18A8395\",\n        \"14.2\" to \"18B92\",\n        \"14.2.1\" to \"18B121\",\n        \"14.3\" to \"18C66\",\n        \"14.4\" to \"18D52\",\n        \"14.4.1\" to \"18D61\",\n        \"14.4.2\" to \"18D70\",\n        \"15.0.1\" to \"19A348\",\n        \"15.0.2\" to \"19A404\",\n        \"15.1\" to \"19B74\",\n        \"15.1.1\" to \"19B81\",\n        \"15.2\" to \"19C56\",\n        \"15.2.1\" to \"19C63\",\n        \"15.3\" to \"19D50\",\n        \"15.3.1\" to \"19D52\",\n    )\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/agent/agenttool/ua/UserAgentException.kt",
    "content": "package com.phodal.shirecore.agent.agenttool.ua\n\nclass UserAgentException(message: String) : RuntimeException(message)"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/ast/ComplexityPoint.kt",
    "content": "/**\n * The MIT License (MIT)\n * <p>\n *     https://github.com/nikolaikopernik/code-complexity-plugin\n *  </p>\n */\npackage com.phodal.shirecore.ast\n\ndata class ComplexityPoint(\n    val complexity: Int,\n    val nesting: Int,\n    val type: PointType) {\n\n    override fun toString(): String = \". \".repeat(nesting) + \"$type + $complexity\"\n}\n\nenum class PointType {\n    LOOP_WHILE,\n    LOOP_FOR,\n    IF,\n    ELSE,\n    SWITCH,\n    CATCH,\n    BREAK,\n    CONTINUE,\n    LOGICAL_AND,\n    LOGICAL_OR,\n    RECURSION,\n    METHOD,\n    UNKNOWN\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/ast/ComplexitySink.kt",
    "content": "/**\n * The MIT License (MIT)\n * <p>\n *     https://github.com/nikolaikopernik/code-complexity-plugin\n *  </p>\n */\npackage com.phodal.shirecore.ast\n\nclass ComplexitySink {\n    private var nesting: Int = 0\n    private val points = mutableListOf<ComplexityPoint>()\n    fun decreaseNesting() {\n        if (nesting > 0) nesting--\n    }\n\n    fun increaseNesting() {\n        nesting++\n    }\n\n    fun increaseComplexity(type: PointType) {\n        increaseComplexity(1, type)\n    }\n\n    fun increaseComplexity(amount: Int, type: PointType) {\n        points.add(\n            ComplexityPoint(\n                complexity = amount,\n                nesting = nesting,\n                type = type\n            )\n        )\n    }\n\n    fun increaseComplexityAndNesting(type: PointType) {\n        points.add(\n            ComplexityPoint(\n                complexity = 1 + nesting,\n                nesting = nesting++,\n                type = type\n            )\n        )\n    }\n\n    fun getComplexity(): Int {\n        return points.sumOf { it.complexity }\n    }\n\n    fun getNesting(): Int {\n        return nesting\n    }\n\n    fun getPoints() = points.toList()\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/ast/ComplexityVisitor.kt",
    "content": "/**\n * The MIT License (MIT)\n * <p>\n *     https://github.com/nikolaikopernik/code-complexity-plugin\n *  </p>\n */\npackage com.phodal.shirecore.ast\n\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.PsiRecursiveElementVisitor\n\nabstract class ComplexityVisitor: PsiRecursiveElementVisitor(true) {\n    override fun visitElement(element: PsiElement) {\n        processElement(element)\n        if (shouldVisitElement(element)) {\n            super.visitElement(element)\n        }\n        postProcess(element)\n    }\n\n    /**\n     * Increases complexity and nesting\n     */\n    protected abstract fun processElement(element: PsiElement)\n\n    /**\n     * Decreases nesting\n     */\n    protected abstract fun postProcess(element: PsiElement)\n\n    /**\n     * @return true if PsiElement is Binary Expression with operations || or &&\n     */\n    protected abstract fun shouldVisitElement(element: PsiElement): Boolean\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/ast/PsiSyntaxCheckingVisitor.kt",
    "content": "package com.phodal.shirecore.ast\n\nimport com.intellij.openapi.application.runReadAction\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.PsiElementVisitor\n\nabstract class PsiSyntaxCheckingVisitor : PsiElementVisitor() {\n    override fun visitElement(element: PsiElement) {\n        runReadAction {\n            element.children.forEach { it.accept(this) }\n        }\n    }\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/completion/ShireLookupElement.kt",
    "content": "package com.phodal.shirecore.completion\n\nimport com.intellij.codeInsight.lookup.LookupElement\nimport com.intellij.codeInsight.lookup.LookupElementDecorator\nimport com.intellij.openapi.util.ClassConditionKey\nimport com.intellij.openapi.vfs.VirtualFile\n\nclass ShireLookupElement<T : LookupElement> private constructor(\n    delegate: T,\n    val priority: Double,\n    val virtualFile: VirtualFile,\n) : LookupElementDecorator<T>(delegate) {\n    fun getFile(): VirtualFile {\n        return virtualFile\n    }\n\n    companion object {\n        private val CLASS_CONDITION_KEY: ClassConditionKey<ShireLookupElement<*>> = ClassConditionKey.create(\n            ShireLookupElement::class.java\n        )\n\n        /**\n         * @param element element\n         * @param priority priority (higher priority puts the item closer to the beginning of the list)\n         * @return decorated lookup element\n         */\n        fun withPriority(element: LookupElement, priority: Double, virtualFile: VirtualFile): LookupElement {\n            val prioritized = element.`as`(CLASS_CONDITION_KEY)\n            val lookupElement = if (prioritized !== element) element else prioritized.delegate\n            return ShireLookupElement(lookupElement, priority, virtualFile)\n        }\n    }\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/config/InteractionType.kt",
    "content": "package com.phodal.shirecore.config\n\nenum class InteractionType(val description: String) {\n    AppendCursor(\"Append content at the current cursor position\"),\n    AppendCursorStream(\"Append content at the current cursor position, stream output\"),\n    OutputFile(\"Output to a new file\"),\n    ReplaceSelection(\"Replace the currently selected content\"),\n    ReplaceCurrentFile(\"Replace the content of the current file\"),\n    InsertBeforeSelection(\"Insert content before the currently selected content\"),\n    RunPanel(\"Show Result in Run panel which is the bottom of the IDE\"),\n    OnPaste(\"Copy the content to the clipboard\"),\n    RightPanel(\"Show Result in Right panel which is the right of the IDE\"),\n    StreamDiff(\"Use streaming diff to show the result\")\n    ;\n\n    companion object {\n        fun from(interaction: String): InteractionType {\n            return when (interaction.lowercase()) {\n                AppendCursor.name.lowercase() -> AppendCursor\n                AppendCursorStream.name.lowercase() -> AppendCursorStream\n                OutputFile.name.lowercase() -> OutputFile\n                ReplaceSelection.name.lowercase() -> ReplaceSelection\n                ReplaceCurrentFile.name.lowercase() -> ReplaceCurrentFile\n                InsertBeforeSelection.name.lowercase() -> InsertBeforeSelection\n                RunPanel.name.lowercase() -> RunPanel\n                OnPaste.name.lowercase() -> OnPaste\n                RightPanel.name.lowercase() -> RightPanel\n                StreamDiff.name.lowercase() -> StreamDiff\n                else -> RunPanel\n            }\n        }\n    }\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/config/ShireActionLocation.kt",
    "content": "package com.phodal.shirecore.config\n\nenum class ShireActionLocation(val location: String, val description: String) {\n    CONTEXT_MENU(\"ContextMenu\", \"Show in Context Menu by Right Click\"),\n    INTENTION_MENU(\"IntentionMenu\", \"Show in Intention Menu by Alt+Enter\"),\n    TERMINAL_MENU(\"TerminalMenu\", \"Show in Terminal panel menu bar\"),\n    COMMIT_MENU(\"CommitMenu\", \"Show in Commit panel menu bar\"),\n    RUN_PANEL(\"RunPanel\", \"Show in Run panel which is the bottom of the IDE\"),\n    INPUT_BOX(\"InputBox\", \"Show in Input Box\"),\n    DATABASE_MENU(\"DatabaseMenu\", \"Show in Database panel menu bar\"),\n    CONSOLE_MENU(\"ConsoleMenu\", \"Show in Console panel menu bar\"),\n    VCS_LOG_MENU(\"VcsLogMenu\", \"Show in VCS Log panel menu bar\"),\n    CHAT_BOX(\"ChatBox\", \"Show in Chat Box, default in Right Panel\"),\n    INLINE_CHAT(\"InlineChat\", \"Show in Inline Chat\"),\n\n    EXT_SONARQUBE_MENU(\"ExtSonarQubeMenu\", \"Show in SonarQube panel menu bar\"),\n    ;\n\n    companion object {\n        fun from(locationName: String): ShireActionLocation {\n            return fromLocationName(locationName) ?: RUN_PANEL\n        }\n\n        private fun fromLocationName(locationName: String): ShireActionLocation? {\n            return entries.firstOrNull { it.location == locationName }\n        }\n\n        fun all(): Array<ShireActionLocation> = entries.toTypedArray()\n\n        fun default(): String = RUN_PANEL.location\n    }\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/config/interaction/EditorInteractionProvider.kt",
    "content": "package com.phodal.shirecore.config.interaction\n\nimport com.intellij.execution.ui.ConsoleViewContentType\nimport com.intellij.openapi.application.invokeLater\nimport com.intellij.openapi.application.runReadAction\nimport com.intellij.openapi.diagnostic.logger\nimport com.intellij.openapi.progress.ProgressManager\nimport com.intellij.openapi.progress.impl.BackgroundableProcessIndicator\nimport com.intellij.openapi.wm.ToolWindowManager\nimport com.intellij.ui.content.impl.ContentManagerImpl\nimport com.phodal.shirecore.ShireCoroutineScope\nimport com.phodal.shirecore.ShirelangNotifications\nimport com.phodal.shirecore.config.InteractionType\nimport com.phodal.shirecore.config.interaction.dto.CodeCompletionRequest\nimport com.phodal.shirecore.config.interaction.task.ChatCompletionTask\nimport com.phodal.shirecore.config.interaction.task.FileGenerateTask\nimport com.phodal.shirecore.config.interaction.task.cancelWithConsole\nimport com.phodal.shirecore.diff.DiffStreamHandler\nimport com.phodal.shirecore.runner.console.cancelWithConsole\nimport com.phodal.shirecore.llm.LlmProvider\nimport com.phodal.shirecore.provider.ide.LocationInteractionContext\nimport com.phodal.shirecore.provider.ide.LocationInteractionProvider\nimport com.phodal.shirecore.runner.console.cancelHandler\nimport com.phodal.shirecore.ui.ShirePanelView\nimport kotlinx.coroutines.flow.Flow\nimport kotlinx.coroutines.flow.cancellable\nimport kotlinx.coroutines.launch\n\nclass EditorInteractionProvider : LocationInteractionProvider {\n    override fun isApplicable(context: LocationInteractionContext): Boolean {\n        return true\n    }\n\n    override fun execute(context: LocationInteractionContext, postExecute: PostFunction) {\n        val targetFile = context.editor?.virtualFile\n\n        when (context.interactionType) {\n            InteractionType.AppendCursor,\n            InteractionType.AppendCursorStream,\n                -> {\n                val task = createTask(\n                    context,\n                    context.prompt,\n                    isReplacement = false,\n                    postExecute = postExecute,\n                    false\n                )?.cancelWithConsole(context.console)\n\n                if (task == null) {\n                    ShirelangNotifications.error(context.project, \"Failed to create code completion task.\")\n                    postExecute.invoke(\"\", null)\n                    return\n                }\n\n                ProgressManager.getInstance()\n                    .runProcessWithProgressAsynchronously(task, BackgroundableProcessIndicator(task))\n            }\n\n            InteractionType.OutputFile -> {\n                val fileName = targetFile?.name\n                val task = FileGenerateTask(\n                    context.project,\n                    context.prompt,\n                    fileName,\n                    postExecute = postExecute\n                ).cancelWithConsole(context.console)\n                ProgressManager.getInstance()\n                    .runProcessWithProgressAsynchronously(task, BackgroundableProcessIndicator(task))\n            }\n\n            InteractionType.ReplaceSelection -> {\n                val task =\n                    createTask(context, context.prompt, true, postExecute, false)?.cancelWithConsole(context.console)\n\n                if (task == null) {\n                    ShirelangNotifications.error(context.project, \"Failed to create code completion task.\")\n                    postExecute.invoke(\"\", null)\n                    return\n                }\n\n                ProgressManager.getInstance()\n                    .runProcessWithProgressAsynchronously(task, BackgroundableProcessIndicator(task))\n            }\n\n            InteractionType.ReplaceCurrentFile -> {\n                val fileName = targetFile?.name\n                val task = FileGenerateTask(\n                    context.project,\n                    context.prompt,\n                    fileName,\n                    postExecute = postExecute\n                ).cancelWithConsole(context.console)\n\n                ProgressManager.getInstance()\n                    .runProcessWithProgressAsynchronously(task, BackgroundableProcessIndicator(task))\n            }\n\n            InteractionType.InsertBeforeSelection -> {\n                val task =\n                    createTask(context, context.prompt, false, postExecute, isInsertBefore = true)?.cancelWithConsole(\n                        context.console\n                    )\n\n                if (task == null) {\n                    ShirelangNotifications.error(context.project, \"Failed to create code completion task.\")\n                    postExecute.invoke(\"\", null)\n                    return\n                }\n\n                ProgressManager.getInstance()\n                    .runProcessWithProgressAsynchronously(task, BackgroundableProcessIndicator(task))\n            }\n\n            InteractionType.RunPanel -> {\n                val flow: Flow<String>? = LlmProvider.provider(context.project)?.stream(context.prompt, \"\", false)\n                ShireCoroutineScope.scope(context.project).launch {\n                    val suggestion = StringBuilder()\n\n                    flow?.cancelWithConsole(context.console)?.cancellable()?.collect { char ->\n                        suggestion.append(char)\n\n                        invokeLater {\n                            context.console?.print(char, ConsoleViewContentType.NORMAL_OUTPUT)\n                        }\n                    }\n\n                    postExecute.invoke(suggestion.toString(), null)\n                }\n            }\n\n            InteractionType.RightPanel -> {\n                val toolWindowManager =\n                    ToolWindowManager.getInstance(context.project).getToolWindow(\"ShireToolWindow\") ?: run {\n                        logger<EditorInteractionProvider>().warn(\"Tool window not found\")\n                        return\n                    }\n\n                val contentManager = toolWindowManager.contentManager\n                val panelView = ShirePanelView(context.project)\n                contentManager.factory.createContent(panelView, \"Shire RightPanel Run\", false).let {\n                    // Default is 3, configurable in the future.\n                    if (contentManager.contentCount >= 3) {\n                        contentManager.getContent(0)?.let { content ->\n                            contentManager.removeContent(content, false)\n                            (content.component as? ShirePanelView)?.cancel(\"This content is removed\")\n                        }\n                    }\n                    contentManager.addContent(it)\n                    (contentManager as ContentManagerImpl).setSelectedContent(it)\n                }\n\n\n                toolWindowManager.activate(null)\n\n                val flow: Flow<String>? = LlmProvider.provider(context.project)?.stream(context.prompt, \"\", false)\n                ShireCoroutineScope.scope(context.project).launch {\n                    val suggestion = StringBuilder()\n                    panelView.onStart()\n                    panelView.addRequestPrompt(context.prompt)\n\n                    flow?.cancelHandler { panelView.handleCancel = it }?.cancellable()?.collect { char ->\n                        suggestion.append(char)\n\n                        invokeLater {\n                            panelView.onUpdate(suggestion.toString())\n                        }\n                    }\n\n                    panelView.onFinish(suggestion.toString())\n                    postExecute.invoke(suggestion.toString(), null)\n                }\n            }\n\n            InteractionType.OnPaste -> {\n                /**\n                 *  already handle in [com.phodal.shirelang.actions.copyPaste.ShireCopyPastePreProcessor]\n                 */\n            }\n\n            InteractionType.StreamDiff -> {\n                if (context.editor == null) {\n                    ShirelangNotifications.error(context.project, \"Editor is null, please open a file to continue.\")\n                    return\n                }\n\n                val code = context.editor.document.text\n                val diffStreamHandler = DiffStreamHandler(\n                    context.project,\n                    editor = context.editor,\n                    0,\n                    code.lines().size,\n                    onClose = {\n                    },\n                    onFinish = {\n                        postExecute.invoke(it, null)\n                        ShirelangNotifications.info(context.project, \"Patch Applied\")\n                    })\n\n                diffStreamHandler.streamDiffLinesToEditor(code, context.prompt)\n            }\n        }\n    }\n\n    private fun createTask(\n        context: LocationInteractionContext,\n        userPrompt: String,\n        isReplacement: Boolean,\n        postExecute: PostFunction,\n        isInsertBefore: Boolean,\n    ): ChatCompletionTask? {\n        if (context.editor == null) {\n            ShirelangNotifications.error(context.project, \"Editor is null, please open a file to continue.\")\n            return null\n        }\n\n        val editor = context.editor\n\n        val offset = if (isInsertBefore) {\n            editor.selectionModel.selectionStart\n        } else {\n            editor.caretModel.offset\n        }\n\n        val request = runReadAction {\n            CodeCompletionRequest.create(\n                editor,\n                offset,\n                userPrompt = userPrompt,\n                isReplacement = isReplacement,\n                postExecute = postExecute,\n                isInsertBefore = isInsertBefore,\n            )\n        } ?: return null\n\n        val task = ChatCompletionTask(request)\n        return task\n    }\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/config/interaction/PostFunction.kt",
    "content": "package com.phodal.shirecore.config.interaction\n\nimport com.intellij.openapi.util.TextRange\n\n/**\n * Don't remove public modifier, it's required Kotlin compile, in IDEA will failed.\n */\npublic typealias PostFunction = (response: String?, textRange: TextRange?) -> Unit\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/config/interaction/dto/CodeCompletionRequest.kt",
    "content": "package com.phodal.shirecore.config.interaction.dto\n\nimport com.intellij.openapi.Disposable\nimport com.intellij.openapi.editor.Editor\nimport com.intellij.openapi.editor.ex.DocumentEx\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.util.Disposer\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.intellij.psi.PsiDocumentManager\nimport com.intellij.psi.PsiElement\nimport com.phodal.shirecore.config.interaction.PostFunction\n\nclass CodeCompletionRequest(\n    val project: Project,\n    val fileUri: VirtualFile,\n    val prefixText: String,\n    val startOffset: Int,\n    val element: PsiElement?,\n    val editor: Editor,\n    val suffixText: String,\n    val isReplacement: Boolean = false,\n    val postExecute: PostFunction,\n    val isInsertBefore: Boolean,\n    val userPrompt: String,\n) : Disposable {\n    companion object {\n        fun create(\n            editor: Editor,\n            offset: Int,\n            element: PsiElement? = null,\n            prefix: String? = null,\n            suffix: String? = null,\n            isReplacement: Boolean = false,\n            postExecute: PostFunction,\n            isInsertBefore: Boolean = false,\n            userPrompt: String,\n        ): CodeCompletionRequest? {\n            val project = editor.project ?: return null\n            val document = editor.document\n            val file = PsiDocumentManager.getInstance(project).getPsiFile(document) ?: return null\n\n            val useTabs = editor.settings.isUseTabCharacter(project)\n            val tabWidth = editor.settings.getTabSize(project)\n            val documentVersion = if (document is DocumentEx) {\n                document.modificationSequence.toLong()\n            } else {\n                document.modificationStamp\n            }\n\n            val suffixText = suffix ?: document.text.substring(offset)\n\n            return CodeCompletionRequest(\n                project,\n                file.virtualFile,\n                prefix ?: document.text,\n                offset,\n                element,\n                editor,\n                suffixText,\n                isReplacement,\n                postExecute = postExecute,\n                isInsertBefore = isInsertBefore,\n                userPrompt\n            )\n\n        }\n    }\n\n    @Volatile\n    var isCancelled = false\n\n    fun cancel() {\n        if (isCancelled) {\n            return\n        }\n        isCancelled = true\n        Disposer.dispose(this)\n    }\n\n    override fun dispose() {\n        isCancelled = true\n    }\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/config/interaction/task/ChatCompletionTask.kt",
    "content": "package com.phodal.shirecore.config.interaction.task\n\nimport com.intellij.openapi.util.TextRange\n\nimport com.intellij.openapi.actionSystem.CustomShortcutSet\nimport com.intellij.openapi.actionSystem.KeyboardShortcut\nimport com.intellij.openapi.application.invokeLater\nimport com.intellij.openapi.diagnostic.logger\nimport com.intellij.openapi.progress.ProgressIndicator\nimport com.intellij.openapi.project.DumbAwareAction\nimport com.phodal.shirecore.ShireCoreBundle\nimport com.phodal.shirecore.ShireCoroutineScope\nimport com.phodal.shirecore.config.interaction.dto.CodeCompletionRequest\nimport com.phodal.shirecore.runner.console.cancelHandler\nimport com.phodal.shirecore.llm.LlmProvider\nimport com.phodal.shirecore.utils.markdown.CodeFence\nimport kotlinx.coroutines.cancel\nimport kotlinx.coroutines.flow.*\nimport kotlinx.coroutines.launch\nimport java.awt.event.KeyEvent\nimport javax.swing.KeyStroke\n\nopen class ChatCompletionTask(private val request: CodeCompletionRequest) :\n    ShireInteractionTask(request.project, ShireCoreBundle.message(\"intentions.chat.code.complete.name\"), request.postExecute) {\n    private val logger = logger<ChatCompletionTask>()\n\n    private var isCanceled: Boolean = false\n\n    private var cancelCallback: ((String) -> Unit)? = null\n\n    override fun run(indicator: ProgressIndicator) {\n        indicator.isIndeterminate = false\n        indicator.fraction = 0.1\n        indicator.text = ShireCoreBundle.message(\"intentions.step.prepare-context\")\n\n        val flow: Flow<String> = LlmProvider.provider(request.project)!!.stream(request.userPrompt, \"\", false)\n        logger.info(\"Prompt: ${request.userPrompt}\")\n\n        val shortcutAction = DumbAwareAction.create {\n            isCanceled = true\n        }.apply {\n            registerCustomShortcutSet(\n                CustomShortcutSet(\n                    KeyboardShortcut(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), null),\n                ),\n                request.editor.component\n            )\n        }\n\n        val editor = request.editor\n        val project = request.project\n\n        var currentOffset = request.startOffset\n        val modifyStart = request.startOffset\n\n        indicator.isIndeterminate = false\n        indicator.fraction = 0.5\n        indicator.text = ShireCoreBundle.message(\"intentions.request.background.process.title\")\n\n        ShireCoroutineScope.scope(request.project).launch {\n            val suggestion = StringBuilder()\n\n            flow.cancelHandler { cancelCallback = it }.cancellable().collect { char ->\n                if (isCanceled) {\n                    cancel()\n                    return@collect\n                }\n\n                suggestion.append(char)\n\n                invokeLater {\n                    if (!isCanceled && !request.isReplacement) {\n                        if (request.isInsertBefore) {\n                            InsertUtil.insertStreamingToDoc(project, char, editor, currentOffset)\n                            currentOffset += char.length\n                        } else {\n                            InsertUtil.insertStreamingToDoc(project, char, editor, currentOffset)\n                            currentOffset += char.length\n                        }\n                    }\n                }\n            }\n\n            val modifyEnd = currentOffset\n\n            if (request.isReplacement) {\n                val parsedContent = CodeFence.parse(suggestion.toString()).text\n                InsertUtil.replaceText(project, editor, parsedContent)\n            }\n\n            indicator.fraction = 0.8\n            logger.info(\"Suggestion: $suggestion\")\n\n            val textRange = TextRange(modifyStart, modifyEnd)\n\n            request.postExecute.invoke(suggestion.toString(), textRange)\n            indicator.fraction = 1.0\n        }.invokeOnCompletion {\n            shortcutAction.unregisterCustomShortcutSet(editor.component)\n        }\n    }\n\n    override fun onThrowable(error: Throwable) {\n        super.onThrowable(error)\n    }\n\n    override fun onCancel() {\n        this.isCanceled = true\n        this.cancelCallback?.invoke(\"This job is canceled\")\n        super.onCancel()\n    }\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/config/interaction/task/CodeCompletionTask.kt",
    "content": "package com.phodal.shirecore.config.interaction.task\n\n\nimport com.intellij.openapi.application.invokeLater\nimport com.intellij.openapi.diagnostic.logger\nimport com.intellij.openapi.progress.ProgressIndicator\nimport com.phodal.shirecore.ShireCoreBundle\nimport com.phodal.shirecore.ShireCoroutineScope\nimport com.phodal.shirecore.config.interaction.dto.CodeCompletionRequest\nimport com.phodal.shirecore.llm.LlmProvider\nimport com.phodal.shirecore.utils.markdown.CodeFence\nimport kotlinx.coroutines.cancel\nimport kotlinx.coroutines.flow.Flow\nimport kotlinx.coroutines.flow.cancellable\nimport kotlinx.coroutines.launch\n\nclass CodeCompletionTask(private val request: CodeCompletionRequest) :\n    ShireInteractionTask(request.project, ShireCoreBundle.message(\"intentions.chat.code.complete.name\"), request.postExecute) {\n    private val logger = logger<CodeCompletionTask>()\n    private var isCanceled: Boolean = false\n\n    override fun run(indicator: ProgressIndicator) {\n        val prompt = completionPrompt()\n\n        val flow: Flow<String> = LlmProvider.provider(request.project)!!.stream(prompt, \"\", false)\n        logger.info(\"Prompt: $prompt\")\n\n        val editor = request.editor\n        ShireCoroutineScope.scope(request.project).launch {\n            var currentOffset = request.startOffset\n\n            val project = request.project\n            val suggestion = StringBuilder()\n\n            flow.cancellable().collect { char ->\n                if (isCanceled) {\n                    cancel()\n                    return@collect\n                }\n\n                val parsedContent = CodeFence.parse(char).text;\n\n                suggestion.append(parsedContent)\n                invokeLater {\n                    if (!isCanceled && !request.isReplacement) {\n                        InsertUtil.insertStreamingToDoc(project, parsedContent, editor, currentOffset)\n                        currentOffset += char.length\n                    }\n                }\n            }\n\n            if (request.isReplacement) {\n                // remove all selection code\n                val selectionModel = editor.selectionModel\n                val start = selectionModel.selectionStart\n                val end = selectionModel.selectionEnd\n                editor.document.deleteString(start, end)\n\n                InsertUtil.insertStringAndSaveChange(\n                    project,\n                    suggestion.toString(),\n                    editor.document,\n                    request.startOffset,\n                    false\n                )\n            }\n\n            logger.info(\"Suggestion: $suggestion\")\n            request.postExecute.invoke(suggestion.toString(), null)\n\n            indicator.fraction = 1.0\n        }\n    }\n\n    private fun completionPrompt(): String {\n        val documentLength = request.editor.document.textLength\n        val prefix = if (request.startOffset > documentLength) {\n            request.prefixText\n        } else {\n            val text = request.editor.document.text\n            text.substring(0, request.startOffset)\n        }\n\n        val prompt = \"complete code for given code: \\n$prefix\"\n\n        return prompt\n    }\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/config/interaction/task/FileGenerateTask.kt",
    "content": "package com.phodal.shirecore.config.interaction.task\n\nimport com.intellij.openapi.application.runInEdt\nimport com.intellij.openapi.diagnostic.logger\nimport com.intellij.openapi.fileEditor.FileEditorManager\nimport com.intellij.openapi.fileTypes.PlainTextLanguage\nimport com.intellij.openapi.progress.ProgressIndicator\nimport com.intellij.openapi.progress.ProgressManager\nimport com.intellij.openapi.progress.Task\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.project.guessProjectDir\nimport com.intellij.openapi.vfs.LocalFileSystem\nimport com.intellij.openapi.vfs.VfsUtil\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.phodal.shirecore.ShireCoreBundle\nimport com.phodal.shirecore.config.interaction.PostFunction\nimport com.phodal.shirecore.llm.LlmProvider\nimport com.phodal.shirecore.utils.markdown.CodeFence\nimport kotlinx.coroutines.flow.cancellable\nimport kotlinx.coroutines.runBlocking\nimport java.nio.file.Path\nimport kotlin.io.path.Path\n\nopen class FileGenerateTask(\n    @JvmField val project: Project,\n    val prompt: String,\n    val fileName: String?,\n    private val codeOnly: Boolean = false,\n    private val taskName: String = ShireCoreBundle.message(\"intentions.request.background.process.title\"),\n    postExecute: PostFunction,\n) :\n    ShireInteractionTask(project, taskName, postExecute) {\n    private val projectRoot = project.guessProjectDir()!!\n\n    override fun run(indicator: ProgressIndicator) {\n        val stream = LlmProvider.provider(project)?.stream(prompt, \"\", false)\n        if (stream == null) {\n            logger<FileGenerateTask>().error(\"Failed to create stream\")\n            postExecute?.invoke(\"\", null)\n            return\n        }\n\n        var result = \"\"\n        runBlocking {\n            stream.cancellable().collect {\n                result += it\n            }\n        }\n\n        val inferFileName = if (fileName == null) {\n            val language = CodeFence.parse(result).ideaLanguage\n            val timestamp = System.currentTimeMillis()\n            \"output-\" + timestamp + if (language == PlainTextLanguage.INSTANCE) \".txt\" else \".$language\"\n        } else {\n            fileName\n        }\n\n        val file = project.guessProjectDir()?.toNioPath()?.resolve(inferFileName)?.toFile()\n        if (file == null) {\n            logger<FileGenerateTask>().error(\"Failed to create file\")\n            postExecute?.invoke(result, null)\n            return\n        }\n        if (!file.exists()) {\n            file.createNewFile()\n        }\n\n        if (codeOnly) {\n            val code = CodeFence.parse(result).text\n            file.writeText(code)\n            refreshAndOpenInEditor(file.toPath(), projectRoot)\n        } else {\n            file.writeText(result)\n            refreshAndOpenInEditor(Path(projectRoot.path), projectRoot)\n        }\n\n        postExecute?.invoke(result, null)\n    }\n\n    private fun refreshAndOpenInEditor(file: Path, parentDir: VirtualFile) = runBlocking {\n        ProgressManager.getInstance().run(RefreshProjectModal(file, parentDir))\n    }\n\n    inner class RefreshProjectModal(private val file: Path, private val parentDir: VirtualFile) :\n        Modal(project, \"Refreshing Project Model\", true) {\n        override fun run(indicator: ProgressIndicator) {\n            repeat(5) {\n                val virtualFile = LocalFileSystem.getInstance().findFileByNioFile(file)\n                if (virtualFile == null) {\n                    VfsUtil.markDirtyAndRefresh(true, true, true, parentDir)\n                } else {\n                    try {\n                        runInEdt {\n                            FileEditorManager.getInstance(project).openFile(virtualFile, true)\n                        }\n                        return\n                    } catch (e: Exception) {\n                        //\n                    }\n                }\n            }\n        }\n    }\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/config/interaction/task/InsertUtil.kt",
    "content": "package com.phodal.shirecore.config.interaction.task\n\nimport com.intellij.openapi.application.runReadAction\nimport com.intellij.openapi.command.WriteCommandAction\nimport com.intellij.openapi.editor.Document\nimport com.intellij.openapi.editor.Editor\nimport com.intellij.openapi.editor.ScrollType\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.util.TextRange\nimport com.intellij.psi.PsiDocumentManager\nimport com.intellij.psi.codeStyle.CodeStyleManager\nimport com.phodal.shirecore.ShireCoreBundle\n\nobject InsertUtil {\n    fun insertStringAndSaveChange(\n        project: Project,\n        content: String,\n        document: Document,\n        startOffset: Int,\n        withReformat: Boolean,\n    ) {\n        if (startOffset < 0 || startOffset > document.textLength) return\n\n        document.insertString(startOffset, content)\n        PsiDocumentManager.getInstance(project).commitDocument(document)\n\n        if (!withReformat) return\n\n        val psiFile = PsiDocumentManager.getInstance(project).getPsiFile(document)\n        psiFile?.let { file ->\n            val reformatRange = TextRange(startOffset, startOffset + content.length)\n            CodeStyleManager.getInstance(project).reformatText(file, listOf(reformatRange))\n        }\n    }\n\n    fun insertStreamingToDoc(project: Project, char: String, editor: Editor, currentOffset: Int) {\n        WriteCommandAction.runWriteCommandAction(\n            project,\n            ShireCoreBundle.message(\"intentions.chat.code.complete.name\"),\n            \"intentions.write.action\",\n            {\n                insertStringAndSaveChange(project, char, editor.document, currentOffset, false)\n            })\n\n        editor.caretModel.moveToOffset(currentOffset + char.length)\n        editor.scrollingModel.scrollToCaret(ScrollType.MAKE_VISIBLE)\n    }\n\n    fun replaceText(project: Project, editor: Editor, output: String) {\n        val primaryCaret = editor.caretModel.primaryCaret;\n        val start = runReadAction { primaryCaret.selectionStart }\n        val end = runReadAction { primaryCaret.selectionEnd }\n        val document = runReadAction { editor.document }\n\n        WriteCommandAction.runWriteCommandAction(project) {\n            document.replaceString(start, end, output)\n        }\n    }\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/config/interaction/task/ShireInteractionTask.kt",
    "content": "package com.phodal.shirecore.config.interaction.task\n\nimport com.intellij.execution.ui.ConsoleView\nimport com.intellij.openapi.progress.Task.Backgroundable\nimport com.intellij.openapi.project.Project\nimport com.phodal.shirecore.config.interaction.PostFunction\nimport com.phodal.shirecore.runner.console.addCancelCallback\nimport java.util.concurrent.CompletableFuture\n\n/**\n * @author lk\n */\nabstract class ShireInteractionTask(project: Project, taskName: String, val postExecute: PostFunction?): Backgroundable(project, taskName) {\n\n    /**\n     * An unexpected exception occurred, causing the shire process cannot be canceled,\n     * postExecute was not executed,it may have used the [CompletableFuture].\n     */\n    override fun onThrowable(error: Throwable) {\n        super.onThrowable(error)\n        postExecute?.invoke(null, null)\n    }\n\n}\n\nfun ShireInteractionTask.cancelWithConsole(consoleView: ConsoleView?): ShireInteractionTask =\n    apply { consoleView?.addCancelCallback { onCancel() } }"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/diff/DiffStreamHandler.kt",
    "content": "/**\n * Copyright 2023 Continue Dev, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.phodal.shirecore.diff\n\nimport com.intellij.openapi.application.ApplicationManager\nimport com.intellij.openapi.command.WriteCommandAction\nimport com.intellij.openapi.command.undo.UndoManager\nimport com.intellij.openapi.editor.Editor\nimport com.intellij.openapi.editor.markup.HighlighterLayer\nimport com.intellij.openapi.editor.markup.RangeHighlighter\nimport com.intellij.openapi.fileEditor.FileDocumentManager\nimport com.intellij.openapi.fileEditor.FileEditorManager\nimport com.intellij.openapi.fileEditor.TextEditor\nimport com.intellij.openapi.fileTypes.PlainTextLanguage\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.phodal.shirecore.ShireCoroutineScope\nimport com.phodal.shirecore.diff.model.DiffLine\nimport com.phodal.shirecore.diff.model.streamDiff\nimport com.phodal.shirecore.llm.LlmProvider\nimport com.phodal.shirecore.utils.markdown.CodeFence\nimport kotlinx.coroutines.flow.Flow\nimport kotlinx.coroutines.flow.cancellable\nimport kotlinx.coroutines.flow.flowOf\nimport kotlinx.coroutines.launch\nimport kotlin.math.min\n\n\n/**\n *\n *     JButton(\"Apply Patch\").apply {\n *\n *        addActionListener {\n *            val lookupFile =\n *                project.lookupFile(\"src/main/java/com/phodal/shire/demo/service/BlogService.java\")!!\n *            val editor = FileEditorManager.getInstance(project).selectedTextEditor\n *            val code = lookupFile.inputStream.bufferedReader().use { it.readText() }\n *\n *            val diffStreamHandler = DiffStreamHandler(\n *                project,\n *                editor = editor!!, 0, code.lines().size,\n *                onClose = {\n *                },\n *                onFinish = {\n *                    ShirelangNotifications.info(project, \"Patch Applied\")\n *                }\n *            )\n *\n *            runInEdt {\n *                diffStreamHandler\n *                    .streamDiffLinesToEditor(\n *                        code,\n *                        \"使用为如下的代码添加删除功能，请使用 Markdown  code 返回完整代码块: $code\"\n *                    )\n *            }\n *        }\n *      }\n */\nclass DiffStreamHandler(\n    private val project: Project,\n    private val editor: Editor,\n    private val startLine: Int,\n    private val endLine: Int,\n    private val onClose: () -> Unit,\n    private val onFinish: (response: String) -> Unit,\n) {\n    private data class CurLineState(\n        var index: Int, var highlighter: RangeHighlighter? = null, var diffBlock: VerticalDiffBlock? = null,\n    )\n\n    private var curLine = CurLineState(startLine)\n\n    private var isRunning: Boolean = false\n    private var hasAcceptedOrRejectedBlock: Boolean = false\n\n    private val unfinishedHighlighters: MutableList<RangeHighlighter> = mutableListOf()\n    private val diffBlocks: MutableList<VerticalDiffBlock> = mutableListOf()\n\n    private val curLineKey = createTextAttributesKey(\"CONTINUE_DIFF_CURRENT_LINE\", 0x40888888, editor)\n    private val unfinishedKey = createTextAttributesKey(\"CONTINUE_DIFF_UNFINISHED_LINE\", 0x20888888, editor)\n\n    init {\n        initUnfinishedRangeHighlights()\n    }\n\n    fun acceptAll() {\n        editor.markupModel.removeAllHighlighters()\n        resetState()\n    }\n\n    fun rejectAll() {\n        // The ideal action here is to undo all changes we made to return the user's edit buffer to the state prior\n        // to our changes. However, if the user has accepted or rejected one or more diff blocks, there isn't a simple\n        // way to undo our changes without also undoing the diff that the user accepted or rejected.\n        if (hasAcceptedOrRejectedBlock) {\n            diffBlocks.forEach { it.handleReject() }\n        } else {\n            undoChanges()\n        }\n\n        resetState()\n    }\n\n    fun streamDiffLinesToEditor(originContent: String, prompt: String) {\n        val lines = originContent.lines()\n\n        isRunning = true\n        val flow: Flow<String> = LlmProvider.provider(project)!!.stream(prompt, \"\", false)\n        var lastLineNo = 0\n        ShireCoroutineScope.scope(project).launch {\n            val suggestion = StringBuilder()\n            flow.cancellable().collect { char ->\n                suggestion.append(char)\n                val code = CodeFence.parse(suggestion.toString())\n                if (PlainTextLanguage.INSTANCE != code.ideaLanguage && code.ideaLanguage.displayName != \"Markdown\" && code.text.isNotEmpty()) {\n                    var value: List<String> = code.text.lines()\n                    value = value.dropLast(1)\n\n                    if (value.isEmpty()) return@collect\n\n                    val newLines = if (lastLineNo < value.size) {\n                        value.subList(lastLineNo, value.size)\n                    } else {\n                        listOf()\n                    }\n\n                    if (newLines.isEmpty()) return@collect\n\n                    val flowValue: Flow<String> = flowOf(*newLines.toTypedArray())\n                    val oldLinesContent = if (lastLineNo + newLines.size <= lines.size) {\n                        lines.subList(lastLineNo, lastLineNo + newLines.size)\n                    } else {\n                        listOf()\n                    }\n                    lastLineNo = value.size\n\n                    streamDiff(oldLinesContent, flowValue).collect {\n                        ApplicationManager.getApplication().invokeLater {\n                            WriteCommandAction.runWriteCommandAction(project) {\n                                updateByDiffType(it)\n                            }\n                        }\n                    }\n                }\n            }\n\n            handleFinishedResponse(suggestion.toString())\n        }\n    }\n\n    private fun updateByDiffType(diffLine: DiffLine) {\n        when (diffLine) {\n            is DiffLine.New -> handleNewLine(diffLine.line)\n            is DiffLine.Old -> handleOldLine()\n            is DiffLine.Same -> handleSameLine()\n        }\n\n        updateProgressHighlighters(diffLine)\n    }\n\n    private fun initUnfinishedRangeHighlights() {\n        for (i in startLine..endLine) {\n            val highlighter = editor.markupModel.addLineHighlighter(\n                unfinishedKey, min(\n                    i, editor.document.lineCount - 1\n                ), HighlighterLayer.LAST\n            )\n            unfinishedHighlighters.add(highlighter)\n        }\n    }\n\n    private fun handleDiffBlockAcceptOrReject(diffBlock: VerticalDiffBlock, didAccept: Boolean) {\n        hasAcceptedOrRejectedBlock = true\n\n        diffBlocks.remove(diffBlock)\n\n        if (!didAccept) {\n            updatePositionsOnReject(diffBlock.startLine, diffBlock.addedLines.size, diffBlock.deletedLines.size)\n        }\n\n        if (diffBlocks.isEmpty()) {\n            onClose()\n        }\n    }\n\n\n    private fun createDiffBlock(): VerticalDiffBlock {\n        val diffBlock = VerticalDiffBlock(\n            editor, project, curLine.index, ::handleDiffBlockAcceptOrReject\n        )\n\n        diffBlocks.add(diffBlock)\n\n        return diffBlock\n    }\n\n    private fun handleSameLine() {\n        if (curLine.diffBlock != null) {\n            curLine.diffBlock!!.onLastDiffLine()\n        }\n\n        curLine.diffBlock = null\n\n        curLine.index++\n    }\n\n    private fun handleNewLine(text: String) {\n        if (curLine.diffBlock == null) {\n            curLine.diffBlock = createDiffBlock()\n        }\n\n        curLine.diffBlock!!.addNewLine(text, curLine.index)\n\n        curLine.index++\n    }\n\n    private fun handleOldLine() {\n        if (curLine.diffBlock == null) {\n            curLine.diffBlock = createDiffBlock()\n        }\n\n        curLine.diffBlock!!.deleteLineAt(curLine.index)\n    }\n\n    private fun updateProgressHighlighters(type: DiffLine) {\n        // Update the highlighter to show the current line\n        curLine.highlighter?.let { editor.markupModel.removeHighlighter(it) }\n        curLine.highlighter = editor.markupModel.addLineHighlighter(\n            curLineKey, min(curLine.index, editor.document.lineCount - 1), HighlighterLayer.LAST\n        )\n\n        // Remove the unfinished lines highlighter\n        if (type is DiffLine.Old && unfinishedHighlighters.isNotEmpty()) {\n            editor.markupModel.removeHighlighter(unfinishedHighlighters.removeAt(0))\n        }\n    }\n\n\n    private fun updatePositionsOnReject(startLine: Int, numAdditions: Int, numDeletions: Int) {\n        val offset = -numAdditions + numDeletions\n\n        diffBlocks.forEach { block ->\n            if (block.startLine > startLine) {\n                block.updatePosition(block.startLine + offset)\n            }\n        }\n    }\n\n    private fun resetState() {\n        // Clear the editor of highlighting/inlays\n        editor.markupModel.removeAllHighlighters()\n        diffBlocks.forEach { it.clearEditorUI() }\n\n        // Clear state vars\n        diffBlocks.clear()\n        curLine = CurLineState(startLine)\n        isRunning = false\n\n        // Close the Edit input\n        onClose()\n    }\n\n\n    private fun undoChanges() {\n        WriteCommandAction.runWriteCommandAction(project) {\n            val undoManager = UndoManager.getInstance(project)\n            val virtualFile = getVirtualFile() ?: return@runWriteCommandAction\n            val fileEditor = FileEditorManager.getInstance(project).getSelectedEditor(virtualFile) as TextEditor\n\n            if (undoManager.isUndoAvailable(fileEditor)) {\n                val numChanges = diffBlocks.sumOf { it.deletedLines.size + it.addedLines.size }\n\n                repeat(numChanges) {\n                    undoManager.undo(fileEditor)\n                }\n            }\n        }\n    }\n\n    private fun getVirtualFile(): VirtualFile? {\n        return FileDocumentManager.getInstance().getFile(editor.document) ?: return null\n    }\n\n    private fun handleFinishedResponse(response: String) {\n        ApplicationManager.getApplication().invokeLater {\n            // Since we only call onLastDiffLine() when we reach a \"same\" line, we need to handle the case where\n            // the last line in the diff stream is in the middle of a diff block.\n            curLine.diffBlock?.onLastDiffLine()\n\n            onFinish(response)\n            cleanupProgressHighlighters()\n        }\n    }\n\n    private fun cleanupProgressHighlighters() {\n        curLine.highlighter?.let { editor.markupModel.removeHighlighter(it) }\n        unfinishedHighlighters.forEach { editor.markupModel.removeHighlighter(it) }\n    }\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/diff/DiffStreamService.kt",
    "content": "/**\n * Copyright 2023 Continue Dev, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.phodal.shirecore.diff\n\nimport com.intellij.openapi.components.Service\nimport com.intellij.openapi.editor.Editor\n\n@Service(Service.Level.PROJECT)\nclass DiffStreamService {\n    private val handlers = mutableMapOf<Editor, DiffStreamHandler>()\n\n    fun register(handler: DiffStreamHandler, editor: Editor) {\n        if (handlers.containsKey(editor)) {\n            handlers[editor]?.rejectAll()\n        }\n        handlers[editor] = handler\n    }\n\n    fun reject(editor: Editor) {\n        handlers[editor]?.rejectAll()\n        handlers.remove(editor)\n    }\n\n    fun accept(editor: Editor) {\n        handlers[editor]?.acceptAll()\n        handlers.remove(editor)\n    }\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/diff/EditorComponentInlaysManager.kt",
    "content": "/**\n * Copyright 2023 Continue Dev, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.phodal.shirecore.diff\n\nimport com.intellij.openapi.Disposable\nimport com.intellij.openapi.editor.Editor\nimport com.intellij.openapi.editor.ex.EditorEx\nimport com.intellij.openapi.editor.ex.util.EditorUtil\nimport com.intellij.openapi.editor.impl.EditorEmbeddedComponentManager\nimport com.intellij.openapi.editor.impl.EditorImpl\nimport com.intellij.openapi.editor.impl.view.FontLayoutService\nimport com.intellij.openapi.util.Disposer\nimport com.intellij.openapi.util.Key\nimport com.intellij.ui.components.JBScrollPane\nimport com.intellij.util.concurrency.annotations.RequiresEdt\nimport com.intellij.util.ui.JBUI\nimport java.awt.Dimension\nimport java.awt.Font\nimport java.awt.event.ComponentAdapter\nimport java.awt.event.ComponentEvent\nimport javax.swing.JComponent\nimport javax.swing.ScrollPaneConstants\nimport kotlin.math.ceil\nimport kotlin.math.max\nimport kotlin.math.min\n\n/**\n * Copied from com.intellij.util.ui.codereview.diff.EditorComponentInlaysManager\n * via https://github.com/cursive-ide/component-inlay-example/blob/master/src/main/kotlin/inlays/EditorComponentInlaysManager.kt\n */\nclass EditorComponentInlaysManager(val editor: EditorImpl, private val onlyOneInlay: Boolean) : Disposable {\n\n    private val managedInlays = mutableMapOf<ComponentWrapper, Disposable>()\n    private val editorWidthWatcher = EditorTextWidthWatcher()\n\n    init {\n        editor.scrollPane.viewport.addComponentListener(editorWidthWatcher)\n        Disposer.register(this, Disposable {\n            editor.scrollPane.viewport.removeComponentListener(editorWidthWatcher)\n        })\n\n        EditorUtil.disposeWithEditor(editor, this)\n    }\n\n\n    @RequiresEdt\n    fun insert(lineIndex: Int, component: JComponent, showAbove: Boolean = false): Disposable? {\n        if (Disposer.isDisposed(this)) return null\n\n        if (onlyOneInlay) {\n            // Dispose all other inlays\n            managedInlays.values.forEach(Disposer::dispose)\n        }\n\n        val wrappedComponent = ComponentWrapper(component)\n        val offset = editor.document.getLineStartOffset(lineIndex)\n\n        return EditorEmbeddedComponentManager.getInstance()\n            .addComponent(\n                editor, wrappedComponent,\n                EditorEmbeddedComponentManager.Properties(\n                    EditorEmbeddedComponentManager.ResizePolicy.none(),\n                    null,\n                    !editor.inlayModel.getBlockElementsInRange(offset,offset).isEmpty(),\n                    showAbove,\n                    0,\n                    offset\n                )\n            )?.also {\n                managedInlays[wrappedComponent] = it\n                Disposer.register(it, Disposable { managedInlays.remove(wrappedComponent) })\n            }\n    }\n\n    private inner class ComponentWrapper(private val component: JComponent) : JBScrollPane(component) {\n        init {\n            isOpaque = false\n            viewport.isOpaque = false\n\n            border = JBUI.Borders.empty()\n            viewportBorder = JBUI.Borders.empty()\n\n            horizontalScrollBarPolicy = ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER\n            verticalScrollBar.preferredSize = Dimension(0, 0)\n            setViewportView(component)\n\n            component.addComponentListener(object : ComponentAdapter() {\n                override fun componentResized(e: ComponentEvent) =\n                    dispatchEvent(ComponentEvent(component, ComponentEvent.COMPONENT_RESIZED))\n            })\n        }\n\n        override fun getPreferredSize(): Dimension {\n            return Dimension(editor.contentComponent.width, component.preferredSize.height)\n        }\n    }\n\n    override fun dispose() {\n        managedInlays.values.forEach(Disposer::dispose)\n    }\n\n    private inner class EditorTextWidthWatcher : ComponentAdapter() {\n\n        var editorTextWidth: Int = 0\n\n        private val maximumEditorTextWidth: Int\n        private val verticalScrollbarFlipped: Boolean\n\n        init {\n            val metrics = editor.getFontMetrics(Font.PLAIN)\n            val spaceWidth = FontLayoutService.getInstance().charWidth2D(metrics, ' '.toInt())\n            // -4 to create some space\n            maximumEditorTextWidth = ceil(spaceWidth * (editor.settings.getRightMargin(editor.project)) - 4).toInt()\n\n            val scrollbarFlip = editor.scrollPane.getClientProperty(JBScrollPane.Flip::class.java)\n            verticalScrollbarFlipped =\n                scrollbarFlip == JBScrollPane.Flip.HORIZONTAL || scrollbarFlip == JBScrollPane.Flip.BOTH\n        }\n\n        override fun componentResized(e: ComponentEvent) = updateWidthForAllInlays()\n        override fun componentHidden(e: ComponentEvent) = updateWidthForAllInlays()\n        override fun componentShown(e: ComponentEvent) = updateWidthForAllInlays()\n\n        private fun updateWidthForAllInlays() {\n            val newWidth = calcWidth()\n            if (editorTextWidth == newWidth) return\n            editorTextWidth = newWidth\n\n            managedInlays.keys.forEach {\n                it.dispatchEvent(ComponentEvent(it, ComponentEvent.COMPONENT_RESIZED))\n                it.invalidate()\n            }\n        }\n\n        private fun calcWidth(): Int {\n            val visibleEditorTextWidth =\n                editor.scrollPane.viewport.width - getVerticalScrollbarWidth() - getGutterTextGap()\n            return min(max(visibleEditorTextWidth, 0), maximumEditorTextWidth)\n        }\n\n        private fun getVerticalScrollbarWidth(): Int {\n            val width = editor.scrollPane.verticalScrollBar.width\n            return if (!verticalScrollbarFlipped) width * 2 else width\n        }\n\n        private fun getGutterTextGap(): Int {\n            return if (verticalScrollbarFlipped) {\n                val gutter = (editor as EditorEx).gutterComponentEx\n                gutter.width - gutter.whitespaceSeparatorOffset\n            } else 0\n        }\n    }\n\n    companion object {\n        val INLAYS_KEY: Key<EditorComponentInlaysManager> = Key.create(\"EditorComponentInlaysManager\")\n\n        fun from(editor: Editor, onlyOneInlay: Boolean): EditorComponentInlaysManager {\n            return synchronized(editor) {\n                val manager = editor.getUserData(INLAYS_KEY)\n                if (manager == null) {\n                    val newManager = EditorComponentInlaysManager(editor as EditorImpl, false)\n                    editor.putUserData(INLAYS_KEY, newManager)\n                    newManager\n                } else manager\n            }\n        }\n    }\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/diff/LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright 2023 Continue Dev, Inc.\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/diff/VerticalDiffBlock.kt",
    "content": "/**\n * Copyright 2023 Continue Dev, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.phodal.shirecore.diff\n\nimport com.intellij.openapi.Disposable\nimport com.intellij.openapi.command.WriteCommandAction\nimport com.intellij.openapi.editor.Editor\nimport com.intellij.openapi.editor.LogicalPosition\nimport com.intellij.openapi.editor.colors.EditorFontType\nimport com.intellij.openapi.editor.colors.TextAttributesKey\nimport com.intellij.openapi.editor.markup.HighlighterLayer\nimport com.intellij.openapi.editor.markup.TextAttributes\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.util.TextRange\nimport com.intellij.ui.Gray\nimport com.intellij.ui.JBColor\nimport com.phodal.shirecore.ShireCoreBundle\nimport java.awt.*\nimport javax.swing.BorderFactory\nimport javax.swing.JButton\nimport javax.swing.JTextArea\nimport kotlin.math.min\n\nclass VerticalDiffBlock(\n    private val editor: Editor,\n    private val project: Project,\n    var startLine: Int,\n    private val onAcceptReject: (VerticalDiffBlock, Boolean) -> Unit,\n) {\n    val deletedLines: MutableList<String> = mutableListOf();\n    val addedLines: MutableList<String> = mutableListOf();\n    private val acceptButton: JButton\n    private val rejectButton: JButton\n    private var deletionInlay: Disposable? = null\n    private var textArea: JTextArea? = null // Used for calculation of the text area height when rendering buttons\n    private var hasRenderedDiffBlock: Boolean = false\n    private val editorComponentInlaysManager = EditorComponentInlaysManager.from(editor, false)\n    private val greenKey = createTextAttributesKey(\"CONTINUE_DIFF_NEW_LINE\", 0x3000FF00, editor)\n\n    init {\n        val (acceptBtn, rejectBtn) = createButtons()\n\n        acceptButton = acceptBtn\n        rejectButton = rejectBtn\n    }\n\n    fun clearEditorUI() {\n        deletionInlay?.dispose()\n        removeGreenHighlighters()\n        removeButtons()\n    }\n\n    fun updatePosition(newLineNumber: Int) {\n        startLine = newLineNumber\n\n        val (x, y) = getButtonsXYPositions()\n\n        rejectButton.location = Point(x, y)\n        acceptButton.location = Point(x + rejectButton.preferredSize.width + 5, y)\n\n        refreshEditor()\n    }\n\n    fun deleteLineAt(line: Int) {\n        val startOffset = editor.document.getLineStartOffset(line)\n        val endOffset = min(editor.document.getLineEndOffset(line) + 1, editor.document.textLength)\n        val deletedText = editor.document.getText(TextRange(startOffset, endOffset))\n\n        deletedLines.add(deletedText.trimEnd())\n\n        // Unable to ensure that text length has not changed, so we need to get it again\n        editor.document.deleteString(startOffset, min(endOffset, editor.document.textLength))\n    }\n\n\n    fun addNewLine(text: String, line: Int) {\n        if (line == editor.document.lineCount) {\n            editor.document.insertString(editor.document.textLength, \"\\n\")\n        }\n\n        val offset = editor.document.getLineStartOffset(line)\n\n        editor.document.insertString(offset, text + \"\\n\")\n        editor.markupModel.addLineHighlighter(greenKey, line, HighlighterLayer.LAST)\n\n        addedLines.add(text)\n    }\n\n    fun onLastDiffLine() {\n        // Handles the case where we are invoking one last time on last line of diff stream, but the block has\n        // already been rendered\n        if (hasRenderedDiffBlock) {\n            return\n        }\n\n        if (deletedLines.size > 0) {\n            renderDeletedLinesInlay()\n        }\n\n        renderButtons()\n\n        hasRenderedDiffBlock = true\n    }\n\n    fun handleReject() {\n        revertDiff()\n        clearEditorUI()\n    }\n\n    private fun refreshEditor() {\n        editor.contentComponent.revalidate()\n        editor.contentComponent.repaint()\n    }\n\n    private fun renderDeletedLinesInlay() {\n        val textArea = createDeletionTextArea(deletedLines.joinToString(\"\\n\"))\n        this.textArea = textArea\n\n        val disposable = editorComponentInlaysManager.insert(startLine, textArea, true)\n        deletionInlay = disposable\n    }\n\n    private fun renderButtons() {\n        val (x, y) = getButtonsXYPositions()\n\n        rejectButton.setBounds(\n            x,\n            y,\n            rejectButton.preferredSize.width,\n            rejectButton.preferredSize.height\n        )\n\n        acceptButton.setBounds(\n            x + rejectButton.width + 2,\n            y,\n            acceptButton.preferredSize.width,\n            acceptButton.preferredSize.height\n        )\n\n        editor.contentComponent.add(acceptButton)\n        editor.contentComponent.add(rejectButton)\n\n        editor.contentComponent.setComponentZOrder(acceptButton, 0)\n        editor.contentComponent.setComponentZOrder(rejectButton, 0)\n\n        refreshEditor()\n    }\n\n    private fun createButtons(): Pair<JButton, JButton> {\n        val rejectBtn =\n            createButton(ShireCoreBundle.message(\"sketch.patch.action.reject\"),\n                JBColor(0x99FF0000.toInt(), 0x99FF0000.toInt())\n            ).apply {\n                addActionListener {\n                    handleReject();\n                    onAcceptReject(this@VerticalDiffBlock, false)\n                }\n\n            }\n\n        val acceptBtn =\n            createButton(ShireCoreBundle.message(\"sketch.patch.action.accept\"), JBColor(0x8AA653, 0x8AA653)).apply {\n                addActionListener {\n                    handleAccept();\n                    onAcceptReject(this@VerticalDiffBlock, true)\n                }\n            }\n\n        return Pair(acceptBtn, rejectBtn)\n    }\n\n    private fun removeButtons() {\n        editor.contentComponent.remove(acceptButton)\n        editor.contentComponent.remove(rejectButton)\n\n        refreshEditor()\n    }\n\n    private fun handleAccept() {\n        clearEditorUI()\n    }\n\n    private fun revertDiff() {\n        WriteCommandAction.runWriteCommandAction(project) {\n            // Delete the added lines\n            val lineCount = editor.document.lineCount\n            val textLength = editor.document.textLength\n            val startOffset = if (startLine >= lineCount) {\n                textLength\n            } else {\n                editor.document.getLineStartOffset(startLine)\n            }\n\n            val endOffset = editor.document.getLineEndOffset(Math.min(lineCount - 1, startLine + addedLines.size - 1)) + 1\n            editor.document.deleteString(startOffset, Math.min(endOffset, textLength))\n\n            // Add the deleted lines back\n            if (deletedLines.isNotEmpty()) {\n                editor.document.insertString(startOffset, deletedLines.joinToString(\"\\n\") + \"\\n\")\n            }\n        }\n    }\n\n    private fun removeGreenHighlighters() {\n        val highlightersToRemove = editor.markupModel.allHighlighters.filter { highlighter ->\n            val highlighterLine = editor.document.getLineNumber(highlighter.startOffset)\n            highlighterLine in startLine until (startLine + addedLines.size)\n        }\n\n        highlightersToRemove.forEach { editor.markupModel.removeHighlighter(it) }\n    }\n\n    private fun createDeletionTextArea(text: String) = JTextArea(text).apply {\n        isEditable = false\n        background = JBColor(0x30FF0000, 0x30FF0000)\n        foreground = JBColor.GRAY\n        border = BorderFactory.createEmptyBorder()\n        lineWrap = false\n        wrapStyleWord = false\n        font = editor.colorsScheme.getFont(EditorFontType.PLAIN)\n    }\n\n    private fun getButtonsXYPositions(): Pair<Int, Int> {\n        val visibleArea = editor.scrollingModel.visibleArea\n        val textAreaHeight = this.textArea?.height ?: 0\n        val lineStartPosition = editor.logicalPositionToXY(LogicalPosition(startLine, 0))\n\n        val xPosition =\n            visibleArea.x + visibleArea.width - acceptButton.preferredSize.width - rejectButton.preferredSize.width - 20\n        val yPosition = lineStartPosition.y - textAreaHeight\n\n        return Pair(xPosition, yPosition)\n    }\n\n    private fun createButton(text: String, backgroundColor: JBColor): JButton {\n        return object : JButton(text) {\n            override fun paintComponent(g: Graphics) {\n                val g2 = g.create() as Graphics2D\n                g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON)\n                g2.color = backgroundColor\n                g2.fillRoundRect(0, 0, width - 1, height - 1, 4, 4)\n                super.paintComponent(g2)\n                g2.dispose()\n            }\n        }.apply {\n            foreground = Gray._240\n            font = Font(\"Arial\", Font.BOLD, 9)\n            isContentAreaFilled = false\n            isOpaque = false\n            border = BorderFactory.createEmptyBorder(4, 2, 4, 2)\n            preferredSize = Dimension(preferredSize.width - 30, 14)\n            cursor = Cursor(Cursor.HAND_CURSOR)\n        }\n    }\n}\n\nfun createTextAttributesKey(name: String, color: Int, editor: Editor): TextAttributesKey {\n    val attributes = TextAttributes().apply {\n        backgroundColor = JBColor(color, color)\n    }\n\n    return TextAttributesKey.createTextAttributesKey(name).also {\n        editor.colorsScheme.setAttributes(it, attributes)\n    }\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/diff/model/StreamDiff.kt",
    "content": "package com.phodal.shirecore.diff.model\n\nimport kotlinx.coroutines.flow.Flow\nimport kotlinx.coroutines.flow.flow\nimport kotlinx.coroutines.flow.toList\n\nsealed class DiffLine {\n    data class Same(val line: String) : DiffLine()\n    data class New(val line: String) : DiffLine()\n    data class Old(val line: String) : DiffLine()\n}\n\nfun streamDiff(oldLines: List<String>, newLines: Flow<String>): Flow<DiffLine> = flow {\n    val newLinesList = newLines.toList()\n    val diffs = diff(oldLines, newLinesList)\n    for (diff in diffs) {\n        emit(diff)\n    }\n}\n\nprivate fun diff(oldLines: List<String>, newLines: List<String>): List<DiffLine> {\n    // 计算 LCS 长度表\n    val lcs = computeLcsTable(oldLines, newLines)\n    // 回溯 LCS 表，构建差异列表\n    val diffs = mutableListOf<DiffLine>()\n    backtrackDiff(lcs, oldLines, newLines, oldLines.size, newLines.size, diffs)\n    return diffs\n}\n\nprivate fun computeLcsTable(oldLines: List<String>, newLines: List<String>): Array<IntArray> {\n    val m = oldLines.size\n    val n = newLines.size\n    val table = Array(m + 1) { IntArray(n + 1) }\n    for (i in 0 until m) {\n        for (j in 0 until n) {\n            if (oldLines[i] == newLines[j]) {\n                table[i + 1][j + 1] = table[i][j] + 1\n            } else {\n                table[i + 1][j + 1] = maxOf(table[i + 1][j], table[i][j + 1])\n            }\n        }\n    }\n    return table\n}\n\nprivate fun backtrackDiff(\n    lcs: Array<IntArray>,\n    oldLines: List<String>,\n    newLines: List<String>,\n    i: Int,\n    j: Int,\n    diffs: MutableList<DiffLine>,\n) {\n    if (i > 0 && j > 0 && oldLines[i - 1] == newLines[j - 1]) {\n        backtrackDiff(lcs, oldLines, newLines, i - 1, j - 1, diffs)\n        diffs.add(DiffLine.Same(oldLines[i - 1]))\n    } else if (j > 0 && (i == 0 || lcs[i][j - 1] >= lcs[i - 1][j])) {\n        backtrackDiff(lcs, oldLines, newLines, i, j - 1, diffs)\n        diffs.add(DiffLine.New(newLines[j - 1]))\n    } else if (i > 0 && (j == 0 || lcs[i][j - 1] < lcs[i - 1][j])) {\n        backtrackDiff(lcs, oldLines, newLines, i - 1, j, diffs)\n        diffs.add(DiffLine.Old(oldLines[i - 1]))\n    }\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/function/guard/base/LocalScanner.kt",
    "content": "package com.phodal.shirecore.function.guard.base\n\n/**\n * GuardScanner is an interface for scanning user input for security vulnerabilities.\n */\ninterface GuardScanner {\n    fun scan(prompt: String): ScanResult\n}\n\ninterface Masker {\n    fun mask(prompt: String): String\n}\n\ninterface LocalScanner : GuardScanner\n\nabstract class EntityRecognizer {\n    abstract fun load()\n\n    abstract fun analyze(text: String, entities: List<String>): List<ScanResult>\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/function/guard/base/ScanResult.kt",
    "content": "package com.phodal.shirecore.function.guard.base\n\n/**\n * ScanResult is a data class that encapsulates the result of a scan.\n */\ndata class ScanResult(\n    var isPassed: Boolean = false,\n    val modifiedInput: String? = null,\n    val message: String? = null\n)"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/function/guard/model/SecretPattern.kt",
    "content": "package com.phodal.shirecore.function.guard.model\n\nimport com.intellij.openapi.diagnostic.logger\nimport kotlinx.serialization.Contextual\nimport kotlinx.serialization.Serializable\n\n@Serializable\nclass SecretPattern(\n    val name: String,\n    val regex: String,\n    val confidence: String,\n) {\n    @Contextual\n    private val regexPattern: Regex? = try {\n        Regex(regex)\n    } catch (e: Exception) {\n        logger<SecretPattern>().error(\"Invalid regex pattern: $regex, name: $name\")\n        null\n    }\n\n    fun matches(text: String): Boolean {\n        return regexPattern?.containsMatchIn(text) ?: false\n    }\n\n    fun mask(text: String): String {\n        return regexPattern?.replace(text, \"****\") ?: text\n    }\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/function/guard/model/SecretPatterns.kt",
    "content": "package com.phodal.shirecore.function.guard.model\n\nimport kotlinx.serialization.Serializable\n\n@Serializable\ndata class SecretPatterns(\n    val patterns: List<SecretPatternItem>,\n    val keywords: List<String> = emptyList(),\n    val models: List<Model> = emptyList()\n)\n\n@Serializable\ndata class SecretPatternItem(\n    val pattern: SecretPattern,\n)\n\n@Serializable\ndata class Model(\n    val name: String,\n    val location: String\n)\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/function/guard/scanner/SecretPatternsScanner.kt",
    "content": "package com.phodal.shirecore.function.guard.scanner\n\nimport com.charleskorn.kaml.Yaml\nimport com.intellij.openapi.components.Service\nimport com.intellij.openapi.diagnostic.logger\nimport com.intellij.openapi.project.Project\nimport com.intellij.psi.search.FilenameIndex\nimport com.phodal.shirecore.schema.SECRET_PATTERN_EXTENSION\nimport com.phodal.shirecore.function.guard.base.LocalScanner\nimport com.phodal.shirecore.function.guard.base.ScanResult\nimport com.phodal.shirecore.function.guard.model.SecretPattern\nimport com.phodal.shirecore.function.guard.model.SecretPatternItem\nimport com.phodal.shirecore.function.guard.model.SecretPatterns\nimport java.net.URL\n\n@Service(Service.Level.PROJECT)\nclass SecretPatternsScanner(val project: Project) : LocalScanner {\n    private val defaultPiiSecrets = \"/secrets/default.shireSecretPattern.yml\"\n    private var patterns: List<SecretPattern>\n\n    init {\n        patterns = initPatterns() + loadFromProject(project)\n    }\n\n    fun getPatterns(): List<SecretPattern> {\n        return patterns\n    }\n\n    private fun initPatterns(): List<SecretPattern> {\n        val file: URL = javaClass.getResource(defaultPiiSecrets)!!\n        val content = file.readText()\n\n        val patterns = Yaml.default.decodeFromString(SecretPatterns.serializer(), content)\n        return patterns.patterns.map(SecretPatternItem::pattern)\n    }\n\n    private fun loadFromProject(project: Project): List<SecretPattern> {\n        return FilenameIndex.getAllFilesByExt(project, SECRET_PATTERN_EXTENSION)\n            .mapNotNull {\n                val content = it.inputStream.reader().readText()\n                try {\n                    Yaml.default.decodeFromString(SecretPatterns.serializer(), content)\n                } catch (e: Exception) {\n                    logger<SecretPatternsScanner>().error(\"Failed to load custom agent configuration\", e)\n                    null\n                }\n            }.flatMap {\n                it.patterns.map(SecretPatternItem::pattern)\n            }\n    }\n\n    fun addPattern(pattern: SecretPattern) {\n        patterns = patterns + pattern\n    }\n\n    fun removePattern(pattern: SecretPattern) {\n        patterns = patterns - pattern\n    }\n\n    fun maskInput(text: String): String {\n        var newText = text\n        patterns.forEach {\n            newText = it.mask(newText)\n        }\n\n        return newText\n    }\n\n    override fun scan(prompt: String): ScanResult {\n        val result = ScanResult()\n        patterns.forEach {\n            if (it.matches(prompt)) {\n                result.isPassed = false\n            }\n        }\n\n        return result\n    }\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/function/shireql/JvmShireQLFuncType.kt",
    "content": "package com.phodal.shirecore.function.shireql\n\nenum class JvmShireQLFuncType(val methodName: String, val description: String) {\n    GET_NAME(\"getName\", \"Get class name\"),\n    NAME(\"name\", \"Get class name\"),\n    EXTENDS(\"extends\", \"Get class extends\"),\n    IMPLEMENTS(\"implements\", \"Get class implements\"),\n    METHOD_CODE_BY_NAME(\"methodCodeByName\", \"Get method code by name\"),\n    FIELD_CODE_BY_NAME(\"fieldCodeByName\", \"Get field code by name\"),\n\n    SUBCLASSES_OF(\"subclassesOf\", \"Get subclasses of class\"),\n    ANNOTATED_OF(\"annotatedOf\", \"Get annotated classes\"),\n    SUPERCLASS_OF(\"superclassOf\", \"Get superclass of class\"),\n    IMPLEMENTS_OF(\"implementsOf\", \"Get implemented interfaces of class\"),\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/llm/ChatMessage.kt",
    "content": "package com.phodal.shirecore.llm\n\nimport kotlinx.serialization.Serializable\n\nenum class ChatRole {\n    system,\n    assistant,\n    user;\n}\n\n@Serializable\ndata class ChatMessage(val role: ChatRole, val content: String)\n\n@Serializable\ndata class CustomRequest(val messages: List<ChatMessage>)\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/llm/LlmConfig.kt",
    "content": "package com.phodal.shirecore.llm\n\nimport com.intellij.json.psi.JsonObject\nimport com.phodal.shire.json.findNumber\nimport com.phodal.shire.json.findString\n\nclass LlmConfig(\n    val title: String,\n    val provider: String = \"openai\",\n    val apiBase: String = \"https://api.openai.com/v1/chat/completions\",\n    val apiKey: String,\n    val model: String,\n    val temperature: Double = 0.0,\n    val maxTokens: Int? = 1024,\n    val requestFormat: Map<String, String> = mapOf(),\n    val responseFormat: String = \"\\$.choices[0].delta.content\",\n    val messageKeys: Map<String, String> = mapOf(),\n) {\n\n    fun checkAvailable(): Boolean = apiKey.isNotBlank() && model.isNotBlank()\n\n    companion object {\n        fun fromJson(modelConfig: JsonObject): LlmConfig? {\n            val title = modelConfig.findString(\"title\") ?: return null\n            val provider = modelConfig.findString(\"provider\") ?: \"openai\"\n            val apiBase = modelConfig.findString(\"apiBase\") ?: \"https://api.openai.com/v1/chat/completions\"\n\n            val apiKey = modelConfig.findString(\"apiKey\") ?: return null\n            val model = modelConfig.findString(\"model\") ?: return null\n\n            val temperature = try {\n                modelConfig.findNumber(\"temperature\")?.toDouble()\n            } catch (e: Exception) {\n                null\n            }\n            val maxTokens = try {\n                modelConfig.findNumber(\"maxTokens\")?.toInt()\n            } catch (e: Exception) {\n                null\n            }\n\n            return LlmConfig(\n                title = title,\n                provider = provider,\n                apiBase = apiBase,\n                apiKey = apiKey,\n                model = model,\n                temperature = temperature ?: 0.0,\n                maxTokens = maxTokens,\n                requestFormat = mapOf(),\n                responseFormat = \"\\$.choices[0].delta.content\",\n                messageKeys = mapOf(),\n            )\n        }\n    }\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/llm/LlmProvider.kt",
    "content": "package com.phodal.shirecore.llm\n\nimport com.intellij.openapi.extensions.ExtensionPointName\nimport com.intellij.openapi.project.Project\nimport com.intellij.psi.search.ProjectScope\nimport com.phodal.shirecore.ShireCoreBundle\nimport com.phodal.shirecore.ShirelangNotifications\nimport com.phodal.shirecore.middleware.post.PostProcessorContext\nimport com.phodal.shire.json.llm.LlmEnv\nimport kotlinx.coroutines.flow.Flow\n\n/**\n * Interface for providing LLM (Language Model) services.\n * This interface defines methods for interacting with LLM services, such as checking applicability, sending prompts,\n * streaming chat completion responses, and clearing messages.\n *\n * Implementations of this interface should provide functionality for interacting with specific LLM services.\n *\n */\ninterface LlmProvider {\n    var project: Project?\n\n    /**\n     * Default timeout for the provider.\n     * This is used to set the default timeout for the provider.\n     * For example, If you want to wait in 10min, you can use:\n     * ```Kotlin\n     * Duration.ofSeconds(defaultTimeout)\n     * ```\n     */\n    val defaultTimeout: Long get() = 600\n\n    /**\n     * Checks if the given project is applicable for some operation.\n     *\n     * @param project the project to check for applicability\n     * @param llmConfig This llmConfig is used to verify if llmProvider is available.\n     * For example, it may be an unsaved configuration that can be used to test LLM connection.\n     *\n     * @return true if the project is applicable, false otherwise\n     */\n    fun isApplicable(project: Project, llmConfig: LlmConfig? = null): Boolean\n\n    /**\n     * Streams chat completion responses from the service.\n     *\n     * @param promptText The text prompt to send to the service.\n     * @param systemPrompt The system prompt to send to the service.\n     * @param keepHistory Flag indicating whether to keep the chat history.\n     * @param llmConfig A default llmConfig, if not provided it will be read from the project settings.\n     * @return A Flow of String values representing the chat completion responses.\n     */\n    fun stream(promptText: String, systemPrompt: String, keepHistory: Boolean = true, llmConfig: LlmConfig? = null): Flow<String>\n\n    /**\n     * config LLM Provider from [PostProcessorContext]\n     */\n    fun configRunLlm(): LlmConfig? {\n        if (project == null) return null\n\n        val modelName = PostProcessorContext.getData()?.llmModelName ?: return null\n        val scope = ProjectScope.getContentScope(project!!)\n\n\n        val modelConfig = LlmEnv.configFromFile(modelName, scope, project!!)\n        if (modelConfig != null) {\n            return LlmConfig.fromJson(modelConfig)\n        }\n\n        return null\n    }\n\n    /**\n     * Clears the message displayed in the UI.\n     */\n    fun clearMessage()\n\n    companion object {\n        private val EP_NAME: ExtensionPointName<LlmProvider> =\n            ExtensionPointName.create(\"com.phodal.shireLlmProvider\")\n\n        /**\n         * Returns an instance of LlmProvider based on the given Project.\n         *\n         * @param project the Project for which to find a suitable LlmProvider\n         * @param llmConfig provide llmConfig as a condition for finding a suitable LlmProvider.\n         *\n         * @return an instance of LlmProvider if a suitable provider is found, null otherwise\n         */\n        fun provider(project: Project, llmConfig: LlmConfig? = null): LlmProvider? {\n            val providers = EP_NAME.extensions.filter { it.isApplicable(project, llmConfig) }\n            return if (providers.isEmpty()) {\n                ShirelangNotifications.error(project, ShireCoreBundle.message(\"shire.llm.notfound\"))\n                null\n            } else {\n                providers.first()\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/middleware/builtin/AppendProcessor.kt",
    "content": "package com.phodal.shirecore.middleware.builtin\n\nimport com.intellij.execution.ui.ConsoleView\nimport com.intellij.openapi.project.Project\nimport com.phodal.shirecore.middleware.post.PostProcessorType\nimport com.phodal.shirecore.middleware.post.PostProcessorContext\nimport com.phodal.shirecore.middleware.post.PostProcessor\n\nclass AppendProcessor : PostProcessor {\n    override val processorName: String = PostProcessorType.Append.handleName\n    override val description: String = \"`append` will append the text to the generated text\"\n\n    override fun isApplicable(context: PostProcessorContext): Boolean = true\n\n    override fun execute(\n        project: Project,\n        context: PostProcessorContext,\n        console: ConsoleView?,\n        args: List<Any>,\n    ): Any {\n\n        context.genText += args.map {\n            if (it.toString().startsWith(\"$\")) {\n                context.compiledVariables[it.toString().substring(1)] ?: \"\"\n            } else {\n                it\n            }\n        }.joinToString(\" \")\n\n        return context.genText ?: \"\"\n    }\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/middleware/builtin/DiffProcessor.kt",
    "content": "package com.phodal.shirecore.middleware.builtin\n\nimport com.intellij.diff.DiffContentFactoryEx\nimport com.intellij.diff.DiffDialogHints\nimport com.intellij.diff.DiffManager\nimport com.intellij.diff.chains.SimpleDiffRequestChain\nimport com.intellij.diff.chains.SimpleDiffRequestProducer\nimport com.intellij.diff.requests.SimpleDiffRequest\nimport com.intellij.execution.ui.ConsoleView\nimport com.intellij.execution.ui.ConsoleViewContentType\nimport com.intellij.openapi.application.runInEdt\nimport com.intellij.openapi.application.runReadAction\nimport com.intellij.openapi.project.Project\nimport com.phodal.shirecore.findFile\nimport com.phodal.shirecore.middleware.post.PostProcessor\nimport com.phodal.shirecore.middleware.post.PostProcessorContext\nimport com.phodal.shirecore.middleware.post.PostProcessorType\n\nclass DiffProcessor : PostProcessor {\n    override val processorName: String = PostProcessorType.Diff.handleName\n    override val description: String =\n        \"`diff` will show the diff of two texts, default is current code and llm response\"\n\n    private val diffFactory = DiffContentFactoryEx.getInstanceEx()\n\n    override fun isApplicable(context: PostProcessorContext): Boolean {\n        return true\n    }\n\n    override fun execute(project: Project, context: PostProcessorContext, console: ConsoleView?, args: List<Any>): Any {\n        if (args.size < 2) {\n            console?.print(\"DiffProcessor: not enough arguments\", ConsoleViewContentType.ERROR_OUTPUT)\n            return \"\"\n        }\n\n        val firstArg = args[0].toString()\n        val virtualFile = runReadAction { project.findFile(firstArg) } ?: let {\n            console?.print(\"DiffProcessor: file not found\", ConsoleViewContentType.ERROR_OUTPUT)\n            return \"\"\n        }\n\n        val currentDocContent = diffFactory.create(project, virtualFile)\n        val newDocContent = diffFactory.create(args[1].toString())\n\n        val diffRequest =\n            SimpleDiffRequest(\"Shire Diff Viewer\", currentDocContent, newDocContent, \"Current code\", \"AI generated\")\n        val producer = SimpleDiffRequestProducer.create(virtualFile.path) {\n            diffRequest\n        }\n\n        val chain = SimpleDiffRequestChain.fromProducer(producer)\n        runInEdt {\n            DiffManager.getInstance().showDiff(project, chain, DiffDialogHints.FRAME)\n        }\n\n        return \"\"\n    }\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/middleware/builtin/FormatCodeProcessor.kt",
    "content": "package com.phodal.shirecore.middleware.builtin\n\nimport com.intellij.execution.ui.ConsoleView\nimport com.intellij.openapi.command.WriteCommandAction\nimport com.intellij.openapi.project.Project\nimport com.intellij.psi.PsiDocumentManager\nimport com.intellij.psi.codeStyle.CodeStyleManager\nimport com.phodal.shirecore.middleware.post.PostProcessorType\nimport com.phodal.shirecore.middleware.post.PostProcessorContext\nimport com.phodal.shirecore.middleware.post.PostProcessor\nimport com.phodal.shirecore.workerThread\nimport kotlinx.coroutines.CoroutineScope\nimport kotlinx.coroutines.launch\n\nclass FormatCodeProcessor : PostProcessor {\n    override val processorName: String = PostProcessorType.FormatCode.handleName\n    override val description: String = \"`formatCode` will format the code of the current file\"\n\n    override fun isApplicable(context: PostProcessorContext): Boolean = true\n\n    override fun execute(project: Project, context: PostProcessorContext, console: ConsoleView?, args: List<Any>): Any {\n        val file = context.currentFile ?: return \"\"\n        val document = PsiDocumentManager.getInstance(project).getDocument(file) ?: return \"\"\n\n        CoroutineScope(workerThread).launch {\n            WriteCommandAction.runWriteCommandAction(project) {\n                val codeStyleManager = CodeStyleManager.getInstance(project)\n                if (context.modifiedTextRange != null) {\n                    codeStyleManager.reformatText(file, listOf(context.modifiedTextRange))\n                } else if (context.genPsiElement != null) {\n                    codeStyleManager.reformat(context.genPsiElement!!)\n                } else {\n                    codeStyleManager.reformatText(file, 0, document.textLength)\n                }\n            }\n        }\n\n        return context.genText ?: \"\"\n    }\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/middleware/builtin/InsertCodeProcessor.kt",
    "content": "package com.phodal.shirecore.middleware.builtin\n\nimport com.intellij.execution.ui.ConsoleView\nimport com.intellij.execution.ui.ConsoleViewContentType\nimport com.intellij.openapi.command.WriteCommandAction\nimport com.intellij.openapi.project.Project\nimport com.phodal.shirecore.provider.codeedit.CodeModifier\nimport com.phodal.shirecore.middleware.post.PostProcessorType\nimport com.phodal.shirecore.middleware.post.PostProcessorContext\nimport com.phodal.shirecore.middleware.post.PostProcessor\n\nclass InsertCodeProcessor : PostProcessor {\n    override val processorName: String = PostProcessorType.InsertCode.handleName\n    override val description: String = \"`insertCode` will insert the code to the current file\"\n\n    override fun isApplicable(context: PostProcessorContext): Boolean = true\n\n    override fun execute(project: Project, context: PostProcessorContext, console: ConsoleView?, args: List<Any>): String {\n        if (context.currentLanguage == null || context.currentFile == null) {\n            console?.print(\"No found current language\\n\", ConsoleViewContentType.ERROR_OUTPUT)\n            return \"\"\n        }\n\n\n        val codeModifier = CodeModifier.forLanguage(context.currentLanguage!!)\n        if (codeModifier == null) {\n            console?.print(\"No code modifier found\\n\", ConsoleViewContentType.NORMAL_OUTPUT)\n            // insert to the end of the file\n            val editor = context.editor ?: return \"\"\n            WriteCommandAction.runWriteCommandAction(project) {\n                editor.document.insertString(editor.caretModel.offset, context.genText ?: \"\")\n            }\n            return \"\"\n        }\n\n        context.genPsiElement = codeModifier.smartInsert(context.currentFile!!.virtualFile!!, project, context.genText ?: \"\")\n        return \"\"\n    }\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/middleware/builtin/InsertNewlineProcessor.kt",
    "content": "package com.phodal.shirecore.middleware.builtin\n\nimport com.intellij.execution.ui.ConsoleView\nimport com.intellij.openapi.command.WriteCommandAction\nimport com.intellij.openapi.project.Project\nimport com.phodal.shirecore.middleware.post.PostProcessorType\nimport com.phodal.shirecore.middleware.post.PostProcessorContext\nimport com.phodal.shirecore.middleware.post.PostProcessor\nimport com.phodal.shirecore.workerThread\nimport kotlinx.coroutines.CoroutineScope\nimport kotlinx.coroutines.launch\n\nclass InsertNewlineProcessor : PostProcessor {\n    override val processorName: String = PostProcessorType.InsertNewline.handleName\n    override val description: String = \"`insertNewline` will insert a newline at the cursor position\"\n\n    override fun isApplicable(context: PostProcessorContext): Boolean = true\n\n    override fun execute(project: Project, context: PostProcessorContext, console: ConsoleView?, args: List<Any>): Any {\n        val editor = context.editor ?: return \"\"\n\n        CoroutineScope(workerThread).launch {\n            WriteCommandAction.runWriteCommandAction(project) {\n                editor.document.insertString(editor.caretModel.offset, \"\\n\")\n            }\n        }\n\n        return editor.document.text\n    }\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/middleware/builtin/OpenFileProcessor.kt",
    "content": "package com.phodal.shirecore.middleware.builtin\n\nimport com.intellij.execution.filters.OpenFileHyperlinkInfo\nimport com.intellij.execution.ui.ConsoleView\nimport com.intellij.openapi.application.runInEdt\nimport com.intellij.openapi.fileEditor.FileEditorManager\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.phodal.shirecore.findFile\nimport com.phodal.shirecore.middleware.post.PostProcessorType\nimport com.phodal.shirecore.middleware.post.PostProcessorContext\nimport com.phodal.shirecore.middleware.post.PostProcessor\n\n\nclass OpenFileProcessor : PostProcessor {\n    override val processorName: String = PostProcessorType.OpenFile.handleName\n    override val description: String = \"`openFile` will open the file in the editor\"\n\n    override fun isApplicable(context: PostProcessorContext): Boolean = true\n\n    override fun execute(project: Project, context: PostProcessorContext, console: ConsoleView?, args: List<Any>): String {\n        val firstArg = args.firstOrNull()\n        val file = firstArg ?: context.pipeData[\"output\"] ?: context.genText\n        if (file !is VirtualFile) {\n            if (file is String) {\n                // check has multiple files\n                val files = file.split(\"\\n\")\n                runInEdt {\n                    val findFiles = files.mapNotNull { project.findFile(it) }\n                    findFiles.map {\n                        console?.printHyperlink(\"$it\", OpenFileHyperlinkInfo(project, it, -1, -1))\n                        // new line\n                        console?.print(\"\\n\", com.intellij.execution.ui.ConsoleViewContentType.NORMAL_OUTPUT)\n                    }\n\n                    findFiles.mapIndexed { index, it ->\n                        val isFocus = index == findFiles.size - 1\n                        FileEditorManager.getInstance(project).openFile(it, isFocus)\n                    }\n                }\n\n                return \"\"\n            } else {\n                console?.print(\"No file to open\\n\", com.intellij.execution.ui.ConsoleViewContentType.ERROR_OUTPUT)\n            }\n\n            return \"\"\n        }\n\n        runInEdt {\n            FileEditorManager.getInstance(project).openFile(file, true)\n        }\n\n        return \"\"\n    }\n}\n\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/middleware/builtin/OpenWebpageProcessor.kt",
    "content": "package com.phodal.shirecore.middleware.builtin\n\nimport com.intellij.execution.ui.ConsoleView\nimport com.intellij.ide.DataManager\nimport com.intellij.ide.IdeBundle\nimport com.intellij.ide.browsers.BrowserLauncher\nimport com.intellij.ide.browsers.OpenInBrowserRequest\nimport com.intellij.ide.browsers.WebBrowserService\nimport com.intellij.ide.browsers.WebBrowserUrlProvider\nimport com.intellij.ide.browsers.actions.findUsingBrowser\nimport com.intellij.openapi.actionSystem.CommonDataKeys\nimport com.intellij.openapi.application.runInEdt\nimport com.intellij.openapi.diagnostic.logger\nimport com.intellij.openapi.fileEditor.FileDocumentManager\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.ui.Messages\nimport com.intellij.psi.PsiDocumentManager\nimport com.intellij.psi.PsiElement\nimport com.phodal.shirecore.middleware.post.PostProcessor\nimport com.phodal.shirecore.middleware.post.PostProcessorContext\nimport com.phodal.shirecore.middleware.post.PostProcessorType\n\nclass OpenWebpageProcessor : PostProcessor {\n    override val processorName: String get() = PostProcessorType.OpenWebpage.handleName\n    override val description: String get() = \"`openWebpage` will open the generated HTML in the browser\"\n\n    override fun isApplicable(context: PostProcessorContext): Boolean {\n        return context.genText?.contains(\"<html\") ?: false\n    }\n\n    override fun execute(project: Project, context: PostProcessorContext, console: ConsoleView?, args: List<Any>): Any {\n        val dataContext = DataManager.getInstance().dataContextFromFocusAsync.blockingGet(10000)\n            ?: throw IllegalStateException(\"No data context\")\n        val editor = CommonDataKeys.EDITOR.getData(dataContext) ?: return \"\"\n        val psiFile = PsiDocumentManager.getInstance(project).getPsiFile(editor.document)\n            ?: throw IllegalStateException(\"No PSI file\")\n\n        val request = object : OpenInBrowserRequest(psiFile, true) {\n            private val lazyElement by lazy { file.findElementAt(editor.caretModel.offset) }\n\n            override val element: PsiElement\n                get() = lazyElement ?: file\n        }\n\n        try {\n            val browser = findUsingBrowser()\n            val urls = WebBrowserService.getInstance().getUrlsToOpen(request, true)\n            if (!urls.isEmpty()) {\n                val url = urls.first()\n                runInEdt {\n                    FileDocumentManager.getInstance().saveAllDocuments()\n                }\n\n                BrowserLauncher.instance.browse(url.toExternalForm(), browser, request.project)\n            }\n        } catch (e: WebBrowserUrlProvider.BrowserException) {\n            Messages.showErrorDialog(e.message, IdeBundle.message(\"browser.error\"))\n        } catch (e: Exception) {\n            logger<OpenWebpageProcessor>().warn(e)\n        }\n\n        return \"\"\n    }\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/middleware/builtin/ParseCodeProcessor.kt",
    "content": "package com.phodal.shirecore.middleware.builtin\n\nimport com.intellij.execution.ui.ConsoleView\nimport com.intellij.openapi.project.Project\nimport com.phodal.shirecore.utils.markdown.CodeFence\nimport com.phodal.shirecore.middleware.post.PostProcessorType\nimport com.phodal.shirecore.middleware.post.PostProcessorContext\nimport com.phodal.shirecore.middleware.post.PostProcessor\n\nclass ParseCodeProcessor : PostProcessor {\n    override val processorName: String = PostProcessorType.ParseCode.handleName\n    override val description: String = \"`parseCode` will parse the markdown from llm response.\"\n\n    override fun isApplicable(context: PostProcessorContext): Boolean = true\n\n    override fun execute(project: Project, context: PostProcessorContext, console: ConsoleView?, args: List<Any>): String {\n        val code = CodeFence.parse(context.genText ?: \"\")\n        val codeText = code.text\n\n        context.genTargetLanguage = code.ideaLanguage\n        context.genTargetExtension = code.extension\n\n        context.pipeData[\"output\"] = codeText\n        context.pipeData[\"code\"] = codeText\n\n        return codeText\n    }\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/middleware/builtin/ParseCommentProcessor.kt",
    "content": "package com.phodal.shirecore.middleware.builtin\n\nimport com.intellij.execution.ui.ConsoleView\nimport com.intellij.openapi.project.Project\nimport com.phodal.shirecore.middleware.post.PostProcessorType\nimport com.phodal.shirecore.middleware.post.PostProcessorContext\nimport com.phodal.shirecore.middleware.post.PostProcessor\nimport com.phodal.shirecore.provider.psi.PsiElementDataBuilder\nimport org.jetbrains.annotations.NonNls\n\nclass ParseCommentProcessor : PostProcessor {\n    override val processorName: String = PostProcessorType.ParseComment.handleName\n    override val description: String = \"`parseComment` will parse the comment from the llm response\"\n\n    override fun isApplicable(context: PostProcessorContext): Boolean = true\n\n    fun preHandleDoc(newDoc: String): @NonNls String {\n        val newDocWithoutCodeBlock = newDoc.removePrefix(\"```java\")\n            .removePrefix(\"```\")\n            .removeSuffix(\"```\")\n\n        val fromSuggestion = buildDocFromSuggestion(newDocWithoutCodeBlock, \"/**\", \"*/\")\n        return fromSuggestion\n    }\n\n    fun buildDocFromSuggestion(suggestDoc: String, commentStart: String, commentEnd: String): String {\n        val startIndex = suggestDoc.indexOf(commentStart)\n        if (startIndex < 0) {\n            return \"\"\n        }\n\n        val docComment = suggestDoc.substring(startIndex)\n        val endIndex = docComment.indexOf(commentEnd, commentStart.length)\n        if (endIndex < 0) {\n            return docComment + commentEnd\n        }\n\n        val substring = docComment.substring(0, endIndex + commentEnd.length)\n        return substring\n    }\n\n    private fun getDocFromOutput(context: PostProcessorContext) =\n        preHandleDoc(context.pipeData[\"output\"] as String? ?: context.genText ?: \"\")\n\n    override fun execute(project: Project, context: PostProcessorContext, console: ConsoleView?, args: List<Any>): String {\n        val defaultComment: String = getDocFromOutput(context)\n        val currentFile = context.currentFile ?: return defaultComment\n\n        val comment = PsiElementDataBuilder.provide(currentFile.language)\n            ?.parseComment(project, defaultComment) ?: return defaultComment\n\n        return comment\n    }\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/middleware/builtin/PatchProcessor.kt",
    "content": "package com.phodal.shirecore.middleware.builtin\n\nimport com.intellij.execution.ui.ConsoleView\nimport com.intellij.execution.ui.ConsoleViewContentType\nimport com.intellij.openapi.application.ApplicationManager\nimport com.intellij.openapi.diff.impl.patch.FilePatch\nimport com.intellij.openapi.diff.impl.patch.PatchReader\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.vcs.changes.patch.AbstractFilePatchInProgress\nimport com.intellij.openapi.vcs.changes.patch.ApplyPatchDefaultExecutor\nimport com.intellij.openapi.vcs.changes.patch.MatchPatchPaths\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.intellij.util.containers.MultiMap\nimport com.phodal.shirecore.middleware.post.PostProcessor\nimport com.phodal.shirecore.middleware.post.PostProcessorContext\nimport com.phodal.shirecore.middleware.post.PostProcessorType\n\nclass PatchProcessor : PostProcessor {\n    override val processorName: String = PostProcessorType.Patch.handleName\n    override val description: String = \"`patch` will apply the patch to the current file, default will use llm response.\"\n\n    override fun isApplicable(context: PostProcessorContext): Boolean = true\n\n    override fun execute(\n        project: Project,\n        context: PostProcessorContext,\n        console: ConsoleView?,\n        args: List<Any>,\n    ): Any {\n        val args = args.map {\n            val argName = it.toString()\n            if (argName.startsWith(\"$\")) {\n                if (argName == \"output\" && context.lastTaskOutput != null) {\n                    context.lastTaskOutput\n                } else {\n                    context.compiledVariables[argName.substring(1)] ?: \"\"\n                }\n            } else {\n                it\n            }\n        }\n\n        if (args.size < 2) {\n            console?.print(\"PatchProcessor: not enough arguments\", ConsoleViewContentType.ERROR_OUTPUT)\n            return \"\"\n        }\n\n        val fileName = args[0].toString()\n        val content = args[1].toString()\n\n        val shelfExecutor = ApplyPatchDefaultExecutor(project)\n\n        val myReader = PatchReader(content)\n        myReader.parseAllPatches()\n\n        val filePatches: MutableList<FilePatch> = myReader.allPatches\n\n        ApplicationManager.getApplication().invokeAndWait {\n            val matchedPatches =\n                MatchPatchPaths(project).execute(filePatches, true)\n\n            val patchGroups = MultiMap<VirtualFile, AbstractFilePatchInProgress<*>>()\n            for (patchInProgress in matchedPatches) {\n                patchGroups.putValue(patchInProgress.base, patchInProgress)\n            }\n\n            if(filePatches.isEmpty() ) {\n                console?.print(\"PatchProcessor: no patches found\", ConsoleViewContentType.ERROR_OUTPUT)\n                return@invokeAndWait\n            }\n\n            val additionalInfo = myReader.getAdditionalInfo(ApplyPatchDefaultExecutor.pathsFromGroups(patchGroups))\n            shelfExecutor.apply(filePatches, patchGroups, null, fileName, additionalInfo)\n        }\n\n        return context.genText ?: \"\"\n    }\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/middleware/builtin/RunCodeProcessor.kt",
    "content": "package com.phodal.shirecore.middleware.builtin\n\nimport com.intellij.execution.ui.ConsoleView\nimport com.intellij.execution.ui.ConsoleViewContentType.*\nimport com.intellij.openapi.application.ApplicationManager\nimport com.intellij.openapi.application.ReadAction\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.vfs.LocalFileSystem\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.intellij.psi.PsiFile\nimport com.intellij.psi.PsiFileFactory\nimport com.intellij.psi.PsiManager\nimport com.phodal.shirecore.middleware.post.PostProcessorType\nimport com.phodal.shirecore.middleware.post.PostProcessorContext\nimport com.phodal.shirecore.middleware.post.PostProcessor\nimport com.phodal.shirecore.provider.shire.FileRunService\n\nclass RunCodeProcessor : PostProcessor {\n    override val processorName: String = PostProcessorType.RunCode.handleName\n    override val description: String = \"`runCode` will run the code, default will be test file.\"\n\n    override fun isApplicable(context: PostProcessorContext): Boolean = true\n\n    override fun execute(\n        project: Project,\n        context: PostProcessorContext,\n        console: ConsoleView?,\n        args: List<Any>,\n    ): String {\n        when (val code = context.pipeData[\"output\"]) {\n            is VirtualFile -> {\n                LocalFileSystem.getInstance().refreshAndFindFileByPath(code.path)\n                val psiFile = ReadAction.compute<PsiFile?, Throwable> {\n                    PsiManager.getInstance(project).findFile(code)\n                }\n\n                psiFile?.let {\n                    doExecute(console, project, code, it)\n                    return \"\"\n                }\n            }\n\n            is String -> {\n                val ext = context.genTargetLanguage?.associatedFileType?.defaultExtension ?: \"txt\"\n                ApplicationManager.getApplication().invokeAndWait {\n                    if (code.contains(\"\\n\")) {\n                        PsiFileFactory.getInstance(project).createFileFromText(\"temp.$ext\", code).let { psiFile ->\n                            if (psiFile.virtualFile == null) {\n                                console?.print(\"Failed to create file for run\\n\", ERROR_OUTPUT)\n                            } else {\n                                doExecute(console, project, psiFile.virtualFile, psiFile)\n                            }\n                        }\n                    } else {\n                        val file = LocalFileSystem.getInstance().refreshAndFindFileByPath(code)\n                        if (file != null) {\n                            val psiFile = ReadAction.compute<PsiFile?, Throwable> {\n                                PsiManager.getInstance(project).findFile(file)\n                            }\n\n                            psiFile?.let {\n                                doExecute(console, project, file, psiFile)\n                            }\n                        }\n                    }\n                }\n            }\n        }\n\n        console?.print(\"No code to run\\n\", ERROR_OUTPUT)\n        return \"\"\n    }\n\n    private fun doExecute(\n        console: ConsoleView?,\n        project: Project,\n        file: VirtualFile,\n        psiFile: PsiFile,\n    ) {\n        val fileRunService = FileRunService.provider(project, file)\n        if (fileRunService == null) {\n            val cliResult = FileRunService.runInCli(project, psiFile)\n            if (cliResult != null) {\n                console?.print(cliResult, NORMAL_OUTPUT)\n                return\n            }\n\n            FileRunService.retryRun(project, file)?.let {\n                console?.print(it, NORMAL_OUTPUT)\n                return\n            }\n\n            console?.print(\"RunCode: No run service found for file: $file\\n\", ERROR_OUTPUT)\n            return\n        }\n\n        console?.print(\"Running code...\\n\", SYSTEM_OUTPUT)\n        val output = fileRunService.runFileAsync(project, file, psiFile)\n        console?.print(output ?: \"\", NORMAL_OUTPUT)\n    }\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/middleware/builtin/SaveFileProcessor.kt",
    "content": "package com.phodal.shirecore.middleware.builtin\n\nimport com.intellij.execution.ui.ConsoleView\nimport com.intellij.execution.ui.ConsoleViewContentType\nimport com.intellij.openapi.Disposable\nimport com.intellij.openapi.application.ApplicationManager\nimport com.intellij.openapi.application.WriteAction\nimport com.intellij.openapi.fileTypes.PlainTextLanguage\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.project.guessProjectDir\nimport com.intellij.openapi.util.Disposer\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.phodal.shirecore.utils.markdown.CodeFence\nimport com.phodal.shirecore.ShireConstants\nimport com.phodal.shirecore.middleware.post.PostProcessorType\nimport com.phodal.shirecore.middleware.post.PostProcessorContext\nimport com.phodal.shirecore.middleware.post.PostProcessor\n\nclass SaveFileProcessor : PostProcessor, Disposable {\n    override val processorName: String = PostProcessorType.SaveFile.handleName\n    override val description: String = \"`saveFile` will save the content / llm response to the file\"\n\n    override fun isApplicable(context: PostProcessorContext): Boolean = true\n\n    override fun execute(\n        project: Project,\n        context: PostProcessorContext,\n        console: ConsoleView?,\n        args: List<Any>,\n    ): String {\n        val fileName: String\n        val ext = getFileExt(context)\n        if (args.isNotEmpty()) {\n            fileName = getValidFilePath(args[0].toString(), ext)\n            handleForProjectFile(project, fileName, context, console)\n        } else {\n            fileName = \"${System.currentTimeMillis()}.$ext\"\n            handleForTempFile(project, fileName, context, console)\n        }\n\n        return fileName\n    }\n\n    private fun getFileExt(context: PostProcessorContext): String {\n        val language = context.genTargetLanguage ?: PlainTextLanguage.INSTANCE\n        return context.genTargetExtension ?: language?.associatedFileType?.defaultExtension ?: \"txt\"\n    }\n\n    private fun handleForTempFile(\n        project: Project,\n        fileName: String,\n        context: PostProcessorContext,\n        console: ConsoleView?,\n    ) {\n        ApplicationManager.getApplication().invokeAndWait {\n            WriteAction.compute<VirtualFile, Throwable> {\n                val outputDir = ShireConstants.outputDir(project)\n\n                val outputFile = outputDir?.createChildData(this, fileName)\n                    ?: throw IllegalStateException(\"Failed to save file\")\n\n                val content = getContent(context)\n                outputFile.setBinaryContent(content?.toByteArray() ?: ByteArray(0))\n                context.pipeData[\"output\"] = outputFile\n\n                project.guessProjectDir()?.refresh(true, true)\n\n                console?.print(\"Saved to ${outputFile.canonicalPath}\\n\", ConsoleViewContentType.SYSTEM_OUTPUT)\n                outputFile\n            }\n        }\n    }\n\n    private fun handleForProjectFile(\n        project: Project,\n        filepath: String,\n        context: PostProcessorContext,\n        console: ConsoleView?\n    ) {\n        var fileName = filepath\n        ApplicationManager.getApplication().invokeAndWait {\n            WriteAction.compute<VirtualFile, Throwable> {\n                val projectDir = project.guessProjectDir()\n                // if filename starts with / means it's an absolute path, we need to get relative path\n                if (fileName.startsWith(\"/\")) {\n                    val projectPath = projectDir?.canonicalPath\n                    if (projectPath != null) {\n                        fileName = fileName.replace(projectPath, \"\")\n                    }\n                }\n\n                // filename may include path, like: `src/main/java/HelloWorld.java`, we need to split it\n                // first check if the file is already in the project\n                var outputFile = projectDir?.findFileByRelativePath(fileName)\n                if (outputFile == null) {\n                    outputFile = createFile(fileName, projectDir)\n                }\n\n                val content = getContent(context)\n                outputFile!!.setBinaryContent(content?.toByteArray() ?: ByteArray(0))\n                context.pipeData[\"output\"] = outputFile\n\n                projectDir?.refresh(true, true)\n\n                console?.print(\"Saved to ${outputFile.canonicalPath}\", ConsoleViewContentType.SYSTEM_OUTPUT)\n                outputFile\n            }\n        }\n    }\n\n    private fun getContent(context: PostProcessorContext): String? {\n        val outputData = context.pipeData[\"output\"]\n\n        if (outputData is String && outputData.isNotEmpty()) {\n            return outputData\n        }\n\n        if (context.lastTaskOutput?.isNotEmpty() == true) {\n            return context.lastTaskOutput\n        }\n\n        return context.genText\n    }\n\n    private fun createFile(\n        fileName: String,\n        projectDir: VirtualFile?,\n    ): VirtualFile {\n        val path = fileName.split(\"/\").dropLast(1)\n        val name = fileName.split(\"/\").last()\n\n        var parentDir = projectDir\n\n        // create directories if not exist\n        for (dir in path) {\n            parentDir = parentDir?.findChild(dir) ?: parentDir?.createChildDirectory(this, dir)\n        }\n\n        val outputFile = parentDir?.createChildData(this, name)\n            ?: throw IllegalStateException(\"Failed to save file\")\n\n        return outputFile\n    }\n\n    override fun dispose() {\n        Disposer.dispose(this)\n    }\n}\n\n/**\n * 根据给定的文件路径和扩展名，返回一个有效的文件路径。\n *\n * <pre>\n *    {@code\n *        String validPath = getValidFilePath(\"example.txt\", \"txt\");\n *        // validPath = \"example.txt\"\n *\n *        String validPath2 = getValidFilePath(\"\", \"txt\");\n *        // validPath2 = \"1633024800000.txt\" (当前时间戳)\n *    }\n *    </pre>\n *\n * @param filePath 文件路径，可以为空或包含特殊字符\n * @param ext 文件扩展名，用于在文件路径无效时生成默认文件名\n * @return 有效的文件路径，如果输入路径无效，则返回基于时间戳的默认文件名\n */\nfun getValidFilePath(filePath: String, ext: String): String {\n    val pathRegex = \"\"\"^([a-zA-Z]:\\\\|\\\\\\\\|/|)([a-zA-Z0-9_\\-\\\\/.]+)$\"\"\".toRegex()\n\n    if (filePath.isBlank()) {\n        return \"${System.currentTimeMillis()}.$ext\"\n    }\n\n    return if (pathRegex.matches(filePath)) {\n        filePath\n    } else if (filePath.contains(\"`\") && filePath.contains(\"```\")) {\n        CodeFence.parse(filePath).text\n    } else {\n        \"${System.currentTimeMillis()}.$ext\"\n    }\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/middleware/builtin/ShowWebviewProcessor.kt",
    "content": "package com.phodal.shirecore.middleware.builtin\n\nimport com.intellij.execution.ui.ConsoleView\nimport com.intellij.ide.DataManager\nimport com.intellij.openapi.application.runInEdt\nimport com.intellij.openapi.diagnostic.logger\nimport com.intellij.openapi.editor.colors.EditorFontType\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.ui.popup.JBPopup\nimport com.intellij.openapi.ui.popup.JBPopupFactory\nimport com.intellij.openapi.ui.popup.JBPopupListener\nimport com.intellij.openapi.ui.popup.LightweightWindowEvent\nimport com.intellij.ui.components.JBTextArea\nimport com.intellij.ui.dsl.builder.*\nimport com.phodal.shirecore.ShireCoroutineScope\nimport com.phodal.shirecore.llm.LlmProvider\nimport com.phodal.shirecore.utils.markdown.CodeFence\nimport com.phodal.shirecore.middleware.post.PostProcessor\nimport com.phodal.shirecore.middleware.post.PostProcessorContext\nimport com.phodal.shirecore.middleware.post.PostProcessorType\nimport com.phodal.shirecore.middleware.builtin.ui.WebViewWindow\nimport com.phodal.shirecore.runner.console.cancelHandler\nimport kotlinx.coroutines.flow.cancellable\nimport kotlinx.coroutines.launch\nimport kotlinx.coroutines.runBlocking\nimport java.awt.event.KeyAdapter\nimport java.awt.event.KeyEvent\n\nclass ShowWebviewProcessor : PostProcessor {\n    override val processorName: String get() = PostProcessorType.ShowWebview.handleName\n    override val description: String get() = \"`showWebview` will show the webview for the content if it's html\"\n\n    private var continueMessage: String = \"\"\n    private var webview: WebViewWindow? = null\n\n    override fun isApplicable(context: PostProcessorContext): Boolean {\n        return true\n    }\n\n    override fun execute(project: Project, context: PostProcessorContext, console: ConsoleView?, args: List<Any>): Any {\n        var html: String? = (context.pipeData[\"output\"])?.toString() ?: context.genText\n\n        val dataContext = DataManager.getInstance().dataContextFromFocusAsync.blockingGet(10000)\n            ?: throw IllegalStateException(\"No data context\")\n\n        var keyAdapter: WebviewKeyAdapter? = null\n\n        runInEdt {\n            webview = WebViewWindow()\n            val component = webview!!.apply { loadHtml(html ?: \"\") }.component\n\n            val panel = panel {\n                row {\n                    cell(component)\n                }\n                row {\n                    textArea()\n                        .align(Align.FILL)\n                        .bindText(::continueMessage)\n                        .applyToComponent {\n                            font = EditorFontType.getGlobalPlainFont()\n                            keyAdapter = WebviewKeyAdapter(project, this, webview, html) {\n                                html = it\n                                continueMessage = \"\"\n                            }\n\n                            addKeyListener(keyAdapter)\n                        }\n                }\n            }\n\n            val popup = JBPopupFactory.getInstance()\n                .createComponentPopupBuilder(panel, null)\n                .setResizable(true)\n                .setMovable(true)\n                .setTitle(\"Preview\")\n                .setFocusable(true)\n                .setRequestFocus(true)\n                .createPopup()\n                .also { keyAdapter?.popup = it }\n\n            popup.showInBestPositionFor(dataContext)\n        }\n\n        return \"\"\n    }\n}\n\nclass WebviewKeyAdapter(\n    val project: Project, val textarea: JBTextArea, val currentWebview: WebViewWindow?,\n    val html: String?,\n    val onEnter: (String) -> Unit,\n) : KeyAdapter() {\n    var popup: JBPopup? = null\n    override fun keyPressed(e: KeyEvent) {\n        if (e.keyCode == KeyEvent.VK_ENTER) {\n            textarea.isEditable = false\n            currentWebview?.loadHtml(\"Processing...\")\n            var result = \"\"\n\n            ShireCoroutineScope.scope(project).launch {\n                val flow = LlmProvider.provider(project)\n                    ?.stream(\n                        \"According user input to modify code, return new code.\" +\n                                \" Use input: ${textarea.text}\\nCode: \\n```html\\n$html\\n```\" +\n                                \"\\n\" +\n                                \"Return new code: \",\n                        \"\",\n                        false\n                    )!!\n\n                var popupListener: JBPopupListener? = null\n\n                runBlocking {\n                    flow.cancelHandler {\n                        if (popup?.isDisposed == true) it.invoke(\"This popup has been disposed\")\n                        else popup?.addListener(object : JBPopupListener {\n                            override fun onClosed(event: LightweightWindowEvent) {\n                                it.invoke(\"This popup has been closed\")\n                            }\n                        }.also { popupListener = it })\n                    }.cancellable().collect {\n                        result += it\n                    }\n\n                    popupListener?.run { popup?.removeListener(this) }\n                    textarea.isEditable = true\n                }\n\n                val newHtml = CodeFence.parse(result).text\n                logger<ShowWebviewProcessor>().info(\"Result: $result\")\n                runInEdt {\n                    currentWebview?.loadHtml(newHtml)\n                }\n\n                onEnter(newHtml)\n                textarea.text = \"\"\n            }\n        }\n    }\n}\n\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/middleware/builtin/TimeMetricProcessor.kt",
    "content": "package com.phodal.shirecore.middleware.builtin\n\nimport com.intellij.execution.ui.ConsoleView\nimport com.intellij.openapi.project.Project\nimport com.phodal.shirecore.middleware.post.PostProcessorType\nimport com.phodal.shirecore.middleware.post.PostProcessorContext\nimport com.phodal.shirecore.middleware.post.PostProcessor\n\nclass TimeMetricProcessor : PostProcessor {\n    private var startTime: Long? = null\n\n    override val processorName: String = PostProcessorType.TimeMetric.handleName\n    override val description: String = \"`timeMetric` will calculate the time metric\"\n\n    override fun isApplicable(context: PostProcessorContext): Boolean = true\n\n    override fun setup(context: PostProcessorContext): String {\n        startTime = System.currentTimeMillis()\n        return startTime.toString()\n    }\n\n    override fun execute(project: Project, context: PostProcessorContext, console: ConsoleView?, args: List<Any>): String {\n        val endTime = System.currentTimeMillis()\n        return (endTime - startTime!!).toString()\n    }\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/middleware/builtin/UpdateEditorTextProcessor.kt",
    "content": "package com.phodal.shirecore.middleware.builtin\n\nimport com.intellij.execution.ui.ConsoleView\nimport com.intellij.openapi.command.WriteCommandAction\nimport com.intellij.openapi.diagnostic.logger\nimport com.intellij.openapi.project.Project\nimport com.phodal.shirecore.middleware.post.PostProcessorType\nimport com.phodal.shirecore.middleware.post.PostProcessorContext\nimport com.phodal.shirecore.middleware.post.PostProcessor\nimport com.phodal.shirecore.workerThread\nimport kotlinx.coroutines.CoroutineScope\nimport kotlinx.coroutines.launch\n\nclass UpdateEditorTextProcessor : PostProcessor {\n    override val processorName: String = PostProcessorType.UpdateEditorText.handleName\n    override val description: String = \"`updateEditorText` will update the editor text from llm response\"\n\n    override fun isApplicable(context: PostProcessorContext): Boolean = true\n\n    override fun execute(\n        project: Project,\n        context: PostProcessorContext,\n        console: ConsoleView?,\n        args: List<Any>,\n    ): Any {\n        val editor = context.editor ?: return \"\"\n        val newText = if(args.isNotEmpty()) {\n            args[0]\n        } else {\n            context.pipeData[\"output\"]\n        }\n\n        if (newText == null) {\n            logger<UpdateEditorTextProcessor>().error(\"no new code to update, pipeData: ${context.pipeData}\")\n            return \"\"\n        }\n\n        CoroutineScope(workerThread).launch {\n            WriteCommandAction.runWriteCommandAction(project) {\n                editor.document.setText(newText.toString())\n            }\n        }\n\n        return newText\n    }\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/middleware/builtin/VerifyCodeProcessor.kt",
    "content": "package com.phodal.shirecore.middleware.builtin\n\nimport com.intellij.execution.ui.ConsoleView\nimport com.intellij.execution.ui.ConsoleViewContentType\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.intellij.psi.PsiManager\nimport com.phodal.shirecore.middleware.post.PostProcessor\nimport com.phodal.shirecore.middleware.post.PostProcessorContext\nimport com.phodal.shirecore.middleware.post.PostProcessorType\nimport com.phodal.shirecore.psi.PsiErrorCollector\n\nclass VerifyCodeProcessor : PostProcessor {\n    override val processorName: String = PostProcessorType.VerifyCode.handleName\n    override val description: String = \"`verifyCode` will verify the code syntax and return the errors\"\n\n    override fun isApplicable(context: PostProcessorContext): Boolean = true\n\n    override fun execute(\n        project: Project,\n        context: PostProcessorContext,\n        console: ConsoleView?,\n        args: List<Any>,\n    ): String {\n        val code = context.pipeData[\"output\"]\n        if (code !is VirtualFile) {\n            console?.print(\"No code to verify\\n\", ConsoleViewContentType.ERROR_OUTPUT)\n            return \"\"\n        }\n\n        val psiFile = PsiManager.getInstance(project).findFile(code)\n        if (psiFile == null) {\n            console?.print(\"No code to verify\\n\", ConsoleViewContentType.ERROR_OUTPUT)\n            return \"\"\n        }\n\n        if (!psiFile.isValid) {\n            console?.print(\"No code to verify\\n\", ConsoleViewContentType.ERROR_OUTPUT)\n            return \"\"\n        }\n\n        val errors: List<String> = PsiErrorCollector.collectSyntaxError(psiFile, project)\n\n        if (errors.isNotEmpty()) {\n            console?.print(\"Syntax errors found:\\n${errors.joinToString(\"\\n\")}\\n\", ConsoleViewContentType.ERROR_OUTPUT)\n        } else {\n            console?.print(\"No syntax errors found\\n\", ConsoleViewContentType.SYSTEM_OUTPUT)\n        }\n\n        return errors.joinToString(\"\\n\")\n    }\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/middleware/builtin/ui/ActionableWebView.kt",
    "content": "package com.phodal.shirecore.middleware.builtin.ui\n\nimport com.intellij.ui.jcef.JBCefBrowser\nimport com.intellij.ui.jcef.JBCefJSQuery\nimport java.util.concurrent.CompletableFuture\n\nclass ActionableWebView(private val browser: JBCefBrowser) {\n    private var future: CompletableFuture<Void> = CompletableFuture.completedFuture(null)\n    private var onErrorAction: (() -> Unit)? = null\n\n    fun open(url: String): ActionableWebView {\n        future = future.thenRun {\n            browser.loadURL(url)\n        }\n        return this\n    }\n\n    fun waitFor(selector: String, timeout: Long): ActionableWebView {\n        future = future.thenCompose {\n            val result = CompletableFuture<Void>()\n            val jsQuery = JBCefJSQuery.create(browser) // 1\n\n            jsQuery.addHandler { response ->\n                if (response == \"success\") {\n                    result.complete(null)\n                } else {\n                    result.completeExceptionally(Exception(\"Timeout waiting for $selector\"))\n                }\n                null\n            }\n\n            val script = \"\"\"\n                (function() {\n                    var interval = setInterval(function() {\n                        if (document.querySelector('$selector') !== null) {\n                            clearInterval(interval);\n                            window['_java_callback_${jsQuery}']('success');\n                        }\n                    }, 100);\n                    setTimeout(function() {\n                        clearInterval(interval);\n                        window['_java_callback_${jsQuery}']('error');\n                    }, $timeout);\n                })();\n            \"\"\"\n\n            val wrappedScript = \"\"\"\n                window['_java_callback_${jsQuery}'] = function(response) {\n                    ${jsQuery.inject(\"response\")}\n                };\n                $script\n            \"\"\"\n\n            browser.cefBrowser.executeJavaScript(wrappedScript, browser.cefBrowser.url, 0)\n\n            result\n        }.exceptionally { throwable ->\n            onErrorAction?.invoke()\n            null\n        }\n        return this\n    }\n\n    fun input(selector: String, text: String): ActionableWebView {\n        future = future.thenRun {\n            val script = \"document.querySelector('$selector').value = '$text';\"\n            browser.cefBrowser.executeJavaScript(script, browser.cefBrowser.url, 0)\n        }\n        return this\n    }\n\n    fun click(selector: String): ActionableWebView {\n        future = future.thenRun {\n            val script = \"document.querySelector('$selector').click();\"\n            browser.cefBrowser.executeJavaScript(script, browser.cefBrowser.url, 0)\n        }\n        return this\n    }\n\n    fun onError(action: () -> Unit): ActionableWebView {\n        onErrorAction = action\n        return this\n    }\n\n    companion object {\n        fun create(browser: JBCefBrowser): ActionableWebView {\n//            // Ensure JCEF is initialized\n//            JBCefApp.getInstance()\n//\n//            // Create the browser component\n//            val browser = JBCefBrowser()\n//\n//            // Add the browser to the tool window content\n//            val contentFactory = ContentFactory.getInstance()\n//            val content = contentFactory.createContent(browser.component, \"\", false)\n//\n//            // Start the action chain as per your DSL\n            return ActionableWebView(browser)\n        }\n    }\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/middleware/builtin/ui/WebViewWindow.kt",
    "content": "package com.phodal.shirecore.middleware.builtin.ui\n\nimport com.intellij.ui.JBColor\nimport com.intellij.ui.jcef.JBCefBrowser\nimport javax.swing.JComponent\n\n/**\n * WebViewWindow is a class that provides a custom webview functionality. It allows developers to\n * create a custom webview within their IntelliJ-based applications. This class is designed to be\n * used in conjunction with the JCEF (JetBrains CEF) plugin, which is a wrapper around the Chromium Embedded Framework.\n *\n */\nclass WebViewWindow {\n    // official doc: https://plugins.jetbrains.com/docs/intellij/jcef.html#executing-javascript\n    private val browser: JBCefBrowser\n\n    init {\n        browser = try {\n            JBCefBrowser.createBuilder()\n                .build()\n        } catch (e: Exception) {\n            JBCefBrowser()\n        }\n\n        browser.component.background = JBColor.WHITE\n    }\n\n    val component: JComponent = browser.component\n\n    fun loadHtml(html: String) {\n        browser.loadHTML(html)\n    }\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/middleware/post/PostProcessor.kt",
    "content": "package com.phodal.shirecore.middleware.post\n\nimport com.intellij.execution.ui.ConsoleView\nimport com.intellij.openapi.extensions.ExtensionPointName\nimport com.intellij.openapi.project.Project\n\ndata class LifecycleProcessorSignature(\n    val funcName: String,\n    val args: List<Any>,\n)\n\n/**\n * The PostProcessor interface defines a contract for post-processing tasks. Implementations of this\n * interface are expected to handle specific post-processing operations, which are identified by a\n * unique name.\n *\n * The interface provides methods to check the applicability of a given context for handling post codes,\n * to set up any necessary initial tasks, to execute the post-processing function, and to perform clean\n * up tasks after execution.\n *\n *\n * @property processorName the unique name of the post-processing function, the built-in functions are defined in [PostProcessorType]\n */\ninterface PostProcessor {\n    val processorName: String\n    val description: String\n\n    /**\n     * This function checks if a given context is applicable for handling post codes.\n     *\n     * @param context the PostCodeHandleContext to be checked for applicability\n     * @return true if the context is applicable for handling post codes, false otherwise\n     */\n    fun isApplicable(context: PostProcessorContext): Boolean\n\n    /**\n     * Some init tasks, like metric for time, etc.\n     */\n    fun setup(context: PostProcessorContext): Any {\n        return \"\"\n    }\n\n    /**\n     * Executes a function with the given project, context, and generated text.\n     *\n     * @param project the project to execute the function on\n     * @param context the context in which the function is executed\n     * @param genText the generated text to be used in the execution\n     * @return a string result of the execution\n     */\n    fun execute(project: Project, context: PostProcessorContext, console: ConsoleView?, args: List<Any>): Any\n\n    /**\n     * Clean up tasks, like metric for time, etc.\n     */\n    fun finish(context: PostProcessorContext): Any? {\n        return \"\"\n    }\n\n    companion object {\n        private val EP_NAME: ExtensionPointName<PostProcessor> =\n            ExtensionPointName.create(\"com.phodal.shirePostProcessor\")\n\n        fun handler(handleName: String): PostProcessor? {\n            return EP_NAME.extensionList.find {\n                it.processorName == handleName\n            }\n        }\n\n        fun allNames(): List<String> {\n            return EP_NAME.extensionList.map { it.processorName }\n        }\n\n        fun all(): List<PostProcessor> {\n            return EP_NAME.extensionList\n        }\n    }\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/middleware/post/PostProcessorContext.kt",
    "content": "package com.phodal.shirecore.middleware.post\n\nimport com.intellij.lang.Language\nimport com.intellij.openapi.editor.Editor\nimport com.intellij.openapi.util.Key\nimport com.intellij.openapi.util.TextRange\nimport com.intellij.openapi.util.UserDataHolderBase\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.PsiFile\n\nclass PostProcessorContext(\n    /**\n     * Convert code to file\n     */\n    var currentFile: PsiFile? = null,\n\n    /**\n     * The language of the code to be handled, which will parse from the GenText when parse code\n     */\n    var currentLanguage: Language? = null,\n\n    /**\n     * Target Language\n     */\n    var genTargetLanguage: Language? = null,\n\n    var genTargetExtension: String? = null,\n\n    /**\n     * The element to be handled, which will be load from current editor when parse code\n     */\n    var genPsiElement: PsiElement? = null,\n\n    /**\n     * The generated text to be handled\n     */\n    var genText: String? = null,\n\n    /**\n     * The data to be passed to the post-processor\n     */\n    val pipeData: MutableMap<String, Any> = mutableMapOf(),\n\n    /**\n     * post text range\n     */\n    val modifiedTextRange: TextRange? = null,\n\n    /**\n     * current editor for modify\n     */\n    val editor: Editor? = null,\n\n    var lastTaskOutput: String? = null,\n\n    var compiledVariables: Map<String, Any?> = mapOf(),\n\n    val llmModelName: String? = null,\n) {\n    companion object {\n        private val DATA_KEY: Key<PostProcessorContext> = Key.create(PostProcessorContext::class.java.name)\n        private val userDataHolderBase = UserDataHolderBase()\n\n        // todo: refactor to GlobalVariableContext\n        fun updateContextAndVariables(context: PostProcessorContext) {\n            context.compiledVariables = dynamicUpdateVariables(context.compiledVariables)\n            userDataHolderBase.putUserData(DATA_KEY, context)\n        }\n\n        private fun dynamicUpdateVariables(variables: Map<String, Any?>): MutableMap<String, Any?> {\n            val userData = userDataHolderBase.getUserData(DATA_KEY)\n            val oldVariables: MutableMap<String, Any?> =\n                userData?.compiledVariables?.toMutableMap() ?: mutableMapOf()\n\n            variables.forEach {\n                if (it.value.toString().startsWith(\"$\")) {\n                    oldVariables.remove(it.key)\n                } else if (it.value != null && it.value.toString().isNotEmpty()) {\n                    oldVariables[it.key] = it.value\n                }\n            }\n\n            return oldVariables\n        }\n\n        fun getData(): PostProcessorContext? {\n            return userDataHolderBase.getUserData(DATA_KEY)\n        }\n\n        fun updateOutput(output: Any?) {\n            val context = getData()\n            if (context != null) {\n                context.lastTaskOutput = output.toString()\n                updateContextAndVariables(context)\n            }\n\n            val compiledVariables = context?.compiledVariables?.toMutableMap()\n            compiledVariables?.set(\"output\", output)\n\n            if (context != null) {\n                context.compiledVariables = compiledVariables ?: mapOf()\n                updateContextAndVariables(context)\n            }\n        }\n\n        fun updateRunConfigVariables(variables: Map<String, String>) {\n            val context = getData()\n            val compiledVariables = context?.compiledVariables?.toMutableMap()\n            compiledVariables?.putAll(variables)\n\n            if (context != null) {\n                context.compiledVariables = compiledVariables ?: mapOf()\n                updateContextAndVariables(context)\n            }\n        }\n    }\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/middleware/post/PostProcessorType.kt",
    "content": "package com.phodal.shirecore.middleware.post\n\n/**\n * Post middleware actions, like\n * Logging, Metrics, CodeVerify, RunCode, ParseCode etc.\n *\n */\nenum class PostProcessorType(var handleName: String) {\n    /**\n     * Metric time spent on the action.\n     */\n    TimeMetric(\"timeMetric\"),\n\n    /**\n     * Check has code error or PSI issue.\n     */\n    VerifyCode(\"verifyCode\"),\n\n    /**\n     * Run generate text code\n     */\n    RunCode(\"runCode\"),\n\n    /**\n     * Parse text to code blocks\n     */\n    ParseCode(\"parseCode\"),\n\n    /**\n     * Save file to the disk\n     */\n    SaveFile(\"saveFile\"),\n\n    /**\n     * Open file in the editor\n     */\n    OpenFile(\"openFile\"),\n\n    /**\n     * Insert code to the editor by current cursor position.\n     */\n    InsertCode(\"insertCode\"),\n\n    /**\n     * Format code\n     */\n    FormatCode(\"formatCode\"),\n\n    /**\n     * Parse comment to the comment block\n     */\n    ParseComment(\"parseComment\"),\n\n    /**\n     * Insert new line\n     */\n    InsertNewline(\"insertNewline\"),\n\n    /**\n     * Append text to the file\n     */\n    Append(\"append\"),\n\n    /**\n     * Patch content to the file\n     */\n    Patch(\"patch\"),\n\n    /**\n     * Diff\n     */\n    Diff(\"diff\"),\n\n    UpdateEditorText(\"updateEditorText\"),\n\n    /**\n     * openWebpage\n     */\n    OpenWebpage(\"openWebpage\"),\n\n    /**\n     *  showWebView\n     */\n    ShowWebview(\"showWebView\"),\n    ;\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/middleware/select/DefaultPsiElementStrategy.kt",
    "content": "package com.phodal.shirecore.middleware.select\n\nimport com.intellij.lang.injection.InjectedLanguageManager\nimport com.intellij.openapi.editor.Editor\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.util.TextRange\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.PsiFile\nimport com.intellij.psi.PsiNameIdentifierOwner\nimport com.intellij.psi.util.PsiTreeUtil\nimport com.intellij.psi.util.PsiUtilBase\n\nopen class DefaultPsiElementStrategy : PsiElementStrategy {\n    /**\n     * Returns the PsiElement to explain in the given project and editor.\n     *\n     * @param project the project in which the element resides (nullable)\n     * @param editor the editor in which the element is located (nullable)\n     * @return the PsiElement to explain, or null if either the project or editor is null, or if no element is found\n     */\n    override fun getElementToAction(project: Project?, editor: Editor?): PsiElement? {\n        if (project == null || editor == null) return null\n\n        val element = PsiUtilBase.getElementAtCaret(editor) ?: return null\n        val psiFile = element.containingFile\n\n        val selectionModel = editor.selectionModel\n        if (selectionModel.hasSelection()) {\n            val startOffset = selectionModel.selectionStart\n            val endOffset = selectionModel.selectionEnd\n\n            val startElement = PsiUtilBase.getElementAtOffset(psiFile, startOffset)\n            val endElement = PsiUtilBase.getElementAtOffset(psiFile, endOffset)\n\n            if (startElement == endElement) return startElement\n        }\n\n        if (InjectedLanguageManager.getInstance(project).isInjectedFragment(psiFile)) return psiFile\n\n        val identifierOwner = PsiTreeUtil.getParentOfType(element, PsiNameIdentifierOwner::class.java)\n        return identifierOwner ?: element\n    }\n\n    /**\n     * This method calculates the frontend element to explain based on the given project, PsiFile, and TextRange.\n     *\n     * @param project the project to which the PsiFile belongs\n     * @param psiFile the PsiFile in which the frontend element is located\n     * @param range the TextRange specifying the range of the frontend element\n     * @return the PsiElement representing the frontend element to explain, or null if the project is null, or the PsiFile is invalid\n     */\n    override fun getElementToAction(project: Project?, psiFile: PsiFile, range: TextRange): PsiElement? {\n        if (project == null || !psiFile.isValid) return null\n\n        val element = PsiUtilBase.getElementAtOffset(psiFile, range.startOffset)\n        if (InjectedLanguageManager.getInstance(project).isInjectedFragment(psiFile)) {\n            return psiFile\n        }\n\n        val injected = InjectedLanguageManager.getInstance(project).findInjectedElementAt(psiFile, range.startOffset)\n        if (injected != null) {\n            return injected.containingFile\n        }\n\n        val psiElement: PsiElement? = PsiTreeUtil.getParentOfType(element, PsiNameIdentifierOwner::class.java)\n        return psiElement ?: element\n    }\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/middleware/select/PsiElementStrategy.kt",
    "content": "package com.phodal.shirecore.middleware.select\n\nimport com.intellij.openapi.editor.Editor\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.util.TextRange\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.PsiFile\n\ninterface PsiElementStrategy {\n    fun getElementToAction(project: Project?, editor: Editor?): PsiElement?\n    fun getElementToAction(project: Project?, psiFile: PsiFile, range: TextRange): PsiElement?\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/middleware/select/SelectElementStrategy.kt",
    "content": "package com.phodal.shirecore.middleware.select\n\nimport com.intellij.openapi.application.runInEdt\nimport com.intellij.openapi.editor.Editor\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.util.NlsSafe\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.PsiWhiteSpace\n\nsealed class SelectedEntry(open val element: Any) {\n    class Text(override val element: String) : SelectedEntry(element)\n    class Entry(override val element: PsiElement) : SelectedEntry(element)\n}\n\nsealed class SelectElementStrategy {\n    /**\n     * Selection element\n     */\n    abstract fun select(project: Project, editor: Editor?): Any?\n\n    abstract fun getSelectedElement(project: Project, editor: Editor?): SelectedEntry?\n\n    /**\n     * Auto select parent block element, like function, class, etc.\n     */\n    object Blocked : SelectElementStrategy() {\n        override fun select(project: Project, editor: Editor?): PsiElement? {\n            if (editor == null) {\n                return null\n            }\n\n            val elementToAction = DefaultPsiElementStrategy().getElementToAction(project, editor) ?: return null\n\n            runInEdt {\n                selectElement(elementToAction, editor)\n            }\n\n            return elementToAction\n        }\n\n        /**\n         * This function selects the specified PsiElement in the editor by setting the selection range from the start offset to the end offset of the element.\n         *\n         * @param elementToExplain the PsiElement to be selected in the editor\n         * @param editor the Editor in which the selection is to be made\n         */\n        private fun selectElement(elementToExplain: PsiElement, editor: Editor) {\n            val startOffset = elementToExplain.textRange.startOffset\n            val endOffset = elementToExplain.textRange.endOffset\n\n            editor.selectionModel.setSelection(startOffset, endOffset)\n        }\n\n        override fun getSelectedElement(project: Project, editor: Editor?): SelectedEntry? {\n            select(project, editor)?.let {\n                return SelectedEntry.Entry(it)\n            }\n\n            return null\n        }\n    }\n\n    object SelectedText : SelectElementStrategy() {\n        override fun select(project: Project, editor: Editor?): @NlsSafe String? {\n            return editor?.selectionModel?.selectedText ?: \"\"\n        }\n\n        override fun getSelectedElement(project: Project, editor: Editor?): SelectedEntry? {\n            val selectedText = select(project, editor) ?: return null\n            return SelectedEntry.Text(selectedText)\n        }\n    }\n\n    object Default : SelectElementStrategy() {\n        override fun select(project: Project, editor: Editor?): PsiElement? {\n            val selectionModel = editor?.selectionModel ?: return null\n            if (!selectionModel.hasSelection()) {\n                return Blocked.select(project, editor)\n            }\n\n            return null\n        }\n\n        override fun getSelectedElement(project: Project, editor: Editor?): SelectedEntry? {\n            return Blocked.getSelectedElement(project, editor)\n        }\n    }\n\n    object SelectAll : SelectElementStrategy() {\n        override fun select(project: Project, editor: Editor?): String? {\n            val selectionModel = editor?.selectionModel ?: return null\n\n            runInEdt {\n                selectionModel.setSelection(0, editor.document.textLength)\n            }\n\n            return editor.document.text\n        }\n\n        override fun getSelectedElement(project: Project, editor: Editor?): SelectedEntry {\n            return SelectedEntry.Text(editor?.document?.text ?: \"\")\n        }\n    }\n\n    companion object {\n        fun fromString(strategy: String): SelectElementStrategy? {\n            return when (strategy.lowercase()) {\n                \"block\" -> Blocked\n                \"select\" -> SelectedText\n                \"selectAll\" -> SelectAll\n                else -> null\n            }\n        }\n\n        fun all(): List<String> {\n            return SelectElementStrategy::class.sealedSubclasses.map { it.simpleName!! }\n        }\n\n        fun getElementAtOffset(psiFile: PsiElement, offset: Int): PsiElement? {\n            var element = psiFile.findElementAt(offset) ?: return null\n\n            if (element is PsiWhiteSpace) {\n                element = element.getParent()\n            }\n\n            return element\n        }\n\n        fun resolvePsiElement(myProject: Project, editor: Editor): PsiElement? {\n            val elementToAction = DefaultPsiElementStrategy().getElementToAction(myProject, editor)\n\n            if (elementToAction != null) {\n                return elementToAction\n            }\n\n            return null\n        }\n\n    }\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/project/ProjectFileUtil.kt",
    "content": "// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage com.phodal.shirecore.project\n\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.roots.ProjectFileIndex\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.intellij.psi.PsiManager\n\n// https://github.com/JetBrains/intellij-community/blob/master/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/ProjectFileIndexImpl.java#L32\nfun isInProject(virtualFile: VirtualFile, project: Project): Boolean {\n    // new version has better method\n    if (virtualFile.path.startsWith(project.basePath ?: return false)) {\n        return true\n    }\n\n    PsiManager.getInstance(project).findFile(virtualFile)?.let {\n        return true\n    }\n\n    return false\n}\n\nfun Project.isInProject(virtualFile: VirtualFile): Boolean {\n    return isInProject(virtualFile, this) || ProjectFileIndex.getInstance(this).isInLibrary(virtualFile)\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/provider/TestingService.kt",
    "content": "package com.phodal.shirecore.provider\n\nimport com.intellij.lang.LanguageExtension\nimport com.intellij.openapi.diagnostic.logger\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.PsiFile\nimport com.phodal.shirecore.provider.codemodel.model.ClassStructure\nimport com.phodal.shirecore.provider.shire.FileRunService\nimport com.phodal.shirecore.psi.PsiErrorCollector\nimport com.phodal.shirecore.variable.toolchain.unittest.AutoTestingPromptContext\n\n/**\n * The `TestService` class is an abstract class that provides a base implementation for writing tests in different programming languages.\n * It extends the `LazyExtensionInstance` class, which allows lazy initialization of the `TestService` instances.\n *\n * @property language The programming language for which the test service is applicable.\n * @property implementationClass The fully qualified name of the implementation class.\n *\n * @constructor Creates a new instance of the `TestService` class.\n */\nabstract class TestingService : FileRunService {\n    abstract fun isApplicable(element: PsiElement): Boolean\n\n    /**\n     * Finds or creates a test file for the given source file, project, and element.\n     *\n     * @param sourceFile The source file for which to find or create a test file.\n     * @param project The project in which the test file should be created.\n     * @param psiElement The element for which the test file should be created.\n     * @return The TestFileContext object representing the found or created test file, or null if it could not be found or created.\n     *\n     * This method is responsible for locating an existing test file associated with the given source file and element,\n     * or creating a new test file if one does not already exist. The test file is typically used for unit testing purposes.\n     * The source file, project, and element parameters are used to determine the context in which the test file should be created.\n     * If a test file is found or created successfully, a TestFileContext object representing the test file is returned.\n     * If a test file cannot be found or created, null is returned.\n     */\n    abstract fun findOrCreateTestFile(\n        sourceFile: PsiFile,\n        project: Project,\n        psiElement: PsiElement,\n    ): AutoTestingPromptContext?\n\n    /**\n     * Looks up the relevant classes in the project for the given element.\n     *\n     * @param project the project in which to perform the lookup\n     * @param element the element for which to find the relevant classes\n     * @return a list of ClassStructure objects representing the relevant classes found in the project\n     */\n    abstract fun lookupRelevantClass(project: Project, element: PsiElement): List<ClassStructure>\n\n    /**\n     * This method is used to collect syntax errors from a given project and write them to an output file.\n     * It takes the output file, the project to check, and an optional action to be executed with the list of errors.\n     *\n     * @param outputFile The virtual file where the syntax errors will be written to.\n     * @param project The project to be analyzed for syntax errors.\n     * @param runAction An optional lambda function that takes a list of strings as its parameter, representing the syntax errors.\n     *                  If provided, this action is invoked with an empty list of errors, indicating no syntax errors were found.\n     */\n    open fun collectSyntaxError(psiFile: PsiFile, project: Project, runAction: ((errors: List<String>) -> Unit)?) {\n        PsiErrorCollector.collectSyntaxError(psiFile, psiFile.virtualFile, project, runAction)\n    }\n\n    /**\n     * Attempts to fix syntax errors in the given Kotlin file within the project.\n     * This method is designed to be overridden by subclasses to provide custom syntax error fixing logic.\n     *\n     * @param outputFile The virtual file that needs to have its syntax errors fixed.\n     * @param project The current project in which the file resides.\n     */\n    open fun tryFixSyntaxError(outputFile: VirtualFile, project: Project, issues: List<String>) {\n        // send to chat panel\n    }\n\n    companion object {\n        val log = logger<TestingService>()\n        private val EP_NAME: LanguageExtension<TestingService> = LanguageExtension(\"com.phodal.shireAutoTesting\")\n\n        fun context(psiElement: PsiElement): TestingService? {\n            return EP_NAME.forLanguage(psiElement.language)\n        }\n    }\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/provider/action/CustomActionLocationExecutor.kt",
    "content": "package com.phodal.shirecore.provider.action\n\nimport com.phodal.shirecore.config.ShireActionLocation\nimport com.phodal.shirecore.llm.LlmProvider\n\ninterface CustomActionLocationExecutor {\n    fun isApplicable(location: ShireActionLocation): Boolean\n\n    fun execute(llmProvider: LlmProvider, location: ShireActionLocation)\n}\n\n\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/provider/action/TerminalLocationExecutor.kt",
    "content": "package com.phodal.shirecore.provider.action\n\nimport com.intellij.openapi.actionSystem.AnActionEvent\nimport com.intellij.openapi.extensions.ExtensionPointName\nimport com.intellij.openapi.project.Project\nimport com.phodal.shirecore.provider.action.terminal.TerminalHandler\nimport java.awt.Component\n\ninterface TerminalLocationExecutor {\n    fun getComponent(e: AnActionEvent): Component?\n    fun bundler(project: Project, userInput: String): TerminalHandler?\n\n    companion object {\n        private val EP_NAME: ExtensionPointName<TerminalLocationExecutor> =\n            ExtensionPointName.create(\"com.phodal.shireTerminalExecutor\")\n\n        fun provide(project: Project): TerminalLocationExecutor? {\n            return EP_NAME.extensionList.firstOrNull()\n        }\n    }\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/provider/action/terminal/TerminalHandler.kt",
    "content": "package com.phodal.shirecore.provider.action.terminal\n\nimport com.intellij.openapi.project.Project\n\nclass TerminalHandler(\n    val userInput: String,\n    val project: Project,\n    val onChunk: (str: String) -> Any?,\n    val onFinish: ((str: String?) -> Any?)?,\n)\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/provider/agent/AgentTool.kt",
    "content": "package com.phodal.shirecore.provider.agent\n\nimport com.intellij.openapi.extensions.ExtensionPointName\nimport com.phodal.shirecore.agent.agenttool.AgentToolContext\nimport com.phodal.shirecore.agent.agenttool.AgentToolResult\n\ninterface AgentTool {\n    val name: String\n    val description: String\n    fun execute(context: AgentToolContext): AgentToolResult\n\n    // extension point\n    companion object {\n        private val EP_NAME = ExtensionPointName<AgentTool>(\"com.phodal.shireAgentTool\")\n\n        fun allTools(): List<AgentTool> {\n            return EP_NAME.extensionList\n        }\n    }\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/provider/codeedit/CodeModifier.kt",
    "content": "package com.phodal.shirecore.provider.codeedit\n\nimport com.intellij.lang.Language\nimport com.intellij.lang.LanguageExtension\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.intellij.psi.PsiElement\n\n/**\n * The `CodeModifier` interface provides methods for modifying code in a given project.\n * It allows for inserting test code, methods, and classes into source files.\n */\ninterface CodeModifier {\n    /**\n     * Checks if the given code language is applicable.\n     *\n     * @param language The language to check.\n     * @return True if the language is applicable, false otherwise.\n     */\n    fun isApplicable(language: Language): Boolean\n\n    /**\n     * According to the source file, project, and code, it will insert the code in a smart way.\n     */\n    fun smartInsert(sourceFile: VirtualFile, project: Project, code: String): PsiElement?\n\n    /**\n     * Inserts the provided test code into the specified source file in the given project.\n     *\n     * @param sourceFile The virtual file representing the source file where the test code will be inserted.\n     * @param project The project in which the source file belongs.\n     * @param code The test code to be inserted into the source file.\n     * @return True if the test code was successfully inserted, false otherwise.\n     */\n    fun insertTestCode(sourceFile: VirtualFile, project: Project, code: String): PsiElement?\n    /**\n     * Inserts a method into the specified source file in the given project.\n     *\n     * @param sourceFile The virtual file representing the source file to insert the method into.\n     * @param project The project in which the source file belongs.\n     * @param code The code of the method to be inserted.\n     * @return `true` if the method was successfully inserted, `false` otherwise.\n     */\n    fun insertMethod(sourceFile: VirtualFile, project: Project, code: String): PsiElement?\n    /**\n     * Inserts a class into the specified source file in the given project.\n     *\n     * @param sourceFile The virtual file representing the source file to insert the class into.\n     * @param project The project in which the source file belongs.\n     * @param code The code representing the class to be inserted.\n     * @return True if the class was successfully inserted, false otherwise.\n     */\n    fun insertClass(sourceFile: VirtualFile, project: Project, code: String): PsiElement?\n\n    companion object {\n        private val languageExtension: LanguageExtension<CodeModifier> =\n            LanguageExtension(\"com.phodal.shireCodeModifier\")\n\n        fun forLanguage(language: Language): CodeModifier? {\n            return languageExtension.forLanguage(language)\n        }\n    }\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/provider/codemodel/ClassStructureProvider.kt",
    "content": "package com.phodal.shirecore.provider.codemodel\n\nimport com.intellij.lang.LanguageExtension\nimport com.intellij.openapi.diagnostic.logger\nimport com.intellij.psi.PsiElement\nimport com.phodal.shirecore.provider.codemodel.model.ClassStructure\n\n/**\n * The ClassContextBuilder interface provides a method to retrieve the class context for a given PsiElement.\n * The class context represents the surrounding context of a class, including its imports, package declaration,\n * and any other relevant information.\n */\ninterface ClassStructureProvider {\n    /**\n     * Retrieves the class context for the given [psiElement].\n     *\n     * @param psiElement the PSI element for which to retrieve the class context\n     * @param gatherUsages specifies whether to gather usages of the class\n     * @return the class context for the given [psiElement], or null if the class context cannot be determined\n     */\n    fun build(psiElement: PsiElement, gatherUsages: Boolean): ClassStructure?\n\n    companion object {\n        private val languageExtension = LanguageExtension<ClassStructureProvider>(\"com.phodal.classStructureProvider\")\n        private val providers: Map<String, ClassStructureProvider> = StructureProvider.loadProviders(languageExtension)\n        private val logger = logger<ClassStructureProvider>()\n\n        fun from(psiElement: PsiElement, gatherUsages: Boolean = false): ClassStructure? {\n            try {\n                return providers[psiElement.language.id]?.build(psiElement, gatherUsages)\n            } catch (e: Exception) {\n                logger.error(\"Error while getting class context from\", e)\n            }\n            return null\n        }\n    }\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/provider/codemodel/FileStructureProvider.kt",
    "content": "package com.phodal.shirecore.provider.codemodel\n\nimport com.intellij.lang.LanguageExtension\nimport com.intellij.psi.PsiFile\nimport com.phodal.shirecore.provider.codemodel.model.FileStructure\n\ninterface FileStructureProvider {\n    fun build(psiFile: PsiFile): FileStructure?\n\n    companion object {\n        private val languageExtension = LanguageExtension<FileStructureProvider>(\"com.phodal.fileStructureProvider\")\n        private val providers: Map<String, FileStructureProvider> = StructureProvider.loadProviders(languageExtension)\n\n        fun from(psiFile: PsiFile): FileStructure? {\n            return providers[psiFile.language.id]?.build(psiFile)\n        }\n    }\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/provider/codemodel/MethodStructureProvider.kt",
    "content": "package com.phodal.shirecore.provider.codemodel\n\nimport com.intellij.lang.LanguageExtension\nimport com.intellij.psi.PsiElement\nimport com.phodal.shirecore.provider.codemodel.model.MethodStructure\n\n/**\n * The MethodContextBuilder interface provides a method for retrieving the method context of a given PsiElement.\n * A method context represents the context in which a method is defined or used within a codebase.\n * @see MethodStructure\n */\ninterface MethodStructureProvider {\n    fun build(psiElement: PsiElement, includeClassContext: Boolean, gatherUsages: Boolean): MethodStructure?\n\n    companion object {\n        private val languageExtension = LanguageExtension<MethodStructureProvider>(\"com.phodal.methodStructureProvider\")\n        private val providers: Map<String, MethodStructureProvider> = StructureProvider.loadProviders(languageExtension)\n\n        fun from(psiElement: PsiElement, includeClassContext: Boolean = false, gatherUsages: Boolean = false): MethodStructure? {\n            return providers[psiElement.language.id]?.build(psiElement, includeClassContext, gatherUsages)\n                ?: MethodStructure(psiElement, psiElement.text, null)\n        }\n    }\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/provider/codemodel/StructureProvider.kt",
    "content": "package com.phodal.shirecore.provider.codemodel\n\nimport com.intellij.lang.Language\nimport com.intellij.lang.LanguageExtension\n\n/**\n * @author lk\n */\nobject StructureProvider {\n\n    private val registeredLanguages = Language.getRegisteredLanguages()\n\n    /**\n     * Load providers for different StructureProviders\n     * and return specific providers based on the language.\n     */\n    fun <T> loadProviders(languageExtension: LanguageExtension<T>): Map<String, T> {\n        return registeredLanguages.mapNotNull {\n            languageExtension.forLanguage(it)?.run {\n                it.id to this\n            }\n        }.toMap()\n    }\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/provider/codemodel/VariableStructureProvider.kt",
    "content": "package com.phodal.shirecore.provider.codemodel\n\nimport com.intellij.lang.LanguageExtension\nimport com.intellij.psi.PsiElement\nimport com.phodal.shirecore.provider.codemodel.model.VariableStructure\n\ninterface VariableStructureProvider {\n    fun build(\n        psiElement: PsiElement,\n        withMethodContext: Boolean,\n        withClassContext: Boolean,\n        gatherUsages: Boolean,\n    ): VariableStructure?\n\n    companion object {\n        private val languageExtension =\n            LanguageExtension<VariableStructureProvider>(\"com.phodal.variableStructureProvider\")\n        private val providers: Map<String, VariableStructureProvider> = StructureProvider.loadProviders(languageExtension)\n\n        fun from(\n            psiElement: PsiElement,\n            includeMethodContext: Boolean = false,\n            includeClassContext: Boolean = false,\n            gatherUsages: Boolean = false,\n        ): VariableStructure {\n            return providers[psiElement.language.id]?.build(psiElement, includeMethodContext, includeClassContext, gatherUsages)\n                ?: VariableStructure(psiElement, psiElement.text, null)\n        }\n    }\n\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/provider/codemodel/base/FormatableElement.kt",
    "content": "package com.phodal.shirecore.provider.codemodel.base\n\nimport com.intellij.psi.PsiElement\n\n/**\n * The `FormatableElement` is an abstract class that represents a formatable element in a given context.\n * It provides information about the root element, text, and name of the element.\n *\n * @property root The root element of the context.\n * @property text The text representation of the context.\n * @property name The name of the element in the context.\n *\n * This class has a method `format()` which formats the named element context into a string representation.\n * The format of the string representation varies depending on the context.\n * For instance:\n * In the context of `com.phodal.shirecore.codemodel.DirectoryStructure`, the formatted string representation will be like a directory structure.\n * In the context of `com.phodal.shirecore.codemodel.FileStructure`, the formatted string representation will be like a file structure.\n * In the context of `com.phodal.shirecore.codemodel.ClassStructure`, the formatted string representation will be like UML.\n * In the context of `com.phodal.shirecore.codemodel.MethodStructure`, the formatted string representation will be just the method signature.\n * In the context of `com.phodal.shirecore.codemodel.VariableStructure`, the formatted string representation will be like a variable declaration.\n *\n * @return The formatted string representation of the named element context.\n */\nabstract class FormatableElement(open val root: PsiElement, open val text: String?, open val name: String?) {\n    /**\n     * Formats the named element context into a string representation.\n     * In [com.phodal.shirecore.codemodel.ClassStructure], the formatted string representation will be like UML.For example:\n     * ```uml\n     * 'package: cc.unitmesh.untitled.demo.controller.UserController\n     * '@RestController, @RequestMapping(\"/user\")\n     * class UserController {\n     *   + @GetMapping     public UserDTO getUsers()\n     * }\n     * ```\n     * In [com.phodal.shirecore.codemodel.MethodStructure],\n     * the formatted string representation will be just the method signature.For Example\n     * ```bash\n     * path: /src/test.go\n     * language: Go\n     * fun name: f3\n     * fun signature: (float64, float64, float64)\n     * ```\n     *\n     * In [com.phodal.shirecore.codemodel.VariableStructure], the formatted string representation will be like:\n     * ```bash\n     * var name: content\n     * var method name: format\n     * var class name: NamedElementContext\n     * ```\n     * @return The formatted string representation of the named element context.\n     */\n    open fun format(): String = \"\"\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/provider/codemodel/model/ClassStructure.kt",
    "content": "package com.phodal.shirecore.provider.codemodel.model\n\nimport com.intellij.openapi.application.runReadAction\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.PsiReference\nimport com.phodal.shirecore.provider.codemodel.MethodStructureProvider\nimport com.phodal.shirecore.provider.codemodel.VariableStructureProvider\nimport com.phodal.shirecore.provider.codemodel.base.FormatableElement\n\nclass ClassStructure(\n    override val root: PsiElement,\n    override val text: String?,\n    override val name: String?,\n    val displayName: String?,\n    val methods: List<PsiElement> = emptyList(),\n    val fields: List<PsiElement> = emptyList(),\n    val superClasses: List<String>? = null,\n    val annotations: List<String> = mutableListOf(),\n    val usages: List<PsiReference> = emptyList(),\n) : FormatableElement(root, text, name) {\n    private fun getFieldNames(): List<String> = fields.map {\n        VariableStructureProvider.from(it,\n            includeMethodContext = false,\n            includeClassContext = false,\n            gatherUsages = false\n        ).shortFormat()\n    }\n\n    private fun getMethodSignatures(): List<String> = methods.mapNotNull {\n        MethodStructureProvider.from(it, false, gatherUsages = false)?.signature\n    }\n\n    override fun format(): String {\n        val className = name ?: \"_\"\n        val classFields = getFieldNames().joinToString(separator = \"\\n  \")\n        val superClasses = when {\n            superClasses.isNullOrEmpty() -> \"\"\n            else -> \" : ${superClasses.joinToString(separator = \", \")}\"\n        }\n        val methodSignatures = getMethodSignatures()\n            .filter { it.isNotBlank() }\n            .joinToString(separator = \"\\n  \") { signature ->\n                \"+ $signature\"\n            }\n\n        val filePath = displayName ?: runReadAction { root.containingFile?.virtualFile?.path }\n        val annotations = if (annotations.isEmpty()) {\n            \"\"\n        } else {\n            \"\\n'\" + annotations.joinToString(separator = \", \")\n        }\n\n        return \"\"\"\n        |'package: $filePath$annotations\n        |class $className$superClasses {\n        |  $classFields\n        |  $methodSignatures\n        |}\n    \"\"\".trimMargin()\n    }\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/provider/codemodel/model/DirectoryStructure.kt",
    "content": "package com.phodal.shirecore.provider.codemodel.model\n\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.PsiFile\nimport com.intellij.psi.PsiNameIdentifierOwner\nimport com.phodal.shirecore.provider.codemodel.base.FormatableElement\n\nclass DirectoryStructure(\n    override val root: PsiFile,\n    override val name: String,\n    private val path: String,\n    /**\n     * For some languages, path may not be enough to uniquely identify a package, like in Java, Kotlin, etc.\n     */\n    private val packageString: String? = null,\n    private val files: List<FileStructure> = emptyList(),\n) : FormatableElement(root, path, name) {\n    /**\n     * This method is used to format the details of the directory, package, and files into a string.\n     * The formatted string includes the directory name, package name (if available), and the details of each file.\n     * Each file's details include the file name and the names of the classes within the file.\n     *\n     * The method first creates a string representation of the file details. For each file, it appends the file name and the names of the classes within the file.\n     * Then, it constructs the final string by appending the directory name, package name (if available), and the file details.\n     *\n     * @return A string representation of the directory, package, and file details. The string is formatted as follows:\n     * ```\n     * directory name: /path/to/file\n     * package: com.example\n     * file `file1` classes: [class1, class2]\n     * file `file2` classes: [class3, class4]\n     * ```\n     * If the package name is not available, it is omitted from the string. If there are no file details, they are also omitted from the string.\n     */\n    override fun format(): String {\n        val fileDetails = files.joinToString(\"\\n\") { structure ->\n            val file = structure.root\n            val classes = structure.classes.mapNotNull {\n                when (it) {\n                    is PsiNameIdentifierOwner -> it.name\n                    else -> null\n                }\n            }.joinToString(\", \")\n            \"file `${file.name}` classes: [$classes]\"\n        }\n\n        val filePath = path\n        val filePackage = if (packageString != null) \"package: $packageString\" else \"\"\n\n        return buildString {\n            append(\"directory name: $filePath\\n\")\n            if (filePackage.isNotEmpty()) append(\"$filePackage\\n\")\n            if (fileDetails.isNotEmpty()) append(\"$fileDetails\\n\")\n        }\n    }\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/provider/codemodel/model/FileStructure.kt",
    "content": "package com.phodal.shirecore.provider.codemodel.model\n\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.PsiFile\nimport com.intellij.psi.PsiNameIdentifierOwner\nimport com.phodal.shirecore.provider.codemodel.ClassStructureProvider\nimport com.phodal.shirecore.provider.codemodel.base.FormatableElement\n\nclass FileStructure(\n    override val root: PsiFile,\n    override val name: String,\n    private val path: String,\n    private val packageString: String? = null,\n    private val imports: List<PsiElement> = emptyList(),\n    /// maybe PsiNameIdentifierOwner ?\n    val classes: List<PsiElement> = emptyList(),\n    private val methods: List<PsiNameIdentifierOwner> = emptyList(),\n) : FormatableElement(root, path, name) {\n    private fun getClassDetail(): List<String> = classes.mapNotNull {\n        ClassStructureProvider.from(it, false)?.format()\n    }\n\n    override fun format(): String {\n        fun getFieldString(fieldName: String, fieldValue: String): String {\n            return if (fieldValue.isNotBlank()) \"$fieldName: $fieldValue\" else \"\"\n        }\n\n        val filePackage = getFieldString(\"file package\", packageString ?: \"\")\n        val fileImports = getFieldString(\n            \"file imports\",\n            if (imports.isNotEmpty()) imports.joinToString(\" \", transform = { it.text }) else \"\"\n        )\n        val classDetails =\n            getFieldString(\n                \"file classes\",\n                if (getClassDetail().isNotEmpty()) getClassDetail().joinToString(\", \") else \"\"\n            )\n        val filePath = getFieldString(\"file path\", path)\n\n        return buildString {\n            append(\"file name: $name\\n\")\n            if (filePackage.isNotEmpty()) append(\"$filePackage\\n\")\n            if (fileImports.isNotEmpty()) append(\"$fileImports\\n\")\n            if (classDetails.isNotEmpty()) append(\"$classDetails\\n\")\n            append(\"$filePath\\n\")\n        }\n    }\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/provider/codemodel/model/MethodStructure.kt",
    "content": "package com.phodal.shirecore.provider.codemodel.model\n\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.PsiReference\nimport com.phodal.shirecore.provider.codemodel.ClassStructureProvider\nimport com.phodal.shirecore.provider.codemodel.base.FormatableElement\nimport com.phodal.shirecore.project.isInProject\n\nclass MethodStructure(\n    override val root: PsiElement,\n    override val text: String,\n    override val name: String?,\n    val signature: String? = null,\n    private val enclosingClass: PsiElement? = null,\n    val language: String? = null,\n    val returnType: String? = null,\n    val paramNames: List<String> = emptyList(),\n    private val includeClassContext: Boolean = false,\n    private val usages: List<PsiReference> = emptyList(),\n    private val fanInOut: List<PsiElement> = emptyList(),\n) : FormatableElement(root, text, name) {\n    private val classContext: ClassStructure? = if (includeClassContext && enclosingClass != null) {\n        ClassStructureProvider.from(enclosingClass, false)\n    } else {\n        null\n    }\n\n    override fun format(): String {\n        val usageString = usages.joinToString(\"\\n\") {\n            val classFile = it.element.containingFile\n            val useText = it.element.text\n            \"${classFile.name} -> $useText\"\n        }\n\n        var query = \"\"\"\n            path: ${root.containingFile?.virtualFile?.path ?: \"_\"}\n            language: ${language ?: \"_\"}\n            fun name: ${name ?: \"_\"}\n            fun signature: ${signature ?: \"_\"}\n            \"\"\".trimIndent()\n\n        if (usageString.isNotEmpty()) {\n            query += \"\\nusages: \\n$usageString\"\n        }\n\n        if (classContext != null) {\n            query += classContext.format()\n        }\n\n        return query\n    }\n\n    fun inputOutputString(): String {\n        if (fanInOut.isEmpty()) return \"\"\n\n        var result = \"\"\n        this.fanInOut.forEach {\n            val context: ClassStructure = ClassStructureProvider.from(it, false) ?: return@forEach\n            val element = context.root\n\n            if (!isInProject(element.containingFile?.virtualFile!!, root.project)) {\n                return@forEach\n            }\n\n            context.let { classContext ->\n                result += \"${classContext.format()}\\n\"\n            }\n        }\n\n        if (result.isEmpty()) {\n            return \"\"\n        }\n\n        return \"\"\"\n            ```uml\n            $result\n            ```\n            \"\"\".trimIndent()\n    }\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/provider/codemodel/model/VariableStructure.kt",
    "content": "package com.phodal.shirecore.provider.codemodel.model\n\nimport com.intellij.openapi.application.runReadAction\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.PsiReference\nimport com.phodal.shirecore.provider.codemodel.ClassStructureProvider\nimport com.phodal.shirecore.provider.codemodel.MethodStructureProvider\nimport com.phodal.shirecore.provider.codemodel.base.FormatableElement\n\nclass VariableStructure(\n    override val root: PsiElement,\n    override val text: String,\n    override val name: String?,\n    private val enclosingMethod: PsiElement? = null,\n    private val enclosingClass: PsiElement?= null,\n    private val usages: List<PsiReference> = emptyList(),\n    private val includeMethodContext: Boolean = false,\n    private val includeClassContext: Boolean = false\n) : FormatableElement(root, text, name) {\n    private val methodContext: MethodStructure? = if (includeMethodContext && enclosingMethod != null) {\n        MethodStructureProvider.from(enclosingMethod, includeClassContext = false, gatherUsages = false)\n    } else {\n        null\n    }\n    private val classContext: ClassStructure? = if (includeClassContext && enclosingClass != null) {\n        ClassStructureProvider.from(enclosingClass, false)\n    } else {\n        null\n    }\n\n    fun shortFormat(): String = runReadAction {  root.text ?: \"\"}\n\n    /**\n     * Returns a formatted string representation of the method.\n     *\n     * The returned string includes the following information:\n     * - The name of the method, or \"_\" if the name is null.\n     * - The name of the method's context, or \"_\" if the context is null.\n     * - The name of the class's context, or \"_\" if the context is null.\n     *\n     * @return A formatted string representation of the method.\n     */\n    override fun format(): String {\n        return \"\"\"\n            var name: ${name ?: \"_\"}\n            var method name: ${methodContext?.name ?: \"_\"}\n            var class name: ${classContext?.name ?: \"_\"}\n        \"\"\".trimIndent()\n    }\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/provider/complexity/ComplexityProvider.kt",
    "content": "package com.phodal.shirecore.provider.complexity\n\nimport com.intellij.lang.Language\nimport com.intellij.lang.LanguageExtension\nimport com.intellij.psi.PsiElement\nimport com.phodal.shirecore.ast.ComplexitySink\nimport com.phodal.shirecore.ast.ComplexityVisitor\n\ninterface ComplexityProvider {\n    fun process(element: PsiElement): Int\n\n    fun visitor(sink: ComplexitySink): ComplexityVisitor\n\n    companion object {\n        private val languageExtension = LanguageExtension<ComplexityProvider>(\"com.phodal.shireComplexityProvider\")\n\n        fun provide(language: Language): ComplexityProvider? {\n            return languageExtension.forLanguage(language)\n        }\n    }\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/provider/context/ActionLocationEditor.kt",
    "content": "package com.phodal.shirecore.provider.context\n\nimport com.intellij.ide.DataManager\nimport com.intellij.openapi.actionSystem.CommonDataKeys\nimport com.intellij.openapi.editor.Editor\nimport com.intellij.openapi.extensions.ExtensionPointName\nimport com.intellij.openapi.fileEditor.FileEditorManager\nimport com.intellij.openapi.project.Project\nimport com.phodal.shirecore.config.ShireActionLocation\nimport com.phodal.shirecore.variable.template.VariableActionEventDataHolder\n\ninterface ActionLocationEditor {\n    fun isApplicable(hole: ShireActionLocation): Boolean\n\n    fun resolve(project: Project, hole: ShireActionLocation): Editor?\n\n    companion object {\n        private val EP_NAME: ExtensionPointName<ActionLocationEditor> =\n            ExtensionPointName.create(\"com.phodal.shireActionLocationEditor\")\n\n        fun provide(project: Project, location: ShireActionLocation? = null): Editor? {\n            if (location == null) {\n                return defaultEditor(project)\n            }\n\n            val locationEditors = EP_NAME.extensionList.filter {\n                it.isApplicable(location)\n            }\n\n\n            if (locationEditors.isNotEmpty()) {\n                return locationEditors.first().resolve(project, location)\n            }\n\n            val dataContext = DataManager.getInstance().dataContextFromFocus.result\n            val contextEditor = dataContext?.getData(CommonDataKeys.EDITOR)\n\n            if (contextEditor != null) {\n                return contextEditor\n            }\n\n            val savedDataContext = VariableActionEventDataHolder.getData()?.dataContext\n            val editor = savedDataContext?.getData(CommonDataKeys.EDITOR)\n            if (editor != null) {\n                return editor\n            }\n\n            return defaultEditor(project)\n        }\n\n        fun defaultEditor(project: Project) =\n            FileEditorManager.getInstance(project).selectedTextEditor\n    }\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/provider/context/BuildSystemProvider.kt",
    "content": "package com.phodal.shirecore.provider.context\n\n\nimport com.intellij.openapi.extensions.ExtensionPointName\nimport com.intellij.openapi.project.Project\nimport com.intellij.serviceContainer.LazyExtensionInstance\nimport com.intellij.util.xmlb.annotations.Attribute\nimport com.phodal.shirecore.variable.toolchain.buildsystem.BuildSystemContext\n\n/**\n * The `BuildSystemProvider` interface represents a provider for build system information.\n * It provides methods to retrieve the name and version of the build tool being used, as well as the name\n * and version of the programming language being used.\n */\nabstract class BuildSystemProvider : LazyExtensionInstance<BuildSystemProvider>() {\n    abstract fun collect(project: Project): BuildSystemContext?\n\n    @Attribute(\"implementationClass\")\n    var implementationClass: String? = null\n\n    override fun getImplementationClassName(): String? {\n        return implementationClass\n    }\n\n    companion object {\n        private val EP_NAME: ExtensionPointName<BuildSystemProvider> =\n            ExtensionPointName.create(\"com.phodal.shireBuildSystemProvider\")\n\n        fun provide(project: Project): List<BuildSystemContext> {\n            return EP_NAME.extensionList.mapNotNull {\n                it.collect(project)\n            }\n        }\n    }\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/provider/context/BuildTool.kt",
    "content": "package com.phodal.shirecore.provider.context\n\nimport com.intellij.execution.configurations.LocatableConfigurationBase\nimport com.intellij.openapi.externalSystem.service.ui.completion.TextCompletionInfo\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.vfs.VirtualFile\n\n/**\n * The `BuildTool` interface provides a set of methods for managing build tasks in a project.\n * It is designed to be implemented by classes that provide specific build tool functionality.\n *\n * This interface includes methods for preparing library data, collecting tasks, and configuring run tasks.\n *\n * Implementations of this interface are expected to provide specific functionality for different build tools.\n */\ninterface BuildTool {\n    fun toolName(): String\n\n    fun prepareLibraryData(project: Project): List<CommonLibraryData>?\n\n    fun collectTasks(project: Project): List<TextCompletionInfo>\n\n    fun configureRun(project: Project, taskName: String, virtualFile: VirtualFile?): LocatableConfigurationBase<*>?\n}\n\ndata class CommonLibraryData(val groupId: String?, val artifactId: String?, val version: String?) {\n    fun prettyString(): String {\n        return \"$groupId:$artifactId:$version\"\n    }\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/provider/context/LanguageToolchainProvider.kt",
    "content": "package com.phodal.shirecore.provider.context\n\nimport com.intellij.lang.LanguageExtension\nimport com.intellij.openapi.project.Project\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.PsiFile\nimport com.intellij.util.concurrency.annotations.RequiresBackgroundThread\nimport kotlin.reflect.KClass\n\nclass ToolchainContextItem(\n    val clazz: KClass<*>,\n    var text: String,\n)\n\ndata class ToolchainPrepareContext(\n    val sourceFile: PsiFile?,\n    val element: PsiElement?,\n    val extraItems: List<ToolchainContextItem> = emptyList(),\n)\n\ninterface LanguageToolchainProvider {\n    fun isApplicable(project: Project, context: ToolchainPrepareContext): Boolean\n\n    @RequiresBackgroundThread\n    suspend fun collect(project: Project, context: ToolchainPrepareContext): List<ToolchainContextItem>\n\n    companion object {\n        private val EP_NAME: LanguageExtension<LanguageToolchainProvider> =\n            LanguageExtension(\"com.phodal.shireLanguageToolchainProvider\")\n\n        suspend fun collectToolchainContext(\n            project: Project,\n            toolchainPrepareContext: ToolchainPrepareContext,\n        ): List<ToolchainContextItem> {\n            val elements = mutableListOf<ToolchainContextItem>()\n\n            toolchainPrepareContext.sourceFile?.language?.let {\n                val provider = EP_NAME.forLanguage(it)\n                if (provider.isApplicable(project, toolchainPrepareContext)) {\n                    elements.addAll(provider.collect(project, toolchainPrepareContext))\n                }\n            }\n\n            elements.addAll(toolchainPrepareContext.extraItems)\n            return elements.distinctBy { it.text }\n        }\n    }\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/provider/function/ToolchainFunctionProvider.kt",
    "content": "package com.phodal.shirecore.provider.function\n\nimport com.intellij.openapi.extensions.ExtensionPointName\nimport com.intellij.openapi.project.Project\n\ninterface ToolchainFunctionProvider {\n    fun isApplicable(project: Project, funcName: String): Boolean\n\n    fun execute(project: Project, funcName: String, args: List<Any>, allVariables: Map<String, Any?>): Any\n\n    companion object {\n        private val EP_NAME: ExtensionPointName<ToolchainFunctionProvider> =\n            ExtensionPointName(\"com.phodal.shireToolchainFunctionProvider\")\n\n        fun all(): List<ToolchainFunctionProvider> {\n            return EP_NAME.extensionList\n        }\n\n        fun lookup(providerName: String): ToolchainFunctionProvider? {\n            return EP_NAME.extensionList.firstOrNull {\n                it.javaClass.simpleName == providerName\n            }\n        }\n\n        fun provide(project: Project, funcName: String): ToolchainFunctionProvider? {\n            return EP_NAME.extensionList.firstOrNull {\n                it.isApplicable(project, funcName)\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/provider/http/HttpHandler.kt",
    "content": "package com.phodal.shirecore.provider.http\n\nimport com.intellij.openapi.extensions.ExtensionPointName\nimport com.intellij.openapi.project.Project\n\ninterface HttpHandler {\n    fun isApplicable(type: HttpHandlerType): Boolean\n\n    fun execute(project: Project, content: String, variablesName: Array<String>, variableTable: MutableMap<String, Any?>) : String?\n\n    companion object {\n        private val EP_NAME: ExtensionPointName<HttpHandler> =\n            ExtensionPointName(\"com.phodal.shireHttpHandler\")\n\n        fun provide(type: HttpHandlerType): HttpHandler? {\n            return EP_NAME.extensionList.find { it.isApplicable(type) }\n        }\n    }\n}\n\nenum class HttpHandlerType(val id: String) {\n    CURL(\"cURL\"),\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/provider/ide/InlineChatProvider.kt",
    "content": "package com.phodal.shirecore.provider.ide\n\nimport com.intellij.openapi.extensions.ExtensionPointName\nimport com.intellij.openapi.project.Project\n\ninterface InlineChatProvider {\n\n    fun addListener(project: Project)\n\n    fun removeListener(project: Project)\n\n    companion object {\n        private val EP_NAME: ExtensionPointName<InlineChatProvider> =\n            ExtensionPointName(\"com.phodal.shireInlineChatProvider\")\n\n        fun provide(): InlineChatProvider? {\n            return EP_NAME.extensionList.firstOrNull()\n        }\n    }\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/provider/ide/LocationInteractionContext.kt",
    "content": "package com.phodal.shirecore.provider.ide\n\nimport com.intellij.execution.ui.ConsoleView\nimport com.intellij.openapi.editor.Editor\nimport com.intellij.openapi.project.Project\nimport com.intellij.psi.PsiElement\nimport com.phodal.shirecore.config.ShireActionLocation\nimport com.phodal.shirecore.config.InteractionType\nimport kotlinx.coroutines.flow.Flow\n\ndata class LocationInteractionContext(\n    val location: ShireActionLocation,\n    /**\n     * the interaction type\n     */\n    val interactionType: InteractionType,\n    /**\n     * the LLM generate text stream, which can be used for [InteractionType.AppendCursorStream]\n     */\n    val streamText: Flow<String>? = null,\n\n    val editor: Editor?,\n\n    val project: Project,\n\n    /**\n     * the [com.phodal.shirecore.llm.ChatMessage]\n     */\n    val prompt: String,\n\n    val selectElement: PsiElement? = null,\n\n    /**\n     * the console view\n     */\n    val console: ConsoleView?,\n)"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/provider/ide/LocationInteractionProvider.kt",
    "content": "package com.phodal.shirecore.provider.ide\n\nimport com.intellij.openapi.extensions.ExtensionPointName\nimport com.phodal.shirecore.config.interaction.PostFunction\n\n/**\n * Interface for managing interactions in different IDE locations.\n * The interactions are categorized into three types:\n * - Terminal: Appends stream in the IDE terminal\n * - Editor: Appends stream in the IDE editor\n * - CommitPanel: Appends stream in the IDE commit panel\n */\ninterface LocationInteractionProvider {\n    fun isApplicable(context: LocationInteractionContext): Boolean\n\n    fun execute(context: LocationInteractionContext, postExecute: PostFunction)\n\n    companion object {\n        private val EP_NAME: ExtensionPointName<LocationInteractionProvider> =\n            ExtensionPointName(\"com.phodal.shireLocationInteraction\")\n\n        fun provide(context: LocationInteractionContext): LocationInteractionProvider? {\n            return EP_NAME.extensionList.firstOrNull {\n                it.isApplicable(context)\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/provider/ide/ShirePromptBuilder.kt",
    "content": "package com.phodal.shirecore.provider.ide\n\nimport com.intellij.openapi.extensions.ExtensionPointName\nimport com.intellij.openapi.project.Project\n\ninterface ShirePromptBuilder {\n    fun build(project: Project, actionLocation: String, originPrompt: String) : String\n\n    companion object {\n        private val EP_NAME: ExtensionPointName<ShirePromptBuilder> =\n            ExtensionPointName(\"com.phodal.shirePromptBuilder\")\n\n        fun provide(): ShirePromptBuilder? {\n            return EP_NAME.extensionList.firstOrNull()\n        }\n    }\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/provider/impl/MarkdownPsiContextVariableProvider.kt",
    "content": "package com.phodal.shirecore.provider.impl\n\nimport com.intellij.openapi.application.ReadAction\nimport com.intellij.openapi.editor.Editor\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.util.NlsSafe\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.PsiFile\nimport com.intellij.psi.PsiManager\nimport com.phodal.shirecore.provider.variable.PsiContextVariableProvider\nimport com.phodal.shirecore.provider.variable.model.PsiContextVariable\nimport org.intellij.markdown.IElementType\nimport org.intellij.markdown.MarkdownElementTypes\nimport org.intellij.markdown.MarkdownTokenTypes\nimport org.intellij.markdown.ast.ASTNode\nimport org.intellij.markdown.flavours.gfm.GFMElementTypes\nimport org.intellij.markdown.flavours.gfm.GFMFlavourDescriptor\nimport org.intellij.markdown.flavours.gfm.GFMTokenTypes\nimport org.intellij.markdown.parser.MarkdownParser\n\nprivate val embeddedHtmlType = IElementType(\"ROOT\")\n\nclass MarkdownPsiContextVariableProvider : PsiContextVariableProvider {\n    override fun resolve(variable: PsiContextVariable, project: Project, editor: Editor, psiElement: PsiElement?): Any {\n        val psiFile = ReadAction.compute<PsiFile?, Throwable> {\n            PsiManager.getInstance(project).findFile(editor.virtualFile)\n        }\n\n        val markdownText = psiFile?.text ?: return \"\"\n\n        return when (variable) {\n            PsiContextVariable.STRUCTURE -> {\n                toHtml(markdownText)\n            }\n\n            else -> \"\"\n        }\n    }\n\n    fun toHtml(markdownText: @NlsSafe String): String {\n        val flavour = GFMFlavourDescriptor()\n        val parsedTree = MarkdownParser(flavour).parse(embeddedHtmlType, markdownText)\n        val markdownNode = MarkdownNode(parsedTree, null, markdownText)\n        return markdownNode.toHtml()\n    }\n\n    private fun MarkdownNode.toHtml(): String {\n        if (node.type == MarkdownTokenTypes.WHITE_SPACE) {\n            return text   // do not trim trailing whitespace\n        }\n\n        val sb = StringBuilder()\n        visit { node, processChildren ->\n            fun wrapChildren(tag: String, level: Int = 0) {\n                sb.append(\"#\".repeat(level))\n                processChildren()\n                sb.append(\"\\n\")\n            }\n\n            val nodeType = node.type\n            val nodeText = node.text\n\n            when (nodeType) {\n                MarkdownElementTypes.ATX_1 -> wrapChildren(\"h1\", 1)\n                MarkdownElementTypes.ATX_2 -> wrapChildren(\"h2\" ,2)\n                MarkdownElementTypes.ATX_3 -> wrapChildren(\"h3\", 3)\n                MarkdownElementTypes.ATX_4 -> wrapChildren(\"h4\", 4)\n                MarkdownElementTypes.ATX_5 -> wrapChildren(\"h5\", 5)\n                MarkdownElementTypes.ATX_6 -> wrapChildren(\"h6\", 6)\n\n                MarkdownTokenTypes.TEXT,\n                MarkdownTokenTypes.WHITE_SPACE,\n                MarkdownTokenTypes.COLON,\n                MarkdownTokenTypes.SINGLE_QUOTE,\n                MarkdownTokenTypes.DOUBLE_QUOTE,\n                MarkdownTokenTypes.LPAREN,\n                MarkdownTokenTypes.RPAREN,\n                MarkdownTokenTypes.LBRACKET,\n                MarkdownTokenTypes.RBRACKET,\n                MarkdownTokenTypes.EXCLAMATION_MARK,\n                GFMTokenTypes.CHECK_BOX,\n                GFMTokenTypes.GFM_AUTOLINK -> {\n                    sb.append(nodeText)\n                }\n\n                MarkdownTokenTypes.ATX_HEADER,\n                MarkdownTokenTypes.ATX_CONTENT -> {\n                    processChildren()\n                }\n\n                else -> {\n                    if (nodeType.name == \"ROOT\") {\n                        processChildren()\n                    }\n\n//                    processChildren()\n                }\n            }\n        }\n\n        return sb.toString().trimEnd()\n    }\n\n\n    class MarkdownNode(val node: ASTNode, val parent: MarkdownNode?, val content: String) {\n        val children: List<MarkdownNode> = node.children.map { MarkdownNode(it, this, content) }\n        val startOffset: Int get() = node.startOffset\n        val type: IElementType get() = node.type\n        var text: String = content.substring(node.startOffset, node.endOffset)\n        fun child(type: IElementType): MarkdownNode? = children.firstOrNull { it.type == type }\n    }\n\n    private fun MarkdownNode.visit(action: (MarkdownNode, () -> Unit) -> Unit) {\n        action(this) {\n            for (child in children) {\n                child.visit(action)\n            }\n        }\n    }\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/provider/psi/PsiCapture.kt",
    "content": "package com.phodal.shirecore.provider.psi\n\nimport com.intellij.lang.Language\nimport com.intellij.lang.LanguageExtension\n\ninterface PsiCapture {\n    fun capture(fileContent: String, type: String): List<Any>\n\n    companion object {\n        private val languageExtension: LanguageExtension<PsiCapture> =\n            LanguageExtension(\"com.phodal.shirePsiCapture\")\n\n        fun provide(language: Language): PsiCapture? {\n            return languageExtension.forLanguage(language)\n        }\n    }\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/provider/psi/PsiElementDataBuilder.kt",
    "content": "package com.phodal.shirecore.provider.psi\n\nimport com.intellij.lang.Language\nimport com.intellij.lang.LanguageExtension\nimport com.intellij.openapi.project.Project\nimport com.intellij.psi.PsiElement\nimport com.phodal.shirecore.provider.codemodel.model.ClassStructure\n\n/**\n * The `PsiElementDataBuilder` interface provides methods for generating test data for a given Kotlin language class.\n *\n * It defines the following methods:\n * - `baseRoute(element: PsiElement): String`: This method is used to lookup the base route of the method for the parent element.\n *   It takes a `PsiElement` as a parameter and returns a `String` representing the base route. If no base route is found, an empty string is returned.\n *\n * - `inboundData(element: PsiElement): Map<String, String>`: This method is used to generate inbound test data for the given element.\n *   It takes a `PsiElement` as a parameter and returns a `Map<String, String>` representing the inbound test data. The keys in the map represent the data field names, and the values represent the corresponding data values.\n *   If no inbound data is found, an empty map is returned.\n *\n * - `outboundData(element: PsiElement): Map<String, String>`: This method is used to generate outbound test data for the given element.\n *   It takes a `PsiElement` as a parameter and returns a `Map<String, String>` representing the outbound test data. The keys in the map represent the data field names, and the values represent the corresponding data values.\n *   If no outbound data is found, an empty map is returned.\n *\n * The `TestDataBuilder` interface also provides a companion object with the following method:\n * - `forLanguage(language: Language): TestDataBuilder?`: This method is used to retrieve a `TestDataBuilder` instance for the specified language.\n *   It takes a `Language` as a parameter and returns a `TestDataBuilder` instance associated with the given language. If no instance is found, null is returned.\n *\n * Note: The `TestDataBuilder` interface is intended to be implemented by concrete classes that provide the actual implementation for generating test data.\n *\n * @see PsiElement\n * @see Language\n * @see LanguageExtension\n */\ninterface PsiElementDataBuilder {\n    /**\n     * Lookup the base route of the Method for parent\n     */\n    fun baseRoute(element: PsiElement): String = \"\"\n\n    fun inboundData(element: PsiElement): Map<String, String> = mapOf()\n\n    fun outboundData(element: PsiElement): Map<String, String> = mapOf()\n\n    fun lookupElement(project: Project, canonicalName: String): ClassStructure? = null\n\n    fun parseComment(project: Project, code: String): String?\n\n    companion object {\n        private val languageExtension: LanguageExtension<PsiElementDataBuilder> =\n            LanguageExtension(\"com.phodal.shirePsiElementDataBuilder\")\n\n        fun provide(language: Language): PsiElementDataBuilder? {\n            return languageExtension.forLanguage(language)\n        }\n    }\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/provider/psi/PsiElementStrategyBuilder.kt",
    "content": "package com.phodal.shirecore.provider.psi\n\nimport com.intellij.lang.Language\nimport com.intellij.lang.LanguageExtension\nimport com.intellij.openapi.project.Project\nimport com.intellij.psi.PsiComment\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.PsiNameIdentifierOwner\nimport com.phodal.shirecore.provider.codemodel.model.ClassStructure\n\ninterface PsiElementStrategyBuilder {\n\n    /**\n     * Looks up a symbol in the given project by its canonical name.\n     * The canonical name is the fully qualified name of the symbol, including the package name.\n     * For example, the canonical name of the class `java.lang.String` is `java.lang.String`.\n     * The canonical name of the method `java.lang.String#length` is `java.lang.String.length()`.\n     *\n     * @param project the project in which to search for the symbol\n     * @param canonicalName the canonical name of the symbol to look up\n     * @return the ClassStructure representing the symbol with the given canonical name, or null if not found\n     */\n    fun lookupElement(project: Project, canonicalName: String): ClassStructure?\n\n    /**\n     * Return the relative [PsiElement] with [PsiComment] for givenElement in the given project.\n     */\n    fun relativeElement(project: Project, givenElement: PsiElement, type: PsiComment): PsiElement?\n\n    /**\n     * Find the nearest target [PsiNameIdentifierOwner] for the given [PsiElement].\n     */\n    fun findNearestTarget(psiElement: PsiElement): PsiNameIdentifierOwner?\n\n    companion object {\n        private val languageExtension: LanguageExtension<PsiElementStrategyBuilder> =\n            LanguageExtension(\"com.phodal.shireElementStrategyBuilder\")\n\n        fun forLanguage(language: Language): PsiElementStrategyBuilder? {\n            return languageExtension.forLanguage(language)\n        }\n    }\n}\n\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/provider/psi/RelatedClassesProvider.kt",
    "content": "package com.phodal.shirecore.provider.psi\n\nimport com.intellij.lang.Language\nimport com.intellij.lang.LanguageExtension\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.PsiFile\n\n/**\n * The `RelatedClassesProvider` interface is used to provide related classes for a given element.\n *\n * This interface is particularly useful in languages like Java, where it can analyze elements such as\n * `PsiMethod` or `PsiField` to find related classes. The analysis includes parameters, return type, and\n * generic types of the `PsiMethod` to find related classes that are part of the project content.\n *\n * The `RelatedClassesProvider` interface also includes methods to clean up unnecessary elements in a `PsiClass`,\n * find superclasses of a `PsiClass`, and determine if an element is part of the project content.\n *\n * The main function of this interface is `lookup(element: PsiElement)`, which is used to look up related classes\n * for the given method. The function takes a `PsiElement` as an argument and returns a list of `PsiElement` objects\n * that are related to the input element.\n *\n * Note: There is a need to investigate whether this function is also needed for fields or classes.\n */\ninterface RelatedClassesProvider {\n    /**\n     * Lookup related classes for the given method.\n     *\n     * todo: spike is need for field or class\n     */\n    fun lookup(element: PsiElement): List<PsiElement>\n\n    fun lookup(element: PsiFile): List<PsiElement>\n\n    companion object {\n        private val languageExtension: LanguageExtension<RelatedClassesProvider> =\n            LanguageExtension(\"com.phodal.shireRelatedClass\")\n\n        fun provide(language: Language): RelatedClassesProvider? {\n            return languageExtension.forLanguage(language)\n        }\n    }\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/provider/shire/FileCreateService.kt",
    "content": "package com.phodal.shirecore.provider.shire\n\nimport com.intellij.lang.Language\nimport com.intellij.lang.LanguageExtension\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.vfs.VirtualFile\n\n\ninterface FileCreateService {\n    fun createFile(prompt: String, project: Project): VirtualFile?\n\n    companion object {\n        private val languageExtension: LanguageExtension<FileCreateService> =\n            LanguageExtension(\"com.phodal.shireFileCreateService\")\n\n        fun provide(language: Language): FileCreateService? {\n            return languageExtension.forLanguage(language)\n        }\n    }\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/provider/shire/FileRunService.kt",
    "content": "package com.phodal.shirecore.provider.shire\n\nimport com.intellij.execution.RunManager\nimport com.intellij.execution.RunnerAndConfigurationSettings\nimport com.intellij.execution.actions.ConfigurationContext\nimport com.intellij.execution.configurations.GeneralCommandLine\nimport com.intellij.execution.configurations.RunConfiguration\nimport com.intellij.execution.configurations.RunProfile\nimport com.intellij.execution.util.ExecUtil\nimport com.intellij.openapi.application.runReadAction\nimport com.intellij.openapi.diagnostic.Logger\nimport com.intellij.openapi.diagnostic.logger\nimport com.intellij.openapi.extensions.ExtensionPointName\nimport com.intellij.openapi.progress.ProgressManager\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.PsiFile\nimport com.intellij.psi.PsiManager\nimport com.phodal.shirecore.runner.RunServiceTask\nimport java.util.concurrent.CompletableFuture\n\ninterface FileRunService {\n    private val logger: Logger get() = logger<FileRunService>()\n\n    fun isApplicable(project: Project, file: VirtualFile): Boolean\n\n    /**\n     * Retrieves the run configuration class for the given project.\n     *\n     * @param project The project for which to retrieve the run configuration class.\n     * @return The run configuration class for the project.\n     */\n    fun runConfigurationClass(project: Project): Class<out RunProfile>?\n\n    /**\n     * Creates a new run configuration for the given project and virtual file.\n     *\n     * 1. Looks up the PSI file from the virtual file using `PsiManager.getInstance(project).findFile(virtualFile)`.\n     * 2. Creates a RunConfigurationSettings instance with the name \"name\" and the specified RunConfigurationType using `RunManager.getInstance(project).createConfiguration(\"name\", RunConfigurationType)`.\n     *\n     * @param project The project for which to create the run configuration.\n     * @param virtualFile The virtual file to associate with the run configuration.\n     * @return The newly created RunConfiguration, or `null` if creation failed.\n     */\n    fun createConfiguration(project: Project, virtualFile: VirtualFile): RunConfiguration? = null\n\n    /**\n     * Creates a new run configuration settings for the given project and virtual file.\n     *\n     * If a configuration with the same name already exists, it will be returned.\n     * Otherwise, a new configuration is created and added to the run manager.\n     *\n     * @param project The project for which the configuration should be created.\n     * @param virtualFile The virtual file for which the configuration should be created.\n     * @return The created or found run configuration settings, or `null` if no suitable configuration could be\n     */\n    fun createRunSettings(\n        project: Project,\n        virtualFile: VirtualFile,\n        testElement: PsiElement?,\n    ): RunnerAndConfigurationSettings? {\n        if (testElement != null) {\n            val settings = createDefaultConfigurations(project, testElement)\n            if (settings != null) {\n                return settings\n            }\n        }\n\n        val runManager = RunManager.getInstance(project)\n        var testConfig = runManager.allConfigurationsList.firstOrNull {\n            val runConfigureClass = runConfigurationClass(project)\n            it.name == virtualFile.nameWithoutExtension && (it.javaClass == runConfigureClass)\n        }\n\n        if (testConfig == null) {\n            testConfig = createConfiguration(project, virtualFile)\n        }\n\n        if (testConfig == null) {\n            logger.warn(\"Failed to find test configuration for: ${virtualFile.nameWithoutExtension}\")\n            return null\n        }\n\n        val settings = runManager.findConfigurationByTypeAndName(testConfig.type, testConfig.name)\n        if (settings == null) {\n            logger.warn(\"Failed to find test configuration for: ${virtualFile.nameWithoutExtension}\")\n            return null\n        }\n\n        settings.isTemporary = true\n        runManager.selectedConfiguration = settings\n\n        return settings\n    }\n\n    fun createDefaultConfigurations(\n        project: Project,\n        element: PsiElement,\n    ): RunnerAndConfigurationSettings? {\n        return runReadAction {\n            ConfigurationContext(element).configurationsFromContext?.firstOrNull()?.configurationSettings\n        }\n    }\n\n    /**\n     * This function is responsible for running a file within a specified project and virtual file. It is a synchronous operation.\n     * [runFileAsync] should be used for asynchronous operations.\n     *\n     * It creates a run configuration using the provided parameters and then attempts to execute it using\n     * the `ExecutionManager`. The function returns `null` if an error occurs during the configuration creation or execution process.\n     *\n     * @param project The project within which the file is to be run.\n     * @param virtualFile The virtual file that represents the file to be run.\n     * @return The result of the run operation, or `null` if an error occurred.\n     */\n    fun runFile(project: Project, virtualFile: VirtualFile, psiElement: PsiElement?): String? {\n        try {\n            val runTask = RunServiceTask(project, virtualFile, psiElement, this)\n            ProgressManager.getInstance().run(runTask)\n        } catch (e: Exception) {\n            logger.error(\"Failed to run file: ${virtualFile.name}\", e)\n            return e.message\n        }\n\n        return null\n    }\n\n    /**\n     * This function is responsible for running a file within a specified project and virtual file asynchronously.\n     *\n     * @param project The project within which the file is to be run.\n     * @param virtualFile The virtual file that represents the file to be run.\n     * @return The result of the run operation, or `null` if an error occurred.\n     */\n    fun runFileAsync(project: Project, virtualFile: VirtualFile, psiElement: PsiElement?): String? {\n        val future: CompletableFuture<String> = CompletableFuture<String>()\n\n        try {\n            val runTask = RunServiceTask(project, virtualFile, psiElement, this, future = future)\n            ProgressManager.getInstance().run(runTask)\n        } catch (e: Exception) {\n            logger.error(\"Failed to run file: ${virtualFile.name}\", e)\n            future.completeExceptionally(e)\n            return e.message\n        }\n\n        return future.get()\n    }\n\n    companion object {\n        val EP_NAME: ExtensionPointName<FileRunService> = ExtensionPointName(\"com.phodal.shireFileRunService\")\n\n        fun provider(project: Project, file: VirtualFile): FileRunService? {\n            val fileRunServices = EP_NAME.extensionList\n            return fileRunServices.firstOrNull {\n                runReadAction { it.isApplicable(project, file) }\n            }\n        }\n\n        fun runInCli(project: Project, psiFile: PsiFile, args: List<String>? = null): String? {\n            val commandLine = when (psiFile.language.displayName.lowercase()) {\n                \"python\" -> GeneralCommandLine(\"python3\", psiFile.virtualFile.path)\n                \"javascript\" -> GeneralCommandLine(\"node\", psiFile.virtualFile.path)\n                \"ecmascript 6\" -> GeneralCommandLine(\"node\", psiFile.virtualFile.path)\n                \"ruby\" -> GeneralCommandLine(\"ruby\", psiFile.virtualFile.path)\n                \"shell script\" -> GeneralCommandLine(\"sh\", psiFile.virtualFile.path)\n                // kotlin script, `kotlinc -script hello.kts`\n                \"kotlin\" -> GeneralCommandLine(\"kotlinc\", \"-script\", psiFile.virtualFile.path)\n                else -> {\n                    logger<FileRunService>().warn(\"Unsupported language: ${psiFile.language.displayName}\")\n                    return null\n                }\n            }\n\n            if (args != null) {\n                commandLine.addParameters(args)\n            }\n\n            commandLine.setWorkDirectory(project.basePath)\n            return try {\n                val output = ExecUtil.execAndGetOutput(commandLine)\n                output.stdout\n            } catch (e: Exception) {\n                e.printStackTrace()\n                e.message\n            }\n        }\n\n        fun runInCli(project: Project, virtualFile: VirtualFile, args: List<String>? = null): String? {\n            val psiFile = runReadAction { PsiManager.getInstance(project).findFile(virtualFile) } ?: return null\n            return runInCli(project, psiFile, args)\n        }\n\n        /**\n         * We will handle Shire UnSupported FileType here\n         */\n        fun retryRun(project: Project, virtualFile: VirtualFile, args: List<String>? = null): String? {\n            val defaultRunService = object : FileRunService {\n                override fun isApplicable(project: Project, file: VirtualFile): Boolean = true\n                override fun runConfigurationClass(project: Project): Class<out RunProfile>? = null\n            }\n\n            val file = runReadAction { PsiManager.getInstance(project).findFile(virtualFile) }\n\n            defaultRunService.createRunSettings(project, virtualFile, file) ?: return null\n\n            return defaultRunService.runFile(project, virtualFile, null)\n        }\n    }\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/provider/shire/ProjectRunService.kt",
    "content": "package com.phodal.shirecore.provider.shire\n\nimport com.intellij.codeInsight.completion.CompletionParameters\nimport com.intellij.codeInsight.completion.CompletionResultSet\nimport com.intellij.codeInsight.lookup.LookupElement\nimport com.intellij.openapi.extensions.ExtensionPointName\nimport com.intellij.openapi.project.Project\n\ninterface ProjectRunService {\n    fun isAvailable(project: Project): Boolean\n\n    fun run(project: Project, taskName: String)\n\n    /**\n     * Return List of available tasks\n     *\n     * This function takes in a Project, CompletionParameters, and CompletionResultSet as parameters\n     * and returns a Map of LookupElement to Priority. It is used to lookup available tasks.\n     *\n     * @param project the project in which the tasks are available\n     * @param parameters the completion parameters for the task lookup\n     * @param result the completion result set to store the lookup results\n     * @return a Map of LookupElement to Priority representing the available tasks\n     */\n    fun lookupAvailableTask(\n        project: Project,\n        parameters: CompletionParameters,\n        result: CompletionResultSet,\n    ): List<LookupElement> {\n        return listOf()\n    }\n\n    fun tasks(project: Project): List<String> {\n        return listOf()\n    }\n\n    companion object {\n        val EP_NAME: ExtensionPointName<ProjectRunService> = ExtensionPointName(\"com.phodal.shireRunProjectService\")\n\n        fun all(): List<ProjectRunService> {\n            return EP_NAME.extensionList\n        }\n\n        fun provider(project: Project): ProjectRunService? {\n            val projectRunServices = EP_NAME.extensionList\n            return projectRunServices.firstOrNull { it.isAvailable(project) }\n        }\n    }\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/provider/shire/RefactoringTool.kt",
    "content": "package com.phodal.shirecore.provider.shire\n\nimport com.intellij.lang.Language\nimport com.intellij.lang.LanguageExtension\nimport com.intellij.openapi.application.ApplicationManager\nimport com.intellij.openapi.command.CommandProcessor\nimport com.intellij.openapi.command.WriteCommandAction\nimport com.intellij.openapi.progress.ProgressManager\nimport com.intellij.openapi.project.Project\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.PsiFile\nimport com.intellij.psi.PsiNamedElement\nimport com.intellij.psi.util.PsiUtilCore\nimport com.intellij.refactoring.RefactoringBundle\nimport com.intellij.refactoring.listeners.RefactoringElementListener\nimport com.intellij.refactoring.rename.RenameProcessor\nimport com.intellij.refactoring.rename.RenameUtil\nimport com.intellij.refactoring.rename.naming.AutomaticRenamerFactory\nimport com.intellij.refactoring.util.CommonRefactoringUtil\nimport com.intellij.usageView.UsageInfo\nimport com.intellij.util.ThrowableRunnable\n\n/**\n * RefactoringTool is an interface that defines operations for performing various code refactoring tasks.\n * It provides functionality to work with PsiFiles and PsiElements, such as looking up files, renaming,\n * safely deleting elements, and moving elements or packages to a new location.\n */\ninterface RefactoringTool {\n    /**\n     * Looks up and retrieves a PsiFile from the given file path.\n     *\n     * @param path The path of the file to be looked up. It should be a valid file path within the project.\n     * @return A PsiFile object if the file is found and successfully loaded, or null if the file doesn't exist or cannot be loaded.\n     */\n    fun lookupFile(path: String): PsiFile?\n\n    /**\n     * Renames a given source name to a target name within the provided PSI file.\n     *\n     * @param sourceName The original name to be renamed.\n     * @param targetName The new name to replace the original name with.\n     * @param psiFile The PSI file where the renaming will take place; can be null if not applicable.\n     * @return A boolean value indicating whether the renaming was successful. Returns true if the\n     *         renaming was successful, false otherwise.\n     */\n    fun rename(sourceName: String, targetName: String, psiFile: PsiFile?): Boolean\n\n    /**\n     * Deletes the given PsiElement in a safe manner, ensuring that no syntax errors or unexpected behavior occur as a result.\n     * The method performs checks before deletion to confirm that it is safe to remove the element from the code structure.\n     *\n     * @param element The PsiElement to be deleted. This should be a valid element within the PSI tree structure.\n     * @return true if the element was successfully deleted without any issues, false otherwise. This indicates whether\n     * the deletion was performed and considered safe.\n     */\n    fun safeDelete(element: PsiElement): Boolean\n\n    /**\n     * Performs a refactoring rename operation on a given PSI element within the specified project.\n     * The method iterates through available renamer factories to find the appropriate one for the\n     * element to be renamed. It then collects usages of the element, checks read-only status,\n     * and performs the rename operation on all found usages, including those in comments if applicable.\n     *\n     * @param myProject The project in which the refactoring operation is taking place.\n     * @param elementToRename The PSI element (which must implement PsiNamedElement) that is to be renamed.\n     * @param newName The new name to assign to the element and its usages.\n     *\n     * Note: The method uses ProgressManager to search for usages and may prompt the user for read-only\n     * file access. It also uses ApplicationManager to schedule the rename operation on the EDT.\n     */\n    fun performRefactoringRename(myProject: Project, elementToRename: PsiNamedElement, newName: String) {\n        for (renamerFactory in AutomaticRenamerFactory.EP_NAME.extensionList) {\n            if (!renamerFactory.isApplicable(elementToRename)) continue\n            val usages: List<UsageInfo> = ArrayList()\n            val renamer = renamerFactory.createRenamer(elementToRename, newName, ArrayList())\n            if (!renamer.hasAnythingToRename()) continue\n\n            val runnable = Runnable {\n                ApplicationManager.getApplication().runReadAction {\n                    renamer.findUsages(usages, false, false)\n                }\n            }\n\n            if (!ProgressManager.getInstance().runProcessWithProgressSynchronously(\n                    runnable, RefactoringBundle.message(\"searching.for.variables\"), true, myProject\n                )\n            ) {\n                return\n            }\n\n            if (!CommonRefactoringUtil.checkReadOnlyStatus(\n                    myProject,\n                    *PsiUtilCore.toPsiElementArray(renamer.elements)\n                )\n            ) return\n\n            val performAutomaticRename = ThrowableRunnable<RuntimeException> {\n                CommandProcessor.getInstance().markCurrentCommandAsGlobal(myProject)\n                val classified = RenameProcessor.classifyUsages(renamer.elements, usages)\n                for (element in renamer.elements) {\n                    val newElementName = renamer.getNewName(element)\n                    if (newElementName != null) {\n                        val infos = classified[element]\n                        RenameUtil.doRename(\n                            element,\n                            newElementName,\n                            infos.toTypedArray(),\n                            myProject,\n                            RefactoringElementListener.DEAF\n                        )\n                    }\n                }\n            }\n\n            ApplicationManager.getApplication().invokeLater {\n                WriteCommandAction.writeCommandAction(myProject)\n                    .withName(\"Rename\").run(performAutomaticRename)\n            }\n        }\n    }\n\n    /**\n     * In Java the canonicalName is the fully qualified name of the target package.\n     * In Kotlin the canonicalName is the fully qualified name of the target package or class.\n     */\n    fun move(element: PsiElement, canonicalName: String): Boolean\n\n    companion object {\n        private val languageExtension: LanguageExtension<RefactoringTool> =\n            LanguageExtension(\"com.phodal.shireRefactoringTool\")\n\n        fun forLanguage(language: Language): RefactoringTool? {\n            val refactoringTool = languageExtension.forLanguage(language)\n            if (refactoringTool != null) {\n                return refactoringTool\n            }\n\n            // If no refactoring tool is found for the specified language, return java\n            val javaLanguage = Language.findLanguageByID(\"JAVA\") ?: return null\n            return languageExtension.forLanguage(javaLanguage)\n        }\n    }\n}\n\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/provider/shire/RevisionProvider.kt",
    "content": "package com.phodal.shirecore.provider.shire\n\nimport com.intellij.codeInsight.completion.CompletionResultSet\nimport com.intellij.openapi.extensions.ExtensionPointName\nimport com.intellij.openapi.project.Project\nimport com.intellij.psi.PsiElement\n\ninterface RevisionProvider {\n    /**\n     * Fetches the changes made in the specified revision in the repository of the given project.\n     *\n     * @param myProject the project in which the Git repository is located\n     * @param revision the revision for which changes need to be fetched\n     * @return a String containing the changes in the specified revision in a unified diff format,\n     * or null if the repository is not found\n     */\n    fun fetchChanges(myProject: Project, revision: String): String?\n\n    fun fetchCompletions(project: Project, result: CompletionResultSet)\n\n    fun commitCode(project: Project, commitMessage: String): String\n\n    fun countHistoryChange(project: Project, element: PsiElement): Int\n\n    companion object {\n        private val EP_NAME: ExtensionPointName<RevisionProvider> =\n            ExtensionPointName(\"com.phodal.shireRevisionProvider\")\n\n        fun provide(): RevisionProvider? {\n            return EP_NAME.extensionList.firstOrNull()\n        }\n    }\n\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/provider/shire/ShireQLDataProvider.kt",
    "content": "package com.phodal.shirecore.provider.shire\n\nimport com.intellij.openapi.extensions.ExtensionPointName\nimport com.intellij.openapi.project.Project\nimport com.phodal.shirecore.variable.vcs.GitEntity\nimport com.phodal.shirecore.variable.vcs.ShireGitCommit\n\nenum class ShireQLDataType(val dataKey: String) {\n    GIT_COMMIT(\"GitCommit\"),\n    GIT_BRANCH(\"GitBranch\"),\n    GIT_FILE_COMMIT(\"GitFileCommit\"),\n    GIT_FILE_BRANCH(\"GitFileBranch\")\n}\n\ninterface ShireQLDataProvider {\n    fun lookupGitData(myProject: Project, dataTypes: List<ShireQLDataType>): Map<ShireQLDataType, List<GitEntity>?>\n\n    fun lookup(myProject: Project, variableType: String): List<ShireGitCommit>? {\n        return when (variableType) {\n            ShireQLDataType.GIT_COMMIT.dataKey -> {\n                return lookupGitData(myProject, listOf(ShireQLDataType.GIT_COMMIT))[ShireQLDataType.GIT_COMMIT] as List<ShireGitCommit>?\n            }\n\n            else -> {\n                null\n            }\n        }\n    }\n\n    companion object {\n        private val EP_NAME: ExtensionPointName<ShireQLDataProvider> =\n            ExtensionPointName(\"com.phodal.shireQLDataProvider\")\n\n        fun all(): List<ShireQLDataProvider> {\n            return EP_NAME.extensionList\n        }\n    }\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/provider/shire/ShireSymbolProvider.kt",
    "content": "package com.phodal.shirecore.provider.shire\n\nimport com.intellij.codeInsight.completion.CompletionParameters\nimport com.intellij.codeInsight.completion.CompletionResultSet\nimport com.intellij.codeInsight.lookup.LookupElement\nimport com.intellij.openapi.extensions.ExtensionPointName\nimport com.intellij.openapi.project.Project\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.PsiNamedElement\n\n/**\n * The symbol provider for Shire completion and execution\n * - Completion will be triggered by like `/symbol:`, and the symbol provider will provide the completion for the symbol.\n * - Execution will be triggered by like `/symbol:java.lang.String`, all load children level elements, like `java.lang.String#length()`\n *\n * For execution, see in [ShireSymbolProvider.resolveSymbol]\n */\ninterface ShireSymbolProvider {\n    val language: String\n\n    /**\n     * Lookup canonical name for different language\n     */\n    fun lookupSymbol(\n        project: Project,\n        parameters: CompletionParameters,\n        result: CompletionResultSet,\n    ): List<LookupElement>\n\n    fun lookupElementByName(project: Project, name: String): List<PsiElement>?\n\n    /**\n     * Resolves the symbol for different programming languages.\n     * For example, in Java:\n     * - If the parent is Root, the children will be packages\n     * - If the parent is Package, the children will be classes\n     * - If the parent is Class, the children will be methods and fields\n     *\n     * Format: `java.lang.String#length`, means:\n     * - `<package>.<class>#<method>`\n     * - `<package>.<class>#<field>`\n     * - `<package>.<class>#<constructor>`\n     *\n     */\n    fun resolveSymbol(project: Project, symbol: String): List<PsiNamedElement>\n\n    companion object {\n        private val EP_NAME: ExtensionPointName<ShireSymbolProvider> =\n            ExtensionPointName(\"com.phodal.shireSymbolProvider\")\n\n        fun all(): List<ShireSymbolProvider> {\n            return EP_NAME.extensionList\n        }\n    }\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/provider/sketch/LanguageSketchProvider.kt",
    "content": "package com.phodal.shirecore.provider.sketch\n\nimport com.intellij.openapi.extensions.ExtensionPointName\nimport com.intellij.openapi.project.Project\nimport com.phodal.shirecore.sketch.LangSketch\n\n\ninterface ExtensionLangSketch: LangSketch {\n    fun getExtensionName(): String\n}\n\ninterface LanguageSketchProvider {\n    fun isSupported(lang: String): Boolean\n\n    fun create(project: Project, content: String): ExtensionLangSketch\n\n    companion object {\n        private val EP_NAME: ExtensionPointName<LanguageSketchProvider> =\n            ExtensionPointName(\"com.phodal.shireLangSketchProvider\")\n\n        fun provide(language: String): LanguageSketchProvider? {\n            val lang = language.lowercase()\n            return EP_NAME.extensionList.firstOrNull {\n                it.isSupported(lang)\n            }\n        }\n    }\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/provider/streaming/LoggingStreamingService.kt",
    "content": "package com.phodal.shirecore.provider.streaming\n\nimport com.intellij.openapi.application.ApplicationManager\nimport com.intellij.openapi.application.WriteAction\nimport com.intellij.openapi.application.runInEdt\nimport com.intellij.openapi.application.runWriteAction\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.intellij.testFramework.LightVirtualFile\nimport com.phodal.shirecore.LLM_LOGGING\nimport com.phodal.shirecore.LLM_LOGGING_JSONL\nimport com.phodal.shirecore.ShireConstants\nimport com.phodal.shirecore.llm.ChatMessage\nimport com.phodal.shirecore.llm.ChatRole\nimport com.phodal.shirecore.runner.console.ShireConsoleViewBase\nimport kotlinx.serialization.encodeToString\nimport kotlinx.serialization.json.Json\n\n/**\n * The `LoggingStreamingService` class is an implementation of the `StreamingServiceProvider` interface.\n * It provides functionality to log streaming data to a file within a project's directory.\n *\n * ### Properties:\n * - `name`: A string that represents the name of the streaming service, initialized to \"logging\".\n * - `result`: A private string that accumulates the streaming data received.\n */\nclass LoggingStreamingService : StreamingServiceProvider {\n    private var outputDir: VirtualFile = LightVirtualFile()\n    override var name: String = \"logging\"\n\n    private var result: String = \"\"\n    private var userPrompt: String = \"\"\n\n    override fun onBeforeStreaming(project: Project, userPrompt: String, console: ShireConsoleViewBase?) {\n        this.userPrompt = userPrompt\n        this.outputDir = ShireConstants.outputDir(project) ?: throw IllegalStateException(\"Project directory not found\")\n        if (outputDir.findChild(LLM_LOGGING) == null) {\n            ApplicationManager.getApplication().invokeAndWait {\n                WriteAction.compute<VirtualFile, Throwable> {\n                    outputDir.createChildData(this, LLM_LOGGING)\n                }\n            }\n        } else {\n            runInEdt {\n                val file = outputDir.findChild(LLM_LOGGING)\n                runWriteAction {\n                    file?.setBinaryContent(ByteArray(0))\n                }\n            }\n        }\n\n        if (outputDir.findChild(LLM_LOGGING_JSONL) == null) {\n            ApplicationManager.getApplication().invokeAndWait {\n                WriteAction.compute<VirtualFile, Throwable> {\n                    outputDir.createChildData(this, LLM_LOGGING_JSONL)\n                }\n            }\n        }\n    }\n\n    override fun onStreaming(project: Project, flow: String, args: List<Any>) {\n        result += flow\n\n        val virtualFile = outputDir.findChild(LLM_LOGGING)\n        val file = virtualFile?.path?.let { java.io.File(it) }\n        file?.appendText(flow)\n    }\n\n    override fun afterStreamingDone(project: Project) {\n        ApplicationManager.getApplication().invokeAndWait {\n            WriteAction.compute<VirtualFile, Throwable> {\n                val virtualFile = outputDir.createChildData(this, LLM_LOGGING_JSONL)\n                val file = java.io.File(virtualFile.path)\n                val value: List<ChatMessage> = listOf(\n                    ChatMessage(ChatRole.user, userPrompt),\n                    ChatMessage(ChatRole.system, result)\n                )\n\n                val result = Json.encodeToString<List<ChatMessage>>(value)\n\n                file.appendText(result)\n                file.appendText(\"\\n\")\n                virtualFile\n            }\n        }\n    }\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/provider/streaming/OnStreamingService.kt",
    "content": "package com.phodal.shirecore.provider.streaming\n\nimport com.intellij.openapi.application.ApplicationManager\nimport com.intellij.openapi.components.Service\nimport com.intellij.openapi.project.Project\nimport com.intellij.psi.PsiElement\nimport com.phodal.shirecore.ShirelangNotifications\nimport com.phodal.shirecore.middleware.post.LifecycleProcessorSignature\nimport com.phodal.shirecore.runner.console.ShireConsoleViewBase\n\n/**\n * The OnStreamingService class is responsible for managing all the [StreamingServiceProvider] instances related to streaming services.\n * It offers methods for registering, clearing, and initiating streaming services within the application.\n *\n * This class is annotated with the @Service annotation at the project level, indicating its role in the service management infrastructure.\n *\n * The class maintains a mutable map to associate [LifecycleProcessorSignature] objects with corresponding [StreamingServiceProvider] instances.\n * It also holds an optional reference to a console view object that can be used for outputting information to the user.\n */\n@Service(Service.Level.PROJECT)\nclass OnStreamingService {\n    val map = mutableMapOf<LifecycleProcessorSignature, StreamingServiceProvider>()\n    var console: ShireConsoleViewBase? = null\n\n    fun registerStreamingService(sign: LifecycleProcessorSignature, console: ShireConsoleViewBase?) {\n        this.console = console\n        val streamingService = StreamingServiceProvider.getStreamingService(sign.funcName)\n        if (streamingService != null) {\n            map[sign] = streamingService\n            streamingService.onCreated(console)\n        }\n    }\n\n    fun clearStreamingService() {\n        map.clear()\n    }\n\n    fun all(): List<StreamingServiceProvider> {\n        return StreamingServiceProvider.all()\n    }\n\n    fun onStart(project: Project, userPrompt: String) {\n        map.forEach { (_, service) ->\n            try {\n                service.onBeforeStreaming(project, userPrompt, console)\n            } catch (e: Exception) {\n                ShirelangNotifications.error(project, \"Error on start streaming service: ${e.message}\")\n            }\n        }\n    }\n\n    fun onStreaming(project: Project, chunk: String) {\n        map.forEach { (sign, service) ->\n            try {\n                service.\n                onStreaming(project, chunk, sign.args)\n            } catch (e: Exception) {\n                ShirelangNotifications.error(project, \"Error on streaming service: ${e.message}\")\n            }\n        }\n    }\n\n    fun onDone(project: Project) {\n        map.forEach { (_, service) ->\n            try {\n                service.afterStreamingDone(project)\n            } catch (e: Exception) {\n                ShirelangNotifications.error(project, \"Error on done streaming service: ${e.message}\")\n            }\n        }\n    }\n\n    fun onStreamingError() {\n        // todo\n    }\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/provider/streaming/ProfilingStreamingService.kt",
    "content": "package com.phodal.shirecore.provider.streaming\n\nimport com.intellij.execution.ui.ConsoleViewContentType\nimport com.intellij.openapi.project.Project\nimport com.intellij.util.io.IOUtil\nimport com.phodal.shirecore.ShirelangNotifications\nimport com.phodal.shirecore.runner.console.ShireConsoleViewBase\n\n\n/**\n * The ProfilingStreamingService class is a concrete implementation of the StreamingServiceProvider interface.\n * It provides profiling capabilities during the streaming process, outputting memory usage information to the console.\n */\nclass ProfilingStreamingService : StreamingServiceProvider {\n    override var name: String = \"profiling\"\n    private var console: ShireConsoleViewBase? = null\n\n    override fun onBeforeStreaming(project: Project, userPrompt: String, console: ShireConsoleViewBase?) {\n        this.console = console\n        console?.print(\"Start profiling: ${getMemory()}\", ConsoleViewContentType.SYSTEM_OUTPUT)\n\n    }\n\n    override fun afterStreamingDone(project: Project) {\n        console?.print(\"End profiling: ${getMemory()}\", ConsoleViewContentType.SYSTEM_OUTPUT)\n        ShirelangNotifications.info(project, \"Memory: ${getMemory()}MB\")\n    }\n\n    private fun getMemory(): Long {\n        val runtime = Runtime.getRuntime()\n        val allocatedMem = runtime.totalMemory()\n        val usedMem = allocatedMem - runtime.freeMemory()\n        return toMb(usedMem)\n    }\n\n    private fun toMb(value: Long): Long {\n        return value / IOUtil.MiB\n    }\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/provider/streaming/StreamingServiceProvider.kt",
    "content": "package com.phodal.shirecore.provider.streaming\n\nimport com.intellij.openapi.Disposable\nimport com.intellij.openapi.project.Project\nimport com.phodal.shirecore.runner.console.ShireConsoleViewBase\n\n/**\n * all - Returns a list of all registered StreamingServiceProvider implementations.\n *\n * @return A list of StreamingServiceProvider instances.\n */\ninterface StreamingServiceProvider : Disposable {\n    var name: String\n\n    /**\n     * When create the service, you can do some initialization here, like start timer, etc.\n     */\n    fun onCreated(console: ShireConsoleViewBase?) {\n        /// do nothing\n    }\n\n    /**\n     * For the start of the LLM streaming, you can do some initialization here, for example, you can create a file to log the data\n     */\n    fun onBeforeStreaming(project: Project, userPrompt: String, console: ShireConsoleViewBase?) {\n        /// do nothing\n    }\n\n    /**\n     * For the streaming data, you can do some processing here, for example, you can log the data to a file\n     */\n    fun onStreaming(project: Project, flow: String, args: List<Any>) {\n        /// do nothing\n    }\n\n    /**\n     * For the end of the streaming, for example, you can do some cleanup here, or show some notification\n     */\n    fun afterStreamingDone(project: Project) {\n        /// do nothing\n    }\n\n    override fun dispose() {\n        /// do nothing\n    }\n\n    companion object {\n        val EP_NAME =\n            com.intellij.openapi.extensions.ExtensionPointName.create<StreamingServiceProvider>(\"com.phodal.shireStreamingService\")\n\n        fun getStreamingService(name: String): StreamingServiceProvider? {\n            return EP_NAME.extensions.firstOrNull { it.name == name }\n        }\n\n        fun all(): List<StreamingServiceProvider> {\n            return EP_NAME.extensions.toList()\n        }\n    }\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/provider/streaming/TimingStreamingService.kt",
    "content": "package com.phodal.shirecore.provider.streaming\n\nimport com.intellij.execution.ui.ConsoleViewContentType\nimport com.intellij.openapi.project.Project\nimport com.phodal.shirecore.ShirelangNotifications\nimport com.phodal.shirecore.runner.console.ShireConsoleViewBase\n\n/**\n * Logging start time and end time for each lifecycle\n */\nclass TimingStreamingService : StreamingServiceProvider {\n    override var name: String = \"timing\"\n\n    private var time: Long = 0\n    private var console: ShireConsoleViewBase? = null\n\n    override fun onCreated(console: ShireConsoleViewBase?) {\n        this.console = console\n        val currentTime = System.currentTimeMillis()\n        time = currentTime\n        // new line\n        console?.print(\"\\n\", ConsoleViewContentType.SYSTEM_OUTPUT)\n        console?.print(\"Start timing: $currentTime \\n\", ConsoleViewContentType.SYSTEM_OUTPUT)\n    }\n\n    override fun afterStreamingDone(project: Project) {\n        val currentTime = System.currentTimeMillis()\n\n        console?.print(\"\\n\", ConsoleViewContentType.SYSTEM_OUTPUT)\n        console?.print(\"End timing: $currentTime \\n\", ConsoleViewContentType.SYSTEM_OUTPUT)\n\n        ShirelangNotifications.info(project, \"Timing: ${currentTime - time}ms\")\n    }\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/provider/variable/PsiContextVariableProvider.kt",
    "content": "package com.phodal.shirecore.provider.variable\n\nimport com.intellij.lang.Language\nimport com.intellij.lang.LanguageExtension\nimport com.intellij.openapi.editor.Editor\nimport com.intellij.openapi.project.Project\nimport com.intellij.psi.PsiElement\nimport com.phodal.shirecore.provider.complexity.ComplexityProvider\nimport com.phodal.shirecore.provider.context.LanguageToolchainProvider\nimport com.phodal.shirecore.provider.context.ToolchainPrepareContext\nimport com.phodal.shirecore.provider.shire.RevisionProvider\nimport com.phodal.shirecore.provider.variable.impl.DefaultPsiContextVariableProvider\nimport com.phodal.shirecore.provider.variable.model.PsiContextVariable\nimport kotlinx.coroutines.runBlocking\nimport java.util.concurrent.CompletableFuture\n\n/**\n * Resolve variables for code struct generation.\n * This is used to provide the variables that are used in the code struct generation.\n */\ninterface PsiContextVariableProvider : VariableProvider<PsiContextVariable> {\n    /**\n     * Calculate the value for the given variable based on the provided PsiElement.\n     *\n     * @param psiElement the PsiElement to use for resolving the variable value\n     * @param variable the PsiVariable for which to calculate the value\n     * @return the calculated value for the variable as a String\n     */\n    override fun resolve(variable: PsiContextVariable, project: Project, editor: Editor, psiElement: PsiElement?): Any\n\n\n    fun collectFrameworkContext(psiElement: PsiElement?, project: Project): String {\n        val future = CompletableFuture<String>()\n        runBlocking {\n            val prepareContext = ToolchainPrepareContext(psiElement?.containingFile, psiElement)\n            val contextItems =\n                LanguageToolchainProvider.collectToolchainContext(project, prepareContext)\n\n            future.complete(contextItems.joinToString(\"\\n\") { it.text })\n        }\n\n        return future.get()\n    }\n\n    fun calculateChangeCount(psiElement: PsiElement?): String {\n        return RevisionProvider.provide()?.countHistoryChange(psiElement?.project!!, psiElement).toString()\n    }\n\n    fun calculateLineCount(psiElement: PsiElement?): String {\n        return psiElement?.containingFile?.text?.lines()?.size.toString()\n    }\n\n    fun calculateComplexityCount(psiElement: PsiElement?): String {\n        if (psiElement?.language == null) {\n            return \"0\"\n        }\n\n        return ComplexityProvider.provide(psiElement.language)?.process(psiElement).toString()\n    }\n\n    companion object {\n        private val languageExtension: LanguageExtension<PsiContextVariableProvider> =\n            LanguageExtension(\"com.phodal.shirePsiVariableProvider\")\n\n        fun provide(language: Language): PsiContextVariableProvider {\n            return languageExtension.forLanguage(language) ?: DefaultPsiContextVariableProvider()\n        }\n    }\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/provider/variable/ShireQLInterpreter.kt",
    "content": "package com.phodal.shirecore.provider.variable\n\nimport com.intellij.lang.Language\nimport com.intellij.lang.LanguageExtension\nimport com.intellij.openapi.project.Project\nimport com.intellij.psi.PsiElement\n\n/**\n * For [com.phodal.shirelang.compiler.hobbit.execute.PsiQueryStatementProcessor]\n */\ninterface ShireQLInterpreter {\n    fun supportsMethod(language: Language, methodName: String): List<String>\n\n    /**\n     * clazz.getName() or clazz.extensions\n     */\n    fun resolveCall(element: PsiElement, methodName: String, arguments: List<Any>): Any\n\n    /**\n     * parentOf or childOf or anyOf ?\n     */\n    fun resolveOfTypedCall(project: Project, methodName: String, arguments: List<Any>): Any\n\n    companion object {\n        private val languageExtension: LanguageExtension<ShireQLInterpreter> =\n            LanguageExtension(\"com.phodal.shirePsiQLInterpreter\")\n\n        fun provide(language: Language): ShireQLInterpreter? {\n            return languageExtension.forLanguage(language)\n        }\n    }\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/provider/variable/ToolchainVariableProvider.kt",
    "content": "package com.phodal.shirecore.provider.variable\n\nimport com.intellij.openapi.extensions.ExtensionPointName\nimport com.intellij.openapi.project.Project\nimport com.intellij.psi.PsiElement\nimport com.phodal.shirecore.provider.variable.model.ToolchainVariable\n\ninterface ToolchainVariableProvider : VariableProvider<ToolchainVariable> {\n    fun isResolvable(variable: ToolchainVariable, psiElement: PsiElement?, project: Project): Boolean\n\n    companion object {\n        private val EP_NAME: ExtensionPointName<ToolchainVariableProvider> =\n            ExtensionPointName(\"com.phodal.shireToolchainVariableProvider\")\n\n        fun all(): List<ToolchainVariableProvider> {\n            return EP_NAME.extensionList\n        }\n\n        fun provide(variable: ToolchainVariable, element: PsiElement?, project: Project): ToolchainVariableProvider? {\n            return EP_NAME.extensionList.firstOrNull {\n                it.isResolvable(variable, element, project)\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/provider/variable/VariableProvider.kt",
    "content": "package com.phodal.shirecore.provider.variable\n\nimport com.intellij.openapi.editor.Editor\nimport com.intellij.openapi.project.Project\nimport com.intellij.psi.PsiElement\n\ninterface VariableProvider<T> {\n    fun resolve(variable: T, project: Project, editor: Editor, psiElement: PsiElement?,): Any\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/provider/variable/impl/DefaultPsiContextVariableProvider.kt",
    "content": "package com.phodal.shirecore.provider.variable.impl\n\nimport com.intellij.openapi.editor.Editor\nimport com.intellij.openapi.project.Project\nimport com.intellij.psi.PsiElement\nimport com.phodal.shirecore.provider.variable.PsiContextVariableProvider\nimport com.phodal.shirecore.provider.variable.model.PsiContextVariable\nimport com.phodal.shirecore.provider.variable.model.PsiContextVariable.*\nimport com.phodal.shirecore.psi.CodeSmellCollector\n\nclass DefaultPsiContextVariableProvider : PsiContextVariableProvider {\n    override fun resolve(\n        variable: PsiContextVariable,\n        project: Project,\n        editor: Editor,\n        psiElement: PsiElement?,\n    ): String {\n        return when (variable) {\n            FRAMEWORK_CONTEXT -> return collectFrameworkContext(psiElement, project)\n            CHANGE_COUNT -> return calculateChangeCount(psiElement)\n            LINE_COUNT -> return calculateLineCount(psiElement)\n            COMPLEXITY_COUNT -> return calculateComplexityCount(psiElement)\n            CODE_SMELL -> return CodeSmellCollector.collectElementProblemAsSting(psiElement!!, project, editor)\n\n            else -> \"\"\n        }\n    }\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/provider/variable/model/ConditionPsiVariable.kt",
    "content": "package com.phodal.shirecore.provider.variable.model\n\nenum class ConditionPsiVariable(\n    override val variableName: String,\n    override val description: String,\n    override var value: Any? = null,\n) : Variable {\n    FILE_PATH(\"filePath\", \"The path of the file\"),\n    FILE_NAME(\"fileName\", \"The name of the file\"),\n    FILE_EXTENSION(\"fileExtension\", \"The extension of the file\"),\n    FILE_CONTENT(\"fileContent\", \"The content of the file\")\n    ;\n\n    companion object {\n        fun from(variableName: String): ConditionPsiVariable? {\n            return values().find { it.variableName == variableName }\n        }\n    }\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/provider/variable/model/ContextVariable.kt",
    "content": "package com.phodal.shirecore.provider.variable.model\n\nenum class ContextVariable(\n    override val variableName: String,\n    override val description: String,\n    override var value: Any? = \"\",\n) :\n    Variable {\n    SELECTION(\"selection\", \"User selection code/element's in text\"),\n    SELECTION_WITH_NUM(\"selectionWithNum\", \"User selection code/element's in text with line number\"),\n    BEFORE_CURSOR(\"beforeCursor\", \"All the text before the cursor\"),\n    AFTER_CURSOR(\"afterCursor\", \"All the text after the cursor\"),\n    FILE_NAME(\"fileName\", \"The name of the file\"),\n    FILE_PATH(\"filePath\", \"The path of the file\"),\n    METHOD_NAME(\"methodName\", \"The name of the method\"),\n    LANGUAGE(\"language\", \"The language of the current file, will use IntelliJ's language ID\"),\n    COMMENT_SYMBOL(\"commentSymbol\", \"The comment symbol of the language, for example, `//` in Java\"),\n    ALL(\"all\", \"All the text\")\n    ;\n\n    companion object {\n        fun from(variableName: String): ContextVariable? {\n            return values().find { it.variableName == variableName }\n        }\n    }\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/provider/variable/model/PsiContextVariable.kt",
    "content": "package com.phodal.shirecore.provider.variable.model\n\n/**\n * Represents the context variables that can be used in the code structure generation process.\n *\n * @property variableName the name of the context variable\n */\nenum class PsiContextVariable(\n    override val variableName: String,\n    override val description: String,\n    override var value: Any? = null,\n) : Variable {\n    /**\n     * Represents the PsiNameIdentifierOwner of the current class, used to retrieve the class name.\n     */\n    CURRENT_CLASS_NAME(\"currentClassName\", \"The name of the current class\"),\n\n    /**\n     * Represents the input and output of PsiElement and PsiFile.\n     */\n    CURRENT_CLASS_CODE(\"currentClassCode\", \"The code of the current class\"),\n\n    CURRENT_METHOD_NAME(\"currentMethodName\", \"The name of the current method\"),\n\n    CURRENT_METHOD_CODE(\"currentMethodCode\", \"The code of the current method\"),\n\n    /**\n     * Represents the input and output of PsiElement and PsiFile.\n     */\n    RELATED_CLASSES(\"relatedClasses\", \"The related classes based on the AST analysis\"),\n\n    /**\n     * Uses TfIDF to search for similar test cases in the code.\n     */\n    SIMILAR_TEST_CASE(\"similarTestCase\", \"The similar test cases based on the TfIDF analysis\"),\n\n    /**\n     * Represents the import statements required for the code structure.\n     */\n    IMPORTS(\"imports\", \"The import statements required for the code structure\"),\n\n    /**\n     * Flag indicating whether the code structure is being generated in a new file.\n     */\n    IS_NEED_CREATE_FILE(\n        \"isNeedCreateFile\",\n        \"Flag indicating whether the code structure is being generated in a new file\"\n    ),\n\n    /**\n     * The name of the target test file where the code structure will be generated.\n     */\n    TARGET_TEST_FILE_NAME(\n        \"targetTestFileName\",\n        \"The name of the target test file where the code structure will be generated\"\n    ),\n\n    /**\n     * underTestMethod\n     */\n    UNDER_TEST_METHOD_CODE(\"underTestMethodCode\", \"The code of the method under test\"),\n\n    /**\n     * Represents the framework information required for the code structure.\n     */\n    FRAMEWORK_CONTEXT(\"frameworkContext\", \"The framework information in dependencies of current project\"),\n\n    /**\n     * codeSmell\n     */\n    CODE_SMELL(\"codeSmell\", \"Include psi error and warning\"),\n\n    METHOD_CALLER(\"methodCaller\", \"The method that initiates the current call\"),\n\n    CALLED_METHOD(\"calledMethod\", \"The method that is being called by the current method\"),\n\n    SIMILAR_CODE(\"similarCode\", \"Recently 20 files similar code based on the tf-idf search\"),\n\n    STRUCTURE(\"structure\", \"The structure of the current class, for programming language will be in UML format.\"),\n\n    /**\n     * Represents the number of changes in the current file.\n     */\n    CHANGE_COUNT(\"changeCount\", \"The number of changes in the current file\"),\n\n    /**\n     * Represents the number of lines in the current file.\n     */\n    LINE_COUNT(\"lineCount\", \"The number of lines in the current file\"),\n\n    /**\n     * Represents the complexity of the current file.\n     */\n    COMPLEXITY_COUNT(\"complexityCount\", \"The complexity of the current file\")\n    ;\n\n    companion object {\n        /**\n         * Returns the PsiVariable with the given variable name.\n         *\n         * @param variableName the variable name to search for\n         * @return the PsiVariable with the given variable name\n         */\n        fun from(variableName: String): PsiContextVariable? {\n            return entries.firstOrNull { it.variableName == variableName }\n        }\n\n        fun all(): List<PsiContextVariable> = entries\n    }\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/provider/variable/model/SystemInfoVariable.kt",
    "content": "package com.phodal.shirecore.provider.variable.model\n\nimport com.intellij.openapi.application.ApplicationInfo\nimport java.util.*\n\nenum class SystemInfoVariable(\n    override val variableName: String,\n    override val description: String,\n) : ToolchainVariable {\n    OS_NAME(\"os.name\", \"The name of the operating system\") {\n        override var value: Any?\n            get() = System.getProperty(\"os.name\")\n            set(_) {}\n    },\n    OS_VERSION(\"os.version\", \"The version of the operating system\") {\n        override var value: Any?\n            get() = System.getProperty(\"os.version\")\n            set(_) {}\n    },\n    OS_ARCH(\"os.arch\", \"The architecture of the operating system\") {\n        override var value: Any?\n            get() = System.getProperty(\"os.arch\")\n            set(_) {}\n    },\n\n    IDE_NAME(\"ide.name\", \"The name of the IDE\") {\n        override var value: Any?\n            get() = System.getProperty(\"idea.platform.prefix\", \"idea\")\n            set(_) {}\n    },\n    IDE_VERSION(\"ide.version\", \"The version of the IDE\") {\n        override var value: Any?\n            get() = ApplicationInfo.getInstance().build.asString()\n            set(_) {}\n    },\n    IDE_CODE(\"ide.code\", \"The code of the IDE\") {\n        override var value: Any?\n            get() = ApplicationInfo.getInstance().build.productCode\n            set(_) {}\n    },\n\n    TIMEZONE(\"timezone\", \"The timezone\") {\n        override var value: Any?\n            get() = TimeZone.getDefault().displayName\n            set(_) {}\n    },\n    DATE(\"date\", \"The current date\") {\n        override var value: Any?\n            get() = Calendar.getInstance().time\n            set(_) {}\n    },\n    TODAY(\"today\", \"Today's date\") {\n        override var value: Any?\n            get() = Calendar.getInstance().time\n            set(_) {}\n    },\n    NOW(\"now\", \"The current time in milliseconds\") {\n        override var value: Any?\n            get() = System.currentTimeMillis()\n            set(_) {}\n    },\n\n    LOCALE(\"locale\", \"The default locale\") {\n        override var value: Any?\n            get() = Locale.getDefault().toString()\n            set(_) {}\n    };\n\n    companion object {\n        fun from(variableName: String): SystemInfoVariable? {\n            return values().firstOrNull { it.variableName == variableName }\n        }\n\n        fun all(): List<SystemInfoVariable> {\n            return values().toList()\n        }\n    }\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/provider/variable/model/ToolchainVariable.kt",
    "content": "package com.phodal.shirecore.provider.variable.model\n\nimport org.reflections.Reflections\nimport kotlin.reflect.KClass\nimport kotlin.reflect.full.companionObjectInstance\nimport kotlin.reflect.full.declaredFunctions\nimport kotlin.reflect.full.functions\n\ninterface ToolchainVariable : Variable {\n    companion object {\n        private val subclasses: Set<KClass<out ToolchainVariable>> by lazy {\n            val reflections = Reflections(\"com.phodal.shirecore.provider.variable.model\")\n            reflections.getSubTypesOf(ToolchainVariable::class.java)\n                .map { it.kotlin }\n                .toSet()\n        }\n\n        fun from(variableName: String): ToolchainVariable? {\n            for (subclass in subclasses) {\n                val companion = subclass.companionObjectInstance ?: continue\n                val fromFunction = companion::class.declaredFunctions.find { it.name == \"from\" } ?: continue\n                val result = fromFunction.call(companion, variableName) as? ToolchainVariable\n                if (result != null) {\n                    return result\n                }\n            }\n            return null\n        }\n\n        fun all(): List<ToolchainVariable> {\n            val allVariables = mutableListOf<ToolchainVariable>()\n            for (subclass in subclasses) {\n                val valuesFunction = subclass.functions.find { it.name == \"values\" } ?: continue\n                val enumConstants = valuesFunction.call() as Array<ToolchainVariable>\n                allVariables.addAll(enumConstants)\n            }\n\n            return allVariables\n        }\n    }\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/provider/variable/model/Variable.kt",
    "content": "package com.phodal.shirecore.provider.variable.model\n\nimport com.phodal.shirecore.provider.variable.model.toolchain.*\n\ninterface Variable {\n    val variableName: String\n    val description: String\n    var value: Any?\n}\n\ndata class DebugValue(\n    override val variableName: String,\n    override var value: Any?,\n    override val description: String,\n) : Variable {\n    companion object {\n        fun description(key: String): String {\n            return PsiContextVariable.from(key)?.description\n                ?: ContextVariable.from(key)?.description\n                ?: SystemInfoVariable.from(key)?.description\n//                ?: ConditionPsiVariable.from(key)?.description\n                /// ...\n                ?: DatabaseToolchainVariable.from(key)?.description\n                ?: TerminalToolchainVariable.from(key)?.description\n                ?: VcsToolchainVariable.from(key)?.description\n                ?: BuildToolchainVariable.from(key)?.description\n                ?: SonarqubeVariable.from(key)?.description\n                ?: \"Unknown\"\n        }\n\n        fun all(): List<Variable> {\n            val allVariables = mutableListOf<Variable>()\n            allVariables.addAll(ContextVariable.values())\n            allVariables.addAll(PsiContextVariable.all())\n            allVariables.addAll(SystemInfoVariable.values())\n//            allVariables.addAll(ConditionPsiVariable.values())\n            /// ...\n            allVariables.addAll(DatabaseToolchainVariable.values())\n            allVariables.addAll(TerminalToolchainVariable.values())\n            allVariables.addAll(VcsToolchainVariable.values())\n            allVariables.addAll(BuildToolchainVariable.values())\n            allVariables.addAll(SonarqubeVariable.values())\n            return allVariables\n        }\n    }\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/provider/variable/model/toolchain/BuildToolchainVariable.kt",
    "content": "package com.phodal.shirecore.provider.variable.model.toolchain\n\nimport com.phodal.shirecore.provider.variable.model.ToolchainVariable\n\n/**\n * Enum representing variables used in the generation of code structures.\n */\nenum class BuildToolchainVariable(\n    override val variableName: String,\n    override var value: Any? = null,\n    override val description: String = \"\",\n) : ToolchainVariable {\n    ProjectDependencies(\"projectDependencies\", description = \"The dependencies of the project\"),\n    ;\n\n    companion object {\n        /**\n         * Returns the PsiVariable with the given variable name.\n         *\n         * @param variableName the variable name to search for\n         * @return the PsiVariable with the given variable name\n         */\n        fun from(variableName: String): BuildToolchainVariable? {\n            return values().firstOrNull { it.variableName == variableName }\n        }\n    }\n}\n\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/provider/variable/model/toolchain/DatabaseToolchainVariable.kt",
    "content": "package com.phodal.shirecore.provider.variable.model.toolchain\n\nimport com.phodal.shirecore.provider.variable.model.ToolchainVariable\n\nenum class DatabaseToolchainVariable(\n    override val variableName: String,\n    override var value: Any? = null,\n    override val description: String = \"\",\n) : ToolchainVariable {\n    DatabaseInfo(\"databaseInfo\", description = \"The database information\"),\n    Databases(\"databases\", description = \"The databases in the database\"),\n    Tables(\"tables\", description = \"The tables in the database\"),\n    Columns(\"columns\", description = \"The columns in the database\")\n    ;\n\n    companion object {\n        /**\n         * Returns the PsiVariable with the given variable name.\n         *\n         * @param variableName the variable name to search for\n         * @return the PsiVariable with the given variable name\n         */\n        fun from(variableName: String): DatabaseToolchainVariable? {\n            return DatabaseToolchainVariable.values().firstOrNull { it.variableName == variableName }\n        }\n    }\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/provider/variable/model/toolchain/SonarqubeVariable.kt",
    "content": "package com.phodal.shirecore.provider.variable.model.toolchain\n\nimport com.phodal.shirecore.provider.variable.model.ToolchainVariable\n\n/**\n * Enum representing variables used in the generation of code structures.\n */\nenum class SonarqubeVariable(\n    override val variableName: String,\n    override var value: Any? = null,\n    override val description: String = \"\",\n) : ToolchainVariable {\n    Issue(\"sonarIssue\", null, \"the issue of current file\"),\n    Results(\"sonarResults\", null, \"the results of current file\")\n    ;\n\n    companion object {\n        /**\n         * Returns the PsiVariable with the given variable name.\n         *\n         * @param variableName the variable name to search for\n         * @return the PsiVariable with the given variable name\n         */\n        fun from(variableName: String): SonarqubeVariable? {\n            return values().firstOrNull { it.variableName == variableName }\n        }\n    }\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/provider/variable/model/toolchain/TerminalToolchainVariable.kt",
    "content": "package com.phodal.shirecore.provider.variable.model.toolchain\n\nimport com.phodal.shirecore.provider.variable.model.ToolchainVariable\n\n/**\n * Enum representing variables used in the generation of code structures.\n */\nenum class TerminalToolchainVariable(\n    override val variableName: String,\n    override var value: Any? = null,\n    override val description: String = \"\",\n) : ToolchainVariable {\n    SHELL_PATH(\"shellPath\", \"/bin/bash\", \"The path to the shell executable\"),\n\n    PWD(\"pwd\", null, \"The current working directory\"),\n    ;\n\n    companion object {\n        /**\n         * Returns the PsiVariable with the given variable name.\n         *\n         * @param variableName the variable name to search for\n         * @return the PsiVariable with the given variable name\n         */\n        fun from(variableName: String): TerminalToolchainVariable? {\n            return values().firstOrNull { it.variableName == variableName }\n        }\n    }\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/provider/variable/model/toolchain/VcsToolchainVariable.kt",
    "content": "package com.phodal.shirecore.provider.variable.model.toolchain\n\nimport com.phodal.shirecore.provider.variable.model.ToolchainVariable\n\n/**\n * Enum representing variables used in the generation of code structures.\n */\nenum class VcsToolchainVariable(\n    override val variableName: String,\n    override var value: Any? = null,\n    override val description: String = \"\",\n) : ToolchainVariable {\n    CurrentChanges(\"currentChanges\", description = \"The code changes in the current working directory\"),\n\n    CurrentBranch(\"currentBranch\", description = \"The name of the current branch\"),\n\n    HistoryCommitMessages(\"historyCommitMessages\", description = \"The commit messages in the history\"),\n\n    Diff(\"diff\", description = \"The diff of the current changes\")\n    ;\n\n    companion object {\n        /**\n         * Returns the PsiVariable with the given variable name.\n         *\n         * @param variableName the variable name to search for\n         * @return the PsiVariable with the given variable name\n         */\n        fun from(variableName: String): VcsToolchainVariable? {\n            return values().firstOrNull { it.variableName == variableName }\n        }\n    }\n}\n\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/psi/CodeSmellCollector.kt",
    "content": "package com.phodal.shirecore.psi\n\nimport com.intellij.codeInsight.daemon.impl.DaemonCodeAnalyzerEx\nimport com.intellij.lang.LanguageCommenters\nimport com.intellij.openapi.editor.Editor\nimport com.intellij.openapi.project.Project\nimport com.intellij.psi.PsiElement\n\nobject CodeSmellCollector  {\n    /**\n     * Collects all the problems found in the given `project`, within the specified `editor` and `element`.\n     *\n     * @param project The project in which the problems are to be collected.\n     * @param editor The editor that is associated with the element.\n     * @param element The PsiElement for which the problems are to be collected.\n     * @return A string containing all the problems found, separated by new lines, or `null` if no problems were found.\n     */\n    private fun collectProblems(project: Project, editor: Editor, element: PsiElement): String? {\n        val range = element.textRange\n        val document = editor.document\n        var errors: MutableList<String> = mutableListOf()\n        DaemonCodeAnalyzerEx.processHighlights(document, project, null, range.startOffset, range.endOffset) {\n            if (it.description != null) {\n                errors.add(it.description)\n            }\n\n            true\n        }\n\n        val commentSymbol = commentPrefix(element)\n        // remove dupcliated descriptions\n        errors = errors.distinct().toMutableList()\n        return errors.joinToString(\"\\n\") {\n            \"$commentSymbol - $it\"\n        }\n    }\n\n    /**\n     * Collects the problems related to the given PsiElement and returns them as a formatted string.\n     *\n     * @param element the PsiElement for which problems need to be collected\n     * @param project the Project in which the element exists\n     * @param editor the Editor used for displaying the problems\n     * @return a formatted string containing the problems related to the element, along with any relevant code snippets\n     */\n    fun collectElementProblemAsSting(\n        element: PsiElement,\n        project: Project,\n        editor: Editor\n    ): String {\n        return collectProblems(project, editor, element) ?: \"\"\n    }\n\n    private fun commentPrefix(element: PsiElement): String {\n        return LanguageCommenters.INSTANCE.forLanguage(element.language)?.lineCommentPrefix ?: \"//\"\n    }\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/psi/PsiErrorCollector.kt",
    "content": "package com.phodal.shirecore.psi\n\nimport com.intellij.codeInsight.daemon.DaemonCodeAnalyzer\nimport com.intellij.codeInsight.daemon.impl.DaemonCodeAnalyzerEx\nimport com.intellij.lang.annotation.HighlightSeverity\nimport com.intellij.openapi.Disposable\nimport com.intellij.openapi.application.runReadAction\nimport com.intellij.openapi.editor.Document\nimport com.intellij.openapi.fileEditor.FileDocumentManager\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.util.Disposer\nimport com.intellij.openapi.util.TextRange\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.PsiErrorElement\nimport com.intellij.psi.PsiFile\nimport com.intellij.util.messages.MessageBusConnection\nimport com.phodal.shirecore.ast.PsiSyntaxCheckingVisitor\nimport java.util.concurrent.CompletableFuture\nimport java.util.concurrent.TimeUnit\n\nobject PsiErrorCollector {\n    fun collectSyntaxError(\n        psiFile: PsiFile,\n        project: Project,\n    ): List<String> {\n        var errors: List<String> = listOf()\n        collectSyntaxError(psiFile, psiFile.virtualFile, project) {\n            errors = it\n        }\n\n        return errors\n    }\n\n    /**\n     * This function is used to collect syntax errors in a given source file using the PSI (Program Structure Interface) of the file.\n     * It takes the source file, a callback function to run after collecting errors, an output file, and the project as parameters.\n     *\n     * @param sourceFile The PSI file from which syntax errors need to be collected.\n     * @param runAction A callback function that takes a list of errors as input and performs some action.\n     * @param outputFile The virtual file where the errors will be collected.\n     * @param project The project to which the files belong.\n     */\n    fun collectSyntaxError(\n        sourceFile: PsiFile,\n        outputFile: VirtualFile,\n        project: Project,\n        runAction: ((errors: List<String>) -> Unit)?,\n    ) {\n        val collectPsiError = sourceFile.collectPsiError()\n        if (collectPsiError.isNotEmpty()) {\n            return runAction?.invoke(collectPsiError) ?: Unit\n        }\n\n        val document = runReadAction { FileDocumentManager.getInstance().getDocument(outputFile) } ?: return\n\n        val range = TextRange(0, document.textLength)\n        val errors = mutableListOf<String>()\n\n        DaemonCodeAnalyzerEx.getInstance(project).restart(sourceFile)\n\n        val hintDisposable = Disposer.newDisposable()\n        val busConnection: MessageBusConnection = project.messageBus.connect(hintDisposable)\n        val future: CompletableFuture<List<String>> = CompletableFuture()\n        busConnection.subscribe(\n            DaemonCodeAnalyzer.DAEMON_EVENT_TOPIC,\n            SimpleCodeErrorListener(document, project, range, errors, busConnection, hintDisposable) {\n                future.complete(it)\n            }\n        )\n\n        future.get(30, TimeUnit.SECONDS)\n    }\n\n    class SimpleCodeErrorListener(\n        private val document: Document,\n        private val project: Project,\n        private val range: TextRange,\n        private val errors: MutableList<String>,\n        private val busConnection: MessageBusConnection,\n        private val hintDisposable: Disposable,\n        private val runAction: ((errors: List<String>) -> Unit)?,\n    ) : DaemonCodeAnalyzer.DaemonListener {\n        override fun daemonFinished() {\n            DaemonCodeAnalyzerEx.processHighlights(\n                document,\n                project,\n                HighlightSeverity.ERROR,\n                range.startOffset,\n                range.endOffset\n            ) {\n                if (it.description != null) {\n                    errors.add(it.description)\n                }\n\n                true\n            }\n\n            runAction?.invoke(errors)\n            busConnection.disconnect()\n            Disposer.dispose(hintDisposable)\n        }\n    }\n}\n\n/**\n * This function is an extension function for PsiFile class in Kotlin.\n * It collects syntax errors present in the PsiFile and returns a list of error messages.\n * It creates a PsiSyntaxCheckingVisitor object to visit each element in the PsiFile.\n * If the element is a PsiErrorElement, it adds a message to the errors list with the error description and position.\n * Finally, it returns the list of error messages.\n */\nfun PsiFile.collectPsiError(): MutableList<String> {\n    val errors = mutableListOf<String>()\n    val visitor = object : PsiSyntaxCheckingVisitor() {\n        override fun visitElement(element: PsiElement) {\n            if (element is PsiErrorElement) {\n                errors.add(\"Syntax error at position ${element.textRange.startOffset}: ${element.errorDescription}\")\n            }\n            super.visitElement(element)\n        }\n    }\n\n    this.accept(visitor)\n    return errors\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/runner/ConfigurationRunner.kt",
    "content": "package com.phodal.shirecore.runner\n\nimport com.intellij.execution.*\nimport com.intellij.execution.configurations.RunConfiguration\nimport com.intellij.execution.executors.DefaultRunExecutor\nimport com.intellij.execution.impl.ExecutionManagerImpl\nimport com.intellij.execution.process.ProcessAdapter\nimport com.intellij.execution.process.ProcessEvent\nimport com.intellij.execution.process.ProcessOutputType\nimport com.intellij.execution.runners.ExecutionEnvironmentBuilder\nimport com.intellij.execution.runners.ProgramRunner\nimport com.intellij.execution.testframework.sm.runner.SMTRunnerEventsAdapter\nimport com.intellij.execution.testframework.sm.runner.SMTRunnerEventsListener\nimport com.intellij.openapi.application.runInEdt\nimport com.intellij.openapi.diagnostic.logger\nimport com.intellij.openapi.progress.ProgressIndicator\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.util.Disposer\nimport com.intellij.openapi.util.Key\nimport com.intellij.util.messages.MessageBusConnection\nimport java.util.concurrent.CountDownLatch\nimport java.util.concurrent.TimeUnit\n\ninterface ConfigurationRunner {\n    fun runnerId() = DefaultRunExecutor.EXECUTOR_ID\n\n    fun executeRunConfigurations(\n        project: Project,\n        settings: RunnerAndConfigurationSettings,\n        testEventsListener: SMTRunnerEventsAdapter? = null,\n        indicator: ProgressIndicator? = null,\n    ) {\n        val runContext = createRunContext()\n        executeRunConfigures(project, settings, runContext, testEventsListener, indicator)\n    }\n\n    fun executeRunConfigures(\n        project: Project,\n        settings: RunnerAndConfigurationSettings,\n        runContext: RunContext,\n        testEventsListener: SMTRunnerEventsAdapter?,\n        indicator: ProgressIndicator?,\n    ) {\n        val connection: MessageBusConnection? = project.messageBus.connect()\n        try {\n            return executeRunConfigurations(connection, settings, runContext, testEventsListener, indicator)\n        } finally {\n            connection?.disconnect()\n        }\n    }\n\n    /**\n     * This function is responsible for executing run configurations with the given parameters.\n     *\n     * @param connection The message bus connection to use.\n     * @param configurations The runner and configuration settings to execute.\n     * @param runContext The run context for the execution.\n     * @param testEventsListener The listener for test events.\n     * @param indicator The progress indicator for the execution.\n     */\n    fun executeRunConfigurations(\n        connection: MessageBusConnection?,\n        configurations: RunnerAndConfigurationSettings,\n        runContext: RunContext,\n        testEventsListener: SMTRunnerEventsListener?,\n        indicator: ProgressIndicator?,\n    ) {\n        testEventsListener?.let {\n            connection?.subscribe(SMTRunnerEventsListener.TEST_STATUS, it)\n        }\n        connection?.let {\n            Disposer.register(runContext, connection)\n        }\n\n        runInEdt {\n            try {\n                configurations.startRunConfigurationExecution(runContext)\n                val handler = CheckExecutionListener(runnerId(), runContext)\n                connection?.subscribe(ExecutionManager.EXECUTION_TOPIC, handler)\n            } catch (e: ExecutionException) {\n                logger<ConfigurationRunner>().warn(\"Failed to start run configuration: ${configurations.name}\")\n                runContext.latch.countDown()\n            }\n        }\n\n        // todo: find a better way\n        if (indicator != null) {\n            while (!indicator.isCanceled) {\n                val result = runContext.latch.await(100, TimeUnit.MILLISECONDS)\n                if (result) break\n            }\n\n            if (indicator.isCanceled) {\n                Disposer.dispose(runContext)\n            }\n        }\n    }\n\n    fun createRunContext(): RunContext {\n        val stderr = StringBuilder()\n        val processListener = object : OutputListener() {\n            override fun onTextAvailable(event: ProcessEvent, outputType: Key<*>) {\n                if (ProcessOutputType.isStderr(outputType)) {\n                    stderr.append(event.text)\n                }\n            }\n        }\n\n        val runContext = RunContext(processListener, null, CountDownLatch(1))\n        return runContext\n    }\n\n\n    /**\n     * This function defines a process run completion action to be executed once a process run by the program runner completes.\n     * It is designed to handle the aftermath of a process execution, including stopping the process and notifying the run context.\n     *\n     * @param runContext The context in which the run operation is being executed. It provides the necessary information\n     *                   and handles to manage the run process, including a latch to synchronize the completion of the run.\n     *                   The run context is also responsible for disposing of resources once the run completes.\n     *\n     * Note: This function uses the 'return@Callback' syntax to exit the lambda expression early in case of a null descriptor.\n     */\n    fun processRunCompletionAction(runContext: RunContext) = ProgramRunner.Callback { descriptor ->\n        // Descriptor can be null in some cases.\n        // For example, IntelliJ Rust's test runner provides null here if compilation fails\n        if (descriptor == null) {\n            runContext.latch.countDown()\n            return@Callback\n        }\n\n        Disposer.register(runContext) {\n            ExecutionManagerImpl.stopProcess(descriptor)\n        }\n        val processHandler = descriptor.processHandler\n        if (processHandler != null) {\n            processHandler.addProcessListener(object : ProcessAdapter() {\n                override fun processTerminated(event: ProcessEvent) {\n                    runContext.latch.countDown()\n                }\n            })\n            runContext.processListener?.let {\n                processHandler.addProcessListener(it)\n            }\n        }\n    }\n\n    @Throws(ExecutionException::class)\n    fun RunnerAndConfigurationSettings.startRunConfigurationExecution(runContext: RunContext): Boolean {\n        val runner = ProgramRunner.getRunner(runnerId(), configuration)\n        val env =\n            ExecutionEnvironmentBuilder.create(DefaultRunExecutor.getRunExecutorInstance(), this)\n                .activeTarget()\n                .build(processRunCompletionAction(runContext))\n\n        if (runner == null || env.state == null) {\n            runContext.latch.countDown()\n            return false\n        }\n\n        runContext.environments.add(env)\n        try {\n            runner.execute(env)\n        } catch (e: ExecutionException) {\n            runContext.latch.countDown()\n            throw e\n        }\n        return true\n    }\n\n    fun executeRunConfigurations(project: Project, configuration: RunConfiguration) {\n        val runManager = RunManager.getInstance(project)\n        val settings = runManager.findConfigurationByTypeAndName(configuration.type, configuration.name)\n\n        if (settings == null) {\n            logger<ConfigurationRunner>().warn(\"Failed to find test configuration for: ${configuration.name}\")\n            return\n        }\n\n        executeRunConfigurations(project, settings)\n    }\n}\n\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/runner/RunContext.kt",
    "content": "package com.phodal.shirecore.runner\n\nimport com.intellij.execution.ExecutionListener\nimport com.intellij.execution.Executor\nimport com.intellij.execution.process.ProcessListener\nimport com.intellij.execution.runners.ExecutionEnvironment\nimport com.intellij.execution.runners.ProgramRunner\nimport com.intellij.openapi.Disposable\nimport java.util.concurrent.CountDownLatch\n\nclass RunContext(\n    val processListener: ProcessListener?,\n    val executionListener: ExecutionListener?,\n    val latch: CountDownLatch,\n    val executor: Executor? = null,\n    val runner: ProgramRunner<*>? = null,\n) : Disposable {\n    val environments: MutableList<ExecutionEnvironment> = mutableListOf()\n    override fun dispose() {}\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/runner/RunServiceExt.kt",
    "content": "// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage com.phodal.shirecore.runner\n\nimport com.intellij.execution.ExecutionListener\nimport com.intellij.execution.process.ProcessHandler\nimport com.intellij.execution.runners.ExecutionEnvironment\n\nclass CheckExecutionListener(\n    private val executorId: String,\n    private val runContext: RunContext,\n) : ExecutionListener {\n    override fun processStartScheduled(executorId: String, env: ExecutionEnvironment) {\n        checkAndExecute(executorId, env) {\n            runContext.executionListener?.processStartScheduled(executorId, env)\n        }\n    }\n\n    override fun processNotStarted(executorId: String, env: ExecutionEnvironment) {\n        checkAndExecute(executorId, env) {\n            runContext.latch.countDown()\n            runContext.executionListener?.processNotStarted(executorId, env)\n        }\n    }\n\n    override fun processStarting(executorId: String, env: ExecutionEnvironment) {\n        checkAndExecute(executorId, env) {\n            runContext.executionListener?.processStarting(executorId, env)\n        }\n    }\n\n    override fun processStarted(executorId: String, env: ExecutionEnvironment, handler: ProcessHandler) {\n        checkAndExecute(executorId, env) {\n            runContext.executionListener?.processStarted(executorId, env, handler)\n        }\n    }\n\n    override fun processTerminating(executorId: String, env: ExecutionEnvironment, handler: ProcessHandler) {\n        checkAndExecute(executorId, env) {\n            runContext.executionListener?.processTerminating(executorId, env, handler)\n        }\n    }\n\n    override fun processTerminated(\n        executorId: String,\n        env: ExecutionEnvironment,\n        handler: ProcessHandler,\n        exitCode: Int\n    ) {\n        checkAndExecute(executorId, env) {\n            runContext.executionListener?.processTerminated(executorId, env, handler, exitCode)\n        }\n    }\n\n    private fun checkAndExecute(executorId: String, env: ExecutionEnvironment, action: () -> Unit) {\n        if (this.executorId == executorId && env in runContext.environments) {\n            action()\n        }\n    }\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/runner/RunServiceTask.kt",
    "content": "// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage com.phodal.shirecore.runner\n\nimport com.intellij.execution.ExecutionListener\nimport com.intellij.execution.ExecutionManager\nimport com.intellij.execution.ExecutionManager.Companion.EXECUTION_TOPIC\nimport com.intellij.execution.RunnerAndConfigurationSettings\nimport com.intellij.execution.executors.DefaultRunExecutor\nimport com.intellij.execution.process.*\nimport com.intellij.execution.runners.ExecutionEnvironment\nimport com.intellij.execution.runners.ExecutionEnvironmentBuilder\nimport com.intellij.execution.runners.ProgramRunner\nimport com.intellij.execution.testframework.Filter\nimport com.intellij.execution.testframework.sm.runner.SMTRunnerEventsAdapter\nimport com.intellij.execution.testframework.sm.runner.SMTestProxy\nimport com.intellij.execution.ui.ExecutionUiService\nimport com.intellij.execution.ui.RunContentDescriptor\nimport com.intellij.openapi.application.ApplicationManager\nimport com.intellij.openapi.application.invokeAndWaitIfNeeded\nimport com.intellij.openapi.application.runInEdt\nimport com.intellij.openapi.diagnostic.logger\nimport com.intellij.openapi.progress.ProgressIndicator\nimport com.intellij.openapi.progress.Task\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.util.Disposer\nimport com.intellij.openapi.util.Key\nimport com.intellij.openapi.util.NlsSafe\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.intellij.psi.PsiElement\nimport com.intellij.util.text.nullize\nimport com.phodal.shirecore.ShireCoreBundle\nimport com.phodal.shirecore.ShirelangNotifications\nimport com.phodal.shirecore.provider.shire.FileRunService\nimport java.util.concurrent.CompletableFuture\n\n\nopen class RunServiceTask(\n    private val project: Project,\n    private val virtualFile: VirtualFile,\n    private val testElement: PsiElement?,\n    private val fileRunService: FileRunService,\n    private val runner: ProgramRunner<*>? = null,\n    private val future: CompletableFuture<String>? = null,\n) : ConfigurationRunner, Task.Backgroundable(\n    project, ShireCoreBundle.message(\"progress.run.task\"), true\n) {\n    override fun runnerId() = runner?.runnerId ?: DefaultRunExecutor.EXECUTOR_ID\n\n    override fun run(indicator: ProgressIndicator) {\n        if (future != null) {\n            runInBackgroundAndCollectToFuture()\n        } else {\n            runAndCollectTestResults(indicator)\n        }\n    }\n\n    private fun runInBackgroundAndCollectToFuture() {\n        val settings: RunnerAndConfigurationSettings =\n            fileRunService.createRunSettings(project, virtualFile, testElement)\n                ?: throw IllegalStateException(\"No run configuration found for file: ${virtualFile.path}\")\n\n        runAnCollectStdOutput(settings, project, future!!)\n    }\n\n    /**\n     * This function is responsible for executing a run configuration and returning the corresponding check result.\n     * It is used within the test framework to run tests and report the results back to the user.\n     *\n     * @param indicator A progress indicator that is used to track the progress of the execution.\n     * @return The check result of the executed run configuration, or `null` if no run configuration could be created.\n     */\n    private fun runAndCollectTestResults(indicator: ProgressIndicator?): RunnerResult? {\n        val settings: RunnerAndConfigurationSettings? =\n            fileRunService.createRunSettings(project, virtualFile, testElement)\n        if (settings == null) {\n            logger<RunServiceTask>().warn(\"No run configuration found for file: ${virtualFile.path}\")\n            return null\n        }\n\n        settings.isActivateToolWindowBeforeRun = false\n\n        val testRoots = mutableListOf<SMTestProxy.SMRootTestProxy>()\n        val testEventsListener = object : SMTRunnerEventsAdapter() {\n            override fun onTestingStarted(testsRoot: SMTestProxy.SMRootTestProxy) {\n                testRoots += testsRoot\n            }\n        }\n\n        val runContext = createRunContext()\n        executeRunConfigures(project, settings, runContext, testEventsListener, indicator)\n\n        @Suppress(\"UnstableApiUsage\")\n        invokeAndWaitIfNeeded { }\n\n        val testResults = testRoots.mapNotNull { it.toCheckResult() }\n        if (testResults.isEmpty()) return RunnerResult.noTestsRun\n\n        val firstFailure = testResults.firstOrNull { it.status != RunnerStatus.Solved }\n        val result = firstFailure ?: testResults.first()\n        return result\n    }\n\n    private fun SMTestProxy.SMRootTestProxy.toCheckResult(): RunnerResult? {\n        if (finishedSuccessfully()) return RunnerResult(RunnerStatus.Solved, \"CONGRATULATIONS\")\n\n        val failedChildren = collectChildren(object : Filter<SMTestProxy>() {\n            override fun shouldAccept(test: SMTestProxy): Boolean = test.isLeaf && !test.finishedSuccessfully()\n        })\n\n        val firstFailedTest = failedChildren.firstOrNull()\n        if (firstFailedTest == null) {\n            ShirelangNotifications.warn(project, \"Testing failed although no failed tests found\")\n            return null\n        }\n\n        val diff = firstFailedTest.diffViewerProvider?.let {\n            CheckResultDiff(it.left, it.right, it.diffTitle)\n        }\n        val message = if (diff != null) getComparisonErrorMessage(firstFailedTest) else getErrorMessage(firstFailedTest)\n        val details = firstFailedTest.stacktrace\n        return RunnerResult(\n            RunnerStatus.Failed,\n            removeAttributes(fillWithIncorrect(message)),\n            diff = diff,\n            details = details\n        )\n    }\n\n    private fun SMTestProxy.finishedSuccessfully(): Boolean {\n        return !hasErrors() && (isPassed || isIgnored)\n    }\n\n    /**\n     * Some testing frameworks add attributes to be shown in console (ex. Jest - ANSI color codes)\n     * which are not supported in Task Description, so they need to be removed\n     */\n    private fun removeAttributes(text: String): String {\n        val buffer = StringBuilder()\n        AnsiEscapeDecoder().escapeText(text, ProcessOutputTypes.STDOUT) { chunk, _ ->\n            buffer.append(chunk)\n        }\n        return buffer.toString()\n    }\n\n    /**\n     * Returns message for test error that will be shown to a user in Check Result panel\n     */\n    @Suppress(\"UnstableApiUsage\")\n    @NlsSafe\n    private fun getErrorMessage(node: SMTestProxy): String = node.errorMessage ?: \"Execution failed\"\n\n    /**\n     * Returns message for comparison error that will be shown to a user in Check Result panel\n     */\n    private fun getComparisonErrorMessage(node: SMTestProxy): String = getErrorMessage(node)\n\n    private fun fillWithIncorrect(message: String): String =\n        message.nullize(nullizeSpaces = true) ?: \"Incorrect\"\n\n    companion object {\n        fun runAnCollectStdOutput(\n            settings: RunnerAndConfigurationSettings, project: Project, completableFuture: CompletableFuture<String>,\n        ) {\n            val executorInstance = DefaultRunExecutor.getRunExecutorInstance()\n            val env = ExecutionEnvironmentBuilder\n                .createOrNull(executorInstance, settings.configuration)\n                ?.build() ?: throw IllegalStateException(\"Failed to create execution environment\")\n\n            val runContentManager = ExecutionManager.getInstance(project).getContentManager()\n\n            val processAdapter = object : ProcessAdapter() {\n                val stdout = StringBuilder()\n                val stderr = StringBuilder()\n                override fun onTextAvailable(event: ProcessEvent, outputType: Key<*>) {\n                    when (outputType) {\n                        ProcessOutputTypes.STDOUT -> stdout.append(event.text)\n                        ProcessOutputTypes.STDERR -> stderr.append(event.text)\n                        ProcessOutputTypes.SYSTEM -> {\n                            // ignore system output\n                        }\n\n                        else -> {}\n                    }\n                }\n\n                override fun processTerminated(event: ProcessEvent) {\n                    when (event.exitCode) {\n                        0 -> completableFuture.complete(stdout.toString())\n                        else -> completableFuture.completeExceptionally(IllegalStateException(\"$stderr\\nProcess terminated with non-zero exit code: ${event.exitCode}\"))\n                    }\n                }\n            }\n\n            settings.isActivateToolWindowBeforeRun = false\n            settings.isTemporary = true\n            settings.isFocusToolWindowBeforeRun = false\n\n            val disposable = Disposer.newDisposable()\n            val connection = ApplicationManager.getApplication().messageBus.connect(disposable)\n            connection.subscribe(EXECUTION_TOPIC, object : ExecutionListener {\n                override fun processStarting(executorId: String, env: ExecutionEnvironment, handler: ProcessHandler) {\n                    handler.addProcessListener(processAdapter)\n                }\n\n                override fun processTerminated(\n                    executorId: String,\n                    env: ExecutionEnvironment,\n                    handler: ProcessHandler,\n                    exitCode: Int,\n                ) {\n                    super.processTerminated(executorId, env, handler, exitCode)\n                    connection.disconnect()\n\n                    if (exitCode != 0) {\n                        completableFuture.completeExceptionally(IllegalStateException(\"Process terminated with non-zero exit code: $exitCode\"))\n                    } else {\n                        val content = runContentManager.getReuseContent(env) ?: return\n                        runInEdt {\n                            runContentManager.removeRunContent(executorInstance, content)\n                        }\n                    }\n                }\n            })\n\n            ExecutionManager.getInstance(project).restartRunProfile(\n                project,\n                executorInstance,\n                env.executionTarget,\n                settings,\n                null\n            )\n        }\n    }\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/runner/RunnerResult.kt",
    "content": "// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage com.phodal.shirecore.runner\n\nimport org.jetbrains.annotations.Nls\n\nclass RunnerResult(\n    val status: RunnerStatus,\n    @Nls(capitalization = Nls.Capitalization.Sentence) val message: String = \"\",\n    val details: String? = null,\n    val diff: CheckResultDiff? = null,\n) {\n\n    companion object {\n        val noTestsRun: RunnerResult\n            get() = RunnerResult(\n                RunnerStatus.Unchecked,\n                \"check.no.tests.with.help.guide\",\n            )\n    }\n\n}\n\ndata class CheckResultDiff(val expected: String, val actual: String, val title: String = \"\")\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/runner/RunnerResultSeverity.kt",
    "content": "// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage com.phodal.shirecore.runner\n\nenum class RunnerResultSeverity {\n  Info, Warning, Error;\n\n  fun isWaring() = this == Warning\n  fun isInfo() = this == Info\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/runner/RunnerStatus.kt",
    "content": "// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage com.phodal.shirecore.runner\n\nenum class RunnerStatus(val rawStatus: String) {\n  Unchecked(\"UNCHECKED\"),\n  Solved(\"CORRECT\"),\n  Failed(\"WRONG\");\n\n  companion object {\n    fun String.toCheckStatus(): RunnerStatus = when (this) {\n      \"CORRECT\" -> Solved\n      \"WRONG\" -> Failed\n      else -> Unchecked\n    }\n  }\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/runner/ShireProcessHandler.kt",
    "content": "package com.phodal.shirecore.runner\n\nimport com.intellij.build.process.BuildProcessHandler\nimport com.intellij.execution.process.AnsiEscapeDecoder\nimport com.intellij.openapi.diagnostic.logger\nimport com.intellij.openapi.externalSystem.util.DiscardingInputStream\nimport com.intellij.openapi.util.Key\nimport java.io.*\nimport java.nio.channels.Channels\nimport java.nio.channels.Pipe\n\nclass ShireProcessHandler(private val myExecutionName: String) : BuildProcessHandler() {\n    private var myProcessInputWriter: OutputStream? = null\n    private var myProcessInputReader: InputStream? = null\n    private val myAnsiEscapeDecoder = AnsiEscapeDecoder()\n\n    init {\n        try {\n            val pipe = Pipe.open()\n            myProcessInputReader = BufferedInputStream(Channels.newInputStream(pipe.source()))\n            myProcessInputWriter = BufferedOutputStream(Channels.newOutputStream(pipe.sink()))\n        } catch (_: IOException) {\n\n        }\n\n    }\n\n    override fun detachIsDefault(): Boolean = true\n    override fun destroyProcessImpl() = Unit\n    override fun detachProcessImpl() {\n        try {\n            notifyProcessDetached()\n        } catch (e: Exception) {\n            // ignore\n            logger<ShireProcessHandler>().warn(e)\n        }\n        finally {\n            notifyProcessTerminated(0)\n        }\n    }\n\n    fun exitWithError() = notifyProcessTerminated(-1)\n\n    override fun notifyTextAvailable(text: String, outputType: Key<*>) {\n        myAnsiEscapeDecoder.escapeText(\n            text, outputType\n        ) { decodedText: String?, attributes: Key<*>? ->\n            super.notifyTextAvailable(\n                decodedText!!, attributes!!\n            )\n        }\n    }\n\n    override fun getProcessInput(): OutputStream? = myProcessInputWriter\n    override fun getExecutionName(): String = myExecutionName\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/runner/console/CustomFlowWrapper.kt",
    "content": "package com.phodal.shirecore.runner.console\n\nimport com.intellij.execution.ui.ConsoleView\nimport kotlinx.coroutines.flow.Flow\nimport kotlinx.coroutines.flow.FlowCollector\nimport kotlinx.coroutines.flow.cancellable\n\n/**\n * This class supports cancel callback for [Flow],\n * the flow consumer cancels the flow collection after callback is called,\n * which can be used to skip processing useless data.\n *\n * @author lk\n */\nclass CustomFlowWrapper<T>(private val delegate: Flow<T>) : Flow<T> {\n\n    private var isCanceled = false\n\n    private var _cancelCallback: ((String) -> Unit)? = null\n\n    override suspend fun collect(collector: FlowCollector<T>) {\n        if (!isCanceled) {\n            delegate.collect(collector)\n        }\n    }\n\n    fun cancelCallback(callback: (String) -> Unit) {\n        _cancelCallback = callback\n    }\n\n    fun cancel(message: String) {\n        check(isCanceled == false) { \"This flow has been canceled\" }\n        isCanceled = true\n        _cancelCallback?.invoke(message)\n    }\n}\n\n\nfun <T> Flow<T>.cancelWithConsole(consoleView: ConsoleView?): Flow<T> =\n    cancelHandler { consoleView?.addCancelCallback(it) }\n\n/**\n * Please use [CustomFlowWrapper] to call it, and it won't work if it's CancellableFlow,\n * so you need to call it before calling [cancellable]\n */\nfun <T> Flow<T>.cancelHandler(handle: ((String) -> Unit) -> Unit): Flow<T> =\n    apply { if (this is CustomFlowWrapper<T>) handle({ cancel(it) }) }"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/runner/console/ShireConsoleViewBase.kt",
    "content": "package com.phodal.shirecore.runner.console\n\nimport com.intellij.execution.console.ConsoleViewWrapperBase\nimport com.intellij.execution.ui.ConsoleView\n\n/**\n * This class provides cancel callbacks for the console view\n * and stop the tasks related to it when it is closed,\n *\n * @author lk\n */\nopen class ShireConsoleViewBase(executionConsole: ConsoleView) : ConsoleViewWrapperBase(executionConsole) {\n\n    open fun cancelCallback(callback: (String) -> Unit) = Unit\n\n    open fun isCanceled() = false\n\n}\n\nfun ConsoleView.addCancelCallback(callback: (String) -> Unit) {\n    if (this is ShireConsoleViewBase) {\n        cancelCallback(callback)\n    }\n}\n\nfun ConsoleView.isCanceled(): Boolean {\n    if (this is ShireConsoleViewBase) {\n        return isCanceled()\n    }\n    return false\n}\n\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/schema/SecretPatternFileProvider.kt",
    "content": "package com.phodal.shirecore.schema\n\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.vfs.VfsUtil\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.jetbrains.jsonSchema.extension.JsonSchemaFileProvider\nimport com.jetbrains.jsonSchema.extension.SchemaType\nimport com.phodal.shirecore.ShireCoreBundle\nimport org.jetbrains.annotations.NonNls\n\n@NonNls\ninternal const val SECRET_PATTERN_EXTENSION = \"shireSecretPattern.yml\"\n\nclass ShireSecretPatternSchemaFileProvider(project: Project) : JsonSchemaFileProvider {\n    @NonNls\n    private val DOT_EXTENSION = \".$SECRET_PATTERN_EXTENSION\"\n\n    @NonNls\n    private val SCHEMA = \"/schemas/shireSecretPattern.schema.json\"\n\n    override fun isAvailable(file: VirtualFile): Boolean = file.nameSequence.endsWith(DOT_EXTENSION)\n    override fun getName(): String = ShireCoreBundle.message(\"schema.pattern.json.display.name\")\n    override fun getSchemaFile(): VirtualFile? = VfsUtil.findFileByURL(javaClass.getResource(SCHEMA)!!)\n    override fun getSchemaType(): SchemaType = SchemaType.embeddedSchema\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/schema/ShireCustomAgentSchemaFileProvider.kt",
    "content": "package com.phodal.shirecore.schema\n\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.vfs.VfsUtil\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.jetbrains.jsonSchema.extension.JsonSchemaFileProvider\nimport com.jetbrains.jsonSchema.extension.SchemaType\nimport com.phodal.shirecore.ShireCoreBundle\nimport org.jetbrains.annotations.NonNls\n\n@NonNls\ninternal const val CUSTOM_AGENT_JSON_EXTENSION = \"shireCustomAgent.json\"\n\nclass ShireCustomAgentSchemaFileProvider(project: Project) : JsonSchemaFileProvider {\n    @NonNls\n    private val DOT_EXTENSION = \".$CUSTOM_AGENT_JSON_EXTENSION\"\n\n    @NonNls\n    private val CUSTOM_AGENT_SCHEMA = \"/schemas/shireCustomAgent.schema.json\"\n\n    override fun isAvailable(file: VirtualFile): Boolean = file.nameSequence.endsWith(DOT_EXTENSION)\n    override fun getName(): String = ShireCoreBundle.message(\"schema.custom-agent.json.display.name\")\n    override fun getSchemaFile(): VirtualFile? = VfsUtil.findFileByURL(javaClass.getResource(CUSTOM_AGENT_SCHEMA)!!)\n    override fun getSchemaType(): SchemaType = SchemaType.embeddedSchema\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/schema/ShireEnvFileProvider.kt",
    "content": "package com.phodal.shirecore.schema\n\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.vfs.VfsUtil\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.jetbrains.jsonSchema.extension.JsonSchemaFileProvider\nimport com.jetbrains.jsonSchema.extension.SchemaType\nimport com.phodal.shirecore.ShireCoreBundle\nimport org.jetbrains.annotations.NonNls\n\n@NonNls\ninternal const val SHIRE_ENV_PATTERN_EXTENSION = \"shireEnv.json\"\n\nclass ShireEnvFileProvider(project: Project) : JsonSchemaFileProvider {\n    @NonNls\n    private val DOT_EXTENSION = \".$SHIRE_ENV_PATTERN_EXTENSION\"\n\n    @NonNls\n    private val SCHEMA = \"/schemas/shireEnv.schema.json\"\n\n    override fun isAvailable(file: VirtualFile): Boolean = file.nameSequence.endsWith(DOT_EXTENSION)\n    override fun getName(): String = ShireCoreBundle.message(\"schema.env.json.display.name\")\n    override fun getSchemaFile(): VirtualFile? = VfsUtil.findFileByURL(javaClass.getResource(SCHEMA)!!)\n    override fun getSchemaType(): SchemaType = SchemaType.embeddedSchema\n}\n\n\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/schema/ShireJsonSchemaProviderFactory.kt",
    "content": "package com.phodal.shirecore.schema\n\nimport com.intellij.openapi.project.Project\nimport com.jetbrains.jsonSchema.extension.JsonSchemaFileProvider\nimport com.jetbrains.jsonSchema.extension.JsonSchemaProviderFactory\n\nclass ShireJsonSchemaProviderFactory : JsonSchemaProviderFactory {\n    override fun getProviders(project: Project): MutableList<JsonSchemaFileProvider> {\n        return mutableListOf(\n            ShireSecretPatternSchemaFileProvider(project),\n            ShireCustomAgentSchemaFileProvider(project),\n            ShireEnvFileProvider(project)\n        )\n    }\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/search/algorithm/BM25Similarity.kt",
    "content": "package com.phodal.shirecore.search.algorithm\n\nimport kotlin.math.log10\n\n/**\n * BM25Similarity is a class that computes the similarity between a given query and a list of documents (chunks).\n * It uses the BM25 algorithm, which is a probabilistic information retrieval model.\n * The BM25 algorithm considers term frequency, inverse document frequency, and document length to compute relevance scores.\n *\n * @property k1 The term frequency saturation parameter.\n * @property b The document length normalization parameter.\n *\n * The class contains two main functions:\n * 1. computeInputSimilarity: Computes the BM25 similarity scores between the query and each document.\n * 2. computeIDF: Computes the inverse document frequency (IDF) for each term in the corpus.\n */\nclass BM25Similarity : Similarity {\n    private val k1 = 1.5\n    private val b = 0.75\n\n    override fun computeInputSimilarity(query: String, chunks: List<List<String>>): List<List<Double>> {\n        val docCount = chunks.size\n        val avgDocLength = chunks.map { it.size }.average()\n        val idfMap = computeIDF(chunks, docCount)\n\n        // Tokenize the query\n        val queryTerms = tokenize(query).groupBy { it }.mapValues { it.value.size }\n\n        return chunks.map { doc ->\n            val docLength = doc.size\n            queryTerms.map { (term, queryTermFreq) ->\n                val tf = doc.count { it == term }.toDouble()\n                val idf = idfMap[term] ?: 0.0\n                val numerator = tf * (k1 + 1)\n                val denominator = tf + k1 * (1 - b + b * (docLength / avgDocLength))\n                idf * (numerator / denominator) * queryTermFreq\n            }\n        }\n    }\n\n    fun computeIDF(chunks: List<List<String>>, docCount: Int): Map<String, Double> {\n        val termDocCount = mutableMapOf<String, Int>()\n\n        chunks.forEach { doc ->\n            doc.toSet().forEach { term ->\n                termDocCount[term] = termDocCount.getOrDefault(term, 0) + 1\n            }\n        }\n\n        return termDocCount.mapValues { (_, count) ->\n            log10((docCount - count + 0.5) / (count + 0.5) + 1.0)\n        }\n    }\n}\n\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/search/algorithm/JaccardSimilarity.kt",
    "content": "package com.phodal.shirecore.search.algorithm\n\nopen class JaccardSimilarity : Similarity {\n    /**\n     * The `tokenLevelJaccardSimilarity` method calculates the Jaccard similarity between a query string and an array of string\n     * arrays (chunks). The Jaccard similarity is a measure of the similarity between two sets and is defined as the size of\n     * the intersection divided by the size of the union of the two sets.\n     *\n     * @param query The query string to compare against the chunks.\n     * @param chunks An array of string arrays (chunks) to compare against the query.\n     * @return A two-dimensional array representing the Jaccard similarity scores between the query and each chunk.\n     */\n    override fun computeInputSimilarity(query: String, chunks: List<List<String>>): List<List<Double>> {\n        val currentFileTokens = tokenize(query)\n        return chunks.map { list ->\n            list.map { it ->\n                val tokenizedFile = tokenize(it)\n                similarityScore(currentFileTokens, tokenizedFile)\n            }\n        }\n    }\n\n    fun similarityScore(set1: Set<String>, set2: Set<String>): Double {\n        val intersectionSize = set1.intersect(set2).size\n        val unionSize = set1.union(set2).size\n        return intersectionSize.toDouble() / unionSize\n    }\n\n    /**\n     * Calculates the similarity score between a given path and a set of strings.\n     *\n     * @param path The path to calculate similarity for.\n     * @param sets The set of strings to compare with the path.\n     * @return A number representing the similarity score between the path and the set of strings.\n     */\n    fun pathSimilarity(path: String, sets: Set<String>): Double {\n        val splitPath = path.split('/')\n\n        val set1 = splitPath.map(::tokenize)\n            .reduce { acc, it -> acc.union(it) }\n\n        val set2 = sets.map(::tokenize)\n            .reduce { acc, it -> acc.union(it) }\n\n        return similarityScore(set1, set2)\n    }\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/search/algorithm/Similarity.kt",
    "content": "package com.phodal.shirecore.search.algorithm\n\nimport com.phodal.shirecore.search.tokenizer.StopwordsBasedTokenizer\n\ninterface Similarity {\n    fun tokenize(input: String): Set<String> {\n        return StopwordsBasedTokenizer.instance().tokenize(input).toSet()\n    }\n\n    fun computeInputSimilarity(query: String, chunks: List<List<String>>): List<List<Double>>\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/search/algorithm/TfIdf.kt",
    "content": "/*\nCopyright (c) 2011, Rob Ellis, Chris Umbel\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n*/\npackage com.phodal.shirecore.search.algorithm\n\nimport com.phodal.shirecore.search.tokenizer.RegexpTokenizer\nimport com.phodal.shirecore.search.tokenizer.Tokenizer\nimport com.phodal.shirecore.search.tokenizer.WordTokenizer\nimport kotlin.math.ln\n\nvar ourStopwords = listOf(\n    \"about\", \"above\", \"after\", \"again\", \"all\", \"also\", \"am\", \"an\", \"and\", \"another\",\n    \"any\", \"are\", \"as\", \"at\", \"be\", \"because\", \"been\", \"before\", \"being\", \"below\",\n    \"between\", \"both\", \"but\", \"by\", \"came\", \"can\", \"cannot\", \"come\", \"could\", \"did\",\n    \"do\", \"does\", \"doing\", \"during\", \"each\", \"few\", \"for\", \"from\", \"further\", \"get\",\n    \"got\", \"has\", \"had\", \"he\", \"have\", \"her\", \"here\", \"him\", \"himself\", \"his\", \"how\",\n    \"if\", \"in\", \"into\", \"is\", \"it\", \"its\", \"itself\", \"like\", \"make\", \"many\", \"me\",\n    \"might\", \"more\", \"most\", \"much\", \"must\", \"my\", \"myself\", \"never\", \"now\", \"of\", \"on\",\n    \"only\", \"or\", \"other\", \"our\", \"ours\", \"ourselves\", \"out\", \"over\", \"own\",\n    \"said\", \"same\", \"see\", \"she\", \"should\", \"since\", \"so\", \"some\", \"still\", \"such\", \"take\", \"than\",\n    \"that\", \"the\", \"their\", \"theirs\", \"them\", \"themselves\", \"then\", \"there\", \"these\", \"they\",\n    \"this\", \"those\", \"through\", \"to\", \"too\", \"under\", \"until\", \"up\", \"very\", \"was\",\n    \"way\", \"we\", \"well\", \"were\", \"what\", \"where\", \"when\", \"which\", \"while\", \"who\",\n    \"whom\", \"with\", \"would\", \"why\", \"you\", \"your\", \"yours\", \"yourself\",\n    \"a\", \"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\", \"i\", \"j\", \"k\", \"l\", \"m\", \"n\",\n    \"o\", \"p\", \"q\", \"r\", \"s\", \"t\", \"u\", \"v\", \"w\", \"x\", \"y\", \"z\", \"$\", \"1\",\n    \"2\", \"3\", \"4\", \"5\", \"6\", \"7\", \"8\", \"9\", \"0\", \"_\"\n)\n\ntypealias DocumentType = Any\ntypealias TfIdfCallback = (index: Int, measure: Double, key: Any?) -> Unit\n\nclass TfIdf<K, V> {\n    private val documents: MutableList<DocumentType> = mutableListOf()\n    private var _idfCache: MutableMap<String, Double> = mutableMapOf()\n\n    var tokenizer: RegexpTokenizer = WordTokenizer()\n\n    companion object {\n        fun tf(term: String, document: DocumentType): Int {\n            return (document as? Map<*, *>)?.get(term) as? Int ?: 0\n        }\n    }\n\n    fun idf(term: String, force: Boolean = false): Double {\n        if (_idfCache[term] != null && !force) {\n            return _idfCache[term]!!\n        }\n\n        val docsWithTerm = documents.count { documentHasTerm(term, it) }\n        val idf = 1 + ln((documents.size.toDouble()) / (1 + docsWithTerm))\n        _idfCache[term] = idf\n        return idf\n    }\n\n    private fun documentHasTerm(term: String, document: DocumentType): Boolean {\n        return ((document as? Map<*, *>)?.get(term) as? Int ?: 0) > 0\n    }\n\n    fun buildDocument(text: DocumentType, key: Any? = null): MutableMap<String, Any?> {\n        val stopOut: Boolean\n        val doc: MutableMap<String, Any?>\n\n        when (text) {\n            is String -> {\n                val tokens = tokenizer.tokenize(text)\n                stopOut = true\n                doc = tokens.fold(mutableMapOf(\"__key\" to key)) { acc, term ->\n                    if (term !in ourStopwords) {\n                        acc[term] = (acc[term] as? Int ?: 0) + 1\n                    }\n                    acc\n                }\n            }\n\n            is List<*> -> {\n                stopOut = false\n                doc = text.filterIsInstance<String>()\n                    .fold(mutableMapOf(\"__key\" to key)) { acc, term ->\n                        acc[term] = (acc[term] as? Int ?: 0) + 1\n                        acc\n                    }\n            }\n\n            else -> {\n                stopOut = false\n                doc = (text as MutableMap<String, Any?>)\n            }\n        }\n\n\n        // remove \"__key\"\n        if (doc.containsKey(\"__key\")) {\n            doc.remove(\"__key\")\n        }\n\n        return doc\n    }\n\n    fun addDocument(document: DocumentType, key: Any? = null, restoreCache: Boolean = false) {\n        documents.add(buildDocument(document, key))\n\n        if (restoreCache) {\n            for (term in _idfCache.keys) {\n                idf(term, true)\n            }\n        } else {\n            _idfCache = mutableMapOf()\n        }\n    }\n\n    fun addDocuments(documents: List<DocumentType>) {\n        documents.forEach { addDocument(it) }\n    }\n\n    fun tfidf(terms: Any, d: Int): Double {\n        val termsList = if (terms is String) {\n            tokenizer.tokenize(terms)\n        } else {\n            terms as List<String>\n        }\n\n        return termsList.fold(0.0) { value, term ->\n            val idf = idf(term)\n            value + (tf(term, documents[d]) * if (idf == Double.POSITIVE_INFINITY) 0.0 else idf)\n        }\n    }\n\n    fun listTerms(index: Int): List<TermData> {\n        val terms = mutableListOf<TermData>()\n        for ((term, value) in documents[index] as Map<String, Int>) {\n            if (term != \"__key\") {\n                terms.add(TermData(term, tf(term, documents[index]), idf(term), tfidf(term, index)))\n            }\n        }\n\n        return terms.sortedByDescending { it.tfidf }\n    }\n\n    data class TermData(val term: String, val tf: Int, val idf: Double, val tfidf: Double)\n\n    /**\n     * This function calculates the Term Frequency-Inverse Document Frequency (TF-IDF) for each document in the collection.\n     * TF-IDF is a numerical statistic that reflects how important a word is to a document in a collection or corpus.\n     *\n     * @param terms The terms for which the TF-IDF is to be calculated. This can be a single term or a collection of terms.\n     * @param callback An optional callback function that is invoked for each document. The callback function is passed three parameters:\n     *                 - The index of the current document.\n     *                 - The calculated TF-IDF value for the current document.\n     *                 - The key of the current document (if it exists).\n     *                 The callback function can be used to perform additional processing or logging for each document.\n     *\n     * @return A list of Double values representing the calculated TF-IDF values for each document in the collection. The order of the values in the list corresponds to the order of the documents in the collection.\n     */\n    fun tfidfs(terms: Any, callback: TfIdfCallback? = null): List<Double> {\n        val tfidfs = MutableList(documents.size) { 0.0 }\n\n        for (i in documents.indices) {\n            tfidfs[i] = tfidf(terms, i)\n            callback?.invoke(i, tfidfs[i], (documents[i] as? Map<String, Any>)?.get(\"__key\"))\n        }\n\n        return tfidfs\n    }\n\n    fun setTokenizer(t: Any) {\n        if (t !is Tokenizer) {\n            throw IllegalArgumentException(\"Expected a valid Tokenizer\")\n        }\n\n        tokenizer = t as RegexpTokenizer\n    }\n\n    fun setStopwords(customStopwords: List<String>): Boolean {\n        ourStopwords = customStopwords\n        return true\n    }\n\n    fun search(query: String): List<Double> {\n        return tfidfs(query, null)\n    }\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/search/function/ScoreText.kt",
    "content": "package com.phodal.shirecore.search.function\n\nimport com.intellij.openapi.vfs.VirtualFile\n\ndata class ScoredText(\n    val text: String,\n    val similarity: Double = 0.0,\n    var index: Int = 0,\n    var count: Int = 0,\n    val file: VirtualFile? = null,\n    var embedding: FloatArray? = null,\n) {\n    override fun toString(): String {\n        return \"Similarity: ${similarity}, Text: $text\"\n    }\n\n\n    override fun equals(other: Any?): Boolean {\n        if (this === other) return true\n        if (javaClass != other?.javaClass) return false\n\n        other as ScoredText\n\n        if (text != other.text) return false\n        if (index != other.index) return false\n        if (count != other.count) return false\n        if (similarity != other.similarity) return false\n        if (file != other.file) return false\n        if (embedding != null) {\n            if (other.embedding == null) return false\n            if (!embedding.contentEquals(other.embedding)) return false\n        } else if (other.embedding != null) return false\n\n        return true\n    }\n\n    override fun hashCode(): Int {\n        var result = text.hashCode()\n        result = 31 * result + index\n        result = 31 * result + count\n        result = 31 * result + similarity.hashCode()\n        result = 31 * result + (file?.hashCode() ?: 0)\n        result = 31 * result + (embedding?.contentHashCode() ?: 0)\n        return result\n    }\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/search/function/SemanticService.kt",
    "content": "package com.phodal.shirecore.search.function\n\nimport cc.unitmesh.document.parser.MdDocumentParser\nimport cc.unitmesh.document.parser.MsOfficeDocumentParser\nimport cc.unitmesh.document.parser.PdfDocumentParser\nimport cc.unitmesh.document.parser.TextDocumentParser\nimport cc.unitmesh.rag.document.DocumentType\nimport com.intellij.openapi.application.PathManager\nimport com.intellij.openapi.components.Service\nimport com.intellij.openapi.diagnostic.Logger\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.project.guessProjectDir\nimport com.intellij.openapi.util.io.FileUtilRt\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.intellij.openapi.vfs.toNioPathOrNull\nimport com.phodal.shirecore.search.indices.DiskSynchronizedEmbeddingSearchIndex\nimport com.phodal.shirecore.search.indices.EmbeddingSearchIndex\nimport com.phodal.shirecore.search.indices.InMemoryEmbeddingSearchIndex\nimport com.phodal.shirecore.search.rank.Reranker\nimport kotlinx.coroutines.*\nimport java.io.File\nimport java.nio.file.Path\n\n@Service(Service.Level.PROJECT)\nclass SemanticService(val project: Project) {\n    private var lastQuery: String = \"\"\n    private var lastSearchChunks: List<ScoredText> = emptyList()\n\n    private var index: EmbeddingSearchIndex = InMemoryEmbeddingSearchIndex(cacheDir())\n    private val logger = Logger.getInstance(SemanticService::class.java)\n\n    suspend fun embed(chunk: String): FloatArray {\n        TODO(\"see in issue: https://github.com/phodal/shire/issues/84\")\n    }\n\n    suspend fun embedList(chunk: Array<out String>): List<ScoredText> {\n        return chunk.mapIndexed { index, text ->\n            ScoredText(\n                index = index,\n                count = text.length,\n                text = text,\n                file = null,\n                embedding = embed(text)\n            )\n        }\n    }\n\n    suspend fun embedding(chunks: List<ScoredText>): List<ScoredText> {\n        val ids = chunks.map { it.text }\n        val embeddings = chunks.mapNotNull { entry ->\n            entry.embedding ?: run {\n                entry.embedding = embed(entry.text)\n                entry\n            }\n\n            entry.embedding\n        }\n\n        index.addEntries(ids zip embeddings)\n\n        index.saveToDisk()\n        return chunks\n    }\n\n    suspend fun searching(input: String, threshold: Double = 0.5): List<ScoredText> {\n        lastQuery = input\n        val inputEmbedding = embed(input)\n        val findClosest = index.findClosest(inputEmbedding, 10)\n        return findClosest.filter { it.similarity > threshold }\n    }\n\n    suspend fun reranking(type: String): List<ScoredText> {\n        return Reranker.create(type, project).rerank(lastQuery, lastSearchChunks)\n    }\n\n    suspend fun splitting(path: List<VirtualFile>): List<ScoredText> =\n        withContext(Dispatchers.IO) {\n            path.map { file ->\n                val inputStream = file.inputStream\n                val extension = file.extension ?: return@map emptyList()\n                val parser = when (val documentType = DocumentType.of(extension)) {\n                    DocumentType.TXT -> TextDocumentParser(documentType)\n                    DocumentType.PDF -> PdfDocumentParser()\n                    DocumentType.HTML -> TextDocumentParser(documentType)\n                    DocumentType.DOC -> MsOfficeDocumentParser(documentType)\n                    DocumentType.XLS -> MsOfficeDocumentParser(documentType)\n                    DocumentType.PPT -> MsOfficeDocumentParser(documentType)\n                    DocumentType.MD -> MdDocumentParser()\n                    null -> {\n                        if (!file.canBeAdded()) {\n                            logger.warn(\"File ${file.path} can't be added to the index\")\n                            return@map emptyList()\n                        }\n\n                        TextDocumentParser(DocumentType.TXT)\n                    }\n                }\n\n                parser.parse(inputStream).mapIndexed { index, document ->\n                    // filter document.text.length < 0\n                    if (document.text.isBlank()) {\n                        return@mapIndexed null\n                    }\n\n                    ScoredText(\n                        text = document.text,\n                        embedding = null,\n                        index = index,\n                        count = document.text.length,\n                        file = file,\n                    )\n                }.filterNotNull()\n            }.flatten()\n        }\n\n    suspend fun configCache(text: String): Any {\n        val type = SemanticStorageType.fromString(text)\n\n        return when (type) {\n            SemanticStorageType.MEMORY -> {\n                index.loadFromDisk()\n            }\n\n            SemanticStorageType.DISK -> {\n                if (index is DiskSynchronizedEmbeddingSearchIndex) {\n                    index.loadFromDisk()\n                } else {\n                    index = DiskSynchronizedEmbeddingSearchIndex(cacheDir())\n\n                    index.loadFromDisk()\n                }\n            }\n        }\n    }\n\n    private fun cacheDir(): Path {\n        return project.guessProjectDir()\n            ?.toNioPathOrNull()\n            ?.resolve(\".shire-cache\")\n            ?.resolve(\"semantic\") ?: systemPath()\n    }\n\n    private fun systemPath(): Path = File(PathManager.getSystemPath())\n        .resolve(\"shire-cache\")\n        .resolve(\"semantic\").toPath()\n}\n\nfun VirtualFile.canBeAdded(): Boolean {\n    if (!this.isValid || this.isDirectory) return false\n    if (this.fileType.isBinary || FileUtilRt.isTooLarge(this.length)) return false\n\n    return true\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/search/function/SemanticStorageType.kt",
    "content": "package com.phodal.shirecore.search.function\n\nenum class SemanticStorageType(val value: String) {\n    MEMORY(\"memory\"),\n    DISK(\"disk\"),\n    ;\n\n    companion object {\n        fun fromString(value: String): SemanticStorageType {\n            return values().firstOrNull { it.value == value }\n                ?: MEMORY\n        }\n    }\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/search/indices/DiskSynchronizedEmbeddingSearchIndex.kt",
    "content": "// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage com.phodal.shirecore.search.indices\n\n\nimport com.intellij.concurrency.ConcurrentCollectionFactory\nimport com.intellij.util.containers.CollectionFactory\nimport com.phodal.shirecore.search.function.ScoredText\nimport kotlinx.coroutines.coroutineScope\nimport kotlinx.coroutines.ensureActive\nimport java.nio.file.Path\nimport java.util.concurrent.locks.ReentrantReadWriteLock\nimport kotlin.concurrent.read\nimport kotlin.concurrent.write\n\n/**\n * Concurrent [EmbeddingSearchIndex] that synchronizes all index change operations with disk and\n * allows simultaneous read operations from multiple consumers.\n * Incremental operations do not rewrite the whole storage file with embeddings.\n * Instead, they change only the corresponding sections in the file.\n */\nclass DiskSynchronizedEmbeddingSearchIndex(val root: Path, limit: Int? = null) : EmbeddingSearchIndex {\n    private var indexToId: MutableMap<Int, String> = CollectionFactory.createSmallMemoryFootprintMap()\n    private var idToEntry: MutableMap<String, IndexEntry> = CollectionFactory.createSmallMemoryFootprintMap()\n    private val uncheckedIds: MutableSet<String> = ConcurrentCollectionFactory.createConcurrentSet()\n    var changed: Boolean = false\n\n    private val lock = ReentrantReadWriteLock()\n\n    private val fileManager = LocalEmbeddingIndexFileManager(root)\n\n    override var limit = limit\n        set(value) = lock.write {\n            if (value != null) {\n                // Shrink index if necessary:\n                while (idToEntry.size > value) {\n                    delete(indexToId[idToEntry.size - 1]!!, all = true, shouldSaveIds = false)\n                }\n                saveIds()\n            }\n            field = value\n        }\n\n    internal data class IndexEntry(\n        var index: Int,\n        var count: Int,\n        val embedding: FloatArray\n    )\n\n    override val size: Int get() = lock.read { idToEntry.size }\n\n    override operator fun contains(id: String): Boolean = lock.read {\n        uncheckedIds.remove(id)\n        id in idToEntry\n    }\n\n    override fun clear() = lock.write {\n        indexToId.clear()\n        idToEntry.clear()\n        uncheckedIds.clear()\n        changed = false\n    }\n\n    override fun onIndexingStart() {\n        uncheckedIds.clear()\n        uncheckedIds.addAll(idToEntry.keys)\n    }\n\n    override fun onIndexingFinish() = lock.write {\n        if (uncheckedIds.size > 0) changed = true\n        uncheckedIds.forEach {\n            delete(it, all = true, shouldSaveIds = false)\n        }\n        uncheckedIds.clear()\n    }\n\n    override suspend fun addEntries(values: Iterable<Pair<String, FloatArray>>,\n                                    shouldCount: Boolean) = coroutineScope {\n        lock.write {\n            for ((id, embedding) in values) {\n                ensureActive()\n                val entry = idToEntry.getOrPut(id) {\n                    changed = true\n                    if (limit != null && idToEntry.size >= limit!!) return@write\n                    val index = idToEntry.size\n                    indexToId[index] = id\n                    IndexEntry(index, 0, embedding)\n                }\n                if (shouldCount || entry.count == 0) {\n                    entry.count += 1\n                }\n            }\n        }\n    }\n\n    override suspend fun saveToDisk() = lock.read { save() }\n\n    override suspend fun loadFromDisk() = coroutineScope {\n        val (ids, embeddings) = fileManager.loadIndex() ?: return@coroutineScope\n        val idToIndex = ids.withIndex().associate { it.value to it.index }\n        val idToEmbedding = (ids zip embeddings).toMap()\n        ensureActive()\n        lock.write {\n            ensureActive()\n            indexToId = CollectionFactory.createSmallMemoryFootprintMap(ids.withIndex().associate { it.index to it.value })\n            idToEntry = CollectionFactory.createSmallMemoryFootprintMap(\n                ids.associateWith { IndexEntry(idToIndex[it]!!, 0, idToEmbedding[it]!!) }\n            )\n        }\n    }\n\n    override fun findClosest(searchEmbedding: FloatArray, topK: Int, similarityThreshold: Double?): List<ScoredText> = lock.read {\n        return idToEntry.mapValues { it.value.embedding }.findClosest(searchEmbedding, topK, similarityThreshold)\n    }\n\n    override fun streamFindClose(searchEmbedding: FloatArray, similarityThreshold: Double?): Sequence<ScoredText> {\n        return LockedSequenceWrapper(lock::readLock) {\n            this.idToEntry // manually use the receiver here to make sure the property is not captured by reference\n                .asSequence()\n                .map { it.key to it.value.embedding }\n                .streamFindClose(searchEmbedding, similarityThreshold)\n        }\n    }\n\n    override fun estimateMemoryUsage() = fileManager.embeddingSizeInBytes.toLong() * size\n\n    override fun estimateLimitByMemory(memory: Long): Int {\n        return (memory / fileManager.embeddingSizeInBytes).toInt()\n    }\n\n    override fun checkCanAddEntry(): Boolean = lock.read {\n        return limit == null || idToEntry.size < limit!!\n    }\n\n    private suspend fun save() = coroutineScope {\n        val ids = idToEntry.toList().sortedBy { it.second.index }.map { it.first }\n        val embeddings = ids.map { idToEntry[it]!!.embedding }\n        fileManager.saveIndex(ids, embeddings)\n    }\n\n    fun deleteEntry(id: String) = lock.write {\n        delete(id)\n    }\n\n    fun addEntry(id: String, embedding: FloatArray) = lock.write {\n        add(id, embedding)\n    }\n\n    /* Optimization for consequent delete and add operations */\n    fun updateEntry(id: String, newId: String, embedding: FloatArray) = lock.write {\n        if (id !in idToEntry) return\n        if (idToEntry[id]!!.count == 1 && newId !in this) {\n            val index = idToEntry[id]!!.index\n            fileManager[index] = embedding\n\n            idToEntry.remove(id)\n            idToEntry[newId] = IndexEntry(index, 1, embedding)\n            indexToId[index] = newId\n\n            saveIds()\n        }\n        else {\n            // Do not apply optimization\n            delete(id)\n            add(newId, embedding)\n        }\n    }\n\n    private fun add(id: String, embedding: FloatArray, shouldCount: Boolean = false) {\n        val entry = idToEntry.getOrPut(id) {\n            changed = true\n            if (limit != null && idToEntry.size >= limit!!) return@add\n            val index = idToEntry.size\n            fileManager[index] = embedding\n            indexToId[index] = id\n            IndexEntry(index, 0, embedding)\n        }\n        if (shouldCount || entry.count == 0) {\n            entry.count += 1\n            if (entry.count == 1) {\n                saveIds()\n            }\n        }\n    }\n\n    private fun delete(id: String, all: Boolean = false, shouldSaveIds: Boolean = true) {\n        val entry = idToEntry[id] ?: return\n        entry.count -= 1\n        if (!all && entry.count > 0) return\n\n        val lastIndex = idToEntry.size - 1\n        val index = entry.index\n\n        val movedId = indexToId[lastIndex]!!\n\n        fileManager.removeAtIndex(index)\n        indexToId[index] = movedId\n        indexToId.remove(lastIndex)\n\n        idToEntry[movedId]!!.index = index\n        idToEntry.remove(id)\n\n        if (shouldSaveIds) saveIds()\n    }\n\n    private fun saveIds() {\n        fileManager.saveIds(idToEntry.toList().sortedBy { it.second.index }.map { it.first })\n    }\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/search/indices/EmbeddingSearchIndex.kt",
    "content": "// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage com.phodal.shirecore.search.indices\n\nimport com.phodal.shirecore.search.function.ScoredText\nimport kotlin.collections.asSequence\nimport kotlin.math.sqrt\nimport kotlin.sequences.filter\nimport kotlin.sequences.map\nimport kotlin.sequences.sortedByDescending\nimport kotlin.sequences.take\nimport kotlin.sequences.toList\nimport kotlin.to\n\ninterface EmbeddingSearchIndex {\n    val size: Int\n    var limit: Int?\n\n    operator fun contains(id: String): Boolean\n    fun clear()\n\n    fun onIndexingStart()\n    fun onIndexingFinish()\n\n    suspend fun addEntries(values: Iterable<Pair<String, FloatArray>>, shouldCount: Boolean = false)\n\n    suspend fun saveToDisk()\n    suspend fun loadFromDisk()\n\n    fun findClosest(searchEmbedding: FloatArray, topK: Int, similarityThreshold: Double? = null): List<ScoredText>\n    fun streamFindClose(searchEmbedding: FloatArray, similarityThreshold: Double? = null): Sequence<ScoredText>\n\n    fun estimateMemoryUsage(): Long\n    fun estimateLimitByMemory(memory: Long): Int\n    fun checkCanAddEntry(): Boolean\n}\n\ninternal fun Map<String, FloatArray>.findClosest(\n    searchEmbedding: FloatArray,\n    topK: Int, similarityThreshold: Double?,\n): List<ScoredText> {\n    return asSequence()\n        .map {\n            it.key to searchEmbedding.times(it.value)\n        }\n        .filter { (_, similarity) -> if (similarityThreshold != null) similarity > similarityThreshold else true }\n        .sortedByDescending { (_, similarity) -> similarity }\n        .take(topK)\n        .map { (id, similarity) -> ScoredText(id, similarity.toDouble()) }\n        .toList()\n}\n\ninternal fun Sequence<Pair<String, FloatArray>>.streamFindClose(\n    queryEmbedding: FloatArray,\n    similarityThreshold: Double?,\n): Sequence<ScoredText> {\n    return map { (id, embedding) -> id to queryEmbedding.times(embedding) }\n        .filter { similarityThreshold == null || it.second > similarityThreshold }\n        .map { (id, similarity) -> ScoredText(id, similarity.toDouble()) }\n}\n\nfun FloatArray.times(other: FloatArray): Float {\n    require(this.size == other.size) {\n        \"Embeddings must have the same size, but got ${this.size} and ${other.size}\"\n    }\n    return this.zip(other).map { (a, b) -> a * b }.sum()\n}\n\nfun FloatArray.normalized(): FloatArray {\n    val norm = sqrt(this.times(this))\n    val normalizedValues = this.map { it / norm }\n    return normalizedValues.toFloatArray()\n}\n\nfun FloatArray.cosine(other: FloatArray): Float {\n    require(this.size == other.size) { \"Embeddings must have the same size\" }\n    val dot = this.times(other)\n    val norm = sqrt(this.times(this)) * sqrt(other.times(other))\n    return dot / norm\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/search/indices/InMemoryEmbeddingSearchIndex.kt",
    "content": "// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage com.phodal.shirecore.search.indices\n\nimport com.intellij.concurrency.ConcurrentCollectionFactory\nimport com.intellij.util.containers.CollectionFactory\nimport com.phodal.shirecore.search.function.ScoredText\nimport java.nio.file.Path\nimport java.util.concurrent.locks.ReentrantReadWriteLock\nimport kotlin.collections.asSequence\nimport kotlin.collections.contains\nimport kotlin.collections.forEach\nimport kotlin.collections.putAll\nimport kotlin.collections.take\nimport kotlin.collections.toList\nimport kotlin.collections.toMap\nimport kotlin.collections.toMutableMap\nimport kotlin.collections.unzip\nimport kotlin.collections.zip\nimport kotlin.concurrent.read\nimport kotlin.concurrent.write\nimport kotlin.sequences.map\nimport kotlin.to\n\n/**\n * Concurrent [com.phodal.shirecore.search.indices.EmbeddingSearchIndex] that stores all embeddings in the memory and allows\n * simultaneous read operations from multiple consumers.\n * Can be persisted to disk.\n */\nclass InMemoryEmbeddingSearchIndex(root: Path, limit: Int? = null) : EmbeddingSearchIndex {\n    private var idToEmbedding: MutableMap<String, FloatArray> = CollectionFactory.createSmallMemoryFootprintMap()\n    private val uncheckedIds: MutableSet<String> = ConcurrentCollectionFactory.createConcurrentSet()\n    private val lock = ReentrantReadWriteLock()\n\n    private val fileManager = LocalEmbeddingIndexFileManager(root)\n\n    override var limit = limit\n        set(value) = lock.write {\n            // Shrink index if necessary:\n            if (value != null && value < idToEmbedding.size) {\n                idToEmbedding = idToEmbedding.toList().take(value).toMap().toMutableMap()\n            }\n            field = value\n        }\n\n    override val size: Int get() = lock.read { idToEmbedding.size }\n\n    override operator fun contains(id: String): Boolean = lock.read {\n        uncheckedIds.remove(id)\n        id in idToEmbedding\n    }\n\n    override fun clear() = lock.write {\n        idToEmbedding.clear()\n        uncheckedIds.clear()\n    }\n\n    override fun onIndexingStart() {\n        uncheckedIds.clear()\n        uncheckedIds.addAll(idToEmbedding.keys)\n    }\n\n    override fun onIndexingFinish() = lock.write {\n        uncheckedIds.forEach { idToEmbedding.remove(it) }\n        uncheckedIds.clear()\n    }\n\n    override suspend fun addEntries(values: Iterable<Pair<String, FloatArray>>,\n                                    shouldCount: Boolean) = lock.write {\n        if (limit != null) {\n            val list = values.toList()\n            idToEmbedding.putAll(list.take(minOf(limit!! - idToEmbedding.size, list.size)))\n        }\n        else {\n            idToEmbedding.putAll(values)\n        }\n    }\n\n    override suspend fun saveToDisk() = lock.read { save() }\n\n    override suspend fun loadFromDisk() = lock.write {\n        val (ids, embeddings) = fileManager.loadIndex() ?: return\n        idToEmbedding = (ids zip embeddings).toMap().toMutableMap()\n    }\n\n    override fun findClosest(searchEmbedding: FloatArray, topK: Int, similarityThreshold: Double?): List<ScoredText> = lock.read {\n        return idToEmbedding.findClosest(searchEmbedding, topK, similarityThreshold)\n    }\n\n    override fun streamFindClose(searchEmbedding: FloatArray, similarityThreshold: Double?): Sequence<ScoredText> {\n        return LockedSequenceWrapper(lock::readLock) {\n            this.idToEmbedding // manually use the receiver here to make sure the property is not captured by reference\n                .asSequence()\n                .map { it.key to it.value }\n                .streamFindClose(searchEmbedding, similarityThreshold)\n        }\n    }\n\n    override fun estimateMemoryUsage() = fileManager.embeddingSizeInBytes.toLong() * size\n\n    override fun estimateLimitByMemory(memory: Long): Int {\n        return (memory / fileManager.embeddingSizeInBytes).toInt()\n    }\n\n    override fun checkCanAddEntry(): Boolean = lock.read {\n        return limit == null || idToEmbedding.size < limit!!\n    }\n\n    private suspend fun save() {\n        val (ids, embeddings) = idToEmbedding.toList().unzip()\n        fileManager.saveIndex(ids, embeddings)\n    }\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/search/indices/LocalEmbeddingIndexFileManager.kt",
    "content": "// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage com.phodal.shirecore.search.indices\n\nimport com.fasterxml.jackson.core.JsonProcessingException\nimport com.fasterxml.jackson.core.util.DefaultIndenter\nimport com.fasterxml.jackson.core.util.DefaultPrettyPrinter\nimport com.fasterxml.jackson.databind.SerializationFeature\nimport com.fasterxml.jackson.module.kotlin.jacksonObjectMapper\nimport com.fasterxml.jackson.module.kotlin.readValue\nimport com.intellij.util.io.outputStream\nimport kotlinx.coroutines.coroutineScope\nimport kotlinx.coroutines.ensureActive\nimport java.io.IOException\nimport java.io.RandomAccessFile\nimport java.nio.ByteBuffer\nimport java.nio.file.Files\nimport java.nio.file.Path\nimport java.util.concurrent.locks.ReentrantReadWriteLock\nimport kotlin.also\nimport kotlin.apply\nimport kotlin.collections.forEach\nimport kotlin.collections.map\nimport kotlin.collections.toMutableList\nimport kotlin.concurrent.read\nimport kotlin.concurrent.write\nimport kotlin.io.buffered\nimport kotlin.io.path.exists\nimport kotlin.io.path.inputStream\nimport kotlin.io.use\nimport kotlin.text.contains\nimport kotlin.text.intern\nimport kotlin.text.lowercase\nimport kotlin.to\n\nclass LocalEmbeddingIndexFileManager(root: Path, private val dimensions: Int = DEFAULT_DIMENSIONS) {\n    private val lock = ReentrantReadWriteLock()\n    private val mapper = jacksonObjectMapper().enable(SerializationFeature.INDENT_OUTPUT)\n\n    private val prettyPrinter = DefaultPrettyPrinter().apply { indentArraysWith(DefaultIndenter.SYSTEM_LINEFEED_INSTANCE) }\n\n    private val rootPath = root\n        get() = field.also { Files.createDirectories(field) }\n    private val idsPath\n        get() = rootPath.resolve(IDS_FILENAME)\n    private val embeddingsPath\n        get() = rootPath.resolve(EMBEDDINGS_FILENAME)\n\n    val embeddingSizeInBytes = dimensions * EMBEDDING_ELEMENT_SIZE\n\n    /** Provides reading access to the embedding vector at the specified index\n     *  without reading the whole file into memory\n     */\n    operator fun get(index: Int): FloatArray = lock.read {\n        RandomAccessFile(embeddingsPath.toFile(), \"r\").use { input ->\n            input.seek(getIndexOffset(index))\n            val buffer = ByteArray(EMBEDDING_ELEMENT_SIZE)\n\n            FloatArray(dimensions) {\n                input.read(buffer)\n                ByteBuffer.wrap(buffer).getFloat()\n            }\n        }\n    }\n\n    /** Provides writing access to embedding vector at the specified index\n     *  without writing the other vectors\n     */\n    operator fun set(index: Int, embedding: FloatArray) = lock.write {\n        RandomAccessFile(embeddingsPath.toFile(), \"rw\").use { output ->\n            output.seek(getIndexOffset(index))\n            val buffer = ByteBuffer.allocate(EMBEDDING_ELEMENT_SIZE)\n            embedding.forEach {\n                output.write(buffer.putFloat(0, it).array())\n            }\n        }\n    }\n\n    /**\n     * Removes the embedding vector at the specified index.\n     * To do so, replaces this vector with the last vector in the file and shrinks the file size.\n     */\n    fun removeAtIndex(index: Int) = lock.write {\n        RandomAccessFile(embeddingsPath.toFile(), \"rw\").use { file ->\n            if (file.length() < embeddingSizeInBytes) return\n            if (file.length() - embeddingSizeInBytes != getIndexOffset(index)) {\n                file.seek(file.length() - embeddingSizeInBytes)\n                val array = ByteArray(EMBEDDING_ELEMENT_SIZE)\n                val embedding = FloatArray(dimensions) {\n                    file.read(array)\n                    ByteBuffer.wrap(array).getFloat()\n                }\n                file.seek(getIndexOffset(index))\n                val buffer = ByteBuffer.allocate(EMBEDDING_ELEMENT_SIZE)\n                embedding.forEach {\n                    file.write(buffer.putFloat(0, it).array())\n                }\n            }\n            file.setLength(file.length() - embeddingSizeInBytes)\n        }\n    }\n\n    suspend fun loadIndex(): Pair<List<String>, List<FloatArray>>? = coroutineScope {\n        ensureActive()\n        lock.read {\n            ensureActive()\n            if (!idsPath.exists() || !embeddingsPath.exists()) return@coroutineScope null\n            val ids = try {\n                mapper.readValue<List<String>>(idsPath.toFile()).map { it.intern() }.toMutableList()\n            }\n            catch (e: JsonProcessingException) {\n                return@coroutineScope null\n            }\n            val buffer = ByteArray(EMBEDDING_ELEMENT_SIZE)\n            embeddingsPath.inputStream().buffered().use { input ->\n                ids to ids.map {\n                    ensureActive()\n                    FloatArray(dimensions) {\n                        input.read(buffer)\n                        ByteBuffer.wrap(buffer).getFloat()\n                    }\n                }\n            }\n        }\n    }\n\n    fun saveIds(ids: List<String>) = lock.write {\n        withNotEnoughSpaceCheck {\n            idsPath.outputStream().buffered().use { output ->\n                mapper.writer(prettyPrinter).writeValue(output, ids)\n            }\n        }\n    }\n\n    suspend fun saveIndex(ids: List<String>, embeddings: List<FloatArray>) = coroutineScope {\n        ensureActive()\n        lock.write {\n            ensureActive()\n            withNotEnoughSpaceCheck {\n                idsPath.outputStream().buffered().use { output ->\n                    mapper.writer(prettyPrinter).writeValue(output, ids)\n                }\n            }\n            val buffer = ByteBuffer.allocate(EMBEDDING_ELEMENT_SIZE)\n            withNotEnoughSpaceCheck {\n                embeddingsPath.outputStream().buffered().use { output ->\n                    embeddings.forEach { embedding ->\n                        ensureActive()\n                        embedding.forEach {\n                            output.write(buffer.putFloat(0, it).array())\n                        }\n                    }\n                }\n            }\n        }\n    }\n\n    private fun getIndexOffset(index: Int): Long = index.toLong() * embeddingSizeInBytes\n\n    private fun withNotEnoughSpaceCheck(task: () -> Unit) {\n        try {\n            task()\n        }\n        catch (e: IOException) {\n            if (e.message?.lowercase()?.contains(\"space\") == true) {\n                idsPath.toFile().delete()\n                embeddingsPath.toFile().delete()\n            }\n            else throw e\n        }\n    }\n\n    companion object {\n        const val DEFAULT_DIMENSIONS = 384\n        const val EMBEDDING_ELEMENT_SIZE = 4\n\n        private const val IDS_FILENAME = \"ids.json\"\n        private const val EMBEDDINGS_FILENAME = \"embeddings.bin\"\n    }\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/search/indices/LockedSequenceWrapper.kt",
    "content": "// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage com.phodal.shirecore.search.indices\n\n\nimport java.util.concurrent.locks.Lock\n\n/**\n * Wrapper around [delegateSequenceProvider] that performs iteration under a single lock provided by [lockProvider].\n * To make sure the lock is acquired and released in the same thread,\n * iteration over this sequence should happen in a single thread.\n * To achieve this behavior in the coroutine context, run iteration with [kotlinx.coroutines.newSingleThreadContext]\n */\nclass LockedSequenceWrapper<T : Any>(private val lockProvider: () -> Lock,\n                                     private val delegateSequenceProvider: () -> Sequence<T>) : Sequence<T> {\n    override fun iterator(): Iterator<T> {\n        val lock = lockProvider()\n        lock.lock()\n\n        var delegate: Iterator<T>? = null\n        try {\n            delegate = delegateSequenceProvider().iterator()\n        }\n        finally {\n            if (delegate == null) {\n                lock.unlock()\n            }\n        }\n\n        return object : Iterator<T> {\n            override fun hasNext(): Boolean {\n                var delegateHasNext = false\n                try {\n                    delegateHasNext = delegate!!.hasNext()\n                }\n                finally {\n                    if (!delegateHasNext) {\n                        // exception or no next element\n                        lock.unlock()\n                    }\n                }\n                return delegateHasNext\n            }\n\n            override fun next(): T {\n                lateinit var delegateNext: T\n                var success = false\n                try {\n                    delegateNext = delegate!!.next()\n                    success = true\n                }\n                finally {\n                    if (!success) {\n                        // exception\n                        lock.unlock()\n                    }\n                }\n                return delegateNext\n            }\n        }\n    }\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/search/rank/LlmReRanker.kt",
    "content": "package com.phodal.shirecore.search.rank\n\nimport com.intellij.openapi.components.Service\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.phodal.shirecore.llm.LlmProvider\nimport com.phodal.shirecore.search.function.ScoredText\nimport kotlinx.coroutines.runBlocking\nimport kotlinx.coroutines.flow.cancellable\n\nfun RERANK_PROMPT(query: String, documentId: String, document: String): String {\n    return \"\"\"\n        You are an expert software developer responsible for helping detect whether the retrieved snippet of code is relevant to the query. For a given input, you need to output a single word: \"Yes\" or \"No\" indicating the retrieved snippet is relevant to the query.\n        \n        Query: Where is the FastAPI server?\n        Snippet:\n        ```/Users/andrew/Desktop/server/main.py\n        from fastapi import FastAPI\n        app = FastAPI()\n        @app.get(\"/\")\n        fun read_root(): Map<String, String> {\n            return mapOf(\"Hello\" to \"World\")\n        }\n        ```\n        Relevant: Yes\n        \n        Query: Where in the documentation does it talk about the UI?\n        Snippet:\n        ```/Users/andrew/Projects/bubble_sort/src/lib.rs\n        fn bubble_sort<T: Ord>(arr: &mut [T]) {\n            for i in 0..arr.size {\n                for j in 1 until arr.size - i {\n                    if (arr[j - 1] > arr[j]) {\n                        arr.swap(j - 1, j)\n                    }\n                }\n            }\n        }\n        ```\n        Relevant: No\n        \n        Query: $query\n        Snippet:\n        ```$documentId\n        $document\n        ```\n        Relevant:\n    \"\"\".trimIndent()\n}\n\n\n@Service(Service.Level.PROJECT)\nclass LLMReranker(val project: Project) : Reranker {\n    override val name = \"llmReranker\"\n\n    private suspend fun scoreChunk(chunk: ScoredText, query: String): Double {\n        val prompt = RERANK_PROMPT(query, getBasename(chunk.file), chunk.text)\n\n        val stream = LlmProvider.provider(project)?.stream(prompt, \"\", false)!!\n        var completion: String = \"\"\n        runBlocking {\n            stream.cancellable().collect {\n                completion += it\n            }\n        }\n\n        if (completion.isBlank()) {\n            return 0.0\n        }\n\n        val answer = completion\n            .trim()\n            .lowercase()\n            .replace(\"\\\"\", \"\")\n            .replace(\"'\", \"\")\n\n        return when (answer) {\n            \"yes\" -> 1.0\n            \"no\" -> 0.0\n            else -> {\n                println(\"Unexpected response from single token reranker: \\\"$answer\\\". Expected \\\"yes\\\" or \\\"no\\\".\")\n                0.0\n            }\n        }\n    }\n\n    private fun getBasename(file: VirtualFile?): String {\n        return file?.path?.substringAfterLast(\"/\") ?: \"unknown\"\n    }\n\n    override suspend fun rerank(query: String, chunks: List<ScoredText>): List<ScoredText> {\n        return chunks.map { chunk ->\n            chunk.copy(similarity = scoreChunk(chunk, query))\n        }.filter { it.similarity > 0.5 }\n    }\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/search/rank/LostInTheMiddleRanker.kt",
    "content": "package com.phodal.shirecore.search.rank\n\nimport com.phodal.shirecore.search.function.ScoredText\n\nclass LostInTheMiddleRanker() : Reranker {\n    override val name = \"lostInTheMiddleRanker\"\n\n    override suspend fun rerank(query: String, chunks: List<ScoredText>): List<ScoredText> {\n        val sortedChunks = chunks.sortedBy { it.similarity }\n        val result = mutableListOf<ScoredText>()\n\n        for (i in sortedChunks.indices) {\n            if (i % 2 == 0) {\n                result.add(sortedChunks[i / 2])\n            } else {\n                result.add(sortedChunks[sortedChunks.size - i / 2 - 1])\n            }\n        }\n\n        return result\n    }\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/search/rank/Reranker.kt",
    "content": "package com.phodal.shirecore.search.rank\n\nimport com.intellij.openapi.project.Project\nimport com.phodal.shirecore.search.function.ScoredText\n\ninterface Reranker {\n    val name: String\n    suspend fun rerank(query: String, chunks: List<ScoredText>): List<ScoredText>\n\n    companion object {\n        fun create(name: String, project: Project): Reranker {\n            return when (name) {\n                \"lostInTheMiddleRanker\" -> LostInTheMiddleRanker()\n                \"llmReranker\" -> LLMReranker(project)\n                else -> throw IllegalArgumentException(\"Unknown reranker: $name\")\n            }\n        }\n    }\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/search/similar/SimilarChunkContext.kt",
    "content": "package com.phodal.shirecore.search.similar\n\nimport com.intellij.lang.Language\nimport com.intellij.lang.LanguageCommenters\n\n\nclass SimilarChunkContext(val language: Language, val paths: List<String>?, val chunks: List<String>?) {\n    fun format(): String {\n        val commentPrefix = commentPrefix(language) ?: return \"\"\n\n        if (paths == null || chunks == null) return \"\"\n\n        val filteredPairs = paths.zip(chunks).filter { it.second.isNotEmpty() }\n\n        val queryBuilder = StringBuilder()\n        for ((path, chunk) in filteredPairs) {\n            val commentedCode = commentCode(chunk, commentPrefix)\n            queryBuilder.append(\"$commentPrefix Compare this snippet from $path:\\n\")\n            queryBuilder.append(commentedCode).append(\"\\n\")\n        }\n\n        return queryBuilder.toString().trim()\n    }\n\n    private fun commentCode(code: String, commentSymbol: String?): String {\n        if (commentSymbol == null) return code\n\n        return code.split(\"\\n\").joinToString(\"\\n\") {\n            \"$commentSymbol $it\"\n        }\n    }\n\n    companion object {\n        fun commentPrefix(language: Language): String? {\n            val commenter = LanguageCommenters.INSTANCE.forLanguage(language)\n            val commentPrefix = commenter.lineCommentPrefix\n            return commentPrefix\n        }\n    }\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/search/similar/SimilarChunkSearcher.kt",
    "content": "package com.phodal.shirecore.search.similar\n\n\nimport com.intellij.openapi.application.runReadAction\nimport com.intellij.openapi.diagnostic.logger\nimport com.intellij.openapi.fileEditor.impl.EditorHistoryManager\nimport com.intellij.openapi.fileTypes.FileType\nimport com.intellij.openapi.roots.ProjectFileIndex\nimport com.intellij.openapi.roots.ProjectRootManager\nimport com.intellij.openapi.vfs.VfsUtilCore\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.PsiManager\nimport com.phodal.shirecore.search.algorithm.JaccardSimilarity\nimport java.io.File\n\nclass SimilarChunksSearch(private var snippetLength: Int = 60, private var maxRelevantFiles: Int = 20) :\n    JaccardSimilarity() {\n\n    fun similarChunksWithPaths(element: PsiElement): SimilarChunkContext {\n        val mostRecentFiles = getMostRecentFiles(element)\n        val mostRecentFilesRelativePaths = mostRecentFiles.mapNotNull { relativePathTo(it, element) }\n\n        val chunks = extractChunks(element, mostRecentFiles)\n        val jaccardSimilarities = computeInputSimilarity(element.text, chunks)\n\n        val similarChunks: List<Pair<String, String>> =\n            jaccardSimilarities.mapIndexedNotNull { fileIndex, jaccardList ->\n                val maxIndex = jaccardList.indexOf(jaccardList.maxOrNull())\n                val targetChunk = chunks[fileIndex][maxIndex]\n\n                if (targetChunk.isNotEmpty()) {\n                    mostRecentFilesRelativePaths[fileIndex] to targetChunk\n                } else {\n                    null\n                }\n            }\n\n        val (paths, chunksText) = similarChunks.unzip()\n        return SimilarChunkContext(element.language, paths, chunksText)\n    }\n\n    private fun relativePathTo(relativeFile: VirtualFile, element: PsiElement): String? {\n        val fileIndex: ProjectFileIndex = ProjectRootManager.getInstance(element.project).fileIndex\n        var contentRoot: VirtualFile? = runReadAction {\n            fileIndex.getContentRootForFile(relativeFile)\n        }\n\n        if (contentRoot == null) {\n            contentRoot = fileIndex.getClassRootForFile(relativeFile)\n        }\n\n        return contentRoot?.let { VfsUtilCore.getRelativePath(relativeFile, it, File.separatorChar) }\n    }\n\n    private fun extractChunks(element: PsiElement, mostRecentFiles: List<VirtualFile>): List<List<String>> {\n        val psiManager: PsiManager = PsiManager.getInstance(element.project)\n        return mostRecentFiles.mapNotNull { file ->\n            val psiFile = psiManager.findFile(file)\n            psiFile?.text\n                ?.split(\"\\n\", limit = snippetLength)\n                ?.filter {\n                    !it.trim().startsWith(\"import \") && !it.trim().startsWith(\"package \")\n                }\n        }\n    }\n\n    private fun getMostRecentFiles(element: PsiElement): List<VirtualFile> {\n        val fileType: FileType = element.containingFile?.fileType ?: return emptyList()\n\n        val recentFiles: List<VirtualFile> = EditorHistoryManager.getInstance(element.project).fileList.filter { file ->\n            file.isValid && file.fileType == fileType && file != element.containingFile.virtualFile\n        }\n\n        val start = (recentFiles.size - maxRelevantFiles + 1).coerceAtLeast(0)\n        val end = (recentFiles.size - 1).coerceAtLeast(0)\n        return recentFiles.subList(start, end)\n    }\n\n    companion object {\n        val INSTANCE: SimilarChunksSearch = SimilarChunksSearch()\n\n        fun createQuery(element: PsiElement, chunkSize: Int = 60): String? {\n            if (element.language.displayName.lowercase() == \"markdown\") {\n                return null\n            }\n\n            return runReadAction {\n                try {\n                    val similarChunksSearch = SimilarChunksSearch(chunkSize).similarChunksWithPaths(element)\n                    if (similarChunksSearch.paths?.isEmpty() == true || similarChunksSearch.chunks?.isEmpty() == true) {\n                        return@runReadAction null\n                    }\n\n                    // todo: change to count query by size\n                    val query = similarChunksSearch.format()\n                    if (query.length < 10) {\n                        return@runReadAction null\n                    }\n\n                    if (query.length > 1024) {\n                        logger<SimilarChunksSearch>().warn(\"Query size is too large: ${query.length}\")\n                        // split to 1024\n                        return@runReadAction query.substring(0, 1024)\n                    }\n\n                    return@runReadAction query\n                } catch (e: Exception) {\n                    return@runReadAction null\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/search/tokenizer/CodeNamingTokenizer.kt",
    "content": "package com.phodal.shirecore.search.tokenizer\n\nclass CodeNamingTokenizer(opts: RegexTokenizerOptions? = null) : RegexpTokenizer(opts) {\n    init {\n        whitespacePattern = Regex(\n            \"(?<=[a-z])(?=[A-Z])|\" +       // camelCase 分词\n                    \"(?<=[A-Z])(?=[A-Z][a-z])|\" +  // PascalCase 分词\n                    \"(?<=[A-Za-z])(?=[0-9])|\" +    // 字母和数字分词\n                    \"(?<=[0-9])(?=[A-Za-z])|\" +    // 数字和字母分词\n                    \"_+\"                           // under_score 分词\n        )\n    }\n\n    override fun tokenize(input: String): List<String> {\n        val results = whitespacePattern.split(input)\n        return if (discardEmpty) {\n            without(results, \"\", \" \").map { it.lowercase() }\n        } else {\n            results.map { it.lowercase() }\n        }\n    }\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/search/tokenizer/RegexpTokenizer.kt",
    "content": "package com.phodal.shirecore.search.tokenizer\n\ninterface RegexTokenizerOptions {\n    val pattern: Regex?\n    val discardEmpty: Boolean\n    val gaps: Boolean?\n}\n\nopen class RegexpTokenizer(opts: RegexTokenizerOptions? = null) : Tokenizer {\n    var whitespacePattern = Regex(\"\\\\s+\")\n    var discardEmpty: Boolean = true\n    private var _gaps: Boolean? = null\n\n    init {\n        val options = opts ?: object : RegexTokenizerOptions {\n            override val pattern: Regex? = null\n            override val discardEmpty: Boolean = true\n            override val gaps: Boolean? = null\n        }\n\n        whitespacePattern = options.pattern ?: whitespacePattern\n        discardEmpty = options.discardEmpty\n        _gaps = options.gaps\n\n        if (_gaps == null) {\n            _gaps = true\n        }\n    }\n\n    override fun tokenize(input: String): List<String> {\n        val results: List<String>\n\n        val output = if (_gaps == true) {\n            results = input.split(whitespacePattern)\n            if (discardEmpty) without(results, \"\", \" \") else results\n        } else {\n            results = whitespacePattern.findAll(input).map { it.value }.toList()\n            results.ifEmpty { emptyList() }\n        }\n\n        return output\n    }\n\n    fun without(arr: List<String>, vararg values: String): List<String> {\n        return arr.filter { it !in values }\n    }\n}\n\nclass WordTokenizer(options: RegexTokenizerOptions? = null) : RegexpTokenizer(options) {\n    init {\n        whitespacePattern = Regex(\"[^A-Za-zА-Яа-я0-9_]+\")\n    }\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/search/tokenizer/StopwordsBasedTokenizer.kt",
    "content": "package com.phodal.shirecore.search.tokenizer\n\nimport com.phodal.shirecore.search.tokenizer.TermSplitter.splitTerms\n\nval CHINESE_STOP_WORDS = listOf(\n    \"的\", \"地\", \"得\", \"和\", \"跟\",\n    \"与\", \"及\", \"向\", \"并\", \"等\",\n    \"更\", \"已\", \"含\", \"做\", \"我\",\n    \"你\", \"他\", \"她\", \"们\", \"某\",\n    \"该\", \"各\", \"每\", \"这\", \"那\",\n    \"哪\", \"什\", \"么\", \"谁\", \"年\",\n    \"月\", \"日\", \"时\", \"分\", \"秒\",\n    \"几\", \"多\", \"来\", \"在\", \"就\",\n    \"又\", \"很\", \"呢\", \"吧\", \"吗\",\n    \"了\", \"嘛\", \"哇\", \"儿\", \"哼\",\n    \"啊\", \"嗯\", \"是\", \"着\", \"都\",\n    \"不\", \"说\", \"也\", \"看\", \"把\",\n    \"还\", \"个\", \"有\", \"小\", \"到\",\n    \"一\", \"为\", \"中\", \"于\", \"对\",\n    \"会\", \"之\", \"第\", \"此\", \"或\",\n    \"共\", \"按\", \"请\"\n)\n\nclass StopwordsBasedTokenizer private constructor() : Tokenizer {\n    companion object {\n        private var instance_: StopwordsBasedTokenizer? = null\n\n        @JvmStatic\n        fun instance(): StopwordsBasedTokenizer {\n            if (instance_ == null) {\n                instance_ = StopwordsBasedTokenizer()\n            }\n            return instance_!!\n        }\n    }\n\n    private val stopWords = listOf(\"we\", \"our\", \"you\", \"it\", \"its\", \"they\", \"them\", \"their\", \"this\", \"that\", \"these\", \"those\", \"is\", \"are\", \"was\", \"were\", \"be\", \"been\", \"being\", \"have\", \"has\", \"had\", \"having\", \"do\", \"does\", \"did\", \"doing\", \"can\", \"don\", \"t\", \"s\", \"will\", \"would\", \"should\", \"what\", \"which\", \"who\", \"when\", \"where\", \"why\", \"how\", \"a\", \"an\", \"the\", \"and\", \"or\", \"not\", \"no\", \"but\", \"because\", \"as\", \"until\", \"again\", \"further\", \"then\", \"once\", \"here\", \"there\", \"all\", \"any\", \"both\", \"each\", \"few\", \"more\", \"most\", \"other\", \"some\", \"such\", \"above\", \"below\", \"to\", \"during\", \"before\", \"after\", \"of\", \"at\", \"by\", \"about\", \"between\", \"into\", \"through\", \"from\", \"up\", \"down\", \"in\", \"out\", \"on\", \"off\", \"over\", \"under\", \"only\", \"own\", \"same\", \"so\", \"than\", \"too\", \"very\", \"just\", \"now\")\n\n    private val programmingKeywords = listOf(\"if\", \"then\", \"else\", \"for\", \"while\", \"with\", \"def\", \"function\", \"return\",\n        \"TODO\", \"import\", \"try\", \"catch\", \"raise\", \"finally\", \"repeat\", \"switch\", \"case\", \"match\", \"assert\", \"continue\",\n        \"break\", \"const\", \"class\", \"enum\", \"struct\", \"static\", \"new\", \"super\", \"this\", \"var\")\n\n    private val javaKeywords = listOf(\"public\", \"private\", \"protected\", \"static\", \"final\", \"abstract\", \"interface\", \"implements\", \"extends\", \"throws\", \"throw\", \"try\", \"catch\", \"finally\", \"synchronized\")\n\n    private val chineseStopWords = CHINESE_STOP_WORDS\n\n    private val stopWordsSet = setOf(\n        *stopWords.toTypedArray(),\n        *programmingKeywords.toTypedArray(),\n        *javaKeywords.toTypedArray(),\n        *chineseStopWords.toTypedArray()\n    )\n\n    override fun tokenize(input: String): List<String> {\n        return splitTerms(input).toList().filter {\n            !stopWordsSet.contains(it)\n        }\n    }\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/search/tokenizer/TermSplitter.kt",
    "content": "package com.phodal.shirecore.search.tokenizer\n\n/**\n * The `TermSplitter` object provides methods for splitting an input string into terms based on different naming styles of identifiers.\n * It supports three naming styles: CamelCase, Numeric, and underscore_case.\n *\n * The `splitTerms` method is a static generator function that splits the input string into terms based on the naming style of the identifiers.\n * It matches all patterns in the input string, creates a set to store unique terms, and adds lowercase versions of the matches to this set.\n * It then splits the matches into terms based on CamelCase, underscore_case, and Numeric naming styles, and adds these terms to the terms array.\n * If the term is longer than 2 characters and matches the alphabetic pattern, it is added to the set of unique terms.\n * Finally, it yields each unique term as a sequence.\n *\n * The `syncSplitTerms` method synchronously splits the input string into terms and returns a list of unique terms.\n */\nobject TermSplitter {\n    private val allPattern = Regex(\"(?<![\\\\p{Alpha}\\\\d_$])[\\\\p{Alpha}_$][\\\\p{Alpha}\\\\d_$]{2,}(?![\\\\p{Alpha}\\\\d_$])\")\n    private val camelCasePattern = Regex(\"(?<=[a-z\\$])(?=[A-Z])\")\n    private val numericPattern = Regex(\"^(\\\\D+)\\\\d+$\")\n    private val alphaNumericPattern = Regex(\"[\\\\p{Alpha}_\\$]{3,}\")\n\n    /**\n     * The `splitTerms` method is a static generator function that splits the input string into terms based on the naming style of the identifiers.\n     * It supports three naming styles: CamelCase, Numeric, and underscore_case.\n     *\n     * @param input - The input string to be split into terms.\n     *\n     * The method works as follows:\n     * 1. It matches all patterns in the input string.\n     * 2. For each match, it creates a new set to store unique terms, and adds the lowercase version of the match to this set.\n     * 3. It then splits the match into terms based on the CamelCase, underscore_case, and Numeric naming styles, and adds these terms to the terms array.\n     * 4. If the term is longer than 2 characters and matches the alphabetic pattern, it is added to the set of unique terms.\n     * 5. Finally, it yields each unique term.\n     *\n     * Note: The method uses the `findAll`, `split`, and `find` methods of the String object, and the `add`\n     * method of the Set object. It also uses the spread operator (...) to add multiple elements to an array.\n     *\n     * @return A sequence of unique terms.\n     */\n    fun splitTerms(input: String): Sequence<String> {\n        return sequence {\n            val matchAll = allPattern.findAll(input)\n            for (match in matchAll) {\n                val matchValue = match.value\n\n                val uniqueTerms = mutableSetOf<String>()\n                uniqueTerms.add(matchValue.lowercase())\n\n                val terms = mutableListOf<String>()\n\n                val camelCaseSplits = matchValue.split(camelCasePattern)\n                if (camelCaseSplits.size > 1) {\n                    terms.addAll(camelCaseSplits)\n                }\n\n                val underscoreSplit = matchValue.split('_')\n                if (underscoreSplit.size > 1) {\n                    terms.addAll(underscoreSplit)\n                }\n\n                val numberSuffixMatch = numericPattern.find(matchValue)\n                numberSuffixMatch?.let {\n                    terms.add(it.groupValues[1])\n                }\n\n                terms.filter { term -> term.length > 2 && alphaNumericPattern.containsMatchIn(term) }\n                    .forEach {\n                        uniqueTerms.add(it.lowercase())\n                    }\n\n                yieldAll(uniqueTerms)\n            }\n        }.distinct()\n    }\n\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/search/tokenizer/Tokenizer.kt",
    "content": "package com.phodal.shirecore.search.tokenizer\n\ninterface Tokenizer {\n    fun tokenize(input: String): List<String>\n\n    fun trim(array: MutableList<String>): List<String> {\n        while (array.last() == \"\") {\n            array.removeAt(array.lastIndex)\n        }\n\n        while (array.first() == \"\") {\n            array.removeAt(0)\n        }\n\n        return array\n    }\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/sketch/LangSketch.kt",
    "content": "package com.phodal.shirecore.sketch\n\nimport com.intellij.lang.Language\nimport com.intellij.openapi.Disposable\nimport com.intellij.openapi.actionSystem.ActionGroup\nimport com.intellij.openapi.actionSystem.ActionManager\nimport com.intellij.openapi.actionSystem.ActionPlaces\nimport com.intellij.openapi.actionSystem.ActionToolbar\nimport com.intellij.openapi.editor.Editor\nimport com.intellij.openapi.editor.colors.EditorColorsListener\nimport com.intellij.openapi.editor.colors.EditorColorsManager\nimport com.intellij.openapi.editor.ex.EditorEx\nimport com.intellij.openapi.project.Project\nimport com.intellij.util.messages.Topic\nimport javax.swing.JComponent\n\ninterface LangSketch : Disposable {\n    fun getViewText(): String\n    fun updateViewText(text: String)\n    fun getComponent(): JComponent\n    fun updateLanguage(language: Language?, originLanguage: String?)\n    fun doneUpdateText(text: String) {}\n\n    fun setupActionBar(project: Project, editor: Editor) {\n        val toolbar = actionToolbar() ?: return\n\n        if (editor is EditorEx) {\n            toolbar.component.setBackground(editor.backgroundColor)\n        }\n        toolbar.component.setOpaque(true)\n        toolbar.targetComponent = editor.contentComponent\n        editor.headerComponent = toolbar.component\n\n        val connect = project.messageBus.connect(this)\n        val topic: Topic<EditorColorsListener> = EditorColorsManager.TOPIC\n        connect.subscribe(topic, EditorColorsListener {\n            if (editor is EditorEx) {\n                toolbar.component.setBackground(editor.backgroundColor)\n            }\n        })\n    }\n\n    fun actionToolbar(): ActionToolbar? {\n        val toolbarActionGroup = ActionManager.getInstance().getAction(\"Shire.ToolWindow.Toolbar\") as? ActionGroup\n            ?: return null\n\n        val toolbar = ActionManager.getInstance()\n            .createActionToolbar(ActionPlaces.MAIN_TOOLBAR, toolbarActionGroup, true)\n        return toolbar\n    }\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/sketch/highlight/CodeHighlightSketch.kt",
    "content": "package com.phodal.shirecore.sketch.highlight\n\nimport com.intellij.lang.Language\nimport com.intellij.openapi.Disposable\nimport com.intellij.openapi.actionSystem.DataProvider\nimport com.intellij.openapi.application.ApplicationManager\nimport com.intellij.openapi.application.ReadAction\nimport com.intellij.openapi.command.WriteCommandAction\nimport com.intellij.openapi.editor.Document\nimport com.intellij.openapi.editor.Editor\nimport com.intellij.openapi.editor.EditorFactory\nimport com.intellij.openapi.editor.EditorKind\nimport com.intellij.openapi.editor.ex.EditorEx\nimport com.intellij.openapi.editor.ex.EditorMarkupModel\nimport com.intellij.openapi.editor.ex.FocusChangeListener\nimport com.intellij.openapi.editor.ex.MarkupModelEx\nimport com.intellij.openapi.editor.highlighter.EditorHighlighterFactory\nimport com.intellij.openapi.fileEditor.FileDocumentManager\nimport com.intellij.openapi.fileTypes.PlainTextLanguage\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.util.Disposer\nimport com.intellij.openapi.util.text.StringUtil\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.intellij.testFramework.LightVirtualFile\nimport com.intellij.ui.JBColor\nimport com.intellij.ui.components.JBPanel\nimport com.intellij.util.concurrency.annotations.RequiresReadLock\nimport com.intellij.util.ui.JBEmptyBorder\nimport com.intellij.util.ui.JBUI\nimport com.phodal.shirecore.sketch.LangSketch\nimport com.phodal.shirecore.utils.markdown.CodeFenceLanguage\nimport java.awt.BorderLayout\nimport java.util.concurrent.atomic.AtomicBoolean\nimport javax.swing.JComponent\n\nclass CodeHighlightSketch(\n    val project: Project, val text: String, private var ideaLanguage: Language?,\n    val editorLineThreshold: Int = 6,\n) :\n    JBPanel<CodeHighlightSketch>(BorderLayout()), DataProvider, LangSketch {\n    private var textLanguage: String? = null\n\n    var editorFragment: EditorFragment? = null\n    private var hasSetupAction = false\n\n    init {\n        if (text.isEmpty() && (ideaLanguage?.displayName != \"Markdown\" && ideaLanguage != PlainTextLanguage.INSTANCE)) {\n            initEditor(text)\n        }\n    }\n\n    fun initEditor(text: String) {\n        if (hasSetupAction) return\n        hasSetupAction = true\n\n        val editor = createCodeViewerEditor(project, text, ideaLanguage, this)\n\n        border = JBEmptyBorder(8)\n        layout = BorderLayout(JBUI.scale(8), 0)\n        background = JBColor(0xEAEEF7, 0x2d2f30)\n\n        editor.component.isOpaque = true\n\n        editorFragment = EditorFragment(editor, editorLineThreshold)\n        add(editorFragment!!.getContent(), BorderLayout.CENTER)\n\n        if (textLanguage != null && textLanguage?.lowercase() != \"markdown\") {\n            setupActionBar(project, editor)\n        }\n    }\n\n    override fun getViewText(): String {\n        return editorFragment?.editor?.document?.text ?: \"\"\n    }\n\n    override fun updateLanguage(language: Language?, originLanguage: String?) {\n        if (ideaLanguage == null || ideaLanguage == PlainTextLanguage.INSTANCE) {\n            ideaLanguage = language\n            textLanguage = originLanguage\n        }\n    }\n\n    override fun updateViewText(text: String) {\n        if (!hasSetupAction && text.isNotEmpty()) {\n            initEditor(text)\n        }\n\n        WriteCommandAction.runWriteCommandAction(project) {\n            val document = editorFragment?.editor?.document\n            val normalizedText = StringUtil.convertLineSeparators(text)\n            document?.replaceString(0, document.textLength, normalizedText)\n        }\n    }\n\n    override fun getComponent(): JComponent = this\n\n    override fun getData(dataId: String): Any? = null\n\n    companion object {\n        private val LINE_NO_REGEX = Regex(\"^\\\\d+:\")\n\n        fun createCodeViewerEditor(\n            project: Project,\n            text: String,\n            ideaLanguage: Language?,\n            disposable: Disposable,\n        ): EditorEx {\n            var editorText = text\n            val language = ideaLanguage ?: CodeFenceLanguage.findLanguage(\"Plain text\")\n            val ext = if (language.displayName == \"Plain text\") {\n                CodeFenceLanguage.lookupFileExt(language.displayName)\n            } else {\n                language.associatedFileType?.defaultExtension ?: \"Unknown\"\n            }\n            /// check text easyline starts with Lineno and :, for example: 1:\n            var isShowLineNo = true\n            editorText.lines().forEach {\n                if (!it.matches(LINE_NO_REGEX)) {\n                    isShowLineNo = false\n                    return@forEach\n                }\n            }\n\n            if (isShowLineNo) {\n                val newLines = text.lines().map { it.replace(LINE_NO_REGEX, \"\") }\n                editorText = newLines.joinToString(\"\\n\")\n            }\n\n            val file = LightVirtualFile(\"shire.${ext}\", language, editorText)\n            val document: Document = file.findDocument() ?: throw IllegalStateException(\"Document not found\")\n\n            return createCodeViewerEditor(project, file, document, disposable, isShowLineNo)\n        }\n\n        fun createCodeViewerEditor(\n            project: Project,\n            file: LightVirtualFile,\n            document: Document,\n            disposable: Disposable,\n            isShowLineNo: Boolean? = false,\n        ): EditorEx {\n            val editor: EditorEx = ReadAction.compute<EditorEx, Throwable> {\n                EditorFactory.getInstance().createViewer(document, project, EditorKind.PREVIEW) as EditorEx\n            }\n\n            disposable.whenDisposed(disposable) {\n                EditorFactory.getInstance().releaseEditor(editor)\n            }\n\n            editor.setFile(file)\n            editor.setCaretEnabled(true)\n\n            val highlighter = ApplicationManager.getApplication()\n                .getService(EditorHighlighterFactory::class.java)\n                .createEditorHighlighter(project, file)\n\n            editor.highlighter = highlighter\n\n            val markupModel: MarkupModelEx = editor.markupModel\n            (markupModel as EditorMarkupModel).isErrorStripeVisible = false\n\n            val settings = editor.settings.also {\n                it.isDndEnabled = false\n                it.isLineNumbersShown = isShowLineNo ?: false\n                it.additionalLinesCount = 0\n                it.isLineMarkerAreaShown = false\n                it.isFoldingOutlineShown = false\n                it.isRightMarginShown = false\n                it.isShowIntentionBulb = false\n                it.isUseSoftWraps = true\n                it.isRefrainFromScrolling = true\n                it.isAdditionalPageAtBottom = false\n                it.isCaretRowShown = false\n            }\n\n            editor.addFocusListener(object : FocusChangeListener {\n                override fun focusGained(focusEditor: Editor) {\n                    settings.isCaretRowShown = true\n                }\n\n                override fun focusLost(focusEditor: Editor) {\n                    settings.isCaretRowShown = false\n                    editor.markupModel.removeAllHighlighters()\n                }\n            })\n\n            return editor\n        }\n    }\n\n    override fun dispose() {\n        // do nothing\n    }\n}\n\n@RequiresReadLock\nfun VirtualFile.findDocument(): Document? {\n    return ReadAction.compute<Document, Throwable> {\n        FileDocumentManager.getInstance().getDocument(this)\n    }\n}\n\nfun Disposable.whenDisposed(listener: () -> Unit) {\n    Disposer.register(this) { listener() }\n}\n\nfun Disposable.whenDisposed(\n    parentDisposable: Disposable,\n    listener: () -> Unit,\n) {\n    val isDisposed = AtomicBoolean(false)\n\n    val disposable = Disposable {\n        if (isDisposed.compareAndSet(false, true)) {\n            listener()\n        }\n    }\n\n    Disposer.register(this, disposable)\n\n    Disposer.register(parentDisposable, Disposable {\n        if (isDisposed.compareAndSet(false, true)) {\n            Disposer.dispose(disposable)\n        }\n    })\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/sketch/highlight/EditorFragment.kt",
    "content": "package com.phodal.shirecore.sketch.highlight\n\nimport com.intellij.icons.AllIcons\nimport com.intellij.openapi.editor.Editor\nimport com.intellij.openapi.editor.event.CaretEvent\nimport com.intellij.openapi.editor.event.CaretListener\nimport com.intellij.openapi.editor.ex.EditorEx\nimport com.intellij.ui.components.JBLabel\nimport com.intellij.util.ui.JBUI\nimport com.intellij.util.ui.components.BorderLayoutPanel\nimport java.awt.Color\nimport java.awt.Dimension\nimport java.awt.Insets\nimport java.awt.event.MouseAdapter\nimport java.awt.event.MouseEvent\nimport javax.swing.Box\nimport javax.swing.JComponent\n\n\nclass EditorPadding(private val editor: Editor, pad: Int) :\n    Box.Filler(Dimension(pad, pad), Dimension(pad, pad), Dimension(pad, pad)) {\n    init {\n        setOpaque(true)\n        editor.caretModel.addCaretListener(object : CaretListener {\n            override fun caretPositionChanged(event: CaretEvent) {\n                this@EditorPadding.repaint()\n            }\n        })\n    }\n\n    override fun getBackground(): Color = editor.contentComponent.getBackground()\n}\n\n\nclass EditorFragment(var editor: EditorEx, private val editorLineThreshold: Int = 6) {\n    private val expandCollapseTextLabel: JBLabel = JBLabel(\"\", 0).apply {\n        isOpaque = true\n        isVisible = false\n    }\n\n    private val content: BorderLayoutPanel = createContentPanel()\n    private var collapsed = false\n\n    private fun createContentPanel(): BorderLayoutPanel {\n        return object : BorderLayoutPanel() {\n            override fun getPreferredSize(): Dimension {\n                val preferredSize = super.getPreferredSize()\n                if (editor.document.lineCount > editorLineThreshold && collapsed) {\n                    val lineHeight = editor.lineHeight\n                    val insets = editor.scrollPane.insets\n                    val editorMaxHeight = calculateMaxHeight(lineHeight, insets)\n                    return Dimension(preferredSize.width, editorMaxHeight)\n                }\n                return preferredSize\n            }\n\n            private fun calculateMaxHeight(lineHeight: Int, insets: Insets): Int {\n                val height = lineHeight * editorLineThreshold + insets.height\n                val headerHeight = editor.headerComponent?.preferredSize?.height ?: 0\n                val labelHeight = expandCollapseTextLabel.preferredSize.height\n                return height + headerHeight + labelHeight + insets.height\n            }\n        }.apply {\n            isOpaque = true\n\n            addToLeft(EditorPadding(editor, 5))\n            addToRight(EditorPadding(editor, 5))\n            addToCenter(editor.component)\n            addToBottom(expandCollapseTextLabel)\n        }\n    }\n\n    init {\n        expandCollapseTextLabel.addMouseListener(object : MouseAdapter() {\n            override fun mouseClicked(e: MouseEvent?) {\n                toggleCollapsedState()\n            }\n        })\n    }\n\n    fun getContent(): JComponent = content\n\n    fun isCollapsed(): Boolean = collapsed\n\n    fun setCollapsed(value: Boolean) {\n        if (collapsed != value) {\n            collapsed = value\n            updateExpandCollapseLabel()\n        }\n    }\n\n    private fun toggleCollapsedState() {\n        setCollapsed(!collapsed)\n    }\n\n    fun updateExpandCollapseLabel() {\n        val linesCount = editor.document.lineCount\n        expandCollapseTextLabel.isVisible = linesCount > editorLineThreshold\n        expandCollapseTextLabel.text = if (collapsed) \"More lines\" else \"\"\n        expandCollapseTextLabel.icon = if (collapsed) AllIcons.General.ChevronDown else AllIcons.General.ChevronUp\n    }\n}\n\nval Insets.width: Int get() = left + right\nval Insets.height: Int get() = top + bottom\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/sketch/highlight/toolbar/ShireCopyToClipboardAction.kt",
    "content": "package com.phodal.shirecore.sketch.highlight.toolbar\n\nimport com.intellij.openapi.actionSystem.ActionUpdateThread\nimport com.intellij.openapi.actionSystem.AnActionEvent\nimport com.intellij.openapi.project.DumbAwareAction\nimport java.awt.Toolkit\nimport java.awt.datatransfer.StringSelection\n\nclass ShireCopyToClipboardAction : DumbAwareAction() {\n    override fun getActionUpdateThread(): ActionUpdateThread = ActionUpdateThread.EDT\n\n    override fun actionPerformed(e: AnActionEvent) {\n        val editor = e.getData(com.intellij.openapi.actionSystem.PlatformDataKeys.EDITOR) ?: return\n        val document = editor.document\n        val text = document.text\n\n        val selection = StringSelection(text)\n        val clipboard = Toolkit.getDefaultToolkit().systemClipboard\n        clipboard.setContents(selection, null)\n    }\n}\n\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/sketch/highlight/toolbar/ShireInsertCodeAction.kt",
    "content": "package com.phodal.shirecore.sketch.highlight.toolbar\n\nimport com.intellij.openapi.actionSystem.ActionUpdateThread\nimport com.intellij.openapi.actionSystem.AnActionEvent\nimport com.intellij.openapi.actionSystem.PlatformDataKeys\nimport com.intellij.openapi.command.WriteCommandAction\nimport com.intellij.openapi.fileEditor.FileEditorManager\nimport com.intellij.openapi.project.DumbAwareAction\nimport com.intellij.psi.PsiDocumentManager\nimport com.intellij.psi.codeStyle.CodeStyleManager\n\nclass ShireInsertCodeAction : DumbAwareAction() {\n    override fun actionPerformed(e: AnActionEvent) {\n        val project = e.project ?: return\n        val editor = e.getData(PlatformDataKeys.EDITOR) ?: return\n        val selectionModel = if (editor.selectionModel.hasSelection()) editor.selectionModel else null\n        val newText = selectionModel?.selectedText ?: editor.document.text.trimEnd()\n\n        val textEditor = FileEditorManager.getInstance(project).selectedTextEditor ?: return\n        val currentSelection = textEditor.selectionModel\n\n        WriteCommandAction.writeCommandAction(project).compute<Any, RuntimeException> {\n            val offset: Int\n            val document = textEditor.document\n\n            if (currentSelection.hasSelection()) {\n                offset = currentSelection.selectionStart\n                document.replaceString(currentSelection.selectionStart, currentSelection.selectionEnd, newText)\n            } else {\n                offset = textEditor.caretModel.offset\n                document.insertString(offset, newText)\n            }\n\n            val psiFile = PsiDocumentManager.getInstance(project).getPsiFile(document) ?: return@compute\n            PsiDocumentManager.getInstance(project).commitDocument(document)\n            CodeStyleManager.getInstance(project).reformatText(psiFile, offset, offset + newText.length)\n        }\n    }\n\n    override fun update(e: AnActionEvent) {\n        val project = e.project\n        if (project == null) {\n            e.presentation.isEnabled = false\n            return\n        }\n\n        val selectedTextEditor = FileEditorManager.getInstance(project).selectedTextEditor\n        if (selectedTextEditor == null || !selectedTextEditor.document.isWritable) {\n            e.presentation.isEnabled = false\n        } else {\n            e.presentation.isEnabledAndVisible = true\n        }\n    }\n\n    override fun getActionUpdateThread(): ActionUpdateThread = ActionUpdateThread.EDT\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/sketch/highlight/toolbar/ShireLanguageLabelAction.kt",
    "content": "package com.phodal.shirecore.sketch.highlight.toolbar\n\nimport com.intellij.openapi.actionSystem.*\nimport com.intellij.openapi.actionSystem.ex.CustomComponentAction\nimport com.intellij.openapi.fileEditor.FileDocumentManager\nimport com.intellij.openapi.project.DumbAwareAction\nimport com.intellij.openapi.util.Key\nimport com.intellij.testFramework.LightVirtualFile\nimport com.intellij.ui.components.JBLabel\nimport com.intellij.util.ui.UIUtil\nimport javax.swing.JComponent\n\nclass ShireLanguageLabelAction: DumbAwareAction(), CustomComponentAction {\n    override fun getActionUpdateThread(): ActionUpdateThread = ActionUpdateThread.BGT\n\n    override fun createCustomComponent(presentation: Presentation, place: String): JComponent {\n        val languageId = presentation.getClientProperty(SHIRE_LANGUAGE_LABEL_KEY) ?: \"\"\n        val label = JBLabel(languageId)\n        label.setOpaque(false)\n        label.foreground = UIUtil.getLabelInfoForeground()\n        return label\n    }\n\n    override fun updateCustomComponent(component: JComponent, presentation: Presentation) {\n        if (component !is JBLabel) return\n\n        val languageId = presentation.getClientProperty(SHIRE_LANGUAGE_LABEL_KEY) ?: \"\"\n        if (languageId.isNotBlank() && component.text.isBlank()) {\n            component.text = languageId\n        }\n    }\n\n    override fun actionPerformed(e: AnActionEvent) {\n\n    }\n\n    override fun update(e: AnActionEvent) {\n        val editor = e.dataContext.getData(CommonDataKeys.EDITOR) ?: return\n        val lightVirtualFile = FileDocumentManager.getInstance().getFile(editor.document)\n                as? LightVirtualFile ?: return\n\n        val language = lightVirtualFile.language ?: return\n\n        e.presentation.putClientProperty(SHIRE_LANGUAGE_LABEL_KEY, language.displayName)\n    }\n\n    companion object {\n        val SHIRE_LANGUAGE_LABEL_KEY: Key<String> = Key.create(\"ShireLanguagePresentationKey\")\n    }\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/sketch/highlight/toolbar/ShireRunCodeAction.kt",
    "content": "package com.phodal.shirecore.sketch.highlight.toolbar\n\nimport com.intellij.ide.scratch.ScratchRootType\nimport com.intellij.openapi.actionSystem.ActionUpdateThread\nimport com.intellij.openapi.actionSystem.AnActionEvent\nimport com.intellij.openapi.application.runWriteAction\nimport com.intellij.openapi.fileEditor.FileDocumentManager\nimport com.intellij.openapi.project.DumbAwareAction\nimport com.intellij.openapi.vfs.readText\nimport com.intellij.psi.PsiManager\nimport com.phodal.shirecore.provider.shire.FileRunService\n\nclass ShireRunCodeAction : DumbAwareAction() {\n    override fun getActionUpdateThread(): ActionUpdateThread = ActionUpdateThread.EDT\n\n    override fun update(e: AnActionEvent) {\n        val project = e.project ?: return\n        val editor = e.getData(com.intellij.openapi.actionSystem.PlatformDataKeys.EDITOR) ?: return\n        val document = editor.document\n        val file = FileDocumentManager.getInstance().getFile(document)\n\n        if (file != null) {\n            val psiFile = PsiManager.getInstance(project).findFile(file)\n            if (psiFile != null) {\n                e.presentation.isEnabled = FileRunService.provider(project, file) != null\n                return\n            }\n        }\n\n        e.presentation.isEnabled = false\n    }\n\n    override fun actionPerformed(e: AnActionEvent) {\n        val editor = e.getData(com.intellij.openapi.actionSystem.PlatformDataKeys.EDITOR) ?: return\n        val project = e.project ?: return\n\n        val document = editor.document\n        val file = FileDocumentManager.getInstance().getFile(document) ?: return\n        val psiFile = PsiManager.getInstance(project).findFile(file)\n            ?: return\n\n        val scratchFile = ScratchRootType.getInstance()\n            .createScratchFile(project, file.name, psiFile.language, file.readText())\n            ?: return\n\n        try {\n            FileRunService.provider(project, file)?.runFile(\n                project,\n                scratchFile,\n                psiFile,\n            )\n        } finally {\n//            runWriteAction {\n//                scratchFile.delete(this)\n//            }\n        }\n    }\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/sketch/lint/SketchCodeInspection.kt",
    "content": "package com.phodal.shirecore.sketch.lint\n\nimport com.intellij.analysis.AnalysisScope\nimport com.intellij.codeInsight.daemon.impl.DaemonProgressIndicator\nimport com.intellij.codeInspection.InspectionEngine\nimport com.intellij.codeInspection.InspectionManager\nimport com.intellij.codeInspection.ProblemDescriptor\nimport com.intellij.codeInspection.ex.GlobalInspectionContextBase\nimport com.intellij.codeInspection.ex.LocalInspectionToolWrapper\nimport com.intellij.lang.annotation.HighlightSeverity\nimport com.intellij.openapi.application.runReadAction\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.ui.popup.JBPopup\nimport com.intellij.openapi.ui.popup.JBPopupFactory\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.intellij.profile.codeInspection.InspectionProjectProfileManager\nimport com.intellij.psi.PsiFile\nimport com.intellij.psi.PsiManager\nimport com.intellij.ui.JBColor\nimport com.intellij.ui.components.JBLabel\nimport com.intellij.ui.components.JBScrollPane\nimport com.intellij.ui.table.JBTable\nimport com.intellij.util.PairProcessor\nimport com.phodal.shirecore.ShireCoreBundle\nimport java.awt.Dimension\nimport java.awt.FlowLayout\nimport java.awt.event.MouseAdapter\nimport java.awt.event.MouseEvent\nimport javax.swing.BorderFactory\nimport javax.swing.JPanel\nimport javax.swing.JTable\nimport javax.swing.table.DefaultTableModel\n\nobject SketchCodeInspection {\n    fun showErrors(errors: List<SketchInspectionError>, panel: JPanel) {\n        val columnNames = arrayOf(\"Line\", \"Description\", \"Highlight Type\")\n        val data = errors.map {\n            arrayOf(it.lineNumber, it.description, it.highlightType.toString())\n        }.toTypedArray()\n\n        val tableModel = DefaultTableModel(data, columnNames)\n\n        val table = JBTable(tableModel).apply {\n            autoResizeMode = JTable.AUTO_RESIZE_ALL_COLUMNS\n        }\n\n        val scrollPane = JBScrollPane(table).apply {\n            preferredSize = Dimension(480, 400)\n        }\n\n        val errorLabel = JBLabel(ShireCoreBundle.message(\"sketch.lint.error\", errors.size)).apply {\n            border = BorderFactory.createEmptyBorder(5, 5, 5, 5)\n            addMouseListener(object : MouseAdapter() {\n                override fun mouseClicked(e: MouseEvent?) {\n                    createPopup(scrollPane, table, errors).showInCenterOf(panel)\n                }\n\n                override fun mouseEntered(e: MouseEvent) {\n                    toolTipText = ShireCoreBundle.message(\"sketch.lint.error.tooltip\")\n                }\n            })\n        }\n\n        val errorPanel = JPanel().apply {\n            background = JBColor.WHITE\n            layout = FlowLayout(FlowLayout.LEFT)\n            add(errorLabel)\n        }\n\n        panel.add(errorPanel)\n    }\n\n    private fun createPopup(\n        scrollPane: JBScrollPane,\n        table: JBTable,\n        errors: List<SketchInspectionError>\n    ): JBPopup = JBPopupFactory.getInstance()\n        .createComponentPopupBuilder(scrollPane, table)\n        .setTitle(\"Found Lint Issues: ${errors.size}\")\n        .setResizable(true)\n        .setMovable(true)\n        .setRequestFocus(true)\n        .createPopup()\n\n    fun runInspections(project: Project, psiFile: PsiFile, originFile: VirtualFile): List<SketchInspectionError> {\n        val globalContext = InspectionManager.getInstance(project).createNewGlobalContext()\n                as? GlobalInspectionContextBase ?: return emptyList()\n\n        val originPsi = runReadAction { PsiManager.getInstance(project).findFile(originFile) }\n            ?: return emptyList()\n\n        globalContext.currentScope = AnalysisScope(originPsi)\n\n        val toolsCopy = collectTools(project, psiFile, globalContext)\n        if (toolsCopy.isEmpty()) {\n            return emptyList()\n        }\n\n        return runReadAction {\n            val indicator = DaemonProgressIndicator()\n            val result: Map<LocalInspectionToolWrapper, List<ProblemDescriptor>> = InspectionEngine.inspectEx(\n                toolsCopy, psiFile, psiFile.textRange, psiFile.textRange, false, false, true,\n                indicator, PairProcessor.alwaysTrue<LocalInspectionToolWrapper?, ProblemDescriptor?>()\n            )\n\n            val problems = result.values.flatten()\n            return@runReadAction problems\n                .sortedBy { it.lineNumber }\n                .distinctBy { it.lineNumber }.map {\n                    SketchInspectionError.Companion.from(it)\n                }\n        }\n    }\n\n    private fun collectTools(\n        project: Project,\n        psiFile: PsiFile,\n        globalContext: GlobalInspectionContextBase\n    ): MutableList<LocalInspectionToolWrapper> {\n        val inspectionProfile = InspectionProjectProfileManager.getInstance(project).currentProfile\n        val toolWrappers = inspectionProfile.getInspectionTools(psiFile)\n            .filter {\n                it.isApplicable(psiFile.language) && it.defaultLevel.severity == HighlightSeverity.ERROR\n            }\n\n        toolWrappers.forEach {\n            it.initialize(globalContext)\n        }\n\n        val toolsCopy: MutableList<LocalInspectionToolWrapper> =\n            ArrayList<LocalInspectionToolWrapper>(toolWrappers.size)\n        for (tool in toolWrappers) {\n            if (tool is LocalInspectionToolWrapper) {\n                toolsCopy.add(tool.createCopy())\n            }\n        }\n\n        return toolsCopy\n    }\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/sketch/lint/SketchInspectionError.kt",
    "content": "package com.phodal.shirecore.sketch.lint\n\nimport com.intellij.codeInspection.ProblemDescriptor\nimport com.intellij.codeInspection.ProblemHighlightType\n\ndata class SketchInspectionError(\n    val lineNumber: Int,\n    val description: String,\n    val highlightType: ProblemHighlightType,\n) {\n    companion object {\n        fun from(problemDescriptor: ProblemDescriptor): SketchInspectionError {\n            return SketchInspectionError(\n                problemDescriptor.lineNumber,\n                problemDescriptor.descriptionTemplate,\n                problemDescriptor.highlightType\n            )\n        }\n    }\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/sketch/patch/DiffLangSketch.kt",
    "content": "package com.phodal.shirecore.sketch.patch\n\nimport com.intellij.icons.AllIcons\nimport com.intellij.lang.Language\nimport com.intellij.openapi.application.ApplicationManager\nimport com.intellij.openapi.command.undo.UndoManager\nimport com.intellij.openapi.diff.impl.patch.PatchReader\nimport com.intellij.openapi.diff.impl.patch.TextFilePatch\nimport com.intellij.openapi.fileEditor.FileEditorManager\nimport com.intellij.openapi.fileEditor.FileEditorProvider\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.ui.DialogWrapper\nimport com.intellij.openapi.vcs.changes.patch.AbstractFilePatchInProgress\nimport com.intellij.openapi.vcs.changes.patch.ApplyPatchDefaultExecutor\nimport com.intellij.openapi.vcs.changes.patch.MatchPatchPaths\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.intellij.testFramework.LightVirtualFile\nimport com.intellij.ui.JBColor\nimport com.intellij.ui.components.panels.VerticalLayout\nimport com.intellij.util.containers.MultiMap\nimport com.intellij.util.ui.JBUI\nimport com.phodal.shirecore.ShireCoreBundle\nimport com.phodal.shirecore.ShirelangNotifications\nimport com.phodal.shirecore.findFile\nimport com.phodal.shirecore.provider.sketch.ExtensionLangSketch\nimport java.awt.BorderLayout\nimport javax.swing.BoxLayout\nimport javax.swing.JButton\nimport javax.swing.JComponent\nimport javax.swing.JPanel\n\nclass DiffLangSketch(private val myProject: Project, private var patchContent: String) : ExtensionLangSketch {\n    private val mainPanel: JPanel = JPanel(VerticalLayout(5))\n    private val myHeaderPanel: JPanel = JPanel(BorderLayout())\n    private val shelfExecutor = ApplyPatchDefaultExecutor(myProject)\n    private val myReader = PatchReader(patchContent).also {\n        try {\n            it.parseAllPatches()\n        } catch (e: Exception) {\n            ShirelangNotifications.error(myProject, \"Failed to parse patch: ${e.message}\")\n        }\n    }\n    private val filePatches: MutableList<TextFilePatch> = myReader.textPatches\n\n    init {\n        val header = createHeaderAction()\n\n        myHeaderPanel.add(header, BorderLayout.EAST)\n        mainPanel.add(myHeaderPanel)\n        mainPanel.border = JBUI.Borders.compound(\n            JBUI.Borders.empty(0, 10),\n            JBUI.Borders.customLine(JBColor.border(), 1, 1, 1, 1)\n        )\n\n        ApplicationManager.getApplication().invokeAndWait {\n            if (filePatches.isEmpty()) {\n                ShirelangNotifications.error(myProject, \"PatchProcessor: no patches found\")\n                return@invokeAndWait\n            }\n\n            filePatches.forEachIndexed { _, patch ->\n                val diffPanel = when {\n                    patch.beforeFileName != null -> {\n                        val originFile = myProject.findFile(patch.beforeFileName!!) ?: return@forEachIndexed\n                        SingleFileDiffView(myProject, originFile, patch)\n                    }\n\n                    patch.afterFileName != null -> {\n                        val content = patch.singleHunkPatchText\n                        val virtualFile = LightVirtualFile(patch.afterFileName!!, content)\n                        SingleFileDiffView(myProject, virtualFile, patch)\n                    }\n\n                    else -> {\n                        val content = patch.singleHunkPatchText\n                        val virtualFile = LightVirtualFile(\"ErrorPatchFile\", content)\n                        SingleFileDiffView(myProject, virtualFile, patch)\n                    }\n                }\n\n                mainPanel.add(diffPanel.getComponent())\n            }\n        }\n    }\n\n    private fun createHeaderAction(): JComponent {\n        val acceptButton = JButton(ShireCoreBundle.message(\"sketch.patch.action.accept\")).apply {\n            icon = AllIcons.Actions.SetDefault\n            toolTipText = ShireCoreBundle.message(\"sketch.patch.action.accept.tooltip\")\n            addActionListener {\n                handleAcceptAction()\n            }\n        }\n\n        val rejectButton = JButton(ShireCoreBundle.message(\"sketch.patch.action.reject\")).apply {\n            this.icon = AllIcons.Actions.Rollback\n            this.toolTipText = ShireCoreBundle.message(\"sketch.patch.action.reject.tooltip\")\n            addActionListener {\n                handleRejectAction()\n            }\n        }\n\n        val viewDiffButton = JButton(ShireCoreBundle.message(\"sketch.patch.action.viewDiff\")).apply {\n            this.toolTipText = ShireCoreBundle.message(\"sketch.patch.action.viewDiff.tooltip\")\n            this.icon = AllIcons.Actions.ListChanges\n            addActionListener {\n                handleViewDiffAction()\n            }\n        }\n\n        val panel = JPanel()\n        panel.layout = BoxLayout(panel, BoxLayout.X_AXIS)\n        panel.add(acceptButton)\n        panel.add(rejectButton)\n        panel.add(viewDiffButton)\n\n        panel.background = JBColor(0xF5F5F5, 0x333333)\n\n        return panel\n    }\n\n    private fun handleAcceptAction() {\n        ApplicationManager.getApplication().invokeAndWait {\n            val matchedPatches =\n                MatchPatchPaths(myProject).execute(filePatches, true)\n\n            val patchGroups = MultiMap<VirtualFile, AbstractFilePatchInProgress<*>>()\n            for (patchInProgress in matchedPatches) {\n                patchGroups.putValue(patchInProgress.base, patchInProgress)\n            }\n\n            if (filePatches.isEmpty()) {\n                ShirelangNotifications.error(myProject, \"PatchProcessor: no patches found\")\n                return@invokeAndWait\n            }\n\n            val pathsFromGroups = ApplyPatchDefaultExecutor.pathsFromGroups(patchGroups)\n            val additionalInfo = myReader.getAdditionalInfo(pathsFromGroups)\n            shelfExecutor.apply(filePatches, patchGroups, null, \"LlmGen.diff\", additionalInfo)\n        }\n    }\n\n    private fun handleRejectAction() {\n        val undoManager = UndoManager.getInstance(myProject)\n        val fileEditor = FileEditorManager.getInstance(myProject).selectedEditor ?: return\n        if (undoManager.isUndoAvailable(fileEditor)) {\n            undoManager.undo(fileEditor)\n        }\n    }\n\n    private fun handleViewDiffAction() {\n        val beforeFileNames = filePatches.mapNotNull { it.beforeFileName }\n        if (beforeFileNames.size > 1) {\n            return defaultView()\n        } else {\n            val editorProvider = FileEditorProvider.EP_FILE_EDITOR_PROVIDER.extensionList.firstOrNull {\n                it.javaClass.simpleName == \"DiffPatchFileEditorProvider\"\n            }\n\n            if (editorProvider != null) {\n                val virtualFile = LightVirtualFile(\"diff.diff\", patchContent)\n                val editor = editorProvider.createEditor(myProject, virtualFile)\n                object : DialogWrapper(myProject) {\n                    init {\n                        title = \"Diff Preview\"\n                        setOKButtonText(\"Accept\")\n                        init()\n                    }\n\n                    override fun doOKAction() {\n                        handleAcceptAction()\n                        super.doOKAction()\n                    }\n\n                    override fun createCenterPanel(): JComponent {\n                        return editor.component\n                    }\n                }.show()\n            } else {\n                return defaultView()\n            }\n        }\n    }\n\n    private fun defaultView() {\n        MyApplyPatchFromClipboardDialog(myProject, patchContent).show()\n    }\n\n    override fun getExtensionName(): String = \"patch\"\n    override fun getViewText(): String = patchContent\n    override fun updateViewText(text: String) {\n        this.patchContent = text\n    }\n\n    override fun getComponent(): JComponent = mainPanel\n    override fun updateLanguage(language: Language?, originLanguage: String?) {}\n    override fun dispose() {}\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/sketch/patch/DiffLangSketchProvider.kt",
    "content": "package com.phodal.shirecore.sketch.patch\n\nimport com.intellij.openapi.project.Project\nimport com.phodal.shirecore.provider.sketch.ExtensionLangSketch\nimport com.phodal.shirecore.provider.sketch.LanguageSketchProvider\n\nclass DiffLangSketchProvider : LanguageSketchProvider {\n    override fun isSupported(lang: String): Boolean = lang == \"diff\" || lang == \"patch\"\n    override fun create(project: Project, content: String): ExtensionLangSketch = DiffLangSketch(project, content)\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/sketch/patch/MyApplyPatchFromClipboardDialog.kt",
    "content": "package com.phodal.shirecore.sketch.patch\n\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.vcs.VcsApplicationSettings\nimport com.intellij.openapi.vcs.VcsBundle\nimport com.intellij.openapi.vcs.changes.patch.ApplyPatchDefaultExecutor\nimport com.intellij.openapi.vcs.changes.patch.ApplyPatchDifferentiatedDialog\nimport com.intellij.openapi.vcs.changes.patch.ApplyPatchMode\nimport com.intellij.testFramework.LightVirtualFile\nimport java.awt.event.ActionEvent\nimport java.awt.event.KeyEvent\nimport javax.swing.JCheckBox\nimport javax.swing.JComponent\n\nclass MyApplyPatchFromClipboardDialog(project: Project, clipboardText: String) :\n    ApplyPatchDifferentiatedDialog(\n        project, ApplyPatchDefaultExecutor(project), emptyList(), ApplyPatchMode.APPLY_PATCH_IN_MEMORY,\n        LightVirtualFile(\"clipboardPatchFile\", clipboardText), null, null,  //NON-NLS\n        null, null, null, false\n    ) {\n    override fun createDoNotAskCheckbox(): JComponent = createAnalyzeOnTheFlyOptionPanel()\n\n    companion object {\n        private fun createAnalyzeOnTheFlyOptionPanel(): JCheckBox {\n            val removeOptionCheckBox =\n                JCheckBox(VcsBundle.message(\"patch.apply.analyze.from.clipboard.on.the.fly.checkbox\"))\n            removeOptionCheckBox.mnemonic = KeyEvent.VK_L\n            removeOptionCheckBox.isSelected = VcsApplicationSettings.getInstance().DETECT_PATCH_ON_THE_FLY\n            removeOptionCheckBox.addActionListener { e: ActionEvent? ->\n                VcsApplicationSettings.getInstance().DETECT_PATCH_ON_THE_FLY = removeOptionCheckBox.isSelected\n            }\n            return removeOptionCheckBox\n        }\n    }\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/sketch/patch/SingleFileDiffView.kt",
    "content": "package com.phodal.shirecore.sketch.patch\n\nimport com.intellij.diff.DiffContentFactoryEx\nimport com.intellij.diff.chains.SimpleDiffRequestChain\nimport com.intellij.diff.chains.SimpleDiffRequestProducer\nimport com.intellij.diff.editor.ChainDiffVirtualFile\nimport com.intellij.diff.editor.DiffEditorTabFilesManager\nimport com.intellij.diff.requests.SimpleDiffRequest\nimport com.intellij.icons.AllIcons\nimport com.intellij.lang.Language\nimport com.intellij.openapi.application.ApplicationManager\nimport com.intellij.openapi.application.runInEdt\nimport com.intellij.openapi.application.runReadAction\nimport com.intellij.openapi.command.undo.UndoManager\nimport com.intellij.openapi.diff.impl.patch.TextFilePatch\nimport com.intellij.openapi.diff.impl.patch.apply.GenericPatchApplier\nimport com.intellij.openapi.fileEditor.FileDocumentManager\nimport com.intellij.openapi.fileEditor.FileEditorManager\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.ui.DialogPanel\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.intellij.openapi.vfs.readText\nimport com.intellij.psi.PsiManager\nimport com.intellij.testFramework.LightVirtualFile\nimport com.intellij.ui.DarculaColors\nimport com.intellij.ui.JBColor\nimport com.intellij.ui.components.JBLabel\nimport com.intellij.ui.components.panels.VerticalLayout\nimport com.intellij.ui.dsl.builder.AlignX\nimport com.intellij.ui.dsl.builder.RightGap\nimport com.intellij.ui.dsl.builder.panel\nimport com.intellij.util.LocalTimeCounter\nimport com.phodal.shirecore.ShireCoreBundle\nimport com.phodal.shirecore.sketch.LangSketch\nimport com.phodal.shirecore.sketch.lint.SketchCodeInspection\nimport java.awt.BorderLayout\nimport java.awt.event.MouseAdapter\nimport java.awt.event.MouseEvent\nimport javax.swing.BorderFactory\nimport javax.swing.JButton\nimport javax.swing.JComponent\nimport javax.swing.JPanel\n\n\nclass SingleFileDiffView(\n    private val myProject: Project,\n    private val currentFile: VirtualFile,\n    val patch: TextFilePatch,\n) : LangSketch {\n    private val mainPanel: JPanel = JPanel(VerticalLayout(5))\n    private val myHeaderPanel: JPanel = JPanel(BorderLayout())\n    private var filePanel: DialogPanel? = null\n    var diffFile: ChainDiffVirtualFile? = null\n    private val oldCode = currentFile.readText()\n    private val appliedPatch = GenericPatchApplier.apply(oldCode, patch.hunks)\n    private val newCode = appliedPatch?.patchedText ?: \"\"\n\n    init {\n        val contentPanel = JPanel(BorderLayout())\n        val actions = createActionButtons()\n        val filepathLabel = JBLabel(currentFile.name).apply {\n            icon = currentFile.fileType.icon\n            border = BorderFactory.createEmptyBorder(2, 10, 2, 10)\n\n            addMouseListener(object : MouseAdapter() {\n                override fun mouseClicked(e: MouseEvent?) {\n                    val isShowDiffSuccess = showDiff()\n                    if (isShowDiffSuccess) return\n\n                    FileEditorManager.getInstance(myProject).openFile(currentFile, true)\n                }\n\n                override fun mouseEntered(e: MouseEvent) {\n                    foreground = JBColor.WHITE\n                    filePanel?.background = JBColor(DarculaColors.BLUE, DarculaColors.BLUE)\n                }\n\n                override fun mouseExited(e: MouseEvent) {\n                    foreground = JBColor.BLACK\n                    filePanel?.background = JBColor.PanelBackground\n                }\n            })\n        }\n\n        filePanel = panel {\n            row {\n                cell(filepathLabel).align(AlignX.FILL).resizableColumn()\n                actions.forEachIndexed { index, action ->\n                    cell(action).align(AlignX.LEFT)\n                    if (index < actions.size - 1) {\n                        this@panel.gap(RightGap.SMALL)\n                    }\n                }\n            }\n        }.apply {\n            background = JBColor.PanelBackground\n        }\n\n        val fileContainer = JPanel(BorderLayout(10, 10)).also {\n            it.add(filePanel)\n        }\n        contentPanel.add(fileContainer, BorderLayout.CENTER)\n\n        mainPanel.add(myHeaderPanel)\n        mainPanel.add(contentPanel)\n\n        ApplicationManager.getApplication().executeOnPooledThread {\n            lintCheckForNewCode(currentFile)\n        }\n    }\n\n    fun lintCheckForNewCode(currentFile: VirtualFile) {\n        if (newCode.isEmpty()) return\n        val newFile = LightVirtualFile(currentFile, newCode, LocalTimeCounter.currentTime())\n        val psiFile = runReadAction { PsiManager.getInstance(myProject).findFile(newFile) } ?: return\n        val errors = SketchCodeInspection.runInspections(myProject, psiFile, currentFile)\n        if (errors.isNotEmpty()) {\n            SketchCodeInspection.showErrors(errors, this@SingleFileDiffView.mainPanel)\n        }\n    }\n\n    private fun showDiff(): Boolean {\n        if (diffFile != null) {\n            showDiffFile(diffFile!!)\n            return true\n        }\n\n        val document = FileDocumentManager.getInstance().getDocument(currentFile) ?: return false\n        val appliedPatch = GenericPatchApplier.apply(document.text, patch.hunks)\n            ?: return false\n\n        val newText = appliedPatch.patchedText\n        val diffFactory = DiffContentFactoryEx.getInstanceEx()\n        val currentDocContent = diffFactory.create(myProject, currentFile)\n        val newDocContent = diffFactory.create(newText)\n\n        val diffRequest =\n            SimpleDiffRequest(\n                \"Shire Diff - ${patch.beforeFileName}\",\n                currentDocContent,\n                newDocContent,\n                \"Original\",\n                \"AI generated\"\n            )\n\n        val producer = SimpleDiffRequestProducer.create(currentFile.path) {\n            diffRequest\n        }\n\n        val chain = SimpleDiffRequestChain.fromProducer(producer)\n        runInEdt {\n            diffFile = ChainDiffVirtualFile(chain, \"Diff\")\n            showDiffFile(diffFile!!)\n        }\n\n        return true\n    }\n\n    private val diffEditorTabFilesManager = DiffEditorTabFilesManager.getInstance(myProject)\n\n    private fun showDiffFile(diffFile: ChainDiffVirtualFile) {\n        diffEditorTabFilesManager.showDiffFile(diffFile, true)\n    }\n\n    private fun createActionButtons(): List<JButton> {\n        val undoManager = UndoManager.getInstance(myProject)\n        val fileEditor = FileEditorManager.getInstance(myProject).getSelectedEditor(currentFile)\n\n        val rollback = JButton(AllIcons.Actions.Rollback).apply {\n            toolTipText = ShireCoreBundle.message(\"sketch.patch.action.rollback.tooltip\")\n            isEnabled = undoManager.isUndoAvailable(fileEditor)\n            border = null\n            isFocusPainted = false\n            isContentAreaFilled = false\n\n            addMouseListener(object : MouseAdapter() {\n                override fun mouseClicked(e: MouseEvent?) {\n                    if (undoManager.isUndoAvailable(fileEditor)) {\n                        undoManager.undo(fileEditor)\n                    }\n                }\n            })\n        }\n\n        return listOf(rollback)\n    }\n\n    override fun getViewText(): String = currentFile.readText()\n\n    override fun updateViewText(text: String) {}\n\n    override fun getComponent(): JComponent = mainPanel\n\n    override fun updateLanguage(language: Language?, originLanguage: String?) {}\n\n    override fun dispose() {}\n\n    fun openDiffView() {\n        showDiff()\n    }\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/sse/CustomAgentSSEExecutor.kt",
    "content": "package com.phodal.shirecore.sse\n\nimport com.intellij.openapi.components.Service\nimport com.intellij.openapi.diagnostic.logger\nimport com.intellij.openapi.project.Project\nimport com.phodal.shirecore.agent.AuthType\nimport com.phodal.shirecore.agent.CustomAgent\nimport com.phodal.shirecore.agent.CustomAgentResponseAction\nimport com.phodal.shirecore.llm.ChatMessage\nimport com.phodal.shirecore.llm.ChatRole\nimport com.phodal.shirecore.llm.CustomRequest\nimport kotlinx.coroutines.flow.Flow\nimport kotlinx.serialization.encodeToString\nimport kotlinx.serialization.json.Json\nimport okhttp3.MediaType.Companion.toMediaTypeOrNull\nimport okhttp3.OkHttpClient\nimport okhttp3.Request\nimport okhttp3.RequestBody.Companion.toRequestBody\nimport java.util.concurrent.TimeUnit\n\n@Service(Service.Level.PROJECT)\nclass CustomAgentSSEExecutor(val project: Project) : CustomSSEHandler() {\n    private var client = OkHttpClient()\n    private val logger = logger<CustomAgentSSEExecutor>()\n    private val messages: MutableList<ChatMessage> = mutableListOf()\n\n    override var requestFormat: String = \"\"\n    override var responseFormat: String = \"\"\n\n    fun execute(promptText: String, agent: CustomAgent): Flow<String> {\n        messages.add(ChatMessage(ChatRole.user, promptText))\n\n        this.requestFormat = agent.connector?.requestFormat ?: this.requestFormat\n        this.responseFormat = agent.connector?.responseFormat ?: this.responseFormat\n\n        val customRequest = CustomRequest(listOf(ChatMessage(ChatRole.user, promptText)))\n        val request = if (requestFormat.isNotEmpty()) {\n            customRequest.updateCustomFormat(requestFormat)\n        } else {\n            Json.encodeToString<CustomRequest>(customRequest)\n        }\n\n        val body = request.toRequestBody(\"application/json; charset=utf-8\".toMediaTypeOrNull())\n        val builder = Request.Builder()\n\n        val auth = agent.auth\n        when (auth?.type) {\n            AuthType.Bearer -> {\n                builder.addHeader(\"Authorization\", \"Bearer ${auth.token}\")\n                builder.addHeader(\"Content-Type\", \"application/json\")\n            }\n\n            null -> {\n                logger.info(\"No auth type found for agent ${agent.name}\")\n            }\n        }\n\n        client = client.newBuilder()\n            .connectTimeout(agent.defaultTimeout, TimeUnit.MINUTES)\n            .readTimeout(agent.defaultTimeout, TimeUnit.MINUTES).build()\n\n        val call = client.newCall(builder.url(agent.url).post(body).build())\n\n        return when (agent.responseAction) {\n            CustomAgentResponseAction.Stream -> {\n                streamSSE(call, messages = messages, project)\n            }\n\n            else -> {\n                streamJson(call, messages)\n            }\n        }\n    }\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/sse/CustomSSEHandler.kt",
    "content": "package com.phodal.shirecore.sse\n\nimport com.fasterxml.jackson.databind.ObjectMapper\nimport com.intellij.openapi.diagnostic.logger\nimport com.intellij.openapi.project.Project\nimport com.nfeld.jsonpathkt.JsonPath\nimport com.nfeld.jsonpathkt.extension.read\nimport com.phodal.shirecore.sse.io.ChatCompletionResult\nimport com.phodal.shirecore.sse.io.JSONBodyResponseCallback\nimport com.phodal.shirecore.sse.io.ResponseBodyCallback\nimport com.phodal.shirecore.sse.io.SSE\nimport com.phodal.shirecore.runner.console.CustomFlowWrapper\nimport com.phodal.shirecore.llm.ChatMessage\nimport com.phodal.shirecore.llm.ChatRole\nimport com.phodal.shirecore.llm.CustomRequest\nimport com.phodal.shirecore.provider.streaming.OnStreamingService\nimport io.reactivex.rxjava3.core.BackpressureStrategy\nimport io.reactivex.rxjava3.core.Flowable\nimport io.reactivex.rxjava3.core.FlowableEmitter\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.channels.awaitClose\nimport kotlinx.coroutines.flow.Flow\nimport kotlinx.coroutines.flow.callbackFlow\nimport kotlinx.coroutines.withContext\nimport kotlinx.serialization.encodeToString\nimport kotlinx.serialization.json.*\nimport okhttp3.Call\nimport okhttp3.Request\nimport org.jetbrains.annotations.VisibleForTesting\n\n/**\n * The `CustomSSEProcessor` class is responsible for processing server-sent events (SSE) in a custom manner.\n * It provides functions to stream JSON and SSE data from a given `Call` instance, and exposes properties for request and response formats.\n *\n * @property hasSuccessRequest A boolean flag indicating whether the request was successful.\n * @property requestFormat A string representing the format of the request.\n * @property responseFormat A string representing the format of the response.\n * @property logger An instance of the logger for logging purposes.\n *\n * @constructor Creates an instance of `CustomSSEProcessor`.\n */\nopen class CustomSSEHandler {\n    open var hasSuccessRequest: Boolean = false\n    private var parseFailedResponses: MutableList<String> = mutableListOf()\n\n    open val requestFormat: String = \"\"\n    open val responseFormat: String = \"\\$.choices[0].delta.content\"\n\n    private val logger = logger<CustomSSEHandler>()\n\n    fun streamJson(call: Call, messages: MutableList<ChatMessage>): Flow<String> = callbackFlow {\n        call.enqueue(JSONBodyResponseCallback(responseFormat) {\n            withContext(Dispatchers.IO) {\n                send(it)\n            }\n\n            messages += ChatMessage(ChatRole.assistant, it)\n            close()\n        })\n        awaitClose()\n    }\n\n    @OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)\n    fun streamSSE(call: Call, messages: MutableList<ChatMessage>, project: Project): Flow<String> {\n        var emit: FlowableEmitter<SSE>? = null\n        val sseFlowable = Flowable\n            .create({ emitter: FlowableEmitter<SSE> ->\n                emit = emitter.apply { call.enqueue(ResponseBodyCallback(emitter, true)) }\n            }, BackpressureStrategy.BUFFER)\n\n        val service = project.getService(OnStreamingService::class.java)\n\n        try {\n            var output = \"\"\n            return CustomFlowWrapper(callbackFlow {\n                withContext(Dispatchers.IO) {\n                    sseFlowable\n                        .doOnError {\n                            it.printStackTrace()\n                            trySend(it.message ?: \"Error occurs\")\n                            close()\n                        }\n                        .runCatching {\n                            blockingForEach { sse ->\n                                if (responseFormat.isNotEmpty()) {\n                                    // {\"id\":\"cmpl-a22a0d78fcf845be98660628fe5d995b\",\"object\":\"chat.completion.chunk\",\"created\":822330,\"model\":\"moonshot-v1-8k\",\"choices\":[{\"index\":0,\"delta\":{},\"finish_reason\":\"stop\",\"usage\":{\"prompt_tokens\":434,\"completion_tokens\":68,\"total_tokens\":502}}]}\n                                    // in some case, the response maybe not equal to our response format, so we need to ignore it\n                                    // {\"id\":\"cmpl-ac26a17e\",\"object\":\"chat.completion.chunk\",\"created\":1858403,\"model\":\"yi-34b-chat\",\"choices\":[{\"delta\":{\"role\":\"assistant\"},\"index\":0}],\"content\":\"\",\"lastOne\":false}\n                                    val chunk: String? = JsonPath.parse(sse!!.data)?.read(responseFormat)\n\n                                    // new JsonPath lib caught the exception, so we need to handle when it is null\n                                    if (chunk == null) {\n                                        if (sse.data == \"[DONE]\") {\n                                            return@blockingForEach\n                                        }\n\n                                        parseFailedResponses.add(sse.data)\n                                        logger.warn(\"Failed to parse response.origin response is: ${sse.data}, response format: $responseFormat\")\n                                    } else {\n                                        hasSuccessRequest = true\n                                        output += chunk\n                                        service?.onStreaming(project, chunk)\n                                        trySend(chunk)\n                                    }\n                                } else {\n                                    val result: ChatCompletionResult =\n                                        ObjectMapper().readValue(sse!!.data, ChatCompletionResult::class.java)\n\n                                    val completion = result.choices[0].message\n                                    if (completion?.content != null) {\n                                        output += completion.content\n                                        service?.onStreaming(project, completion.content)\n                                        trySend(completion.content)\n                                    }\n                                }\n                            }\n                        }\n\n                    // when stream finished, check if any response parsed succeeded\n                    // if not, notice user check response format\n                    if (!hasSuccessRequest) {\n                        val errorMsg = \"\"\"\n                                        |**Failed** to parse response.please check your response format: \n                                        |**$responseFormat** origin responses is: \n                                        |- ${parseFailedResponses.joinToString(\"\\n- \")}\n                                        |\"\"\".trimMargin()\n\n                        // TODO add refresh feature\n                        // don't use trySend, it may be ignored by 'close()` op\n                        service?.onStreamingError()\n                        send(errorMsg)\n                    }\n\n                    messages += ChatMessage(ChatRole.assistant, output)\n                    close()\n                }\n                awaitClose()\n            }).also { it.cancelCallback { emit?.onComplete() } }\n        } catch (e: Exception) {\n            if (hasSuccessRequest) {\n                logger.info(\"Failed to stream\", e)\n            } else {\n                logger.error(\"Failed to stream\", e)\n            }\n\n            return callbackFlow {\n                close()\n            }\n        } finally {\n            parseFailedResponses.clear()\n        }\n    }\n}\n\n@VisibleForTesting\nfun Request.Builder.appendCustomHeaders(customRequestHeader: String): Request.Builder = apply {\n    runCatching {\n        Json.parseToJsonElement(customRequestHeader)\n            .jsonObject[\"customHeaders\"].let { customFields ->\n            customFields?.jsonObject?.forEach { (key, value) ->\n                header(key, value.jsonPrimitive.content)\n            }\n        }\n    }.onFailure {\n        logger<CustomRequest>().warn(\"Failed to parse custom request header\", it)\n    }\n}\n\n@VisibleForTesting\nfun JsonObject.updateCustomBody(customRequest: String): JsonObject {\n    return runCatching {\n        buildJsonObject {\n            // copy origin object\n            val customRequestJson = Json.parseToJsonElement(customRequest).jsonObject\n            customRequestJson[\"fields\"]?.jsonObject?.let { fieldsObj ->\n                val messages: JsonArray = this@updateCustomBody[\"messages\"]?.jsonArray ?: buildJsonArray {}\n                val contentOfFirstMessage = if (messages.isNotEmpty()) {\n                    messages.last().jsonObject[\"content\"]?.jsonPrimitive?.content ?: \"\"\n                } else \"\"\n                fieldsObj.forEach { (fieldKey, fieldValue) ->\n                    if (fieldValue is JsonObject) {\n                        put(fieldKey, buildJsonObject {\n                            fieldValue.forEach { (subKey, subValue) ->\n                                if (subValue is JsonPrimitive && subValue.content == \"\\$content\") {\n                                    put(subKey, JsonPrimitive(contentOfFirstMessage))\n                                } else {\n                                    put(subKey, subValue)\n                                }\n                            }\n                        })\n                    } else if (fieldValue is JsonPrimitive && fieldValue.content == \"\\$content\") {\n                        put(fieldKey, JsonPrimitive(contentOfFirstMessage))\n                    } else {\n                        put(fieldKey, fieldValue)\n                    }\n                }\n\n                return@buildJsonObject\n            }\n\n            this@updateCustomBody.forEach { u, v -> put(u, v) }\n            customRequestJson[\"customFields\"]?.let { customFields ->\n                customFields.jsonObject.forEach { (key, value) ->\n                    put(key, value)\n                }\n            }\n\n            // TODO clean code with magic literals\n            var roleKey = \"role\"\n            var contentKey = \"content\"\n            customRequestJson.jsonObject[\"messageKeys\"]?.let {\n                roleKey = it.jsonObject[\"role\"]?.jsonPrimitive?.content ?: \"role\"\n                contentKey = it.jsonObject[\"content\"]?.jsonPrimitive?.content ?: \"content\"\n            }\n\n            val messages: JsonArray = this@updateCustomBody[\"messages\"]?.jsonArray ?: buildJsonArray { }\n            this.put(\"messages\", buildJsonArray {\n                messages.forEach { message ->\n                    val role: String = message.jsonObject[\"role\"]?.jsonPrimitive?.content ?: \"user\"\n                    val content: String = message.jsonObject[\"content\"]?.jsonPrimitive?.content ?: \"\"\n                    add(buildJsonObject {\n                        put(roleKey, role)\n                        put(contentKey, content)\n                    })\n                }\n            })\n        }\n    }.getOrElse {\n        logger<CustomSSEHandler>().error(\"Failed to parse custom request body\", it)\n        this\n    }\n}\n\nfun CustomRequest.updateCustomFormat(format: String): String {\n    val requestContentOri = Json.encodeToString<CustomRequest>(this)\n    return Json.parseToJsonElement(requestContentOri)\n        .jsonObject.updateCustomBody(format).toString()\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/sse/io/JSONBodyResponseCallback.kt",
    "content": "package com.phodal.shirecore.sse.io\n\nimport com.nfeld.jsonpathkt.JsonPath\nimport com.nfeld.jsonpathkt.extension.read\nimport kotlinx.coroutines.runBlocking\nimport okhttp3.Call\nimport okhttp3.Callback\nimport okhttp3.Response\nimport java.io.IOException\n\nclass JSONBodyResponseCallback(private val responseFormat: String,private val callback: suspend (String)->Unit): Callback {\n    override fun onFailure(call: Call, e: IOException) {\n        runBlocking {\n            callback(\"error. ${e.message}\")\n        }\n    }\n\n    override fun onResponse(call: Call, response: Response) {\n        val responseBody: String? = response.body?.string()\n        if (responseFormat.isEmpty()) {\n            runBlocking {\n                callback(responseBody ?: \"\")\n            }\n\n            return\n        }\n\n        val responseContent: String = JsonPath.parse(responseBody)?.read(responseFormat) ?: \"\"\n\n        runBlocking() {\n            callback(responseContent)\n        }\n    }\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/sse/io/OpenAIDto.kt",
    "content": "package com.phodal.shirecore.sse.io\n\nimport com.fasterxml.jackson.annotation.JsonAlias\nimport com.fasterxml.jackson.annotation.JsonInclude\nimport com.fasterxml.jackson.annotation.JsonProperty\nimport com.fasterxml.jackson.databind.JsonNode\n\ndata class ChatFunctionCall(\n    val name: String? = null,\n    val arguments: JsonNode? = null,\n)\n\ndata class ChatMessage(\n    val role: String,\n    @JsonInclude\n    val content: String? = null,\n    val name: String? = null,\n    @JsonProperty(\"function_call\")\n    val functionCall: ChatFunctionCall? = null,\n)\n\ndata class ChatCompletionChoice(\n    val index: Int? = null,\n    @JsonAlias(\"delta\")\n    val message: ChatMessage? = null,\n    @JsonProperty(\"finish_reason\")\n    val finishReason: String? = null,\n)\n\ndata class Usage(\n    @JsonProperty(\"prompt_tokens\")\n    val promptTokens: Long = 0,\n    @JsonProperty(\"completion_tokens\")\n    val completionTokens: Long = 0,\n    @JsonProperty(\"total_tokens\")\n    val totalTokens: Long = 0,\n)\n\ndata class ChatCompletionResult(\n    val id: String? = null,\n    val `object`: String? = null,\n    val created: Long = 0,\n    val model: String? = null,\n    val choices: List<ChatCompletionChoice>,\n    val usage: Usage? = null,\n)"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/sse/io/ResponseBodyCallback.kt",
    "content": "// MIT License\n//\n//Copyright (c) [year] [fullname]\n//\n//Permission is hereby granted, free of charge, to any person obtaining a copy\n//of this software and associated documentation files (the \"Software\"), to deal\n//in the Software without restriction, including without limitation the rights\n//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n//copies of the Software, and to permit persons to whom the Software is\n//furnished to do so, subject to the following conditions:\n//\n//The above copyright notice and this permission notice shall be included in all\n//copies or substantial portions of the Software.\n//\n//THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n//SOFTWARE.\npackage com.phodal.shirecore.sse.io\n\nimport com.intellij.openapi.diagnostic.logger\nimport io.reactivex.rxjava3.core.FlowableEmitter\nimport okhttp3.Call\nimport okhttp3.Callback\nimport okhttp3.Response\nimport java.io.BufferedReader\nimport java.io.IOException\nimport java.io.InputStreamReader\nimport java.nio.charset.StandardCharsets\n\n\n/**\n * Callback to parse Server Sent Events (SSE) from raw InputStream and\n * emit the events with io.reactivex.FlowableEmitter to allow streaming of\n * SSE.\n */\nclass ResponseBodyCallback(private val emitter: FlowableEmitter<SSE>, private val emitDone: Boolean) : Callback {\n    override fun onResponse(call: Call, response: Response) {\n        var reader: BufferedReader? = null\n        try {\n            if (!response.isSuccessful) {\n                if (response.body == null) {\n                    throw ShireHttpException(\"Response body is null\", response.code)\n                } else {\n                    throw ShireHttpException(response.body?.string() ?: \"\", response.code)\n                }\n            }\n\n            val inputStream = response.body!!.byteStream()\n            reader = BufferedReader(InputStreamReader(inputStream, StandardCharsets.UTF_8))\n            var line: String? = null\n            var sse: SSE? = null\n            while (!emitter.isCancelled && reader.readLine().also { line = it } != null) {\n                sse = when {\n                    line!!.startsWith(\"data:\") -> {\n                        val data = line!!.substring(5).trim { it <= ' ' }\n                        SSE(data)\n                    }\n\n                    line == \"\" && sse != null -> {\n                        if (sse.isDone) {\n                            if (emitDone) {\n                                emitter.onNext(sse)\n                            }\n                            break\n                        }\n                        emitter.onNext(sse)\n                        null\n                    }\n                    // starts with event:\n                    line!!.startsWith(\"event:\") -> {\n                        // https://github.com/sysid/sse-starlette/issues/16\n                        val eventName = line!!.substring(6).trim { it <= ' ' }\n                        if (eventName == \"ping\") {\n                            // skip ping event and data\n                            emitter.onNext(sse!!)\n                            emitter.onNext(sse)\n                        }\n\n                        null\n                    }\n\n                    // skip `: ping` comments for: https://github.com/sysid/sse-starlette/issues/16\n                    line!!.startsWith(\": ping\") -> {\n                        null\n                    }\n\n                    else -> {\n                        when {\n                            // sometimes the server maybe returns empty line\n                            line == \"\" -> {\n                                null\n                            }\n\n                            // : is comment\n                            // https://html.spec.whatwg.org/multipage/server-sent-events.html#parsing-an-event-stream\n                            line!!.startsWith(\":\") -> {\n                                null\n                            }\n\n                            else -> {\n                                throw SSEFormatException(\"Invalid sse format! '$line'\")\n                            }\n                        }\n                    }\n                }\n            }\n\n            emitter.onComplete()\n        } catch (t: Throwable) {\n            logger<ResponseBodyCallback>().error(\"Error while reading SSE\", t)\n            onFailure(call, IOException(t))\n        } finally {\n            if (reader != null) {\n                try {\n                    reader.close()\n                } catch (e: IOException) {\n                    // do nothing\n                }\n            }\n        }\n    }\n\n    override fun onFailure(call: Call, e: IOException) {\n        emitter.onError(e)\n    }\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/sse/io/SSE.kt",
    "content": "package com.phodal.shirecore.sse.io\n\nclass SSE(val data: String) {\n    fun toBytes(): ByteArray {\n        return String.format(\"data: %s\\n\\n\", this.data).toByteArray()\n    }\n\n    val isDone: Boolean\n        get() = DONE_DATA.equals(this.data, ignoreCase = true)\n\n    companion object {\n        private const val DONE_DATA = \"[DONE]\"\n    }\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/sse/io/SSEFormatException.kt",
    "content": "package com.phodal.shirecore.sse.io\n\nclass SSEFormatException(msg: String?) : Throwable(msg)"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/sse/io/ShireHttpException.kt",
    "content": "package com.phodal.shirecore.sse.io\n\nclass ShireHttpException(val error: String, private val statusCode: Int) : RuntimeException(error) {\n    override fun toString(): String {\n        return \"ShireHttpException(statusCode=$statusCode, message=$message)\"\n    }\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/task/Graph.kt",
    "content": "package com.phodal.shirecore.task\n\ndata class Node(val id: Int, val name: String)\n\ndata class Edge(val from: Node, val to: Node)\n\nclass Graph {\n    private val nodes = mutableListOf<Node>()\n    private val edges = mutableListOf<Edge>()\n    private val adjList = mutableMapOf<Node, MutableList<Node>>()\n\n    fun addNode(node: Node) {\n        nodes.add(node)\n        adjList[node] = mutableListOf()\n    }\n\n    fun addEdge(edge: Edge) {\n        edges.add(edge)\n        adjList[edge.from]?.add(edge.to)\n    }\n\n    fun getNodes(): List<Node> = nodes\n\n    fun getEdges(): List<Edge> = edges\n\n    fun getAdjList(): Map<Node, List<Node>> = adjList\n\n\n\n    fun topologicalSort(graph: Graph): List<Node> {\n        val visited = mutableSetOf<Node>()\n        val stack = mutableListOf<Node>()\n        val adjList = graph.getAdjList()\n\n        fun dfs(node: Node) {\n            if (node !in visited) {\n                visited.add(node)\n                adjList[node]?.forEach { dfs(it) }\n                stack.add(node)\n            }\n        }\n\n        graph.getNodes().forEach { if (it !in visited) dfs(it) }\n\n        return stack.reversed()\n    }\n\n    fun hasCycle(graph: Graph): Boolean {\n        val visited = mutableSetOf<Node>()\n        val recStack = mutableSetOf<Node>()\n        val adjList = graph.getAdjList()\n\n        fun dfs(node: Node): Boolean {\n            if (node !in visited) {\n                visited.add(node)\n                recStack.add(node)\n                adjList[node]?.forEach {\n                    if (it !in visited && dfs(it)) {\n                        return true\n                    } else if (it in recStack) {\n                        return true\n                    }\n                }\n            }\n            recStack.remove(node)\n            return false\n        }\n\n        return graph.getNodes().any { dfs(it) }\n    }\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/ui/CustomProgressBar.kt",
    "content": "package com.phodal.shirecore.ui\n\nimport com.intellij.icons.AllIcons\nimport com.intellij.ui.components.JBLabel\nimport com.intellij.util.ui.JBUI\nimport java.awt.BorderLayout\nimport java.awt.event.MouseAdapter\nimport java.awt.event.MouseEvent\nimport javax.swing.JPanel\nimport javax.swing.JProgressBar\n\nclass CustomProgressBar(private val view: ShirePanelView) : JPanel(BorderLayout()) {\n    private val progressBar: JProgressBar = JProgressBar()\n\n    var isIndeterminate = progressBar.isIndeterminate\n        set(value) {\n            progressBar.isIndeterminate = value\n            field = value\n        }\n\n    private val cancelLabel = JBLabel(AllIcons.Actions.CloseHovered)\n\n    init {\n\n        cancelLabel.setBorder(JBUI.Borders.empty(0, 5))\n        cancelLabel.addMouseListener(object : MouseAdapter() {\n            override fun mouseClicked(e: MouseEvent?) {\n                view.cancel(\"This progressBar is canceled\")\n            }\n        })\n\n        add(progressBar, BorderLayout.CENTER)\n        add(cancelLabel, BorderLayout.EAST)\n    }\n\n    override fun setVisible(visible: Boolean) {\n        super.setVisible(visible)\n        progressBar.isVisible = visible\n        cancelLabel.isVisible = visible\n    }\n\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/ui/ShirePanelView.kt",
    "content": "package com.phodal.shirecore.ui\n\nimport com.intellij.openapi.application.runInEdt\nimport com.intellij.openapi.fileTypes.PlainTextLanguage\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.ui.DialogPanel\nimport com.intellij.openapi.ui.NullableComponent\nimport com.intellij.openapi.ui.SimpleToolWindowPanel\nimport com.intellij.ui.components.JBScrollPane\nimport com.intellij.ui.components.panels.VerticalLayout\nimport com.intellij.ui.dsl.builder.AlignX\nimport com.intellij.ui.dsl.builder.AlignY\nimport com.intellij.ui.dsl.builder.Cell\nimport com.intellij.ui.dsl.builder.panel\nimport com.intellij.util.ui.JBUI\nimport com.intellij.util.ui.UIUtil\nimport com.phodal.shirecore.provider.sketch.ExtensionLangSketch\nimport com.phodal.shirecore.provider.sketch.LanguageSketchProvider\nimport com.phodal.shirecore.sketch.LangSketch\nimport com.phodal.shirecore.sketch.highlight.CodeHighlightSketch\nimport com.phodal.shirecore.ui.input.ShireChatBoxInput\nimport com.phodal.shirecore.utils.markdown.CodeFence\nimport com.phodal.shirecore.utils.markdown.CodeFenceLanguage\nimport java.awt.BorderLayout\nimport javax.swing.JComponent\nimport javax.swing.JPanel\nimport javax.swing.ScrollPaneConstants\nimport javax.swing.SwingUtilities\n\nclass ShirePanelView(val project: Project, showInput: Boolean = true) : SimpleToolWindowPanel(true, true),\n    NullableComponent {\n    private var progressBar: CustomProgressBar = CustomProgressBar(this)\n    private var shireInput: ShireChatBoxInput = ShireChatBoxInput(project)\n\n    private var myList = JPanel(VerticalLayout(JBUI.scale(0))).apply {\n        this.isOpaque = true\n        this.background = UIUtil.getLabelBackground()\n    }\n\n    private var userPrompt: JPanel = JPanel(BorderLayout()).apply {\n        this.isOpaque = true\n        this.background = JBUI.CurrentTheme.CustomFrameDecorations.titlePaneInactiveBackground()\n        this.border = JBUI.Borders.empty(10, 0)\n    }\n\n    private var contentPanel = JPanel(BorderLayout()).apply {\n        this.isOpaque = true\n        this.background = UIUtil.getLabelBackground()\n    }\n\n    private var panelContent: DialogPanel = panel {\n        row { cell(progressBar).fullWidth() }\n        row { cell(userPrompt).fullWidth().fullHeight() }\n        row { cell(myList).fullWidth().fullHeight() }\n    }\n\n    private val scrollPanel: JBScrollPane = JBScrollPane(\n        panelContent,\n        ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,\n        ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER\n    ).apply {\n        this.verticalScrollBar.autoscrolls = true\n    }\n\n    var handleCancel: ((String) -> Unit)? = null\n\n    init {\n        contentPanel.add(scrollPanel, BorderLayout.CENTER)\n\n        if (showInput) {\n            shireInput.also {\n                border = JBUI.Borders.empty(8)\n            }\n\n            contentPanel.add(shireInput, BorderLayout.SOUTH)\n        }\n\n        setContent(contentPanel)\n    }\n\n    fun onStart() {\n        initializePreAllocatedBlocks(project)\n        progressBar.isIndeterminate = true\n    }\n\n    private val blockViews: MutableList<LangSketch> = mutableListOf()\n    private fun initializePreAllocatedBlocks(project: Project) {\n        repeat(16) {\n            runInEdt {\n                val codeBlockViewer = CodeHighlightSketch(project, \"\", PlainTextLanguage.INSTANCE)\n                blockViews.add(codeBlockViewer)\n                myList.add(codeBlockViewer)\n            }\n        }\n    }\n\n    fun addRequestPrompt(text: String) {\n        runInEdt {\n            val codeBlockViewer = CodeHighlightSketch(project, text, CodeFenceLanguage.findLanguage(\"Markdown\")).apply {\n                initEditor(text)\n            }\n\n            codeBlockViewer.editorFragment?.setCollapsed(true)\n            codeBlockViewer.editorFragment!!.updateExpandCollapseLabel()\n\n            val panel = panel {\n                row {\n                    cell(codeBlockViewer).fullWidth()\n                }\n            }.also {\n                it.border = JBUI.Borders.empty(10, 0)\n            }\n\n            userPrompt.add(panel, BorderLayout.CENTER)\n\n            this.revalidate()\n            this.repaint()\n        }\n    }\n\n    fun onUpdate(text: String) {\n        val codeFenceList = CodeFence.parseAll(text)\n\n        runInEdt {\n            codeFenceList.forEachIndexed { index, codeFence ->\n                if (index < blockViews.size) {\n                    var langSketch: ExtensionLangSketch? = null\n                    if (codeFence.originLanguage != null && codeFence.isComplete && blockViews[index] !is ExtensionLangSketch) {\n                        langSketch = LanguageSketchProvider.provide(codeFence.originLanguage)\n                            ?.create(project, codeFence.text)\n                    }\n\n                    if (langSketch != null) {\n                        val oldComponent = blockViews[index]\n                        blockViews[index] = langSketch\n                        myList.remove(index)\n                        myList.add(langSketch.getComponent(), index)\n\n                        oldComponent.dispose()\n                    } else {\n                        blockViews[index].apply {\n                            updateLanguage(codeFence.ideaLanguage, codeFence.originLanguage)\n                            updateViewText(codeFence.text)\n                        }\n                    }\n                } else {\n                    val codeBlockViewer = CodeHighlightSketch(project, codeFence.text, PlainTextLanguage.INSTANCE)\n                    blockViews.add(codeBlockViewer)\n                    myList.add(codeBlockViewer.getComponent())\n                }\n            }\n\n            while (blockViews.size > codeFenceList.size) {\n                val lastIndex = blockViews.lastIndex\n                blockViews.removeAt(lastIndex)\n                myList.remove(lastIndex)\n            }\n\n            myList.revalidate()\n            myList.repaint()\n\n            scrollToBottom()\n        }\n    }\n\n    fun onFinish(text: String) {\n        runInEdt {\n            blockViews.filter { it.getViewText().isNotEmpty() }.forEach {\n                it.doneUpdateText(text)\n            }\n\n            blockViews.filter { it.getViewText().isEmpty() }.forEach {\n                myList.remove(it.getComponent())\n            }\n        }\n\n        progressBar.isIndeterminate = false\n        progressBar.isVisible = false\n        scrollToBottom()\n    }\n\n    private fun scrollToBottom() {\n        SwingUtilities.invokeLater {\n            val verticalScrollBar = scrollPanel.verticalScrollBar\n            verticalScrollBar.value = verticalScrollBar.maximum\n        }\n    }\n\n    fun resize(maxHeight: Int = 480) {\n        val height = myList.components.sumOf { it.height }\n        if (height < maxHeight) {\n            this.minimumSize = JBUI.size(800, height)\n        } else {\n            this.minimumSize = JBUI.size(800, maxHeight)\n        }\n    }\n\n    override fun isNull(): Boolean {\n        return !isVisible\n    }\n\n    fun cancel(s: String) = runCatching { handleCancel?.invoke(s) }\n}\n\nfun <T : JComponent> Cell<T>.fullWidth(): Cell<T> {\n    return this.align(AlignX.FILL)\n}\n\nfun <T : JComponent> Cell<T>.fullHeight(): Cell<T> {\n    return this.align(AlignY.FILL)\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/ui/input/DarculaNewUIUtil.kt",
    "content": "// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage com.phodal.shirecore.ui.input\n\nimport com.intellij.ide.ui.laf.darcula.DarculaUIUtil\nimport com.intellij.util.ui.JBInsets\nimport com.intellij.util.ui.MacUIUtil\nimport org.jetbrains.annotations.ApiStatus\nimport org.jetbrains.annotations.ApiStatus.ScheduledForRemoval\nimport java.awt.*\nimport java.awt.geom.Path2D\nimport java.awt.geom.RoundRectangle2D\nimport kotlin.math.max\n\n@ApiStatus.Internal\nobject DarculaNewUIUtil {\n  /**\n   * Paints rounded border for a focusable component. Non focused border rect is inside [rect], focused/outlined border can come outside\n   */\n  fun paintComponentBorder(g: Graphics, rect: Rectangle, outline: DarculaUIUtil.Outline?, focused: Boolean, enabled: Boolean,\n                           bw: Int = DarculaUIUtil.BW.get(), arc: Float = DarculaUIUtil.COMPONENT_ARC.float) {\n    val g2 = g.create() as Graphics2D\n\n    try {\n      g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON)\n      g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,\n                          if (MacUIUtil.USE_QUARTZ) RenderingHints.VALUE_STROKE_PURE else RenderingHints.VALUE_STROKE_NORMALIZE)\n\n      val lw = DarculaUIUtil.LW.get()\n\n      when {\n        enabled && outline != null -> {\n          outline.setGraphicsColor(g2, focused)\n          paintRectangle(g2, rect, arc, bw)\n        }\n\n        focused -> {\n          DarculaUIUtil.Outline.focus.setGraphicsColor(g2, true)\n          paintRectangle(g2, rect, arc, bw)\n        }\n\n        else -> {\n          g2.color = DarculaUIUtil.getOutlineColor(enabled, focused)\n          paintRectangle(g2, rect, arc, lw)\n        }\n      }\n    }\n    finally {\n      g2.dispose()\n    }\n  }\n\n  /**\n   * Will be removed after the next 25.1 release\n   */\n  @ApiStatus.Internal\n  @Deprecated(\"Use drawRoundedRectangle instead\")\n  @ScheduledForRemoval\n  fun paintComponentBorder(g: Graphics, rect: Rectangle, color: Color, arc: Float, thick: Int) {\n    drawRoundedRectangle(g, rect, color, arc, thick)\n  }\n\n  fun drawRoundedRectangle(g: Graphics, rect: Rectangle, color: Color, arc: Float, thick: Int) {\n    val g2 = g.create() as Graphics2D\n\n    try {\n      g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON)\n      g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,\n                          if (MacUIUtil.USE_QUARTZ) RenderingHints.VALUE_STROKE_PURE else RenderingHints.VALUE_STROKE_NORMALIZE)\n\n      g2.color = color\n      paintRectangle(g2, rect, arc, thick)\n    }\n    finally {\n      g2.dispose()\n    }\n  }\n\n  @Deprecated(\"Use fillInsideComponentBorder instead\")\n  fun fillInsideComponentBorder(g: Graphics, rect: Rectangle, color: Color) {\n    fillInsideComponentBorder(g, rect, color, DarculaUIUtil.COMPONENT_ARC.float)\n  }\n\n  /**\n   * Fills part of a component inside the border. The resulting rectangle is a little bit smaller than [rect],\n   * so inside background doesn't protrude outside the line border (can be visible near rounded corners).\n   * This method should be used together with [paintComponentBorder],[drawRoundedRectangle], or other methods that draw lined border.\n   */\n  fun fillInsideComponentBorder(g: Graphics, rect: Rectangle, color: Color, arc: Float = DarculaUIUtil.COMPONENT_ARC.float) {\n    if (rect.width <= 0 || rect.height <= 0) {\n      return\n    }\n\n    val g2 = g.create() as Graphics2D\n\n    try {\n      g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON)\n      g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,\n                          if (MacUIUtil.USE_QUARTZ) RenderingHints.VALUE_STROKE_PURE else RenderingHints.VALUE_STROKE_NORMALIZE)\n\n      val border = Path2D.Float(Path2D.WIND_EVEN_ODD)\n      // Reduce size a little bit, so inside background is not protruded outside the border near rounded corners\n      border.append(RoundRectangle2D.Float(0.5f, 0.5f, rect.width - 1f, rect.height - 1f, arc, arc), false)\n      g2.translate(rect.x, rect.y)\n      g2.color = color\n      g2.fill(border)\n    }\n    finally {\n      g2.dispose()\n    }\n  }\n\n  fun fillRoundedRectangle(g: Graphics, rect: Rectangle, color: Color, arc: Float = DarculaUIUtil.COMPONENT_ARC.float) {\n    if (rect.width <= 0 || rect.height <= 0) {\n      return\n    }\n\n    val g2 = g.create() as Graphics2D\n\n    try {\n      g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON)\n      g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,\n                          if (MacUIUtil.USE_QUARTZ) RenderingHints.VALUE_STROKE_PURE else RenderingHints.VALUE_STROKE_NORMALIZE)\n\n      val border = Path2D.Float(Path2D.WIND_EVEN_ODD)\n      border.append(RoundRectangle2D.Float(0f, 0f, rect.width.toFloat(), rect.height.toFloat(), arc, arc), false)\n      g2.translate(rect.x, rect.y)\n      g2.color = color\n      g2.fill(border)\n    }\n    finally {\n      g2.dispose()\n    }\n  }\n\n  /**\n   * Using DarculaUIUtil.doPaint and similar methods doesn't give good results when line thickness is 1 (right corners too thin)\n   */\n  private fun paintRectangle(g: Graphics2D, rect: Rectangle, arc: Float, thick: Int) {\n    val addToRect = thick - DarculaUIUtil.LW.get()\n    if (addToRect > 0) {\n      @Suppress(\"UseDPIAwareInsets\")\n      JBInsets.addTo(rect, Insets(addToRect, addToRect, addToRect, addToRect))\n    }\n\n    val w = thick.toFloat()\n    val border = Path2D.Float(Path2D.WIND_EVEN_ODD)\n    border.append(RoundRectangle2D.Float(0f, 0f, rect.width.toFloat(), rect.height.toFloat(), arc, arc), false)\n    val innerArc = max(arc - thick * 2, 0.0f)\n    border.append(RoundRectangle2D.Float(w, w, rect.width - w * 2, rect.height - w * 2, innerArc, innerArc), false)\n\n    g.translate(rect.x, rect.y)\n    g.fill(border)\n  }\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/ui/input/ShireChatBoxInput.kt",
    "content": "package com.phodal.shirecore.ui.input\n\nimport com.intellij.codeInsight.lookup.LookupManagerListener\nimport com.intellij.icons.AllIcons\nimport com.intellij.lang.Language\nimport com.intellij.openapi.Disposable\nimport com.intellij.openapi.application.ApplicationManager\nimport com.intellij.openapi.fileEditor.FileEditorManagerEvent\nimport com.intellij.openapi.fileEditor.FileEditorManagerListener\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.PsiFile\nimport com.intellij.psi.PsiManager\nimport com.intellij.ui.components.JBList\nimport com.intellij.ui.components.JBScrollPane\nimport com.intellij.util.ui.JBUI\nimport com.phodal.shirecore.ShireCoreBundle\nimport com.phodal.shirecore.provider.psi.RelatedClassesProvider\nimport com.phodal.shirecore.provider.shire.FileCreateService\nimport com.phodal.shirecore.provider.shire.FileRunService\nimport com.phodal.shirecore.relativePath\nimport java.awt.BorderLayout\nimport java.awt.Component\nimport java.awt.Dimension\nimport java.awt.FlowLayout\nimport java.awt.event.MouseAdapter\nimport java.awt.event.MouseEvent\nimport javax.swing.*\n\ndata class modelWrapper(val psiElement: PsiElement, var panel: JPanel? = null, var namePanel: JPanel? = null)\n\nclass ShireChatBoxInput(val project: Project) : JPanel(BorderLayout()), Disposable {\n    private var scratchFile: VirtualFile? = null\n    private val listModel = DefaultListModel<modelWrapper>()\n    private val elementsList = JBList(listModel)\n    private var inputSection: ShireInputSection\n\n    init {\n        setupElementsList()\n        inputSection = ShireInputSection(project, this)\n        inputSection.addListener(object : ShireInputListener {\n            override fun onStop(component: ShireInputSection) {\n                inputSection.showSendButton()\n            }\n\n            override fun onSubmit(component: ShireInputSection, trigger: ShireInputTrigger) {\n                val prompt = component.text\n                component.text = \"\"\n\n                if (prompt.isEmpty() || prompt.isBlank()) {\n                    component.showTooltip(ShireCoreBundle.message(\"chat.input.empty.tips\"))\n                    return\n                }\n\n                val virtualFile = createShireFile(prompt)\n                this@ShireChatBoxInput.scratchFile = virtualFile\n\n                FileRunService.provider(project, virtualFile!!)\n                    ?.runFile(project, virtualFile, null)\n\n                listModel.clear()\n                elementsList.clearSelection()\n            }\n        })\n\n        this.border = BorderFactory.createEmptyBorder(10, 10, 10, 10)\n        this.add(inputSection, BorderLayout.CENTER)\n        this.add(elementsList, BorderLayout.NORTH)\n\n        setupEditorListener()\n        setupRelatedListener()\n    }\n\n    private fun setupEditorListener() {\n        project.messageBus.connect(this).subscribe(\n            FileEditorManagerListener.FILE_EDITOR_MANAGER,\n            object : FileEditorManagerListener {\n                override fun selectionChanged(event: FileEditorManagerEvent) {\n                    val file = event.newFile ?: return\n                    val psiFile = PsiManager.getInstance(project).findFile(file) ?: return\n                    RelatedClassesProvider.provide(psiFile.language) ?: return\n                    ApplicationManager.getApplication().invokeLater {\n                        listModel.addIfAbsent(psiFile)\n                    }\n                }\n            }\n        )\n    }\n\n    private fun setupRelatedListener() {\n        project.messageBus.connect(this)\n            .subscribe(LookupManagerListener.TOPIC, ShireInputLookupManagerListener(project) {\n                ApplicationManager.getApplication().invokeLater {\n                    val relatedElements = RelatedClassesProvider.provide(it.language)?.lookup(it)\n                    updateElements(relatedElements)\n                }\n            })\n    }\n\n    private fun setupElementsList() {\n        elementsList.selectionMode = ListSelectionModel.SINGLE_SELECTION\n        elementsList.layoutOrientation = JList.HORIZONTAL_WRAP\n        elementsList.visibleRowCount = 2\n        elementsList.cellRenderer = ElementListCellRenderer()\n        elementsList.setEmptyText(\"\")\n        \n        val scrollPane = JBScrollPane(elementsList)\n        scrollPane.preferredSize = Dimension(-1, 80)\n        scrollPane.horizontalScrollBarPolicy = ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS\n        scrollPane.verticalScrollBarPolicy = ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED\n\n        elementsList.addMouseListener(object : MouseAdapter() {\n            override fun mouseClicked(e: MouseEvent) {\n                val list = e.source as JBList<*>\n                val index = list.locationToIndex(e.point)\n                if (index != -1) {\n                    val wrapper = listModel.getElementAt(index)\n                    val cellBounds = list.getCellBounds(index, index)\n                    wrapper.panel?.components?.firstOrNull { it.contains(e.x - cellBounds.x - it.x, it.height - 1) }?.let {\n                        when {\n                            it is JPanel -> {\n                                listModel.removeElement(wrapper)\n                                wrapper.psiElement.containingFile?.let { psiFile ->\n                                    val relativePath = psiFile.virtualFile.relativePath(project)\n                                    inputSection.appendText(\"\\n/\" + \"file\" + \":${relativePath}\")\n                                    listModel.indexOf(wrapper.psiElement).takeIf { it != -1 }?.let { listModel.remove(it) }\n                                    val relatedElements = RelatedClassesProvider.provide(psiFile.language)?.lookup(psiFile)\n                                    updateElements(relatedElements)\n                                }\n                            }\n                            it is JLabel && it.icon == AllIcons.Actions.Close -> listModel.removeElement(wrapper)\n                            else -> list.clearSelection()\n                        }\n                    } ?: list.clearSelection()\n                }\n            }\n        })\n\n        add(scrollPane, BorderLayout.NORTH)\n    }\n\n    private fun updateElements(elements: List<PsiElement>?) {\n        elements?.forEach { listModel.addIfAbsent(it) }\n    }\n\n    private fun createShireFile(prompt: String): VirtualFile? {\n        val findLanguageByID = Language.findLanguageByID(\"Shire\")\n            ?: throw IllegalStateException(\"Shire language not found\")\n        val provide = FileCreateService.provide(findLanguageByID)\n            ?: throw IllegalStateException(\"FileCreateService not found\")\n\n        return provide.createFile(prompt, project)\n    }\n\n    override fun dispose() {\n        scratchFile?.delete(this)\n    }\n}\n\nprivate fun DefaultListModel<modelWrapper>.addIfAbsent(psiFile: PsiElement) {\n    val isValid = when (psiFile) {\n        is PsiFile -> psiFile.isValid\n        else -> true\n    }\n    if (!isValid) return\n\n    if (elements().asIterator().asSequence().none { it.psiElement == psiFile }) {\n        addElement(modelWrapper(psiFile))\n    }\n}\n\nprivate class ElementListCellRenderer : ListCellRenderer<modelWrapper> {\n    override fun getListCellRendererComponent(\n        list: JList<out modelWrapper>,\n        value: modelWrapper,\n        index: Int,\n        isSelected: Boolean,\n        cellHasFocus: Boolean,\n    ): Component {\n        val psiElement = value.psiElement\n        val panel = value.panel ?: JPanel(FlowLayout(FlowLayout.LEFT, 3, 0)).apply {\n            accessibleContext.accessibleName = \"Element Panel\"\n\n            border = JBUI.Borders.empty(2, 5)\n\n            val namePanel = JPanel(layout)\n            val iconLabel = JLabel(psiElement.containingFile?.fileType?.icon ?: AllIcons.FileTypes.Unknown)\n            namePanel.add(iconLabel)\n\n            val nameLabel = JLabel(psiElement.containingFile?.name ?: \"Unknown\")\n            namePanel.add(nameLabel)\n\n            add(namePanel)\n            val closeLabel = JLabel(AllIcons.Actions.Close)\n            closeLabel.border = JBUI.Borders.empty()\n            add(closeLabel, BorderLayout.EAST)\n\n            value.panel = this\n            value.namePanel = namePanel\n        }\n        val namePanel = value.namePanel\n        if (isSelected) {\n            namePanel?.background = list.selectionBackground\n            namePanel?.foreground = list.selectionForeground\n        } else {\n            namePanel?.background = list.background\n            namePanel?.foreground = list.foreground\n        }\n\n        return panel\n    }\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/ui/input/ShireCoolBorder.kt",
    "content": "package com.phodal.shirecore.ui.input\n\nimport com.intellij.ide.ui.laf.darcula.DarculaUIUtil\nimport com.intellij.openapi.editor.Editor\nimport com.intellij.openapi.editor.ex.EditorEx\nimport com.intellij.openapi.editor.ex.FocusChangeListener\nimport com.intellij.openapi.ui.ErrorBorderCapable\nimport com.intellij.util.ui.JBInsets\nimport com.intellij.util.ui.UIUtil\nimport java.awt.*\nimport java.awt.geom.Path2D\nimport java.awt.geom.Rectangle2D\nimport java.awt.geom.RoundRectangle2D\nimport javax.swing.JComponent\nimport javax.swing.border.Border\nimport javax.swing.border.LineBorder\n\nclass ShireCoolBorder(private val editor: EditorEx, parent: JComponent) : Border, ErrorBorderCapable {\n    init {\n        editor.addFocusListener(object : FocusChangeListener {\n            override fun focusGained(editor2: Editor) {\n                parent.repaint()\n            }\n\n            override fun focusLost(editor2: Editor) {\n                parent.repaint()\n            }\n        })\n    }\n\n    override fun paintBorder(c: Component, g: Graphics, x: Int, y: Int, width: Int, height: Int) {\n        val r = Rectangle(x, y, width, height)\n        JBInsets.removeFrom(r, JBInsets.create(1, 1))\n\n        DarculaNewUIUtil.fillInsideComponentBorder(g, r, c.background)\n        val enabled = c.isEnabled\n        val hasFocus = UIUtil.isFocusAncestor(c)\n        DarculaNewUIUtil.paintComponentBorder(g, r, DarculaUIUtil.getOutline(c as JComponent), hasFocus, enabled)\n    }\n\n    override fun getBorderInsets(c: Component): Insets = JBInsets.create(Insets(3, 8, 3, 3)).asUIResource()\n    override fun isBorderOpaque(): Boolean = true\n}\n\nclass ShireLineBorder(color: Color, thickness: Int, roundedCorners: Boolean, val radius: Int) :\n    LineBorder(color, thickness, roundedCorners) {\n\n    override fun paintBorder(component: Component?, graphics: Graphics?, x: Int, y: Int, width: Int, height: Int) {\n        if (thickness > 0 && graphics is Graphics2D) {\n            val oldColor = graphics.color\n            graphics.color = lineColor\n            val offs = thickness\n            val size = offs + offs\n            val outer: Shape\n            val inner: Shape\n            if (roundedCorners) {\n                val arc: Float = (radius * 2).toFloat()\n                outer = RoundRectangle2D.Float(x.toFloat(), y.toFloat(), width.toFloat(), height.toFloat(), arc, arc)\n                inner = RoundRectangle2D.Float(\n                    (x + offs).toFloat(), (y + offs).toFloat(), (width - size).toFloat(),\n                    (height - size).toFloat(), arc, arc\n                )\n            } else {\n                outer = Rectangle2D.Float(x.toFloat(), y.toFloat(), width.toFloat(), height.toFloat())\n                inner = Rectangle2D.Float(\n                    (x + offs).toFloat(),\n                    (y + offs).toFloat(),\n                    (width - size).toFloat(),\n                    (height - size).toFloat()\n                )\n            }\n            val shape = Path2D.Float(0)\n            shape.append(outer, false)\n            shape.append(inner, false)\n            graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON)\n            graphics.fill(shape)\n            graphics.color = oldColor\n        }\n    }\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/ui/input/ShireInputListener.kt",
    "content": "package com.phodal.shirecore.ui.input\n\nimport com.intellij.openapi.editor.ex.EditorEx\nimport java.util.*\n\ninterface ShireInputListener : EventListener {\n    fun editorAdded(editor: EditorEx) {}\n    fun onSubmit(component: ShireInputSection, trigger: ShireInputTrigger) {}\n    fun onStop(component: ShireInputSection) {}\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/ui/input/ShireInputLookupManagerListener.kt",
    "content": "package com.phodal.shirecore.ui.input\n\nimport com.intellij.codeInsight.lookup.Lookup\nimport com.intellij.codeInsight.lookup.LookupEvent\nimport com.intellij.codeInsight.lookup.LookupListener\nimport com.intellij.codeInsight.lookup.LookupManagerListener\nimport com.intellij.codeInsight.lookup.impl.LookupImpl\nimport com.intellij.openapi.application.runReadAction\nimport com.intellij.openapi.project.Project\nimport com.intellij.psi.PsiFile\nimport com.intellij.psi.PsiManager\nimport com.phodal.shirecore.completion.ShireLookupElement\n\nclass ShireInputLookupManagerListener(\n    private val project: Project,\n    private val callback: ((PsiFile) -> Unit)? = null,\n) : LookupManagerListener {\n    override fun activeLookupChanged(oldLookup: Lookup?, newLookup: Lookup?) {\n        if (newLookup !is LookupImpl) return\n\n        newLookup.addLookupListener(object : LookupListener {\n            override fun itemSelected(event: LookupEvent) {\n                if (event.item !is ShireLookupElement<*>) return\n\n                val lookupElement = event.item as ShireLookupElement<*>\n\n                runReadAction {\n                    val file = lookupElement.getFile()\n                    val psiFile = PsiManager.getInstance(project).findFile(file)\n                    if (psiFile != null) {\n                        callback?.invoke(psiFile)\n                    }\n                }\n            }\n        })\n    }\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/ui/input/ShireInputSection.kt",
    "content": "package com.phodal.shirecore.ui.input\n\nimport com.intellij.icons.AllIcons\nimport com.intellij.ide.IdeTooltip\nimport com.intellij.ide.IdeTooltipManager\nimport com.intellij.openapi.Disposable\nimport com.intellij.openapi.actionSystem.Presentation\nimport com.intellij.openapi.actionSystem.impl.ActionButton\nimport com.intellij.openapi.application.runWriteAction\nimport com.intellij.openapi.command.WriteCommandAction\nimport com.intellij.openapi.editor.event.DocumentEvent\nimport com.intellij.openapi.editor.event.DocumentListener\nimport com.intellij.openapi.editor.ex.EditorEx\nimport com.intellij.openapi.project.DumbAwareAction\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.ui.ComponentValidator\nimport com.intellij.openapi.ui.popup.Balloon.Position\nimport com.intellij.openapi.wm.IdeFocusManager\nimport com.intellij.openapi.wm.impl.InternalDecorator\nimport com.intellij.ui.HintHint\nimport com.intellij.util.EventDispatcher\nimport com.intellij.util.ui.JBEmptyBorder\nimport com.intellij.util.ui.JBUI\nimport com.intellij.util.ui.UIUtil\nimport com.intellij.util.ui.components.BorderLayoutPanel\nimport com.phodal.shirecore.ShireCoreBundle\nimport com.phodal.shirecore.config.interaction.task.InsertUtil.insertStringAndSaveChange\nimport java.awt.CardLayout\nimport java.awt.Color\nimport java.awt.Dimension\nimport java.awt.Point\nimport java.awt.event.MouseAdapter\nimport java.awt.event.MouseEvent\nimport javax.swing.Box\nimport javax.swing.JComponent\nimport javax.swing.JPanel\nimport kotlin.math.max\nimport kotlin.math.min\n\nclass ShireInputSection(private val project: Project, val disposable: Disposable?) : BorderLayoutPanel() {\n    private val input: ShireInputTextField\n    private val documentListener: DocumentListener\n    private val sendButtonPresentation: Presentation\n    private val stopButtonPresentation: Presentation\n    private val sendButton: ActionButton\n    private val stopButton: ActionButton\n    private val buttonPanel = JPanel(CardLayout())\n\n    val editorListeners = EventDispatcher.create(ShireInputListener::class.java)\n\n    var text: String\n        get() {\n            return input.text\n        }\n        set(text) {\n            input.recreateDocument()\n            input.text = text\n        }\n\n    init {\n        val sendButtonPresentation = Presentation(ShireCoreBundle.message(\"chat.panel.send\"))\n        sendButtonPresentation.icon = AllIcons.Actions.Execute\n        this.sendButtonPresentation = sendButtonPresentation\n        sendButton = ActionButton(\n            DumbAwareAction.create {\n                editorListeners.multicaster.onSubmit(this@ShireInputSection, ShireInputTrigger.Button)\n            },\n            this.sendButtonPresentation, \"\", Dimension(20, 20)\n        )\n\n        val stopButtonPresentation = Presentation(\"Stop\")\n        stopButtonPresentation.icon = AllIcons.Actions.Suspend\n        this.stopButtonPresentation = stopButtonPresentation\n        stopButton = ActionButton(\n            DumbAwareAction.create {\n                editorListeners.multicaster.onStop(this@ShireInputSection)\n            },\n            this.stopButtonPresentation, \"\", Dimension(20, 20)\n        )\n\n        input = ShireInputTextField(project, listOf(), disposable, this)\n\n        documentListener = object : DocumentListener {\n            override fun documentChanged(event: DocumentEvent) {\n                val inputHeight = input.preferredSize?.height\n                if (inputHeight == input.height) return\n\n                revalidate()\n            }\n        }\n\n        input.addDocumentListener(documentListener)\n        input.recreateDocument()\n\n        input.border = JBEmptyBorder(4)\n\n        addToCenter(input)\n        val layoutPanel = BorderLayoutPanel()\n        val horizontalGlue = Box.createHorizontalGlue()\n        horizontalGlue.addMouseListener(object : MouseAdapter() {\n            override fun mouseClicked(e: MouseEvent?) {\n                IdeFocusManager.getInstance(project).requestFocus(input, true)\n                input.caretModel.moveToOffset(input.text.length - 1)\n            }\n        })\n        layoutPanel.setOpaque(false)\n\n        buttonPanel.add(sendButton, \"Send\")\n        buttonPanel.add(stopButton, \"Stop\")\n\n        layoutPanel.addToCenter(horizontalGlue)\n        layoutPanel.addToRight(buttonPanel)\n        addToBottom(layoutPanel)\n\n        ComponentValidator(disposable!!).installOn((this)).revalidate()\n\n        addListener(object : ShireInputListener {\n            override fun editorAdded(editor: EditorEx) {\n                this@ShireInputSection.initEditor()\n            }\n        })\n    }\n\n    fun showStopButton() {\n        (buttonPanel.layout as? CardLayout)?.show(buttonPanel, \"Stop\")\n        stopButton.isEnabled = true\n    }\n\n    fun showTooltip(text: String) {\n        showTooltip(input, Position.above, text)\n    }\n\n    fun showTooltip(component: JComponent, position: Position, text: String) {\n        val point = Point(component.x, component.y)\n        val tipComponent = IdeTooltipManager.initPane(\n            text, HintHint(component, point).setAwtTooltip(true).setPreferredPosition(position), null\n        )\n        val tooltip = IdeTooltip(component, point, tipComponent)\n        IdeTooltipManager.getInstance().show(tooltip, true)\n    }\n\n    fun showSendButton() {\n        (buttonPanel.layout as? CardLayout)?.show(buttonPanel, \"Send\")\n        buttonPanel.isEnabled = true\n    }\n\n    fun initEditor() {\n        val editorEx = this.input.editor as? EditorEx ?: return\n        setBorder(ShireCoolBorder(editorEx, this))\n        UIUtil.setOpaqueRecursively(this, false)\n        this.revalidate()\n    }\n\n    override fun getPreferredSize(): Dimension {\n        val result = super.getPreferredSize()\n        result.height = max(min(result.height, maxHeight), minimumSize.height)\n        return result\n    }\n\n    fun appendText(text: String) {\n        WriteCommandAction.runWriteCommandAction(\n            project,\n            \"Append text\",\n            \"intentions.write.action\",\n            {\n                insertStringAndSaveChange(project, text, input.editor!!.document, input.editor!!.document.textLength, false)\n            })\n    }\n\n    /**\n     * Set the content of the input field.\n     */\n    fun setContent(trimMargin: String) {\n        val focusManager = IdeFocusManager.getInstance(project)\n        focusManager.requestFocus(input, true)\n        this.input.recreateDocument()\n        this.input.text = trimMargin\n    }\n\n    override fun getBackground(): Color? {\n        // it seems that the input field is not ready when this method is called\n        if (this.input == null) return super.getBackground()\n\n        val editor = input.editor ?: return super.getBackground()\n        return editor.colorsScheme.defaultBackground\n    }\n\n    override fun setBackground(bg: Color?) {}\n\n    fun addListener(listener: ShireInputListener) {\n        editorListeners.addListener(listener)\n    }\n\n    fun moveCursorToStart() {\n        input.caretModel.moveToOffset(0)\n    }\n\n    private val maxHeight: Int\n        get() {\n            val decorator = UIUtil.getParentOfType(InternalDecorator::class.java, this)\n            val contentManager = decorator?.contentManager ?: return JBUI.scale(200)\n            return contentManager.component.height / 2\n        }\n\n    val focusableComponent: JComponent get() = input\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/ui/input/ShireInputTextField.kt",
    "content": "package com.phodal.shirecore.ui.input\n\nimport com.intellij.openapi.Disposable\nimport com.intellij.openapi.actionSystem.*\nimport com.intellij.openapi.actionSystem.ex.AnActionListener\nimport com.intellij.openapi.command.CommandProcessor\nimport com.intellij.openapi.editor.Document\nimport com.intellij.openapi.editor.Editor\nimport com.intellij.openapi.editor.EditorModificationUtil\nimport com.intellij.openapi.editor.actions.EnterAction\nimport com.intellij.openapi.editor.actions.IncrementalFindAction\nimport com.intellij.openapi.editor.event.DocumentListener\nimport com.intellij.openapi.editor.ex.EditorEx\nimport com.intellij.openapi.fileEditor.impl.text.TextEditorProvider\nimport com.intellij.openapi.fileTypes.FileTypes\nimport com.intellij.openapi.project.DumbAwareAction\nimport com.intellij.openapi.project.Project\nimport com.intellij.testFramework.LightVirtualFile\nimport com.intellij.ui.EditorTextField\nimport com.intellij.util.EventDispatcher\nimport com.intellij.util.messages.MessageBusConnection\nimport com.intellij.util.ui.JBUI\nimport com.phodal.shirecore.sketch.highlight.findDocument\nimport com.phodal.shirecore.utils.markdown.CodeFenceLanguage\nimport java.awt.Color\nimport java.awt.event.KeyEvent\nimport java.util.*\nimport javax.swing.KeyStroke\n\nclass ShireInputTextField(\n    project: Project,\n    private val listeners: List<DocumentListener>,\n    val disposable: Disposable?,\n    val inputSection: ShireInputSection,\n) : EditorTextField(project, FileTypes.PLAIN_TEXT), Disposable {\n    private var editorListeners: EventDispatcher<ShireInputListener> = inputSection.editorListeners\n\n    init {\n        isOneLineMode = false\n        setFontInheritedFromLAF(true)\n        addSettingsProvider {\n            it.putUserData(IncrementalFindAction.SEARCH_DISABLED, true)\n            it.colorsScheme.lineSpacing = 1.2f\n            it.settings.isUseSoftWraps = true\n            it.isEmbeddedIntoDialogWrapper = true\n            it.contentComponent.setOpaque(false)\n        }\n\n        DumbAwareAction.create {\n            object : AnAction() {\n                override fun actionPerformed(actionEvent: AnActionEvent) {\n                    val editor = editor ?: return\n                    CommandProcessor.getInstance().executeCommand(project, {\n                        val eol = \"\\n\"\n                        val caretOffset = editor.caretModel.offset\n                        editor.document.insertString(caretOffset, eol)\n                        editor.caretModel.moveToOffset(caretOffset + eol.length)\n                        EditorModificationUtil.scrollToCaret(editor)\n                    }, null, null)\n                }\n            }\n        }.registerCustomShortcutSet(\n            CustomShortcutSet(\n                KeyboardShortcut(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, KeyEvent.CTRL_DOWN_MASK), null),\n                KeyboardShortcut(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, KeyEvent.META_DOWN_MASK), null)\n            ), this\n        )\n\n        val connect: MessageBusConnection = project.messageBus.connect(disposable ?: this)\n        val topic = AnActionListener.TOPIC\n        connect.subscribe(topic, object : AnActionListener {\n            override fun afterActionPerformed(action: AnAction, event: AnActionEvent, result: AnActionResult) {\n                if (event.dataContext.getData(CommonDataKeys.EDITOR) === editor && action is EnterAction) {\n                    editorListeners.multicaster.onSubmit(inputSection, ShireInputTrigger.Key)\n                }\n            }\n        })\n\n        listeners.forEach { listener ->\n            document.addDocumentListener(listener)\n        }\n    }\n\n    override fun onEditorAdded(editor: Editor) {\n        editorListeners.multicaster.editorAdded((editor as EditorEx))\n    }\n\n    public override fun createEditor(): EditorEx {\n        val editor = super.createEditor()\n        editor.setVerticalScrollbarVisible(true)\n        setBorder(JBUI.Borders.empty())\n        editor.setShowPlaceholderWhenFocused(true)\n        editor.caretModel.moveToOffset(0)\n        editor.scrollPane.setBorder(border)\n        editor.contentComponent.setOpaque(false)\n        return editor\n    }\n\n    override fun getBackground(): Color {\n        val editor = editor ?: return super.getBackground()\n        return editor.colorsScheme.defaultBackground\n    }\n\n    override fun getData(dataId: String): Any? {\n        if (!PlatformCoreDataKeys.FILE_EDITOR.`is`(dataId)) {\n            return super.getData(dataId)\n        }\n\n        val currentEditor = editor ?: return super.getData(dataId)\n        return TextEditorProvider.getInstance().getTextEditor(currentEditor)\n    }\n\n    override fun dispose() {\n        listeners.forEach {\n            editor?.document?.removeDocumentListener(it)\n        }\n    }\n\n    fun recreateDocument() {\n        val id = UUID.randomUUID()\n        val language = CodeFenceLanguage.findLanguage(\"Shire\")\n        val file = LightVirtualFile(\"ShireInput-$id\", language, \"\")\n\n        val document = file.findDocument() ?: throw IllegalStateException(\"Can't create in-memory document\")\n\n        initializeDocumentListeners(document)\n        setDocument(document)\n        inputSection.initEditor()\n    }\n\n    private fun initializeDocumentListeners(inputDocument: Document) {\n        listeners.forEach { listener ->\n            inputDocument.addDocumentListener(listener)\n        }\n    }\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/ui/input/ShireInputTrigger.kt",
    "content": "package com.phodal.shirecore.ui.input\n\nenum class ShireInputTrigger {\n    Button,\n    Key\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/utils/markdown/CodeFence.kt",
    "content": "package com.phodal.shirecore.utils.markdown\n\nimport ai.grazie.nlp.utils.length\nimport com.intellij.lang.Language\nimport com.phodal.shirecore.utils.markdown.CodeFenceLanguage.findLanguage\nimport com.phodal.shirecore.utils.markdown.CodeFenceLanguage.lookupFileExt\n\nclass CodeFence(\n    val ideaLanguage: Language,\n    val text: String,\n    var isComplete: Boolean,\n    val extension: String?,\n    val originLanguage: String? = null,\n) {\n    companion object {\n        private var lastTxtBlock: CodeFence? = null\n        val shireStartRegex = Regex(\"<shire>\")\n        val shireEndRegex = Regex(\"</shire>\")\n\n        fun parse(content: String): CodeFence {\n            val markdownRegex = Regex(\"```([\\\\w#+\\\\s]*)\")\n\n            val lines = content.replace(\"\\\\n\", \"\\n\").lines()\n\n            // 检查是否存在 shire 开始标签\n            val startMatch = shireStartRegex.find(content)\n            if (startMatch != null) {\n                val endMatch = shireEndRegex.find(content)\n                val isComplete = endMatch != null\n\n                // 提取内容：如果有结束标签就截取中间内容，没有就取整个后续内容\n                val shireContent = if (isComplete) {\n                    content.substring(startMatch.range.last + 1, endMatch.range.first).trim()\n                } else {\n                    content.substring(startMatch.range.last + 1).trim()\n                }\n\n                return CodeFence(findLanguage(\"Shire\"), shireContent, isComplete, \"shire\", \"Shire\")\n            }\n\n            // 原有的 Markdown 代码块解析逻辑\n            var codeStarted = false\n            var codeClosed = false\n            var languageId: String? = null\n            val codeBuilder = StringBuilder()\n\n            for (line in lines) {\n                if (!codeStarted) {\n                    val matchResult: MatchResult? = markdownRegex.find(line.trimStart())\n                    if (matchResult != null) {\n                        val substring = matchResult.groups[1]?.value\n                        languageId = substring\n                        codeStarted = true\n                    }\n                } else if (line.startsWith(\"```\")) {\n                    codeClosed = true\n                    break\n                } else {\n                    codeBuilder.append(line).append(\"\\n\")\n                }\n            }\n\n            val trimmedCode = codeBuilder.trim().toString()\n            val language = findLanguage(languageId ?: \"\")\n            val extension =\n                language.associatedFileType?.defaultExtension ?: lookupFileExt(languageId ?: \"txt\")\n\n            return if (trimmedCode.isEmpty()) {\n                CodeFence(language, \"\", codeClosed, extension, languageId)\n            } else {\n                CodeFence(language, trimmedCode, codeClosed, extension, languageId)\n            }\n        }\n\n        fun parseAll(content: String): List<CodeFence> {\n            val codeFences = mutableListOf<CodeFence>()\n            var currentIndex = 0\n\n            val startMatches = shireStartRegex.findAll(content)\n            for (startMatch in startMatches) {\n                // 处理标签前的文本\n                if (startMatch.range.first > currentIndex) {\n                    val beforeText = content.substring(currentIndex, startMatch.range.first)\n                    if (beforeText.isNotEmpty()) {\n                        parseMarkdownContent(beforeText, codeFences)\n                    }\n                }\n\n                // 处理 shire 标签内容\n                val searchRegion = content.substring(startMatch.range.first)\n                val endMatch = shireEndRegex.find(searchRegion)\n                val isComplete = endMatch != null\n\n                val shireContent = if (isComplete) {\n                    searchRegion.substring(startMatch.range.length, endMatch!!.range.first).trim()\n                } else {\n                    searchRegion.substring(startMatch.range.length).trim()\n                }\n\n                codeFences.add(CodeFence(findLanguage(\"Shire\"), shireContent, isComplete, \"shire\", \"Shire\"))\n                currentIndex = if (isComplete) {\n                    startMatch.range.first + endMatch!!.range.last + 1\n                } else {\n                    content.length\n                }\n            }\n\n            // 处理最后剩余的内容\n            if (currentIndex < content.length) {\n                val remainingContent = content.substring(currentIndex)\n                parseMarkdownContent(remainingContent, codeFences)\n            }\n\n            return codeFences\n        }\n\n        private fun parseMarkdownContent(content: String, codeFences: MutableList<CodeFence>) {\n            val regex = Regex(\"```([\\\\w#+\\\\s]*)\")\n            val lines = content.replace(\"\\\\n\", \"\\n\").lines()\n\n            var codeStarted = false\n            var languageId: String? = null\n            val codeBuilder = StringBuilder()\n            val textBuilder = StringBuilder()\n\n            for (line in lines) {\n                if (!codeStarted) {\n                    val matchResult = regex.find(line.trimStart())\n                    if (matchResult != null) {\n                        if (textBuilder.isNotEmpty()) {\n                            val textBlock = CodeFence(findLanguage(\"markdown\"), textBuilder.trim().toString(), true, \"txt\")\n                            lastTxtBlock = textBlock\n                            codeFences.add(textBlock)\n                            textBuilder.clear()\n                        }\n\n                        languageId = matchResult.groups[1]?.value\n                        codeStarted = true\n                    } else {\n                        textBuilder.append(line).append(\"\\n\")\n                    }\n                } else {\n                    if (line.startsWith(\"```\")) {\n                        val codeContent = codeBuilder.trim().toString()\n                        val codeFence = parse(\"```$languageId\\n$codeContent\\n```\")\n                        codeFences.add(codeFence)\n\n                        codeBuilder.clear()\n                        codeStarted = false\n\n                        languageId = null\n                    } else {\n                        codeBuilder.append(line).append(\"\\n\")\n                    }\n                }\n            }\n\n            val ideaLanguage = findLanguage(languageId ?: \"markdown\")\n            if (textBuilder.isNotEmpty()) {\n                val normal = CodeFence(ideaLanguage, textBuilder.trim().toString(), true, null, languageId)\n                codeFences.add(normal)\n            }\n\n            if (codeStarted) {\n                val codeContent = codeBuilder.trim().toString()\n                if (codeContent.isNotEmpty()) {\n                    val codeFence = parse(\"```$languageId\\n$codeContent\\n\")\n                    codeFences.add(codeFence)\n                } else {\n                    val defaultLanguage = CodeFence(ideaLanguage, codeContent, false, null, languageId)\n                    codeFences.add(defaultLanguage)\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/utils/markdown/CodeFenceLanguage.kt",
    "content": "package com.phodal.shirecore.utils.markdown\n\nimport com.intellij.lang.Language\nimport com.intellij.openapi.fileTypes.PlainTextLanguage\n\nobject CodeFenceLanguage {\n    /**\n     * Returns the corresponding file extension for a given programming language identifier.\n     *\n     * This function maps a language identifier (e.g., \"Java\", \"Python\") to its typical file extension (e.g., \"java\", \"py\").\n     * If the language identifier is not recognized, the function returns the input string as the file extension.\n     *\n     * @param languageId The identifier of the programming language (case-insensitive).\n     * @return The file extension corresponding to the given language identifier.\n     */\n    fun lookupFileExt(languageId: String): String {\n        return when (languageId.lowercase()) {\n            \"c#\" -> \"cs\"\n            \"c++\" -> \"cpp\"\n            \"c\" -> \"c\"\n            \"java\" -> \"java\"\n            \"javascript\" -> \"js\"\n            \"kotlin\" -> \"kt\"\n            \"python\" -> \"py\"\n            \"ruby\" -> \"rb\"\n            \"swift\" -> \"swift\"\n            \"typescript\" -> \"ts\"\n            \"markdown\" -> \"md\"\n            \"sql\" -> \"sql\"\n            \"plantuml\" -> \"puml\"\n            \"shell\" -> \"sh\"\n            \"objective-c\" -> \"m\"\n            \"objective-c++\" -> \"mm\"\n            \"go\" -> \"go\"\n            \"html\" -> \"html\"\n            \"css\" -> \"css\"\n            \"dart\" -> \"dart\"\n            \"scala\" -> \"scala\"\n            \"rust\" -> \"rs\"\n            \"http request\" -> \"http\"\n            else -> languageId\n        }\n    }\n\n    /**\n     * Searches for a language by its name and returns the corresponding [Language] object. If the language is not found,\n     * [PlainTextLanguage.INSTANCE] is returned.\n     *\n     * @param languageName The name of the language to find.\n     * @return The [Language] object corresponding to the given name, or [PlainTextLanguage.INSTANCE] if the language is not found.\n     */\n    fun findLanguage(languageName: String): Language {\n        val fixedLanguage = when (languageName) {\n            \"csharp\" -> \"c#\"\n            \"cpp\" -> \"c++\"\n            \"shell\" -> \"Shell Script\"\n            \"sh\" -> \"Shell Script\"\n            \"http\" -> \"HTTP Request\"\n            else -> languageName\n        }\n\n        val languages = Language.getRegisteredLanguages()\n        val registeredLanguages = languages\n            .filter { it.displayName.isNotEmpty() }\n\n        return registeredLanguages.find { it.displayName.equals(fixedLanguage, ignoreCase = true) }\n            ?: PlainTextLanguage.INSTANCE\n    }\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/utils/markdown/MarkdownUtil.kt",
    "content": "// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage com.phodal.shirecore.utils.markdown\n\nimport org.intellij.markdown.IElementType\nimport org.intellij.markdown.flavours.gfm.GFMFlavourDescriptor\nimport org.intellij.markdown.html.HtmlGenerator\nimport org.intellij.markdown.parser.MarkdownParser\n\n// https://github.com/JetBrains/markdown/issues/72\nprivate val embeddedHtmlType = IElementType(\"ROOT\")\n\nobject MarkdownUtil {\n  fun toHtml(markdownText: String): String {\n    val flavour = GFMFlavourDescriptor()\n    val parsedTree = MarkdownParser(flavour).parse(embeddedHtmlType, markdownText)\n    return HtmlGenerator(markdownText, parsedTree, flavour).generateHtml()\n  }\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/utils/markdown/PostCodeProcessor.kt",
    "content": "package com.phodal.shirecore.utils.markdown\n\nclass PostCodeProcessor(\n    private val prefixCode: String,\n    private val suffixCode: String,\n    private val completeCode: String,\n    private val indentSize: Int = 4\n) {\n    private val indent = \" \".repeat(indentSize)\n    private val javaMethodPattern = Regex(\"^(?:^|\\\\s+)(?:@[A-Z]\\\\w+|(?:(?:public|private|protected)\\\\s+)?.*\\\\{)\")\n    private val endLinePattern = Regex(\"\\n\\\\s+\\n\\\\s+|\\n\\\\s+\\n|\\n\\n\\\\s+\")\n\n    fun execute(): String {\n        if (completeCode.isEmpty()) {\n            return completeCode\n        }\n\n        var code = completeCode\n        val prefix = prefixCode.trim()\n        if (completeCode.startsWith(prefix)) {\n            code = completeCode.substring(prefix.length)\n        }\n\n        var lines: MutableList<String> = code.split(\"\\n\").toMutableList()\n        val isFirstLineNeedIndent = !prefix.endsWith(indent)\n\n        if (javaMethodPattern.matches(lines[0])) {\n            if (isFirstLineNeedIndent && lines[0].startsWith(indent)) {\n                lines[0] = lines[0].substring(indent.length)\n            }\n        }\n\n        if (!lines.last().startsWith(indent)) {\n            lines = lines.map { indent + it }.toMutableList()\n        }\n\n        val results = lines.joinToString(\"\\n\")\n        val leftBraceCount = (prefix + code + suffixCode).count { it == '{' }\n        val rightBraceCount = (prefix + code + suffixCode).count { it == '}' }\n\n        val reversed = results.reversed()\n        var toRemoveBrace = rightBraceCount - leftBraceCount\n        val stringBuilder = StringBuilder()\n\n        for (i in reversed.indices) {\n            if (toRemoveBrace > 0 && (reversed[i] == '}' || reversed[i] == '\\n' || reversed[i] == ' ')) {\n                if (reversed[i] == '}') {\n                    toRemoveBrace--\n                } else {\n                    stringBuilder.append(reversed[i])\n                }\n            } else {\n                stringBuilder.append(reversed[i])\n            }\n        }\n\n        val output = stringBuilder.reverse().toString()\n        return endLinePattern.replace(output, \"\\n\")\n    }\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/variable/frontend/Component.kt",
    "content": "package com.phodal.shirecore.variable.frontend\n\nimport kotlinx.serialization.Serializable\n\n/**\n * the Design System Component\n */\n@Serializable\ndata class Component(\n    val name: String,\n    val path: String,\n    val signature: String = \"\",\n    val props: List<String> = emptyList(),\n) {\n    fun format(): String {\n        return \"\"\"\n            |component name: $name\n            |component path: $path\n            |input signature: $signature\n            |component props: $props\n        \"\"\".trimMargin()\n    }\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/variable/frontend/ComponentProvider.kt",
    "content": "package com.phodal.shirecore.variable.frontend\n\ninterface ComponentProvider {\n    fun getPages(): List<Component>\n    fun getComponents(): List<Component>\n    fun getRoutes(): Map<String, String>\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/variable/template/TemplateContext.kt",
    "content": "package com.phodal.shirecore.variable.template\n\ninterface TemplateContext {\n}\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/variable/template/VariableActionEventDataHolder.kt",
    "content": "package com.phodal.shirecore.variable.template\n\nimport com.intellij.openapi.actionSystem.DataContext\nimport com.intellij.openapi.util.Key\nimport com.intellij.openapi.util.UserDataHolderBase\n\n/**\n * The VariableActionEventDataHolder class serves as a temporary storage for data context related to VCS variable actions.\n * It uses a data class structure to encapsulate the data and provides a companion object to manage the data storage and retrieval.\n *\n * This class is designed to be used with a key-value pair mechanism where the data context is stored against a specific key.\n * The companion object provides static methods to put and get the data context.\n *\n * Usage:\n *\n * To store data:\n * ```Kotlin\n * val eventData = VariableActionEventDataHolder(dataContext)\n * VariableActionEventDataHolder.putData(eventData)\n * ```\n *\n * To retrieve data:\n * ```Kotlin\n * val eventData = VariableActionEventDataHolder.getData()\n * ```\n *\n * @param dataContext The DataContext object that holds the relevant information for VCS variable actions.\n *                    It is optional and can be null if no data needs to be stored initially.\n */\ndata class VariableActionEventDataHolder(val dataContext: DataContext? = null) {\n    companion object {\n        private val DATA_KEY: Key<VariableActionEventDataHolder> = Key.create(VariableActionEventDataHolder::class.java.name)\n        private val dataHolder = UserDataHolderBase()\n\n        fun putData(context: VariableActionEventDataHolder) {\n            dataHolder.putUserData(DATA_KEY, context)\n        }\n\n        fun getData(): VariableActionEventDataHolder? {\n            return dataHolder.getUserData(DATA_KEY)\n        }\n    }\n}"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/variable/toolchain/buildsystem/BuildSystemContext.kt",
    "content": "package com.phodal.shirecore.variable.toolchain.buildsystem\n\nimport com.phodal.shirecore.variable.template.TemplateContext\n\nclass BuildSystemContext(\n    val buildToolName: String,\n    val buildToolVersion: String,\n    val languageName: String,\n    val languageVersion: String,\n    /**\n     * The `taskString` variable represents a string that can be used to store various types of task information or commands.\n     *\n     * This variable can be used in different scenarios, such as:\n     * - Parsing the `package.json` file and retrieving the scripts field.\n     * - Running a Gradle task to obtain a list of all available tasks.\n     * - Storing any other task-related information or commands.\n     *\n     * The content of the `taskString` variable will depend on the specific use case and can be customized accordingly.\n     *\n     * Example usage:\n     * ```\n     * val taskString: String = \"npm run build\"\n     * ```\n     * In this example, the `taskString` variable is assigned the value `\"npm run build\"`, which represents a command to run the `build` script defined in the `package.json` file.\n     *\n     * Please note that the actual content and usage of the `taskString` variable will vary depending on the context and requirements of the application or system using it.\n     */\n    val taskString: String = \"\",\n    val libraries: List<String> = emptyList()\n) : TemplateContext\n"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/variable/toolchain/refactoring/RefactorInstElement.kt",
    "content": "package com.phodal.shirecore.variable.toolchain.refactoring\n\ndata class RefactorInstElement(\n    val isClass: Boolean,\n    val isMethod: Boolean,\n    val methodName: String,\n    val canonicalName: String,\n    val className: String,\n    val pkgName: String\n)"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/variable/toolchain/unittest/AutoTestingPromptContext.kt",
    "content": "package com.phodal.shirecore.variable.toolchain.unittest\n\nimport com.intellij.lang.Language\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.intellij.psi.PsiElement\nimport com.phodal.shirecore.variable.template.TemplateContext\n\ndata class AutoTestingPromptContext(\n    val isNewFile: Boolean,\n    val outputFile: VirtualFile,\n    val relatedClasses: List<String> = emptyList(),\n    val testClassName: String?,\n    val language: Language,\n    /**\n     * In Java, it is the current class.\n     * In Kotlin, it is the current class or current function.\n     * In JavaScript, it is the current class or current function.\n     */\n    val currentObject: String? = null,\n    val imports: List<String> = emptyList(),\n    /** Since 1.5.4, since some languages have different test code insertion strategies,\n     * we need to pass in the test element text\n     */\n    val testElement: PsiElement? = null,\n) : TemplateContext"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/variable/vcs/ShireFileBranch.kt",
    "content": "package com.phodal.shirecore.variable.vcs\n\ndata class ShireFileBranch(\n    val name: String,\n    override val count: Int,\n    override val commits: List<ShireGitCommit>\n) : CommitModel(count, commits)"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/variable/vcs/ShireFileCommit.kt",
    "content": "package com.phodal.shirecore.variable.vcs\n\ndata class ShireFileCommit(\n    val filename: String,\n    val path: String,\n    val status: String,\n    override val count: Int,\n    override val commits: List<ShireGitCommit>\n) : CommitModel(count, commits)"
  },
  {
    "path": "core/src/main/kotlin/com/phodal/shirecore/variable/vcs/ShireGitCommit.kt",
    "content": "package com.phodal.shirecore.variable.vcs\n\nsealed class GitEntity\n\n// Base class for models containing commits\nsealed class CommitModel(\n    open val count: Int,\n    open val commits: List<ShireGitCommit>\n) : GitEntity()\n\ndata class ShireGitCommit(\n    val hash: String,\n    val authorName: String,\n    val authorEmail: String,\n    val authorDate: Long,\n    val committerName: String,\n    val committerEmail: String,\n    val committerDate: Long,\n    val message: String,\n    val fullMessage: String\n) : GitEntity()\n\n"
  },
  {
    "path": "core/src/main/resources/com.phodal.shirecore.xml",
    "content": "<idea-plugin package=\"com.phodal.shirecore\">\n    <resource-bundle>messages.ShireCoreBundle</resource-bundle>\n    <extensions defaultExtensionNs=\"com.intellij\">\n        <projectService\n                id=\"DiffStreamService\"\n                serviceImplementation=\"com.phodal.shirecore.diff.DiffStreamService\"/>\n\n        <notificationGroup id=\"Shirelang.notification.group\" displayType=\"STICKY_BALLOON\"\n                           bundle=\"messages.ShireCoreBundle\"\n                           key=\"name\"/>\n\n    </extensions>\n\n    <extensionPoints>\n        <extensionPoint qualifiedName=\"com.phodal.shireFileRunService\"\n                        interface=\"com.phodal.shirecore.provider.shire.FileRunService\"\n                        dynamic=\"true\"/>\n\n        <extensionPoint qualifiedName=\"com.phodal.shireRunProjectService\"\n                        interface=\"com.phodal.shirecore.provider.shire.ProjectRunService\"\n                        dynamic=\"true\"/>\n\n        <extensionPoint qualifiedName=\"com.phodal.shireSymbolProvider\"\n                        interface=\"com.phodal.shirecore.provider.shire.ShireSymbolProvider\"\n                        dynamic=\"true\"/>\n\n        <extensionPoint qualifiedName=\"com.phodal.shireQLDataProvider\"\n                        interface=\"com.phodal.shirecore.provider.shire.ShireQLDataProvider\"\n                        dynamic=\"true\"/>\n\n        <extensionPoint qualifiedName=\"com.phodal.shireAgentTool\"\n                        interface=\"com.phodal.shirecore.provider.agent.AgentTool\"\n                        dynamic=\"true\"/>\n\n        <extensionPoint qualifiedName=\"com.phodal.shireRevisionProvider\"\n                        interface=\"com.phodal.shirecore.provider.shire.RevisionProvider\"\n                        dynamic=\"true\"/>\n\n        <extensionPoint qualifiedName=\"com.phodal.shireBuildSystemProvider\"\n                        interface=\"com.phodal.shirecore.provider.context.BuildSystemProvider\"\n                        dynamic=\"true\"/>\n\n        <extensionPoint qualifiedName=\"com.phodal.shireElementStrategyBuilder\"\n                        interface=\"com.phodal.shirecore.provider.psi.PsiElementStrategyBuilder \"\n                        dynamic=\"true\"/>\n\n        <extensionPoint qualifiedName=\"com.phodal.shireRefactoringTool\"\n                        beanClass=\"com.intellij.lang.LanguageExtensionPoint\" dynamic=\"true\">\n            <with attribute=\"implementationClass\" implements=\"com.phodal.shirecore.provider.shire.RefactoringTool\"/>\n        </extensionPoint>\n\n        <extensionPoint qualifiedName=\"com.phodal.shirePsiCapture\"\n                        beanClass=\"com.intellij.lang.LanguageExtensionPoint\" dynamic=\"true\">\n            <with attribute=\"implementationClass\" implements=\"com.phodal.shirecore.provider.psi.PsiCapture\"/>\n        </extensionPoint>\n\n        <extensionPoint qualifiedName=\"com.phodal.shireRelatedClass\"\n                        beanClass=\"com.intellij.lang.LanguageExtensionPoint\" dynamic=\"true\">\n            <with attribute=\"implementationClass\" implements=\"com.phodal.shirecore.provider.psi.RelatedClassesProvider\"/>\n        </extensionPoint>\n\n        <extensionPoint qualifiedName=\"com.phodal.shireAutoTesting\"\n                        beanClass=\"com.intellij.lang.LanguageExtensionPoint\" dynamic=\"true\">\n            <with attribute=\"implementationClass\" implements=\"com.phodal.shirecore.provider.TestingService\"/>\n        </extensionPoint>\n\n        <extensionPoint qualifiedName=\"com.phodal.shireFileCreateService\"\n                        beanClass=\"com.intellij.lang.LanguageExtensionPoint\"\n                        dynamic=\"true\">\n            <with attribute=\"implementationClass\" implements=\"com.phodal.shirecore.provider.shire.FileCreateService\"/>\n        </extensionPoint>\n\n        <!-- Toolchain Provider -->\n        <extensionPoint qualifiedName=\"com.phodal.shireLanguageToolchainProvider\"\n                        beanClass=\"com.intellij.lang.LanguageExtensionPoint\" dynamic=\"true\">\n            <with attribute=\"implementationClass\"\n                  implements=\"com.phodal.shirecore.provider.context.LanguageToolchainProvider\"/>\n        </extensionPoint>\n\n        <extensionPoint qualifiedName=\"com.phodal.shirePsiVariableProvider\"\n                        beanClass=\"com.intellij.lang.LanguageExtensionPoint\" dynamic=\"true\">\n            <with attribute=\"implementationClass\"\n                  implements=\"com.phodal.shirecore.provider.variable.PsiContextVariableProvider\"/>\n        </extensionPoint>\n\n        <!-- PSI Query Expression -->\n        <extensionPoint qualifiedName=\"com.phodal.shirePsiQLInterpreter\"\n                        beanClass=\"com.intellij.lang.LanguageExtensionPoint\" dynamic=\"true\">\n            <with attribute=\"implementationClass\"\n                  implements=\"com.phodal.shirecore.provider.variable.ShireQLInterpreter\"/>\n        </extensionPoint>\n\n        <!-- Code Editor -->\n        <extensionPoint qualifiedName=\"com.phodal.shireCodeModifier\"\n                        beanClass=\"com.intellij.lang.LanguageExtensionPoint\" dynamic=\"true\">\n            <with attribute=\"implementationClass\"\n                  implements=\"com.phodal.shirecore.provider.codeedit.CodeModifier\"/>\n        </extensionPoint>\n\n        <!-- Code DataStructure -->\n        <extensionPoint qualifiedName=\"com.phodal.classStructureProvider\"\n                        beanClass=\"com.intellij.lang.LanguageExtensionPoint\" dynamic=\"true\">\n            <with attribute=\"implementationClass\"\n                  implements=\"com.phodal.shirecore.provider.codemodel.ClassStructureProvider\"/>\n        </extensionPoint>\n\n        <extensionPoint qualifiedName=\"com.phodal.fileStructureProvider\"\n                        beanClass=\"com.intellij.lang.LanguageExtensionPoint\" dynamic=\"true\">\n            <with attribute=\"implementationClass\"\n                  implements=\"com.phodal.shirecore.provider.codemodel.FileStructureProvider\"/>\n        </extensionPoint>\n\n        <extensionPoint qualifiedName=\"com.phodal.methodStructureProvider\"\n                        beanClass=\"com.intellij.lang.LanguageExtensionPoint\" dynamic=\"true\">\n            <with attribute=\"implementationClass\"\n                  implements=\"com.phodal.shirecore.provider.codemodel.MethodStructureProvider\"/>\n        </extensionPoint>\n\n        <extensionPoint qualifiedName=\"com.phodal.variableStructureProvider\"\n                        beanClass=\"com.intellij.lang.LanguageExtensionPoint\" dynamic=\"true\">\n            <with attribute=\"implementationClass\"\n                  implements=\"com.phodal.shirecore.provider.codemodel.VariableStructureProvider\"/>\n        </extensionPoint>\n\n        <!-- PSI DATA Builder -->\n        <extensionPoint qualifiedName=\"com.phodal.shirePsiElementDataBuilder\"\n                        beanClass=\"com.intellij.lang.LanguageExtensionPoint\" dynamic=\"true\">\n            <with attribute=\"implementationClass\"\n                  implements=\"com.phodal.shirecore.provider.psi.PsiElementDataBuilder\"/>\n        </extensionPoint>\n\n        <!-- Toolchain Variable Provider -->\n        <extensionPoint qualifiedName=\"com.phodal.shireToolchainVariableProvider\"\n                        interface=\"com.phodal.shirecore.provider.variable.ToolchainVariableProvider\"\n                        dynamic=\"true\">\n        </extensionPoint>\n\n        <!-- Toolchain Function Provider -->\n        <extensionPoint qualifiedName=\"com.phodal.shireToolchainFunctionProvider\"\n                        interface=\"com.phodal.shirecore.provider.function.ToolchainFunctionProvider\"\n                        dynamic=\"true\">\n        </extensionPoint>\n\n        <!-- Post Code Middleware -->\n        <extensionPoint qualifiedName=\"com.phodal.shirePostProcessor\"\n                        interface=\"com.phodal.shirecore.middleware.post.PostProcessor\"\n                        dynamic=\"true\"/>\n\n        <!-- Location Interaction  -->\n        <extensionPoint qualifiedName=\"com.phodal.shireLocationInteraction\"\n                        interface=\"com.phodal.shirecore.provider.ide.LocationInteractionProvider\"\n                        dynamic=\"true\"/>\n        <extensionPoint qualifiedName=\"com.phodal.shireInlineChatProvider\"\n                        interface=\"com.phodal.shirecore.provider.ide.InlineChatProvider\"\n                        dynamic=\"true\"/>\n        <extensionPoint qualifiedName=\"com.phodal.shirePromptBuilder\"\n                        interface=\"com.phodal.shirecore.provider.ide.ShirePromptBuilder\"\n                        dynamic=\"true\"/>\n\n        <extensionPoint qualifiedName=\"com.phodal.shireLlmProvider\"\n                        interface=\"com.phodal.shirecore.llm.LlmProvider\"\n                        dynamic=\"true\"/>\n\n        <extensionPoint qualifiedName=\"com.phodal.shireActionLocationEditor\"\n                        interface=\"com.phodal.shirecore.provider.context.ActionLocationEditor\"\n                        dynamic=\"true\"/>\n\n        <extensionPoint qualifiedName=\"com.phodal.shireTerminalExecutor\"\n                        interface=\"com.phodal.shirecore.provider.action.TerminalLocationExecutor\"\n                        dynamic=\"true\"/>\n\n        <extensionPoint qualifiedName=\"com.phodal.shireHttpHandler\"\n                        interface=\"com.phodal.shirecore.provider.http.HttpHandler\"\n                        dynamic=\"true\"/>\n\n        <!-- Code Complexity -->\n        <extensionPoint qualifiedName=\"com.phodal.shireComplexityProvider\"\n                        beanClass=\"com.intellij.lang.LanguageExtensionPoint\" dynamic=\"true\">\n            <with attribute=\"implementationClass\"\n                  implements=\"com.phodal.shirecore.provider.complexity.ComplexityProvider\"/>\n        </extensionPoint>\n\n        <!-- Lang Sketch Provider -->\n        <extensionPoint qualifiedName=\"com.phodal.shireLangSketchProvider\"\n                        interface=\"com.phodal.shirecore.provider.sketch.LanguageSketchProvider\"\n                        dynamic=\"true\">\n        </extensionPoint>\n\n        <!--        streamingService -->\n        <extensionPoint qualifiedName=\"com.phodal.shireStreamingService\"\n                        interface=\"com.phodal.shirecore.provider.streaming.StreamingServiceProvider\"\n                        dynamic=\"true\">\n        </extensionPoint>\n    </extensionPoints>\n\n    <extensions defaultExtensionNs=\"JavaScript.JsonSchema\">\n        <ProviderFactory implementation=\"com.phodal.shirecore.schema.ShireJsonSchemaProviderFactory\"/>\n    </extensions>\n\n    <extensions defaultExtensionNs=\"com.phodal\">\n        <!-- code processors -->\n        <shirePostProcessor implementation=\"com.phodal.shirecore.middleware.builtin.TimeMetricProcessor\"/>\n\n        <shirePostProcessor implementation=\"com.phodal.shirecore.middleware.builtin.VerifyCodeProcessor\"/>\n        <shirePostProcessor implementation=\"com.phodal.shirecore.middleware.builtin.ParseCodeProcessor\"/>\n        <shirePostProcessor implementation=\"com.phodal.shirecore.middleware.builtin.RunCodeProcessor\"/>\n        <shirePostProcessor implementation=\"com.phodal.shirecore.middleware.builtin.InsertCodeProcessor\"/>\n        <shirePostProcessor implementation=\"com.phodal.shirecore.middleware.builtin.FormatCodeProcessor\"/>\n        <shirePostProcessor implementation=\"com.phodal.shirecore.middleware.builtin.PatchProcessor\"/>\n        <shirePostProcessor implementation=\"com.phodal.shirecore.middleware.builtin.DiffProcessor\"/>\n\n        <shirePostProcessor implementation=\"com.phodal.shirecore.middleware.builtin.AppendProcessor\"/>\n        <shirePostProcessor implementation=\"com.phodal.shirecore.middleware.builtin.InsertNewlineProcessor\"/>\n        <shirePostProcessor implementation=\"com.phodal.shirecore.middleware.builtin.UpdateEditorTextProcessor\"/>\n        <shirePostProcessor implementation=\"com.phodal.shirecore.middleware.builtin.ParseCommentProcessor\"/>\n\n        <shirePostProcessor implementation=\"com.phodal.shirecore.middleware.builtin.SaveFileProcessor\"/>\n        <shirePostProcessor implementation=\"com.phodal.shirecore.middleware.builtin.OpenFileProcessor\"/>\n\n        <shirePostProcessor implementation=\"com.phodal.shirecore.middleware.builtin.OpenWebpageProcessor\"/>\n        <shirePostProcessor implementation=\"com.phodal.shirecore.middleware.builtin.ShowWebviewProcessor\"/>\n\n        <!--   EditorInteractionProvider-->\n        <shireLocationInteraction implementation=\"com.phodal.shirecore.config.interaction.EditorInteractionProvider\"/>\n\n        <shireLangSketchProvider implementation=\"com.phodal.shirecore.sketch.patch.DiffLangSketchProvider\"/>\n\n        <!-- Markdown Impl -->\n        <shirePsiVariableProvider\n                language=\"Markdown\"\n                implementationClass=\"com.phodal.shirecore.provider.impl.MarkdownPsiContextVariableProvider\"/>\n\n        <shireStreamingService\n                implementation=\"com.phodal.shirecore.provider.streaming.LoggingStreamingService\"/>\n\n        <shireStreamingService\n                implementation=\"com.phodal.shirecore.provider.streaming.TimingStreamingService\"/>\n\n        <shireStreamingService\n                implementation=\"com.phodal.shirecore.provider.streaming.ProfilingStreamingService\"/>\n    </extensions>\n</idea-plugin>\n"
  },
  {
    "path": "core/src/main/resources/messages/ShireCoreBundle.properties",
    "content": "name=Shire\nprogress.run.task=Running task\nshire.ref.loading=Loading\nschema.custom-agent.json.display.name=Shire CustomAgent Schema\nintentions.request.background.process.title=Your LLM handle generate file\nintentions.chat.code.complete.name=Code complete\nintentions.step.prepare-context=Prepare context\nshell.command.suggestion.action.default.text=How to checkout a branch?\nschema.pattern.json.display.name=Shire Secret Pattern\nschema.env.json.display.name=Shire Env Schema\nshire.llm.notfound=No LLM provider found\n\nintentions.assistant.name=Shire intention action\nintentions.assistant.popup.title=Shire Intention Action\n\nsketch.patch.action.accept=Accept\nsketch.patch.action.accept.tooltip=Accept the change\nsketch.patch.action.reject=Reject\nsketch.patch.action.reject.tooltip=Reject the change\nsketch.patch.action.viewDiff=View Diff\nsketch.patch.action.viewDiff.tooltip=View the diff\n# rollback\nsketch.patch.action.rollback=Rollback\nsketch.patch.action.rollback.tooltip=Rollback the change\nchat.panel.send=Send\nchat.input.empty.tips=Input cannot be empty \nsketch.lint.error.tooltip=Click to view all errors\nsketch.lint.error=Found Lint issue: {0}\n"
  },
  {
    "path": "core/src/main/resources/schemas/shireCustomAgent.schema.json",
    "content": "{\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"type\": \"array\",\n  \"items\": {\n    \"type\": \"object\",\n    \"properties\": {\n      \"name\": {\n        \"type\": \"string\"\n      },\n      \"description\": {\n        \"type\": \"string\"\n      },\n      \"url\": {\n        \"type\": \"string\",\n        \"format\": \"uri\"\n      },\n      \"auth\": {\n        \"type\": \"object\",\n        \"properties\": {\n          \"type\": {\n            \"type\": \"string\",\n            \"enum\": [\n              \"Bearer\"\n            ]\n          },\n          \"token\": {\n            \"type\": \"string\"\n          }\n        },\n        \"required\": [\n          \"type\",\n          \"token\"\n        ]\n      },\n      \"connector\": {\n        \"type\": \"object\",\n        \"properties\": {\n          \"requestFormat\": {\n            \"type\": \"string\",\n            \"description\": \"JSONPath expression to specify the format of the request\"\n          },\n          \"responseFormat\": {\n            \"type\": \"string\",\n            \"description\": \"JSONPath expression to specify the format of the request\"\n          }\n        }\n      },\n      \"responseAction\": {\n        \"type\": \"string\",\n        \"enum\": [\n          \"Direct\",\n          \"TextChunk\",\n          \"WebView\",\n          \"Shire\"\n        ]\n      },\n      \"defaultTimeout\": {\n        \"type\": \"number\"\n      },\n      \"enabled\": {\n        \"type\": \"boolean\"\n      }\n    },\n    \"required\": [\n      \"name\",\n      \"url\",\n      \"description\",\n      \"responseAction\"\n    ],\n    \"additionalProperties\": true\n  }\n}\n"
  },
  {
    "path": "core/src/main/resources/schemas/shireEnv.schema.json",
    "content": "{\n  \"title\": \"Shire Environment JSON schema\",\n  \"$schema\": \"https://json-schema.org/draft-07/schema#\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"models\": {\n      \"type\": \"array\",\n      \"items\": {\n        \"type\": \"object\",\n        \"properties\": {\n          \"title\": {\n            \"type\": \"string\"\n          },\n          \"provider\": {\n            \"type\": \"string\",\n            \"enum\": [\n              \"openai\"\n            ]\n          },\n          \"apiBase\": {\n            \"type\": \"string\"\n          },\n          \"apiKey\": {\n            \"type\": \"string\"\n          },\n          \"model\": {\n            \"type\": \"string\"\n          },\n          \"temperature\": {\n            \"type\": \"number\"\n          },\n          \"maxTokens\": {\n            \"type\": \"number\"\n          }\n        },\n        \"required\": [\n          \"title\",\n          \"apiKey\",\n          \"model\"\n        ]\n      }\n    }\n  },\n  \"patternProperties\": {\n    \"^models$\": {\n      \"not\": {\n        \"type\": \"object\",\n        \"properties\": {\n          \"Security\": {\n            \"type\": \"object\",\n            \"properties\": {\n              \"Auth\": {\n                \"type\": \"object\",\n                \"patternProperties\": {\n                  \".+\": {\n                    \"properties\": {\n                      \"Type\": {\n                        \"enum\": [\n                          \"OAuth2\",\n                          \"Mock\"\n                        ]\n                      },\n                      \"Grant Type\": {\n                        \"enum\": [\n                          \"Authorization Code\",\n                          \"Implicit\",\n                          \"Password\",\n                          \"Client Credentials\",\n                          \"Device Authorization\"\n                        ]\n                      },\n                      \"Auth URL\": {\n                        \"type\": \"string\"\n                      },\n                      \"Token URL\": {\n                        \"type\": \"string\"\n                      },\n                      \"Redirect URL\": {\n                        \"type\": \"string\"\n                      },\n                      \"Client ID\": {\n                        \"type\": \"string\"\n                      },\n                      \"Client Secret\": {\n                        \"type\": \"string\"\n                      },\n                      \"Client Credentials\": {\n                        \"anyOf\": [\n                          {\n                            \"type\": \"boolean\"\n                          },\n                          {\n                            \"enum\": [\n                              \"basic\",\n                              \"in body\",\n                              \"none\"\n                            ]\n                          }\n                        ]\n                      },\n                      \"Username\": {\n                        \"type\": \"string\"\n                      },\n                      \"Password\": {\n                        \"type\": \"string\"\n                      },\n                      \"Acquire Automatically\": {\n                        \"type\": \"boolean\"\n                      },\n                      \"Scope\": {\n                        \"type\": \"string\"\n                      },\n                      \"State\": {\n                        \"type\": \"string\"\n                      },\n                      \"PKCE\": {\n                        \"anyOf\": [\n                          {\n                            \"type\": \"boolean\"\n                          },\n                          {\n                            \"type\": \"object\",\n                            \"properties\": {\n                              \"Code Challenge Method\": {\n                                \"enum\": [\n                                  \"SHA-256\",\n                                  \"Plain\"\n                                ]\n                              },\n                              \"Code Verifier\": {\n                                \"type\": \"string\",\n                                \"pattern\": \"^[A-Za-z0-9.\\\\-~]{43,128}$\"\n                              }\n                            }\n                          }\n                        ]\n                      },\n                      \"Open Complete URI\": {\n                        \"type\": \"boolean\"\n                      },\n                      \"Device Auth URL\": {\n                        \"type\": \"string\"\n                      },\n                      \"Start Polling After Browser\": {\n                        \"type\": \"boolean\"\n                      },\n                      \"Use ID Token\": {\n                        \"type\": \"boolean\"\n                      },\n                      \"Custom Request Parameters\": {\n                        \"type\": \"object\",\n                        \"properties\": {\n                          \"resource\": {\n                            \"$ref\": \"#/$defs/customParameter\"\n                          },\n                          \"audience\": {\n                            \"$ref\": \"#/$defs/customParameter\"\n                          },\n                          \".+\": {\n                            \"$ref\": \"#/$defs/customParameter\"\n                          }\n                        }\n                      },\n                      \"Token\": {\n                        \"type\": \"string\"\n                      },\n                      \"ID Token\": {\n                        \"type\": \"string\"\n                      }\n                    },\n                    \"required\": [\n                      \"Type\"\n                    ]\n                  }\n                }\n              }\n            }\n          }\n        },\n        \"patternProperties\": {\n          \".+\": {\n            \"anyOf\": [\n              {\n                \"type\": \"boolean\"\n              },\n              {\n                \"type\": \"string\"\n              }\n            ],\n            \"description\": \"User defined variable\"\n          }\n        },\n        \"description\": \"Environment\"\n      }\n    }\n  },\n  \"$defs\": {\n    \"customParameterScalar\": {\n      \"oneOf\": [\n        {\n          \"type\": \"string\"\n        },\n        {\n          \"type\": \"object\",\n          \"properties\": {\n            \"Value\": {\n              \"type\": \"string\"\n            },\n            \"Use\": {\n              \"enum\": [\n                \"In Auth Request\",\n                \"In Token Request\",\n                \"Everywhere\"\n              ]\n            }\n          },\n          \"required\": [\n            \"Value\"\n          ]\n        }\n      ]\n    },\n    \"customParameter\": {\n      \"oneOf\": [\n        {\n          \"$ref\": \"#/$defs/customParameterScalar\"\n        },\n        {\n          \"type\": \"array\",\n          \"items\": {\n            \"$ref\": \"#/$defs/customParameterScalar\"\n          }\n        }\n      ]\n    }\n  }\n}"
  },
  {
    "path": "core/src/main/resources/schemas/shireSecretPattern.schema.json",
    "content": "{\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"patterns\": {\n      \"type\": \"array\",\n      \"items\": {\n        \"type\": \"object\",\n        \"properties\": {\n          \"pattern\": {\n            \"type\": \"object\",\n            \"properties\": {\n              \"name\": {\n                \"type\": \"string\",\n                \"description\": \"Name of the pattern\"\n              },\n              \"regex\": {\n                \"type\": \"string\",\n                \"description\": \"Must be a regular expression, optionally but recommended to be quoted, and must be surrounded with '/'. Example: '/Code coverage: \\\\d+\\\\.\\\\d+/'\",\n                \"format\": \"regex\"\n              },\n              \"confidence\": {\n                \"type\": \"string\",\n                \"enum\": [\"high\", \"medium\", \"low\"]\n              }\n            },\n            \"required\": [\"name\", \"regex\", \"confidence\"]\n          }\n        },\n        \"required\": [\"pattern\"]\n      }\n    }\n  },\n  \"required\": [\"patterns\"]\n}\n"
  },
  {
    "path": "core/src/main/resources/secrets/default.shireSecretPattern.yml",
    "content": "patterns:\n  - pattern:\n      name: times\n      regex: \\d{1,2}:\\d{2} ?(?:[ap]\\.?m\\.?)?|\\d[ap]\\.?m\\.?\n      confidence: high\n  - pattern:\n      name: phones\n      regex: ((?:(?<![\\d-])(?:\\+?\\d{1,3}[-.\\s*]?)?(?:\\(?\\d{3}\\)?[-.\\s*]?)?\\d{3}[-.\\s*]?\\d{4}(?![\\d-]))|(?:(?<![\\d-])(?:(?:\\(\\+?\\d{2}\\))|(?:\\+?\\d{2}))\\s*\\d{2}\\s*\\d{3}\\s*\\d{4}(?![\\d-])))\n      confidence: high\n  - pattern:\n      name: phones_with_exts\n      regex: ((?:(?:\\+?1\\s*(?:[.-]\\s*)?)?(?:\\(\\s*(?:[2-9]1[02-9]|[2-9][02-8]1|[2-9][02-8][02-9])\\s*\\)|(?:[2-9]1[02-9]|[2-9][02-8]1|[2-9][02-8][02-9]))\\s*(?:[.-]\\s*)?)?(?:[2-9]1[02-9]|[2-9][02-9]1|[2-9][02-9]{2})\\s*(?:[.-]\\s*)?(?:[0-9]{4})(?:\\s*(?:#|x\\.?|ext\\.?|extension)\\s*(?:\\d+)?))\n      confidence: high\n  - pattern:\n      name: emails\n      regex: ([a-z0-9!#$%&'*+\\/=?^_`{|.}~-]+@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)\n      confidence: high\n  - pattern:\n      name: street_addresses\n      regex: \\d{1,4} [\\w\\s]{1,20}(?:street|st|avenue|ave|road|rd|highway|hwy|square|sq|trail|trl|drive|dr|court|ct|park|parkway|pkwy|circle|cir|boulevard|blvd)\\W?(?=\\s|$)\n      confidence: high\n  - pattern:\n      name: po_boxes\n      regex: P\\.? ?O\\.? Box \\d+\n      confidence: high\n  - pattern:\n      name: ukphones\n      regex: ^((00|0)?44[1-9]{1}[0-9]{9})$\n      confidence: high\n  - pattern:\n      name: email - 3\n      regex: \\b[\\w\\-+.]+@+\\w+.+[A-z]{3}\n      confidence: high\n  - pattern:\n      name: ssn - 3\n      regex: \"\\b(?!000|666)[0-8][0-9]{2}-(?!00)[0-9]{2}-(?!0000)[0-9]{4}\\b\"\n      confidence: high\n  - pattern:\n      name: ssn_number\n      regex: (?!000|666|333)0*(?:[0-6][0-9][0-9]|[0-7][0-6][0-9]|[0-7][0-7][0-2])[-](?!00)[0-9]{2}[- ](?!0000)[0-9]{4}\n      confidence: high\n  - pattern:\n      name: visa_credit_card\n      regex: 4[0-9]{15}\n      confidence: high\n  - pattern:\n      name: american_express_credit-card\n      regex: 3[47][0-9]{13}\n      confidence: high\n  - pattern:\n      name: otp\n      regex: ^[0-9]{6}$\n      confidence: high\n  - pattern:\n      name: credit card - 2\n      regex: 4[0-9]{12}(?:[0-9]{3})?|(?:5[1-5][0-9]{2}|222[1-9]|22[3-9][0-9]|2[3-6][0-9]{2}|27[01][0-9]|2720)[0-9]{12} |3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|6(?:011|5[0-9]{2})[0-9]{12}|(?:2131|1800|35\\d{3})\\d{11}\n      confidence: high\n  - pattern:\n      name: UK Phone Numbers\n      regex: \\b([0O]?[1lI][1lI])?[4A][4A][\\dOIlZEASB]{10,11}\\b\n      confidence: high\n  - pattern:\n      name: US Phone Numbers\n      regex: \\b((\\+|\\b)[1l][\\-\\. ])?\\(?\\b[\\dOlZSB]{3,5}([\\-\\. ]|\\) ?)[\\dOlZSB]{3}[\\-\\. ][\\dOlZSB]{4}\\b\n      confidence: high\n  - pattern:\n      name: Email Addresses\n      regex: \\b[a-z0-9._%\\+\\-—|]+@[a-z0-9.\\-—|]+\\.[a-z|]{2,6}\\b\n      confidence: high\n  - pattern:\n      name: Credit card - 3\n      regex: \\b((4\\d{3}|5[1-5]\\d{2}|2\\d{3}|3[47]\\d{1,2})[\\s\\-]?\\d{4,6}[\\s\\-]?\\d{4,6}?([\\s\\-]\\d{3,4})?(\\d{3})?)\\b\n      confidence: high\n  - pattern:\n      name: Amex Card\n      regex: \\b3[47][0-9]{13}\\b\n      confidence: high\n  - pattern:\n      name: BCGlobal\n      regex: \\b(6541|6556)[0-9]{12}\\b\n      confidence: high\n  - pattern:\n      name: Carte Blanche Card\n      regex: \\b389[0-9]{11}\\b\n      confidence: high\n  - pattern:\n      name: Diners Club Card\n      regex: \\b3(?:0[0-5]|[68][0-9])[0-9]{11}\\b\n      confidence: high\n  - pattern:\n      name: Discover Card\n      regex: \\b65[4-9][0-9]{13}|64[4-9][0-9]{13}|6011[0-9]{12}|(622(?:12[6-9]|1[3-9][0-9]|[2-8][0-9][0-9]|9[01][0-9]|92[0-5])[0-9]{10})\\b\n      confidence: high\n  - pattern:\n      name: Insta Payment Card\n      regex: \\b63[7-9][0-9]{13}\\b\n      confidence: high\n  - pattern:\n      name: JCB Card\n      regex: \\b(?:2131|1800|35\\d{3})\\d{11}\\b\n      confidence: high\n  - pattern:\n      name: Korean Local Card\n      regex: \\b9[0-9]{15}\\b\n      confidence: high\n  - pattern:\n      name: Laser Card\n      regex: \\b(6304|6706|6709|6771)[0-9]{12,15}\\b\n      confidence: high\n  - pattern:\n      name: Maestro Card\n      regex: \\b(5018|5020|5038|6304|6759|6761|6763)[0-9]{8,15}\\b\n      confidence: high\n  - pattern:\n      name: MasterCard\n      regex: \\b(?:4[0-9]{12}(?:[0-9]{3})?|[25][1-7][0-9]{14}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\\d{3})\\d{11})\\b\n      confidence: high\n  - pattern:\n      name: Solo Card\n      regex: \\b(6334|6767)[0-9]{12}|(6334|6767)[0-9]{14}|(6334|6767)[0-9]{15}\\b\n      confidence: high\n  - pattern:\n      name: Switch Card\n      regex: \\b(4903|4905|4911|4936|6333|6759)[0-9]{12}|(4903|4905|4911|4936|6333|6759)[0-9]{14}|(4903|4905|4911|4936|6333|6759)[0-9]{15}|564182[0-9]{10}|564182[0-9]\n      confidence: high\n  - pattern:\n      name: Argentina National Identity (DNI) Number\n      regex: \\d{2}\\.\\d{3}\\.\\d{3}\n      confidence: high\n  - pattern:\n      name: Canada Passport ID\n      regex: \\b[\\w]{2}[\\d]{6}\\b\n      confidence: high\n  - pattern:\n      name: Croatia Vat ID card number\n      regex: \\bHR\\d{11}\\b\n      confidence: high\n  - pattern:\n      name: Czech Republic Vat ID card number\n      regex: \\bCZ\\d{8,10}\\b\n      confidence: high\n  - pattern:\n      name: Denmark Personal ID number\n      regex: \\b\\d{10}|\\d{6}[-\\s]\\d{4}\\b\n      confidence: high\n  - pattern:\n      name: France National ID card (CNI)\n      regex: \\b\\d{12}\\b\n      confidence: high\n  - pattern:\n      name: France Social Security Number (INSEE)\n      regex: \\b\\d{13}|\\d{13}\\s\\d{2}\\b\n      confidence: high\n  - pattern:\n      name: France Passport ID\n      regex: \\b\\d{2}11\\d{5}\\b\n      confidence: high\n  - pattern:\n      name: Germany ID card number\n      regex: \\bl\\d{8}\\b\n      confidence: high\n  - pattern:\n      name: Germany Passport ID\n      regex: \\b[cfghjk]\\d{3}\\w{5}\\d\\b\n      confidence: high\n  - pattern:\n      name: Germany Driver's License ID\n      regex: \\b[\\d\\w]\\d{2}[\\d\\w]{6}\\d[\\d\\w]\\b\n      confidence: high\n  - pattern:\n      name: Ireland Personal Public Service (PPS) Number\n      regex: \\b\\d{7}\\w{1,2}\\b\n      confidence: high\n  - pattern:\n      name: Netherlands Citizen's Service (BSN) number\n      regex: \\b\\d{8}|\\d{3}[-\\.\\s]\\d{3}[-\\.\\s]\\d{3}\\b\n      confidence: high\n  - pattern:\n      name: Poland National ID (PESEL)\n      regex: \\b\\d{11}\\b\n      confidence: high\n  - pattern:\n      name: Portugal Citizen Card Number\n      regex: \\d{9}[\\w\\d]{2}|\\d{8}-\\d[\\d\\w]{2}\\d\n      confidence: high\n  - pattern:\n      name: Spain Social Security Number (SSN)\n      regex: \\b\\d{2}\\/?\\d{8}\\/?\\d{2}\\b\n      confidence: high\n  - pattern:\n      name: Spain Social Security Number (SSN) - 2\n      regex: \\b\\d{3}[ -.]\\d{2}[ -.]\\d{4}\\b`\n      confidence: high\n  - pattern:\n      name: Sweden Passport ID\n      regex: \\b\\d{8}\\b\n      confidence: high\n  - pattern:\n      name: United Kingdom Passport ID\n      regex: \\b\\d{9}\\b\n      confidence: high\n  - pattern:\n      name: United Kingdom Driver's license ID\n      regex: \\b[\\w9]{5}\\d{6}[\\w9]{2}\\d{5}\\b\n      confidence: high\n  - pattern:\n      name: United Kingdom National Health Service (NHS) number\n      regex: \\b\\d{3}\\s\\d{3}\\s\\d{4}\\b\n      confidence: high\n  - pattern:\n      name: ipv4\n      regex: (?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\n      confidence: high\n  - pattern:\n      name: prices\n      regex: '[$]\\s?[+-]?[0-9]{1,3}(?:(?:,?[0-9]{3}))*(?:\\.[0-9]{1,2})?'\n      confidence: high\n  - pattern:\n      name: hex_colors\n      regex: (#(?:[0-9a-fA-F]{8})|#(?:[0-9a-fA-F]{3}){1,2})\\b\n      confidence: high\n  - pattern:\n      name: credit_cards\n      regex: ((?:(?:\\d{4}[- ]?){3}\\d{4}|\\d{15,16}))(?![\\d])\n      confidence: high\n  - pattern:\n      name: visa_cards\n      regex: 4\\d{3}[\\s-]?\\d{4}[\\s-]?\\d{4}[\\s-]?\\d{4}\n      confidence: high\n  - pattern:\n      name: master_cards\n      regex: 5[1-5]\\d{2}[\\s-]?\\d{4}[\\s-]?\\d{4}[\\s-]?\\d{4}\n      confidence: high\n  - pattern:\n      name: btc_addresses\n      regex: (?<![a-km-zA-HJ-NP-Z0-9])[13][a-km-zA-HJ-NP-Z0-9]{26,33}(?![a-km-zA-HJ-NP-Z0-9])\n      confidence: high\n  - pattern:\n      name: ssn_number - 3\n      regex: (?:\\d{3}-\\d{2}-\\d{4})\n      confidence: high\n  - pattern:\n      name: md5_hashes\n      regex: '[0-9a-fA-F]{32}'\n      confidence: high\n  - pattern:\n      name: sha1_hashes\n      regex: '[0-9a-fA-F]{40}'\n      confidence: high\n  - pattern:\n      name: sha256_hashes\n      regex: '[0-9a-fA-F]{64}'\n      confidence: high\n  - pattern:\n      name: isbn13\n      regex: (?:[\\d]-?){12}[\\dxX]\n      confidence: high\n  - pattern:\n      name: isbn10\n      regex: (?:[\\d]-?){9}[\\dxX]\n      confidence: high\n  - pattern:\n      name: mac_addresses\n      regex: (([0-9a-fA-F]{2}[:-]){5}([0-9a-fA-F]{2}))\n      confidence: high\n  - pattern:\n      name: iban_numbers\n      regex: '[A-Z]{2}\\d{2}[A-Z0-9]{4}\\d{7}([A-Z\\d]?){0,16}'\n      confidence: high\n  - pattern:\n      name: git_repos\n      regex: ((git|ssh|http(s)?)|(git@[\\w\\.]+))(:(\\/\\/)?)([\\w\\.@\\:/\\-~]+)(\\.git)(\\/)?\n      confidence: high\n  - pattern:\n        name: Driver's License Number (simplified)\n        regex: ^[A-Z]{2}-\\d{6}$\n        confidence: high\n  - pattern:\n        name: Passport Number (simplified) - 3\n        regex: ^[A-Z]\\d{7}$\n        confidence: high\n  - pattern:\n        name: Social Security Number (SSN) - 3\n        regex: ^\\d{3}-\\d{2}-\\d{4}$\n        confidence: high\n  - pattern:\n        name: Social Security Number (SSN) - 4\n        regex: (?:\\\\b\\\\d{3}-?\\\\d{2}-?(\\\\d{4})\\\\b)\n        confidence: high\n  - pattern:\n        name: Date of Birth\n        regex: ^\\d{2}/\\d{2}/\\d{4}$|^\\d{4}-\\d{2}-\\d{2}$\n        confidence: high\n  - pattern:\n      name: Arista network configuration\n      regex: \"via\\\\ \\\\d{1,3}\\\\.\\\\d{1,3}\\\\.\\\\d{1,3}\\\\.\\\\d{1,3},\\\\ \\\\d{2}:\\\\d{2}:\\\\d{2}\"\n      confidence: high\n  - pattern:\n      name: BBVA Compass Routing Number - California\n      regex: \"^321170538$\"\n      confidence: high\n  - pattern:\n      name: Bank of America Routing Numbers - California\n      regex: \"^(?:121|026)00(?:0|9)(?:358|593)$\"\n      confidence: high\n  - pattern:\n      name: Box Links\n      regex: \"https://app.box.com/[s|l]/\\\\S+\"\n      confidence: high\n  - pattern:\n      name: CVE Number\n      regex: \"CVE-\\\\d{4}-\\\\d{4,7}\"\n      confidence: high\n  - pattern:\n      name: California Drivers License\n      regex: \"^[A-Z]{1}\\\\d{7}$\"\n      confidence: high\n  - pattern:\n      name: Chase Routing Numbers - California\n      regex: \"^322271627$\"\n      confidence: high\n  - pattern:\n      name: Cisco Router Config\n      regex: \"service\\\\ timestamps\\\\ [a-z]{3,5}\\\\ datetime\\\\ msec|boot-[a-z]{3,5}-marker|interface\\\\ [A-Za-z0-9]{0,10}[E,e]thernet\"\n      confidence: high\n  - pattern:\n      name: Citibank Routing Numbers - California\n      regex: \"^32(?:11|22)71(?:18|72)4$\"\n      confidence: high\n  - pattern:\n      name: DSA Private Key\n      regex: \"-----BEGIN DSA PRIVATE KEY-----(?:[a-zA-Z0-9\\\\+\\\\=\\\\/\\\"']|\\\\s)+?-----END DSA PRIVATE KEY-----\"\n      confidence: high\n  - pattern:\n      name: Dropbox Links\n      regex: \"https://www.dropbox.com/(?:s|l)/\\\\S+\"\n      confidence: high\n  - pattern:\n      name: EC Private Key\n      regex: \"-----BEGIN (?:EC|ECDSA) PRIVATE KEY-----(?:[a-zA-Z0-9\\\\+\\\\=\\\\/\\\"']|\\\\s)+?-----END (?:EC|ECDSA) PRIVATE KEY-----\"\n      confidence: high\n  - pattern:\n      name: Encrypted DSA Private Key\n      regex: \"-----BEGIN DSA PRIVATE KEY-----\\\\s.*,ENCRYPTED(?:.|\\\\s)+?-----END DSA PRIVATE KEY-----\"\n      confidence: high\n  - pattern:\n      name: Encrypted EC Private Key\n      regex: \"-----BEGIN (?:EC|ECDSA) PRIVATE KEY-----\\\\s.*,ENCRYPTED(?:.|\\\\s)+?-----END (?:EC|ECDSA) PRIVATE KEY-----\"\n      confidence: high\n  - pattern:\n      name: Encrypted Private Key\n      regex: \"-----BEGIN ENCRYPTED PRIVATE KEY-----(?:.|\\\\s)+?-----END ENCRYPTED PRIVATE KEY-----\"\n      confidence: high\n  - pattern:\n      name: Encrypted PuTTY SSH DSA Key\n      regex: \"PuTTY-User-Key-File-2: ssh-dss\\\\s*Encryption: aes(?:.|\\\\s?)*?Private-MAC:\"\n      confidence: high\n  - pattern:\n      name: Encrypted RSA Private Key\n      regex: \"-----BEGIN RSA PRIVATE KEY-----\\\\s.*,ENCRYPTED(?:.|\\\\s)+?-----END RSA PRIVATE KEY-----\"\n      confidence: high\n  - pattern:\n      name: Google Application Identifier\n      regex: \"[0-9]+-\\\\w+.apps.googleusercontent.com\"\n      confidence: high\n  - pattern:\n      name: HIPAA PHI National Drug Code\n      regex: \"^\\\\d{4,5}-\\\\d{3,4}-\\\\d{1,2}$\"\n      confidence: high\n  - pattern:\n      name: Huawei config file\n      regex: \"sysname\\\\ HUAWEI|set\\\\ authentication\\\\ password\\\\ simple\\\\ huawei\"\n      confidence: high\n  - pattern:\n      name: Individual Taxpayer Identification Numbers (ITIN)\n      regex: \"^9\\\\d{2}(?:[ \\\\-]?)[7,8]\\\\d(?:[ \\\\-]?)\\\\d{4}$\"\n      confidence: high\n  - pattern:\n      name: John the Ripper\n      regex: \"[J,j]ohn\\\\ [T,t]he\\\\ [R,r]ipper|john-[1-9].[1-9].[1-9]|Many\\\\ salts:|Only\\\\ one\\\\ salt:|openwall.com/john/|List.External:[0-9a-zA-Z]*|Loaded\\\\ [0-9]*\\\\ password hash|guesses:\\\\ \\\\d*\\\\ \\\\ time:\\\\ \\\\d*:\\\\d{2}:\\\\d{2}:\\\\d{2}|john\\\\.pot\"\n      confidence: high\n  - pattern:\n      name: KeePass 1.x CSV Passwords\n      regex: \"\\\"Account\\\",\\\"Login Name\\\",\\\"Password\\\",\\\"Web Site\\\",\\\"Comments\\\"\"\n      confidence: high\n  - pattern:\n      name: KeePass 1.x XML Passwords\n      regex: \"<pwlist>\\\\s*?<pwentry>[\\\\S\\\\s]*?<password>[\\\\S\\\\s]*?<\\\\/pwentry>\\\\s*?<\\\\/pwlist>\"\n      confidence: high\n  - pattern:\n      name: Large number of US Phone Numbers\n      regex: \"\\\\d{3}-\\\\d{3}-\\\\d{4}|\\\\(\\\\d{3}\\\\)\\\\ ?\\\\d{3}-?\\\\d{4}\"\n      confidence: high\n  - pattern:\n      name: Large number of US Zip Codes\n      regex: \"^(\\\\d{5}-\\\\d{4}|\\\\d{5})$\"\n      confidence: high\n  - pattern:\n      name: Lightweight Directory Access Protocol\n      regex: \"(?:dn|cn|dc|sn):\\\\s*[a-zA-Z0-9=, ]*\"\n      confidence: high\n  - pattern:\n      name: Metasploit Module\n      regex: \"require\\\\ 'msf/core'|class\\\\ Metasploit|include\\\\ Msf::Exploit::\\\\w+::\\\\w+\"\n      confidence: high\n  - pattern:\n      name: MySQL database dump\n      regex: \"DROP DATABASE IF EXISTS(?:.|\\\\n){5,300}CREATE DATABASE(?:.|\\\\n){5,300}DROP TABLE IF EXISTS(?:.|\\\\n){5,300}CREATE TABLE\"\n      confidence: high\n  - pattern:\n      name: MySQLite database dump\n      regex: \"DROP\\\\ TABLE\\\\ IF\\\\ EXISTS\\\\ \\\\[[a-zA-Z]*\\\\];|CREATE\\\\ TABLE\\\\ \\\\[[a-zA-Z]*\\\\];\"\n      confidence: high\n  - pattern:\n      name: Network Proxy Auto-Config\n      regex: \"proxy\\\\.pac|function\\\\ FindProxyForURL\\\\(\\\\w+,\\\\ \\\\w+\\\\)\"\n      confidence: high\n  - pattern:\n      name: Nmap Scan Report\n      regex: \"Nmap\\\\ scan\\\\ report\\\\ for\\\\ [a-zA-Z0-9.]+\"\n      confidence: high\n  - pattern:\n      name: PGP Header\n      regex: \"-{5}(?:BEGIN|END)\\\\ PGP\\\\ MESSAGE-{5}\"\n      confidence: high\n  - pattern:\n      name: PGP Private Key Block\n      regex: \"-----BEGIN PGP PRIVATE KEY BLOCK-----(?:.|\\\\s)+?-----END PGP PRIVATE KEY BLOCK-----\"\n      confidence: high\n  - pattern:\n      name: PKCS7 Encrypted Data\n      regex: \"(?:Signer|Recipient)Info(?:s)?\\\\ ::=\\\\ \\\\w+|[D|d]igest(?:Encryption)?Algorithm|EncryptedKey\\\\ ::= \\\\w+\"\n      confidence: high\n  - pattern:\n      name: Password etc passwd\n      regex: \"[a-zA-Z0-9\\\\-]+:[x|\\\\*]:\\\\d+:\\\\d+:[a-zA-Z0-9/\\\\- \\\"]*:/[a-zA-Z0-9/\\\\-]*:/[a-zA-Z0-9/\\\\-]+\"\n      confidence: high\n  - pattern:\n      name: Password etc shadow\n      regex: \"[a-zA-Z0-9\\\\-]+:(?:(?:!!?)|(?:\\\\*LOCK\\\\*?)|\\\\*|(?:\\\\*LCK\\\\*?)|(?:\\\\$.*\\\\$.*\\\\$.*?)?):\\\\d*:\\\\d*:\\\\d*:\\\\d*:\\\\d*:\\\\d*:\"\n      confidence: high\n  - pattern:\n      name: PlainText Private Key\n      regex: \"-----BEGIN PRIVATE KEY-----(?:.|\\\\s)+?-----END PRIVATE KEY-----\"\n      confidence: high\n  - pattern:\n      name: PuTTY SSH DSA Key\n      regex: \"PuTTY-User-Key-File-2: ssh-dss\\\\s*Encryption: none(?:.|\\\\s?)*?Private-MAC:\"\n      confidence: high\n  - pattern:\n      name: PuTTY SSH RSA Key\n      regex: \"PuTTY-User-Key-File-2: ssh-rsa\\\\s*Encryption: none(?:.|\\\\s?)*?Private-MAC:\"\n      confidence: high\n  - pattern:\n      name: Public Key Cryptography System (PKCS)\n      regex: \"protocol=\\\"application/x-pkcs[0-9]{0,2}-signature\\\"\"\n      confidence: high\n  - pattern:\n      name: Public encrypted key\n      regex: \"-----BEGIN PUBLIC KEY-----(?:.|\\\\s)+?-----END PUBLIC KEY-----\"\n      confidence: high\n  - pattern:\n      name: RSA Private Key\n      regex: \"-----BEGIN RSA PRIVATE KEY-----(?:[a-zA-Z0-9\\\\+\\\\=\\\\/\\\"']|\\\\s)+?-----END RSA PRIVATE KEY-----\"\n      confidence: high\n  - pattern:\n      name: SSL Certificate\n      regex: \"-----BEGIN CERTIFICATE-----(?:.|\\\\n)+?\\\\s-----END CERTIFICATE-----\"\n      confidence: high\n  - pattern:\n      name: SWIFT Codes\n      regex: \"[A-Za-z]{4}(?:GB|US|DE|RU|CA|JP|CN)[0-9a-zA-Z]{2,5}$\"\n      confidence: high\n  - pattern:\n      name: Samba Password config file\n      regex: \"[a-z]*:\\\\d{3}:[0-9a-zA-Z]*:[0-9a-zA-Z]*:\\\\[U\\\\ \\\\]:.*\"\n      confidence: high\n  - pattern:\n      name: Slack 2FA Backup Codes\n      regex: \"Two-Factor\\\\s*\\\\S*Authentication\\\\s*\\\\S*Backup\\\\s*\\\\S*Codes(?:.|\\\\n)*[Ss]lack(?:.|\\\\n)*\\\\d{9}\"\n      confidence: high\n  - pattern:\n      name: UK Drivers License Numbers\n      regex: \"[A-Z]{5}\\\\d{6}[A-Z]{2}\\\\d{1}[A-Z]{2}\"\n      confidence: high\n  - pattern:\n      name: UK Passport Number\n      regex: \"\\\\d{10}GB[RP]\\\\d{7}[UMF]{1}\\\\d{9}\"\n      confidence: high\n  - pattern:\n      name: USBank Routing Numbers - California\n      regex: \"^12(?:1122676|2235821)$\"\n      confidence: high\n  - pattern:\n      name: United Bank Routing Number - California\n      regex: \"^122243350$\"\n      confidence: high\n  - pattern:\n      name: Wells Fargo Routing Numbers - California\n      regex: \"^121042882$\"\n      confidence: high\n  - pattern:\n      name: aws_access_key\n      regex: \"((access[-_]?key[-_]?id)|(ACCESS[-_]?KEY[-_]?ID)|([Aa]ccessKeyId)|(access[_-]?id)).{0,20}AKIA[a-zA-Z0-9+/]{16}[^a-zA-Z0-9+/]\"\n      confidence: high\n  - pattern:\n      name: aws_credentials_context\n      regex: \"access_key_id|secret_access_key|AssetSync.configure\"\n      confidence: high\n  - pattern:\n      name: aws_secret_key\n      regex: \"((secret[-_]?access[-_]?key)|(SECRET[-_]?ACCESS[-_]?KEY|(private[-_]?key))|([Ss]ecretAccessKey)).{0,20}[^a-zA-Z0-9+/][a-zA-Z0-9+/]{40}\\\\b\"\n      confidence: high\n  - pattern:\n      name: facebook_secret\n      regex: \"(facebook_secret|FACEBOOK_SECRET|facebook_app_secret|FACEBOOK_APP_SECRET)[a-z_ =\\\\s\\\"'\\\\:]{0,5}[^a-zA-Z0-9][a-f0-9]{32}[^a-zA-Z0-9]\"\n      confidence: high\n  - pattern:\n      name: github_key\n      regex: \"(GITHUB_SECRET|GITHUB_KEY|github_secret|github_key|github_token|GITHUB_TOKEN|github_api_key|GITHUB_API_KEY)[a-z_ =\\\\s\\\"'\\\\:]{0,10}[^a-zA-Z0-9][a-zA-Z0-9]{40}[^a-zA-Z0-9]\"\n      confidence: high\n  - pattern:\n      name: google_two_factor_backup\n      regex: \"(?:BACKUP VERIFICATION CODES|SAVE YOUR BACKUP CODES)[\\\\s\\\\S]{0,300}@\"\n      confidence: high\n  - pattern:\n      name: heroku_key\n      regex: \"(heroku_api_key|HEROKU_API_KEY|heroku_secret|HEROKU_SECRET)[a-z_ =\\\\s\\\"'\\\\:]{0,10}[^a-zA-Z0-9-]\\\\w{8}(?:-\\\\w{4}){3}-\\\\w{12}[^a-zA-Z0-9\\\\-]\"\n      confidence: high\n  - pattern:\n      name: microsoft_office_365_oauth_context\n      regex: \"https://login.microsoftonline.com/common/oauth2/v2.0/token|https://login.windows.net/common/oauth2/token\"\n      confidence: high\n  - pattern:\n      name: pgSQL Connection Information\n      regex: \"(?:postgres|pgsql)\\\\:\\\\/\\\\/\"\n      confidence: high\n  - pattern:\n      name: slack_api_key\n      regex: \"(slack_api_key|SLACK_API_KEY|slack_key|SLACK_KEY)[a-z_ =\\\\s\\\"'\\\\:]{0,10}[^a-f0-9][a-f0-9]{32}[^a-f0-9]\"\n      confidence: high\n  - pattern:\n      name: slack_api_token\n      regex: \"(xox[pb](?:-[a-zA-Z0-9]+){4,})\"\n      confidence: high\n  - pattern:\n      name: ssh_dss_public\n      regex: \"ssh-dss [0-9A-Za-z+/]+[=]{2}\"\n      confidence: high\n  - pattern:\n      name: ssh_rsa_public\n      regex: \"ssh-rsa AAAA[0-9A-Za-z+/]+[=]{0,3} [^@]+@[^@]+\"\n      confidence: high\n  - pattern:\n      name: IBAN\n      regex: '[a-zA-Z]{2}[0-9]{2}[a-zA-Z0-9]{4}[0-9]{7}([a-zA-Z0-9]?){0,16}'\n      confidence: high\n  - pattern:\n      name: GPS Data\n      regex: '^([-+]?)([\\d]{1,2})(((\\.)(\\d+)(,)))(\\s*)(([-+]?)([\\d]{1,3})((\\.)(\\d+))?)'\n      confidence: high\n  - pattern:\n      name: Blood Type\n      regex: '^(A|B|AB|O)[-+]$'\n      confidence: high\n  - pattern:\n      name: Date of Birth  - 2\n      regex: '^([1-9]|[12][0-9]|3[01])(\\/?\\.\\-?\\-?\\s?)(0[1-9]|1[12])(\\/?\\.?\\-?\\s?)(19[0-9][0-9]|20[0][0-9]|20[1][0-8])$'\n      confidence: high\n  - pattern:\n      name: Tax Number\n      regex: '^[0-9]{10}$'\n      confidence: high\n  - pattern:\n      name: Bitcoin Address\n      regex: '^[13][a-km-zA-HJ-NP-Z0-9]{26,33}$'\n      confidence: high\n"
  },
  {
    "path": "core/src/main/resources/tokenizers-engine.properties",
    "content": "tokenizers_version=0.19.1-0.28.0"
  },
  {
    "path": "core/src/test/kotlin/com/phodal/shirecore/diff/model/StreamDiffTest.kt",
    "content": "package com.phodal.shirecore.diff.model\n\nimport kotlinx.coroutines.flow.flowOf\nimport kotlinx.coroutines.flow.toList\nimport kotlinx.coroutines.runBlocking\nimport org.junit.Assert.assertEquals\nimport org.junit.Test\n\nclass StreamDiffTest {\n    @Test\n    fun `add multiple lines`() = runBlocking {\n        val oldLines = listOf(\"first item\", \"fourth val\")\n        val newLines = flowOf(\"first item\", \"second arg\", \"third param\", \"fourth val\")\n\n        val diffResults = streamDiff(oldLines, newLines).toList()\n\n        assertEquals(\n            listOf(\n                DiffLine.Same(\"first item\"),\n                DiffLine.New(\"second arg\"),\n                DiffLine.New(\"third param\"),\n                DiffLine.Same(\"fourth val\")\n            ),\n            diffResults\n        )\n    }\n\n    @Test\n    fun `remove multiple lines`() = runBlocking {\n        val oldLines = listOf(\"first item\", \"second arg\", \"third param\", \"fourth val\")\n        val newLines = flowOf(\"first item\", \"fourth val\")\n\n        val diffResults = streamDiff(oldLines, newLines).toList()\n\n        assertEquals(\n            listOf(\n                DiffLine.Same(\"first item\"),\n                DiffLine.Old(\"second arg\"),\n                DiffLine.Old(\"third param\"),\n                DiffLine.Same(\"fourth val\")\n            ),\n            diffResults\n        )\n    }\n\n    @Test\n    fun `empty old lines`() = runBlocking {\n        val oldLines = emptyList<String>()\n        val newLines = flowOf(\"first item\", \"second arg\")\n\n        val diffResults = streamDiff(oldLines, newLines).toList()\n\n        assertEquals(\n            listOf(\n                DiffLine.New(\"first item\"),\n                DiffLine.New(\"second arg\")\n            ),\n            diffResults\n        )\n    }\n\n    @Test\n    fun `empty new lines`() = runBlocking {\n        val oldLines = listOf(\"first item\", \"second arg\")\n        val newLines = flowOf<String>() // 空的新行流\n\n        val diffResults = streamDiff(oldLines, newLines).toList()\n\n        assertEquals(\n            listOf(\n                DiffLine.Old(\"first item\"),\n                DiffLine.Old(\"second arg\")\n            ),\n            diffResults\n        )\n    }\n}\n"
  },
  {
    "path": "core/src/test/kotlin/com/phodal/shirecore/guard/model/SecretPatternsScannerTest.kt",
    "content": "package com.phodal.shirecore.guard.model\n\nimport com.intellij.testFramework.LightPlatformTestCase\nimport com.phodal.shirecore.function.guard.model.SecretPattern\nimport com.phodal.shirecore.function.guard.scanner.SecretPatternsScanner\n\n/**\n * Unit tests for the SecretPatternsManager class.\n */\nclass SecretPatternsScannerTest : LightPlatformTestCase() {\n\n    //    fun `should add a new pattern to the list of patterns`() {\n    fun testShouldAddNewPatternToListOfPatterns() {\n        val secretPatterns: SecretPatternsScanner = SecretPatternsScanner(project)\n        // Given\n        val newPattern = SecretPattern(\"Email\", \"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\\\.[a-zA-Z]{2,}\", \"medium\")\n\n        // When\n        secretPatterns.addPattern(newPattern)\n        val updatedPatterns = secretPatterns.getPatterns()\n\n        // Then\n        assertTrue(updatedPatterns.contains(newPattern))\n    }\n\n    //    fun `should remove a pattern from the list of patterns`() {\n    fun testShouldRemovePatternFromListOfPatterns() {\n        val secretPatterns: SecretPatternsScanner = SecretPatternsScanner(project)\n        val patternToRemove = SecretPattern(\"Credit Card\", \"[0-9]{4} [0-9]{4} [0-9]{4} [0-9]{4}\", \"high\")\n\n        // When\n        secretPatterns.removePattern(patternToRemove)\n        val remainingPatterns = secretPatterns.getPatterns()\n\n        // Then\n        assertFalse(remainingPatterns.contains(patternToRemove))\n    }\n\n    //    fun `should find patterns that match the text`() {\n    fun testShouldFindPatternsThatMatchText() {\n        val secretPatterns: SecretPatternsScanner = SecretPatternsScanner(project)\n        val testText = \"My email is example@example.com and my phone number is 123-456-7890.\"\n\n        // When\n        val output = secretPatterns.maskInput(testText)\n\n        // Then\n        assertEquals(\"My email is **** and my phone number is ****.\", output)\n    }\n}\n"
  },
  {
    "path": "core/src/test/kotlin/com/phodal/shirecore/middleware/builtin/SaveFileProcessorTest.kt",
    "content": "package com.phodal.shirecore.middleware.builtin\n\nimport com.intellij.testFramework.fixtures.BasePlatformTestCase\n\nclass FileNameTest : BasePlatformTestCase() {\n    fun testReturnTimestampWithExtensionWhenFilePathIsBlank() {\n        val ext = \"txt\"\n        val filePath = \"\"\n        val result = getValidFilePath(filePath, ext)\n\n        // Check if the result matches a timestamp followed by the correct extension\n        assertTrue(result.matches(Regex(\"\"\"\\d+\\.txt\"\"\")))\n    }\n\n    fun testReturnValidFilePathIfItMatchesRegex() {\n        val ext = \"txt\"\n        val filePath = \"C:\\\\Users\\\\John\\\\Documents\\\\file.txt\"\n        val result = getValidFilePath(filePath, ext)\n\n        // Since the path is valid, it should return the same file path\n        assertEquals(filePath, result)\n    }\n\n    // \"docs/api.yml\"\n    fun testReturnValidFilePathIfItMatchesRegexForLinux() {\n        val ext = \"yml\"\n        val filePath = \"docs/api.yml\"\n        val result = getValidFilePath(filePath, ext)\n\n        // Since the path is valid, it should return the same file path\n        assertEquals(filePath, result)\n    }\n\n    fun testReturnTimestampWithExtensionIfPathIsInvalid() {\n        val ext = \"txt\"\n        val filePath = \"Invalid\\\\Path\\\\test\"\n        val result = getValidFilePath(filePath, ext)\n\n        assertEquals(result, filePath)\n    }\n\n    fun testReturnTimestampWithExtensionIfPathIsInvalidForLinux() {\n        val ext = \"txt\"\n        val filePath = \"/home/user/Documents/file.shire\"\n        val result = getValidFilePath(filePath, ext)\n\n        assertEquals(result, filePath)\n    }\n\n    fun testReturnParsedTextWhenFilePathContainsCodeFence() {\n        val ext = \"txt\"\n        val filePath = \"```\\nsome code block\\n```\"\n        val parsedText = \"some code block\"\n\n        // Mock or simulate the CodeFence.parse method\n        val result = getValidFilePath(filePath, ext)\n\n        // Assuming the CodeFence.parse method extracts \"some code block\"\n        assertEquals(parsedText, result)\n    }\n}\n"
  },
  {
    "path": "core/src/test/kotlin/com/phodal/shirecore/provider/impl/MarkdownPsiContextVariableProviderTest.kt",
    "content": "package com.phodal.shirecore.provider.impl\n\nimport com.intellij.testFramework.fixtures.BasePlatformTestCase\n\nclass MarkdownPsiContextVariableProviderTest: BasePlatformTestCase() {\n    fun testShouldSuccessParseMarkdownHeading() {\n        val markdownText = \"\"\"# Hello World\n            | sample\n            | ## h2\n            | ### h3\n            | #### h4\n        \"\"\".trimMargin()\n        val html = MarkdownPsiContextVariableProvider().toHtml(markdownText)\n\n        assertEquals(\"# Hello World\\n##  h2\\n###  h3\\n####  h4\", html)\n    }\n}"
  },
  {
    "path": "core/src/test/kotlin/com/phodal/shirecore/search/TfIdfTest.kt",
    "content": "package com.phodal.shirecore.search\n\nimport com.phodal.shirecore.search.algorithm.TfIdf\nimport junit.framework.TestCase.assertEquals\nimport org.junit.Test\n\nclass TfIdfTest {\n\n    @Test\n    fun shouldBuildDocumentFromString() {\n        // given\n        val tfIdf = TfIdf<String, Any>()\n        val text = \"apple orange apple banana\"\n        val expectedDocument = mapOf(\"apple\" to 2, \"orange\" to 1, \"banana\" to 1)\n\n        // when\n        val document = tfIdf.buildDocument(text)\n\n        // then\n        assertEquals(expectedDocument, document)\n    }\n\n    @Test\n    fun shouldReturnTfIdfValuesForAGivenQuery() {\n        // given\n        val tfIdf = TfIdf<String, Any>()\n        val chunks = listOf(\"chunk1\", \"chunk2\", \"chunk3\")\n        val query = \"chunk1\"\n        tfIdf.addDocuments(chunks)\n\n        // when\n        val result = tfIdf.search(query)\n\n        // then\n        assertEquals(3, result.size)\n    }\n\n    @Test\n    fun shouldExecuteTheCallbackFunctionIfProvided() {\n        // given\n        val tfIdf = TfIdf<String, Any>()\n        val chunks = listOf(\"chunk1\", \"chunk2\", \"chunk3\")\n        val query = \"chunk1\"\n        tfIdf.addDocuments(chunks)\n\n        // when\n        tfIdf.search(query)\n    }\n}\n"
  },
  {
    "path": "core/src/test/kotlin/com/phodal/shirecore/search/algorithm/BM25SimilarityTest.kt",
    "content": "package com.phodal.shirecore.search.algorithm\n\nimport junit.framework.TestCase.*\nimport org.junit.Test\nimport kotlin.math.log10\n\n/**\n * Unit tests for BM25Similarity class.\n */\nclass BM25SimilarityTest {\n    @Test\n    fun testComputeInputSimilarity() {\n        val bm25 = BM25Similarity()\n\n        val query = \"sample query\"\n        val chunks = listOf(\n            listOf(\"this\", \"is\", \"a\", \"sample\", \"document\"),\n            listOf(\"this\", \"document\", \"is\", \"another\", \"sample\"),\n            listOf(\"one\", \"more\", \"sample\", \"document\")\n        )\n\n        val similarity = bm25.computeInputSimilarity(query, chunks)\n\n        assertNotNull(similarity)\n        assertEquals(3, similarity.size) // We have 3 documents\n        similarity.forEach { docSim ->\n            assertEquals(2, docSim.size) // Our query has 2 terms\n        }\n\n        // Print similarity for manual inspection (not a part of actual tests)\n        similarity.forEachIndexed { index, docSim ->\n            println(\"Document $index: $docSim\")\n        }\n    }\n\n    @Test\n    fun testComputeIDF() {\n        val bm25 = BM25Similarity()\n\n        val chunks = listOf(\n            listOf(\"this\", \"is\", \"a\", \"sample\", \"document\"),\n            listOf(\"this\", \"document\", \"is\", \"another\", \"sample\"),\n            listOf(\"one\", \"more\", \"sample\", \"document\")\n        )\n        val docCount = chunks.size\n\n        val idfMap = bm25.computeIDF(chunks, docCount)\n\n        assertNotNull(idfMap)\n        assertTrue(idfMap.isNotEmpty())\n        assertEquals(8, idfMap.size) // There are 8 unique terms\n\n        // Validate some IDF values manually\n        val expectedIDFThis = log10((docCount - 2 + 0.5) / (2 + 0.5) + 1.0)\n        val expectedIDFSample = log10((docCount - 3 + 0.5) / (3 + 0.5) + 1.0)\n        val expectedIDFOne = log10((docCount - 1 + 0.5) / (1 + 0.5) + 1.0)\n\n        assertEquals(expectedIDFThis, idfMap[\"this\"])\n        assertEquals(expectedIDFSample, idfMap[\"sample\"])\n        assertEquals(expectedIDFOne, idfMap[\"one\"])\n\n        // Print IDF map for manual inspection (not a part of actual tests)\n        idfMap.forEach { (term, idf) ->\n            println(\"Term: $term, IDF: $idf\")\n        }\n    }\n\n    @Test\n    fun `should compute similarity for query and documents correctly`() {\n        val similarity = BM25Similarity()\n        val chunks = listOf(\n            listOf(\"apple\", \"banana\", \"apple\"),\n            listOf(\"banana\", \"cherry\"),\n            listOf(\"apple\", \"cherry\")\n        )\n        val query = \"apple banana\"\n\n        val result = similarity.computeInputSimilarity(query, chunks)\n\n        assertNotNull(result)\n        assertEquals(3, result.size) // Ensure one result per document\n        assertEquals(2, result[0].size) // Ensure one score per term in the query\n\n        // Check if the computed similarity values are non-negative\n        val allPositive = result.flatten().all { it >= 0.0 }\n        assertTrue(allPositive)\n    }\n\n    @Test\n    fun `should handle query term not present in documents`() {\n        val similarity = BM25Similarity()\n        val chunks = listOf(\n            listOf(\"apple\", \"banana\", \"apple\"),\n            listOf(\"banana\", \"cherry\"),\n            listOf(\"apple\", \"cherry\")\n        )\n        val query = \"apple orange\"\n\n        val result = similarity.computeInputSimilarity(query, chunks)\n\n        assertNotNull(result)\n        assertEquals(3, result.size) // Ensure one result per document\n        assertEquals(2, result[0].size) // Ensure one score per term in the query\n\n        // Check if the score for the term 'orange' is 0.0 as it is not present in the documents\n        assertEquals(0.0, result[0][1])\n    }\n}\n"
  },
  {
    "path": "core/src/test/kotlin/com/phodal/shirecore/search/algorithm/JaccardSimilarityTest.kt",
    "content": "package com.phodal.shirecore.search.algorithm\n\nimport org.junit.Test\nimport junit.framework.TestCase.assertEquals\n\nclass JaccardSimilarityTest {\n    @Test\n    fun should_calculate_token_level_jaccard_similarity() {\n        // given\n        val jaccardSimilarity = JaccardSimilarity()\n        val query = \"test query\"\n        val chunks = listOf(listOf(\"test\", \"query\"), listOf(\"another\", \"test\"), listOf(\"query\", \"test\"))\n\n        // when\n        val result = jaccardSimilarity.computeInputSimilarity(query, chunks)\n\n        // then\n        assertEquals(3, result.size)\n        assertEquals(0.5, result[0][0])\n        assertEquals(0.0, result[1][0])\n        assertEquals(0.5, result[2][0])\n    }\n\n    @Test\n    fun should_return_a_set_of_tokens() {\n        // given\n        val jaccardSimilarity = JaccardSimilarity()\n        val input = \"test input\"\n\n        // when\n        val result = jaccardSimilarity.tokenize(input)\n\n        // then\n        assertEquals(2, result.size)\n    }\n\n    fun should_return_correct_similarity_score() {\n        // given\n        val jaccardSimilarity = JaccardSimilarity()\n        val set1 = setOf(\"test\", \"query\")\n        val set2 = setOf(\"query\", \"another\")\n\n        // when\n        val result = jaccardSimilarity.similarityScore(set1, set2)\n\n        // then\n        assertEquals(0.33, result, 0.01)\n    }\n\n    @Test\n    fun should_return_the_correct_similarity_score() {\n        // given\n        val jaccardSimilarity = JaccardSimilarity()\n        val path1 = \"folder1/folder2/file1\"\n        val set2 = setOf(\"folder1\", \"folder2\", \"file2\")\n        val expectedScore: Double = 2.0 / 3.0\n\n        // when\n        val actualScore = jaccardSimilarity.pathSimilarity(path1, set2)\n\n        // then\n        assertEquals(expectedScore, actualScore)\n    }\n}\n"
  },
  {
    "path": "core/src/test/kotlin/com/phodal/shirecore/search/function/LocalEmbeddingTest.kt",
    "content": "package com.phodal.shirecore.search.function\n\nimport com.phodal.shirecore.search.indices.normalized\nimport org.junit.Assert.assertArrayEquals\nimport org.junit.Test\n\nclass FloatArrayExtensionTest {\n\n    @Test\n    fun should_return_normalized_float_array() {\n        // Given\n        val inputArray = floatArrayOf(1.0f, 2.0f, 3.0f)\n\n        // When\n        val normalizedArray = inputArray.normalized()\n\n        // Then\n        val expectedArray = floatArrayOf(0.26726124f, 0.5345225f, 0.8017837f)\n        assertArrayEquals(expectedArray, normalizedArray, 0.0001f)\n    }\n}\n"
  },
  {
    "path": "core/src/test/kotlin/com/phodal/shirecore/search/tokenizer/TermSplitterTest.kt",
    "content": "package com.phodal.shirecore.search.tokenizer\n\nimport com.phodal.shirecore.search.tokenizer.TermSplitter.splitTerms\nimport junit.framework.TestCase.assertEquals\nimport org.junit.Test\n\nclass TermSplitterTest {\n\n    @Test\n    fun should_splitTerms_when_inputIsCamelCase() {\n        // Given\n        val input = \"HelloWorld_helloWorld123\"\n\n        // When\n        val result = splitTerms(input).toList()\n\n        // Then\n        val expected = listOf(\n            \"helloworld_helloworld123\",\n            \"hello\",\n            \"world_hello\",\n            \"world123\",\n            \"helloworld\",\n            \"helloworld123\",\n            \"helloworld_helloworld\"\n        )\n        assertEquals(expected, result)\n    }\n\n    @Test\n    fun should_splitTerms_when_inputIsUnderscoreCase() {\n        // Given\n        val input = \"underscore_case_example\"\n\n        // When\n        val result = splitTerms(input).toList()\n\n        // Then\n        val expected = listOf(\"underscore_case_example\", \"underscore\", \"case\", \"example\")\n        assertEquals(expected, result)\n    }\n\n    @Test\n    fun should_splitTerms_when_inputContainsNumericSuffix() {\n        // Given\n        val input = \"example123\"\n\n        // When\n        val result = splitTerms(input).toList()\n\n        // Then\n        val expected = listOf(\"example123\", \"example\")\n        assertEquals(expected, result)\n    }\n\n    @Test\n    fun should_splitTerms_when_inputIsMixedNamingStyles() {\n        // Given\n        val input = \"CamelCase_with123Numbers\"\n\n        // When\n        val result = splitTerms(input).toList()\n\n        // Then\n        val expected = listOf(\"camelcase_with123numbers\", \"camel\", \"case_with123numbers\", \"camelcase\", \"with123numbers\")\n        assertEquals(expected, result)\n    }\n\n    @Test\n    fun should_syncSplitTerms_when_inputIsCamelCase() {\n        // Given\n        val input = \"CamelCaseExample\"\n\n        // When\n        val result = splitTerms(input).toList()\n\n        // Then\n        val expected = listOf(\"camelcaseexample\", \"camel\", \"case\", \"example\")\n        assertEquals(expected, result)\n    }\n\n    @Test\n    fun should_syncSplitTerms_when_inputIsUnderscoreCase() {\n        // Given\n        val input = \"underscore_case_example\"\n\n        // When\n        val result = splitTerms(input).toList()\n\n        // Then\n        val expected = listOf(\"underscore_case_example\", \"underscore\", \"case\", \"example\")\n        assertEquals(expected, result)\n    }\n\n    @Test\n    fun should_syncSplitTerms_when_inputContainsNumericSuffix() {\n        // Given\n        val input = \"example123\"\n\n        // When\n        val result = splitTerms(input).toList()\n\n        // Then\n        val expected = listOf(\"example123\", \"example\")\n        assertEquals(expected, result)\n    }\n\n    @Test\n    fun should_handle_java_controller_in_real_world() {\n        // Given\n        val input = \"\"\"\n    @PostMapping\n    @ResponseStatus(code = HttpStatus.CREATED)\n    public String postTicket(@RequestBody TicketCreateRequest ticketCreateRequest){\n        Ticket ticket = ticketMapper.toEntity(ticketCreateRequest);\n        List<Food> foods = ticketCreateRequest.getFood().stream()\n                .map((foodId) -> foodService.findById(foodId))\n                .collect(Collectors.toList());\n        ticket.setFoods(foods);\n        return ticketService.postTicket(ticket);\n    }            \n    \"\"\".trimMargin()\n\n        // When\n        val result = splitTerms(input).toList()\n\n        // Then\n        val expected = listOf(\n            \"postmapping\", \"post\", \"mapping\", \"responsestatus\", \"response\", \"status\", \"code\", \"httpstatus\", \"http\", \"created\",\n            \"public\", \"string\", \"postticket\", \"ticket\", \"requestbody\", \"request\", \"body\", \"ticketcreaterequest\", \"create\",\n            \"ticketmapper\", \"mapper\", \"toentity\", \"entity\", \"list\", \"food\", \"foods\", \"getfood\", \"get\", \"stream\", \"map\",\n            \"foodid\", \"foodservice\", \"service\", \"findbyid\", \"find\", \"collect\", \"collectors\", \"tolist\", \"setfoods\", \"set\",\n            \"return\", \"ticketservice\"\n        )\n        assertEquals(expected, result)\n    }\n}\n"
  },
  {
    "path": "core/src/test/kotlin/com/phodal/shirecore/task/GraphTest.kt",
    "content": "package com.phodal.shirecore.task\n\nimport org.junit.Assert.*\nimport org.junit.Test\n\n\nclass GraphTest {\n\n    @Test\n    fun should_addNode_toGraph_when_addNode() {\n        // given\n        val graph = Graph()\n        val node = Node(1, \"Node1\")\n\n        // when\n        graph.addNode(node)\n\n        // then\n        assertEquals(listOf(node), graph.getNodes())\n    }\n\n    @Test\n    fun should_addEdge_toGraph_when_addEdge() {\n        // given\n        val graph = Graph()\n        val node1 = Node(1, \"Node1\")\n        val node2 = Node(2, \"Node2\")\n        val edge = Edge(node1, node2)\n\n        // when\n        graph.addNode(node1)\n        graph.addNode(node2)\n        graph.addEdge(edge)\n\n        // then\n        assertEquals(listOf(edge), graph.getEdges())\n    }\n\n    @Test\n    fun should_returnTopologicalSort_when_topologicalSort() {\n        // given\n        val graph = Graph()\n        val node1 = Node(1, \"Node1\")\n        val node2 = Node(2, \"Node2\")\n        val node3 = Node(3, \"Node3\")\n        val edge1 = Edge(node1, node2)\n        val edge2 = Edge(node2, node3)\n\n        graph.addNode(node1)\n        graph.addNode(node2)\n        graph.addNode(node3)\n        graph.addEdge(edge1)\n        graph.addEdge(edge2)\n\n        // when\n        val result = graph.topologicalSort(graph)\n\n        // then\n        assertEquals(listOf(node1, node2, node3), result)\n    }\n\n    @Test\n    fun should_returnTrue_when_hasCycle() {\n        // given\n        val graph = Graph()\n        val node1 = Node(1, \"Node1\")\n        val node2 = Node(2, \"Node2\")\n        val node3 = Node(3, \"Node3\")\n        val edge1 = Edge(node1, node2)\n        val edge2 = Edge(node2, node3)\n        val edge3 = Edge(node3, node1)\n\n        graph.addNode(node1)\n        graph.addNode(node2)\n        graph.addNode(node3)\n        graph.addEdge(edge1)\n        graph.addEdge(edge2)\n        graph.addEdge(edge3)\n\n        // when\n        val result = graph.hasCycle(graph)\n\n        // then\n        assertTrue(result)\n    }\n\n    @Test\n    fun should_returnFalse_when_noCycle() {\n        // given\n        val graph = Graph()\n        val node1 = Node(1, \"Node1\")\n        val node2 = Node(2, \"Node2\")\n        val node3 = Node(3, \"Node3\")\n        val edge1 = Edge(node1, node2)\n        val edge2 = Edge(node2, node3)\n\n        graph.addNode(node1)\n        graph.addNode(node2)\n        graph.addNode(node3)\n        graph.addEdge(edge1)\n        graph.addEdge(edge2)\n\n        // when\n        val result = graph.hasCycle(graph)\n\n        // then\n        assertFalse(result)\n    }\n}\n"
  },
  {
    "path": "core/src/test/kotlin/com/phodal/shirecore/utils/markdown/CodeFenceTest.kt",
    "content": "package com.phodal.shirecore.utils.markdown\n\nimport com.intellij.testFramework.fixtures.BasePlatformTestCase\nimport kotlin.collections.get\n\nclass CodeFenceTest : BasePlatformTestCase() {\n\n    fun testShould_handle_code_not_complete_from_markdown() {\n        val markdown = \"\"\"\n            |```java\n            |public class HelloWorld {\n            |    public static void main(String[] args) {\n            |        System.out.println(\"Hello, World\");\n        \"\"\".trimMargin()\n\n        val code = CodeFence.parse(markdown)\n        assertEquals(\n            code.text, \"\"\"\n            |public class HelloWorld {\n            |    public static void main(String[] args) {\n            |        System.out.println(\"Hello, World\");\n        \"\"\".trimMargin()\n        )\n        assertTrue(!code.isComplete)\n    }\n\n    fun testShould_handle_code_for_when_code() {\n        val markdown = \"\"\"\n            |下面是\n            |```java\n        \"\"\".trimMargin()\n\n        val code = CodeFence.parse(markdown)\n        assertEquals(\n            code.text, \"\"\"\n        \"\"\".trimMargin()\n        )\n        assertTrue(!code.isComplete)\n    }\n\n    fun testShould_handle_pure_markdown_content() {\n        val content = \"```markdown\\\\nGET /wp/v2/posts\\\\n```\"\n        val code = CodeFence.parse(content)\n        assertEquals(code.text, \"GET /wp/v2/posts\")\n    }\n\n    fun testShould_handle_http_request() {\n        val content = \"```http request\\\\nGET /wp/v2/posts\\\\n```\"\n        val code = CodeFence.parse(content)\n        assertEquals(code.text, \"GET /wp/v2/posts\")\n    }\n\n    fun testShould_parse_code_from_markdown_java_hello_world() {\n        val markdown = \"\"\"\n            |Java Hello, world\n            |```java\n            |public class HelloWorld {\n            |    public static void main(String[] args) {\n            |        System.out.println(\"Hello, World\");\n            |    }\n            |}\n            |```\n            |\n            |Python Hello, world\n            |\n            |```http request\n            |DELETE /api/blog/1\n            |Content-Type: application/json\n        \"\"\".trimMargin()\n\n        val codeFences = CodeFence.parseAll(markdown)\n\n        assertEquals(codeFences.size, 4)\n\n        val code = codeFences[1]\n\n        assertEquals(\n            code.text, \"\"\"\n            |public class HelloWorld {\n            |    public static void main(String[] args) {\n            |        System.out.println(\"Hello, World\");\n            |    }\n            |}\n        \"\"\".trimMargin()\n        )\n\n        assertTrue(code.isComplete)\n\n        val last = codeFences.last()\n        assertEquals(last.text, \"DELETE /api/blog/1\\nContent-Type: application/json\")\n        assertEquals(last.ideaLanguage.displayName, \"HTTP Request\")\n        assertEquals(false, last.isComplete)\n    }\n\n    fun testShould_parse_code_for_http_request() {\n        val markdown = \"\"\"\n            |Java Hello, world\n            |```http request\n            |GET /api\n        \"\"\".trimMargin()\n\n        val codeFences = CodeFence.parseAll(markdown)\n\n        assertEquals(codeFences.size, 2)\n\n        val last = codeFences.last()\n        assertEquals(last.text, \"GET /api\")\n        assertEquals(last.ideaLanguage.displayName, \"HTTP Request\")\n        assertEquals(false, last.isComplete)\n    }\n\n    fun testShould_parse_code_for_empty_http_request() {\n        val markdown = \"\"\"\n            |Java Hello, world\n            |```http request\n        \"\"\".trimMargin()\n\n        val codeFences = CodeFence.parseAll(markdown)\n\n        assertEquals(codeFences.size, 2)\n\n        val last = codeFences.last()\n        assertEquals(last.text, \"\")\n        assertEquals(last.ideaLanguage.displayName, \"HTTP Request\")\n        assertEquals(false, last.isComplete)\n    }\n\n    fun testSupportMultipleLanguage() {\n        val markdown = \"\"\"\n            |Java Hello, world\n            |```http request\n            |GET /api\n            |```\n            |HELLO\n        \"\"\".trimMargin()\n\n        val codeFences = CodeFence.parseAll(markdown)\n\n        assertEquals(codeFences.size, 3)\n\n        val last = codeFences.last()\n        assertEquals(last.text, \"HELLO\")\n        assertEquals(last.ideaLanguage.displayName, \"Markdown\")\n        assertEquals(true, last.isComplete)\n    }\n\n\n    fun testShouldParseHtmlCode() {\n        val content = \"\"\"\n// patch to call tools for step 3 with DevIns language, should use DevIns code fence\n<shire>\n/patch:src/main/index.html\n```patch\n// the index.html code\n```\n</shire>\n\"\"\".trimIndent()\n        val code = CodeFence.parse(content)\n        assertEquals(\n            code.text, \"\"\"\n/patch:src/main/index.html\n```patch\n// the index.html code\n```\n\"\"\".trimIndent()\n        )\n        assertTrue(code.isComplete)\n    }\n\n    fun testShouldParseUndoneHtmlCode() {\n        val content = \"\"\"\n// patch to call tools for step 3 with DevIns language, should use DevIns code fence\n<shire>\n/patch:src/main/index.html\n```patch\n// the index.html code\n```\n\"\"\".trimIndent()\n        val code = CodeFence.parse(content)\n        assertFalse(code.isComplete)\n        assertEquals(\n            code.text, \"\"\"\n/patch:src/main/index.html\n```patch\n// the index.html code\n```\n\"\"\".trimIndent()\n        )\n    }\n\n    /// parse all with shires\n    fun testShouldParseAllWithDevin() {\n        val content = \"\"\"\n            |<shire>\n            |// the index.html code\n            |</shire>\n            |\n            |```java\n            |public class HelloWorld {\n            |    public static void main(String[] args) {\n            |        System.out.println(\"Hello, World\");\n            |    }\n            |}\n            |```\n        \"\"\".trimMargin()\n\n        val codeFences = CodeFence.parseAll(content)\n        assertEquals(codeFences.size, 3)\n        assertEquals(\n            codeFences[0].text, \"\"\"\n            |// the index.html code\n        \"\"\".trimMargin()\n        )\n        assertTrue(codeFences[0].isComplete)\n        assertEquals(\n            codeFences[2].text, \"\"\"\n            |public class HelloWorld {\n            |    public static void main(String[] args) {\n            |        System.out.println(\"Hello, World\");\n            |    }\n            |}\n        \"\"\".trimMargin()\n        )\n        assertTrue(codeFences[2].isComplete)\n    }\n}\n"
  },
  {
    "path": "core/src/test/resources/META-INF/plugin.xml",
    "content": "<idea-plugin package=\"com.phodal.shire\" xmlns:xi=\"http://www.w3.org/2001/XInclude\">\n    <id>com.phodal.shire</id>\n\n    <xi:include href=\"/META-INF/com.phodal.shirecore.xml\" xpointer=\"xpointer(/idea-plugin/*)\"/>\n    <!--suppress PluginXmlValidity -->\n\n    <extensions defaultExtensionNs=\"com.phodal\">\n        <shireSymbolProvider implementation=\"com.phodal.shirelang.impl.DefaultShireSymbolProvider\" />\n    </extensions>\n</idea-plugin>\n"
  },
  {
    "path": "docs/CNAME",
    "content": "shire.phodal.com"
  },
  {
    "path": "docs/_config.yml",
    "content": "remote_theme: pmarsceill/just-the-docs\n\ntitle: Shire - AI coding agent language\ndescription: Shire offers a straightforward AI coding agent language that enables communication between an LLM and control IDE for automated programming.\n\nheading_anchors: true\n\nfooter_content: \"This code is distributed under the MPL 2.0 license. See `LICENSE` in this directory.\"\n\n# Footer last edited timestamp\nlast_edit_timestamp: true # show or hide edit time - page must have `last_modified_date` defined in the frontmatter\nlast_edit_time_format: \"%b %e %Y at %I:%M %p\" # uses ruby's time format: https://ruby-doc.org/stdlib-2.7.0/libdoc/time/rdoc/Time.html\n\n# Footer \"Edit this page on GitHub\" link text\ngh_edit_link: true # show or hide edit this page link\ngh_edit_link_text: \"Edit this page on GitHub.\"\ngh_edit_repository: \"https://github.com/phodal/shire\" # the github URL for your repo\ngh_edit_branch: \"master\" # the branch that your docs are served from\ngh_edit_source: docs # the source that your files originate from\ngh_edit_view_mode: \"tree\" # \"tree\" or \"edit\" if you want the user to jump into the editor immediately\n\ncallouts_level: quiet # or loud\ncallouts:\n  highlight:\n    color: yellow\n  important:\n    title: Important\n    color: blue\n  new:\n    title: New\n    color: green\n  note:\n    title: Note\n    color: purple\n  warning:\n    title: Warning\n    color: red\n\n# Enable or disable the site search\n# Supports true (default) or false\nsearch_enabled: true\n\nsearch:\n  # Split pages into sections that can be searched individually\n  # Supports 1 - 6, default: 2\n  heading_level: 4\n  # Maximum amount of previews per search result\n  # Default: 3\n  previews: 3\n  # Maximum amount of words to display before a matched word in the preview\n  # Default: 5\n  preview_words_before: 5\n  # Maximum amount of words to display after a matched word in the preview\n  # Default: 10\n  preview_words_after: 10\n  # Set the search token separator\n  # Default: /[\\s\\-/]+/\n  # Example: enable support for hyphenated search words\n  tokenizer_separator: /[\\s/]+/\n  # Display the relative url in search results\n  # Supports true (default) or false\n  rel_url: true\n  # Enable or disable the search button that appears in the bottom right corner of every page\n  # Supports true or false (default)\n  button: false\n\n\n# Back to top link\nback_to_top: true\nback_to_top_text: \"Back to top\"\n\n# Google Analytics Tracking (optional)\n# e.g, UA-1234567-89\n#ga_tracking: UA-1234567-89\nga_tracking_anonymize_ip: true # Use GDPR compliant Google Analytics settings (true by default)\n\nurl: \"https://shire.phodal.com\" # the base hostname & protocol for your site\n\n# Aux links for the upper right navigation\naux_links:\n  \"View in on GitHub\":\n    - \"//github.com/phodal/shire\"\n\n# Makes Aux links open in a new tab. Default is false\naux_links_new_tab: true\n#\n#mermaid:\n#  # Version of mermaid library\n#  # Pick an available version from https://cdn.jsdelivr.net/npm/mermaid/\n#  version: \"10.4.0\"\n\n\nplugins:\n  - jekyll-seo-tag\n  - jekyll-sitemap\n"
  },
  {
    "path": "docs/_includes/head_custom.html",
    "content": "<script src=\"https://cdn.jsdelivr.net/gh/highlightjs/cdn-release/build/highlight.min.js\"></script>\n"
  },
  {
    "path": "docs/_includes/js/custom.js",
    "content": "function shire_lang(hljs) {\n  let regex = hljs.regex\n  const TEMPLATE_VARIABLES = {\n    className: \"template-variable\",\n    variants: [\n      { // jinja templates Ansible\n        begin: /\\{\\{/,\n        end: /\\}\\}/,\n      },\n      { // Ruby i18n\n        begin: /%\\{/,\n        end: /\\}/,\n      },\n    ],\n  }\n  const STRING = {\n    className: \"string\",\n    relevance: 0,\n    variants: [\n      {\n        begin: /'/,\n        end: /'/,\n      },\n      {\n        begin: /\"/,\n        end: /\"/,\n      },\n      // { begin: /\\S+/ }\n    ],\n    contains: [\n      hljs.BACKSLASH_ESCAPE,\n      TEMPLATE_VARIABLES,\n    ],\n  }\n  const DATE_RE = \"[0-9]{4}(-[0-9][0-9]){0,2}\"\n  const TIME_RE = \"([Tt \\\\t][0-9][0-9]?(:[0-9][0-9]){2})?\"\n  const FRACTION_RE = \"(\\\\.[0-9]*)?\"\n  const ZONE_RE = \"([ \\\\t])*(Z|[-+][0-9][0-9]?(:[0-9][0-9])?)?\"\n  const TIMESTAMP = {\n    className: \"number\",\n    begin: \"\\\\b\" + DATE_RE + TIME_RE + FRACTION_RE + ZONE_RE + \"\\\\b\",\n  }\n\n  let FRONTMATTER = {\n    className: \"meta\",\n    begin: \"^---\\\\s*$\",\n    end: \"^---\\\\s*$\",\n    contains: [\n      {\n        className: \"attr\",\n        variants: [\n          // added brackets support\n          {\n            begin: /\\w[\\w :()\\./-]*:(?=[ \\t]|$)/,\n          },\n          { // double quoted keys - with brackets\n            begin: /\"\\w[\\w :()\\./-]*\":(?=[ \\t]|$)/,\n          },\n          { // single quoted keys - with brackets\n            begin: /'\\w[\\w :()\\./-]*':(?=[ \\t]|$)/,\n          },\n        ],\n      },\n      TIMESTAMP,\n      // numbers are any valid C-style number that\n      // sit isolated from other words\n      {\n        className: \"number\",\n        begin: hljs.C_NUMBER_RE + \"\\\\b\",\n        relevance: 0,\n      },\n      STRING,\n    ],\n  }\n\n  let INLINE_HTML = {\n    begin: /<\\/?[A-Za-z_]/,\n    end: \">\",\n    subLanguage: \"xml\",\n    relevance: 0,\n  }\n  let HORIZONTAL_RULE = {\n    begin: \"^[-\\\\*]{3,}\",\n    end: \"$\",\n  }\n  let CODE = {\n    className: \"code\",\n    variants: [\n      // TODO: fix to allow these to work with sublanguage also\n      { begin: \"(`{3,})[^`](.|\\\\n)*?\\\\1`*[ ]*\" },\n      { begin: \"(~{3,})[^~](.|\\\\n)*?\\\\1~*[ ]*\" },\n      // needed to allow markdown as a sublanguage to work\n      {\n        begin: \"```\",\n        end: \"```+[ ]*$\",\n      },\n      {\n        begin: \"~~~\",\n        end: \"~~~+[ ]*$\",\n      },\n      { begin: \"`.+?`\" },\n      {\n        begin: \"(?=^( {4}|\\\\t))\",\n        // use contains to gobble up multiple lines to allow the block to be whatever size\n        // but only have a single open/close tag vs one per line\n        contains: [\n          {\n            begin: \"^( {4}|\\\\t)\",\n            end: \"(\\\\n)$\",\n          },\n        ],\n        relevance: 0,\n      },\n    ],\n  }\n  let LIST = {\n    className: \"bullet\",\n    begin: \"^[ \\t]*([*+-]|(\\\\d+\\\\.))(?=\\\\s+)\",\n    end: \"\\\\s+\",\n    excludeEnd: true,\n  }\n  let LINK_REFERENCE = {\n    begin: /^\\[[^\\n]+\\]:/,\n    returnBegin: true,\n    contains: [\n      {\n        className: \"symbol\",\n        begin: /\\[/,\n        end: /\\]/,\n        excludeBegin: true,\n        excludeEnd: true,\n      },\n      {\n        className: \"link\",\n        begin: /:\\s*/,\n        end: /$/,\n        excludeBegin: true,\n      },\n    ],\n  }\n  let URL_SCHEME = /[A-Za-z][A-Za-z0-9+.-]*/\n  let LINK = {\n    variants: [\n      // too much like nested array access in so many languages\n      // to have any real relevance\n      {\n        begin: /\\[.+?\\]\\[.*?\\]/,\n        relevance: 0,\n      },\n      // popular internet URLs\n      {\n        begin: /\\[.+?\\]\\(((data|javascript|mailto):|(?:http|ftp)s?:\\/\\/).*?\\)/,\n        relevance: 2,\n      },\n      {\n        begin: regex.concat(/\\[.+?\\]\\(/, URL_SCHEME, /:\\/\\/.*?\\)/),\n        relevance: 2,\n      },\n      // relative urls\n      {\n        begin: /\\[.+?\\]\\([./?&#].*?\\)/,\n        relevance: 1,\n      },\n      // whatever else, lower relevance (might not be a link at all)\n      {\n        begin: /\\[.*?\\]\\(.*?\\)/,\n        relevance: 0,\n      },\n    ],\n    returnBegin: true,\n    contains: [\n      {\n        // empty strings for alt or link text\n        match: /\\[(?=\\])/,\n      },\n      {\n        className: \"string\",\n        relevance: 0,\n        begin: \"\\\\[\",\n        end: \"\\\\]\",\n        excludeBegin: true,\n        returnEnd: true,\n      },\n      {\n        className: \"link\",\n        relevance: 0,\n        begin: \"\\\\]\\\\(\",\n        end: \"\\\\)\",\n        excludeBegin: true,\n        excludeEnd: true,\n      },\n      {\n        className: \"symbol\",\n        relevance: 0,\n        begin: \"\\\\]\\\\[\",\n        end: \"\\\\]\",\n        excludeBegin: true,\n        excludeEnd: true,\n      },\n    ],\n  }\n  let BOLD = {\n    className: \"strong\",\n    contains: [], // defined later\n    variants: [\n      {\n        begin: /_{2}(?!\\s)/,\n        end: /_{2}/,\n      },\n      {\n        begin: /\\*{2}(?!\\s)/,\n        end: /\\*{2}/,\n      },\n    ],\n  }\n  let ITALIC = {\n    className: \"emphasis\",\n    contains: [], // defined later\n    variants: [\n      {\n        begin: /\\*(?![*\\s])/,\n        end: /\\*/,\n      },\n      {\n        begin: /_(?![_\\s])/,\n        end: /_/,\n        relevance: 0,\n      },\n    ],\n  }\n\n  // 3 level deep nesting is not allowed because it would create confusion\n  // in cases like `***testing***` because where we don't know if the last\n  // `***` is starting a new bold/italic or finishing the last one\n  let BOLD_WITHOUT_ITALIC = hljs.inherit(BOLD, { contains: [] })\n  let ITALIC_WITHOUT_BOLD = hljs.inherit(ITALIC, { contains: [] })\n  BOLD.contains.push(ITALIC_WITHOUT_BOLD)\n  ITALIC.contains.push(BOLD_WITHOUT_ITALIC)\n\n  let CONTAINABLE = [\n    INLINE_HTML,\n    LINK,\n  ];\n\n  [\n    BOLD,\n    ITALIC,\n    BOLD_WITHOUT_ITALIC,\n    ITALIC_WITHOUT_BOLD,\n  ].forEach(m => {\n    m.contains = m.contains.concat(CONTAINABLE)\n  })\n\n  CONTAINABLE = CONTAINABLE.concat(BOLD, ITALIC)\n\n  let HEADER = {\n    className: \"section\",\n    variants: [\n      {\n        begin: \"^#{1,6}\",\n        end: \"$\",\n        contains: CONTAINABLE,\n      },\n      {\n        begin: \"(?=^.+?\\\\n[=-]{2,}$)\",\n        contains: [\n          { begin: \"^[=-]*$\" },\n          {\n            begin: \"^\",\n            end: \"\\\\n\",\n            contains: CONTAINABLE,\n          },\n        ],\n      },\n    ],\n  }\n\n  let BLOCKQUOTE = {\n    className: \"quote\",\n    begin: \"^>\\\\s+\",\n    contains: CONTAINABLE,\n    end: \"$\",\n  }\n\n  let ENTITY = {\n    //https://spec.commonmark.org/0.31.2/#entity-references\n    scope: \"literal\",\n    match: /&([a-zA-Z0-9]+|#[0-9]{1,7}|#[Xx][0-9a-fA-F]{1,6});/,\n  }\n\n  let COMMAND_KEYWORDS = [\n    \"file\",\n    \"rev\",\n    \"refactor\",\n    \"symbol\",\n    \"write\",\n    \"run\",\n    \"commit\",\n    \"file-func\",\n    \"browse\",\n    \"refactor\",\n  ]\n\n  const keywordPattern = COMMAND_KEYWORDS.join(\"|\")\n  let COMMAND = {\n    className: \"command\",\n    begin: new RegExp(`/(?:${keywordPattern})`),\n    contains: [\n      {\n        className: \"symbol\",\n        begin: /:/, // 匹配语义号\n      },\n      {\n        className: \"url\",\n        begin: /(https?:\\/\\/[^\\s]+)/, // 匹配 URL\n      },\n      {\n        className: \"file\",\n        begin: /[a-zA-Z0-9_\\-\\/\\.]+/, // 匹配文件路径\n        relevance: 0,\n      },\n      // HEAD~1\n      {\n        className: \"git-rev\",\n        begin: /HEAD~[0-9]+/,\n      },\n      {\n        className: \"string\",\n        begin: /\"/, end: /\"/, // 匹配字符串\n        contains: [\n          {\n            begin: /\\\\./, // 匹配转义字符\n          },\n        ],\n      },\n    ],\n  }\n\n  return {\n    name: \"shire\",\n    aliases: [\n      \"sre\",\n      \"shire\",\n    ],\n    keywords: {\n      keyword: COMMAND_KEYWORDS,\n    },\n    contains: [\n      COMMAND,\n      FRONTMATTER,\n      HEADER,\n      INLINE_HTML,\n      LIST,\n      BOLD,\n      ITALIC,\n      BLOCKQUOTE,\n      CODE,\n      HORIZONTAL_RULE,\n      LINK,\n      LINK_REFERENCE,\n      ENTITY,\n    ],\n  }\n}\n\ndocument.addEventListener(\"DOMContentLoaded\", (event) => {\n  document.querySelectorAll(\"pre code\").forEach((el) => {\n    let langs = hljs.listLanguages()\n    if (!langs.includes(\"shire\")) {\n      hljs.registerLanguage(\"shire\", function() {\n        console.log(\"registering shire\")\n        return shire_lang(hljs)\n      })\n      hljs.highlightAll()\n    }\n  })\n})\n"
  },
  {
    "path": "docs/_sass/custom/custom.scss",
    "content": "iframe {\n  border: none;\n}\n\n.site-title {\n  font-size: 14px !important;\n}\n\npre {\n  background: #f5f6fa; /* 代码块背景色 */\n}\n\n.hljs {\n  color: #000000; /* 普通文本 */\n  line-height: 1.5;\n\n  white-space: preserve;\n}\n\nspan.hljs-meta, span.hljs-variable {\n  color: #6897bb; /* 元数据和变量 */\n}\n\nspan.hljs-keyword {\n  color: #cc7832; /* 关键字 */\n}\n\nspan.hljs-title.class_ {\n  color: #a9b7c6; /* 类名 */\n}\n\nspan.hljs-title {\n  color: #ffc66d; /* 函数名 */\n}\n\nspan.hljs-symbol,\nspan.hljs-file,\nspan.hljs-git-rev,\nspan.hljs-string {\n  color: #6a8759; /* 字符串 */\n}\n\nspan.hljs-number {\n  color: #6897bb; /* 数字 */\n}\n\nspan.hljs-url {\n  color: #7253ed; /* URL */\n}\n\nspan.hljs-command {\n  color: #c00; /* URL */\n}\n\nspan.hljs-property, span.hljs-attr, span.hljs-params .hljs-title.class_ {\n  color: #a9b7c6; /* 属性、属性名、参数名 */\n}\n\nspan.hljs-built_in, span.hljs-literal, span.hljs-params {\n  color: #a9b7c6; /* 内置函数、文字、参数 */\n}\n\nspan.hljs-hex_number {\n  color: #6897bb; /* 十六进制数字 */\n}\n\nspan.hljs-comment {\n  color: #808080; /* 注释 */\n}\n\ncode.hljs.language-undefined {\n  color: #000000; /* 未定义语言代码 */\n}\n"
  },
  {
    "path": "docs/cloud/cloud.md",
    "content": "---\nlayout: default\ntitle: Cloud\nnav_order: 8\nhas_children: true\npermalink: /cloud\n---\n\n"
  },
  {
    "path": "docs/cloud/http-api-tool.md",
    "content": "---\nlayout: default\ntitle: Http API Tool\nnav_order: 2\nparent: Cloud\n---\n\n在 [#11](https://github.com/phodal/shire/issues/11) 中，我们引入了一个远程调用的能力，即你可以在 Shire 中调用远程 API，作为上下文\n的一部分。\n\n## Quick Start\n\n先看个例子：\n\n```shire\n---\nvariables:\n  \"demo\": /demo.md/ { thread(\".shire/toolchain/bigmodel.curl.sh\") }\n---\n\nhi\n\n$demo\n```\n\n在这个例子中，我们定义了一个变量 `demo`，我们调用 `bigmodel.curl.sh` 来获取一个远程的 API 数据。\n\n如下是 `bigmodel.curl.sh` 的内容：\n\n```shell\ncurl --location 'https://open.bigmodel.cn/api/paas/v4/chat/completions' \\\n--header 'Authorization: Bearer ${apiKey}' \\\n--header 'Content-Type: application/json' \\\n--data '{\n    \"model\": \"glm-4\",\n    \"messages\": [\n        {\n            \"role\": \"user\",\n            \"content\": \"你好\"\n        }\n    ]\n}'\n```\n\n这里我们使用了一个变量 `apiKey`，它可以通过 `*.shireEnv.json` 文件来设置\n\n```json\n{\n  \"development\": {\n    \"apiKey\": \"123456\"\n  }\n}\n```\n\n当前，只支持简单的环境变量，即上面的 `development` 为环境名，`apiKey` 为变量名。\n\n### `.shireEnv.json` 文件\n\n`.shireEnv.json` 用于存储环境变量，Shire 将会自动加载这种文件，当前只支持 `development` 环境。\n\n### cURL.sh\n\n在 Shire 中，我们使用 cURL 来调用远程 API，以简化调用的过程。 注意：\n\n- Shire 通过 JetBrains 的 HttpClient 来转换 cURL 脚本，因此，不一定支持所有的 cURL 语法。\n- Shire 只支持 `${xxx}` 形式的变量替换，不支持 `$xxx` 形式的变量替换。\n- Shire 使用 OkHttpClient 来调用远程 API，因此，不一定支持所有的 cURL 语法。\n\n### 结合 JsonPath\n\n> JSONPath 是一种类似于 XPath 的语法，用于从 JSON 文档中选择数据。在 Shire 中，我们可以使用 JsonPath 来选择我们需要的数据。\n\n```shire\n---\nvariables:\n  \"api\": /sampl.sh/ { thread(\".shire/toolchain/bigmodel.curl.sh\") | jsonpath(\"$.choices[0].message.content\") }\n---\n\nhi\n\n$api\n```\n\n输出示例：\n\n```bash\nPrepare for running httpClient.shire...\nShire Script: /Users/phodal/IdeaProjects/shire-demo/.shire/toolchain/httpClient.shire\nShire Script Compile output:\nhi\n你好👋！我是人工智能助手智谱清言，可以叫我小智🤖，很高兴见到你，欢迎问我任何问题。\n\n--------------------\n你好！很高兴见到你。如果你有任何问题或需要帮助，请随时告诉我。我在这里为你提供信息和支持。\n\nProcess finished with exit code 0\n```\n\n"
  },
  {
    "path": "docs/cloud/remote-agent.md",
    "content": "---\nlayout: default\ntitle: Remote AI Agent\nnav_order: 1\nparent: Cloud\n---\n\n远程 AI Agent \n\n"
  },
  {
    "path": "docs/data-privacy/data-privacy.md",
    "content": "---\nlayout: default\ntitle: Data Privacy\nnav_order: 6\nhas_children: true\npermalink: /data-privacy\n---\n\nShire 在 AI 数据安全上提供了一些保护机制，用于对 AI 模型的输入、输出数据进行保护。\n\n**Pipeline 函数保护数据**通过自定义规则对数据进行处理，诸如：\n\n- PatternAction\n  - 使用 `replace` 函数，将敏感信息替换为占位符。\n- PsiMask\n  - 使用 `mask` 函数，对敏感信息进行脱敏处理。\n\n**AI 数据安全保护函数**用于对 AI 模型的输入、输出数据进行保护。主要是用在 AI 模型的输入输出数据中，对敏感信息进行保护，保护的方式包括：\n\n- `beforeStreaming`，在 Streaming 开始前对生成的内容进行处理，将敏感信息替换为占位符。\n- `onStreaming`，在 Streaming 过程中对生成的内容，检查是否有敏感信息。\n\n\n"
  },
  {
    "path": "docs/data-privacy/guarding-functions.md",
    "content": "---\nlayout: default\ntitle: AI 数据安全保护函数\nnav_order: 2\nparent: Data Privacy\n---\n\n数据安全保护函数（AI Data Guarding Functions）是用于进行对与模型交互的数据进行数据保护、去敏感化等操作的一种机制。\n\n- NER 命名实体识别 (Named-entity recognition) Scanner\n- Pattern/Regex Scanner\n\n## `redact` 函数\n\n在 redact 函数中, 我们使用 [db/pii-stable.yml](https://github.com/mazen160/secrets-patterns-db/blob/master/db/pii-stable.yml) \n作为敏感数据的配置文件, 用于对数据进行脱敏处理。\n\n普通变量使用示例：\n\n```shire\n---\nvariables:\n  \"phoneNumber\": \"086-1234567890\"\n  \"var2\": /.*ple.shire/ { cat | redact }\n---\n```    \n\n## 使用自定义 `.shireSecretPattern.yaml`\n\n在 Shire 中支持与 [Secrets Patterns DB](https://github.com/mazen160/secrets-patterns-db) 相似的配置文件，用于对数据进行脱敏处理。\n你可以在项目中新建一个 `.shireSecretPattern.yaml`结尾的文件，用于定义自定义的敏感数据规则，如：`Phodal.shireSecretPattern.yaml`。\n\n在该文件中，你可以定义一些敏感数据的规则，如：\n\n```yaml\npatterns:\n  - pattern:\n      name: Slack Token\n      regex: \"(xox[pborsa]-[0-9]{12}-[0-9]{12}-[0-9]{12}-[a-z0-9]{32})\"\n      confidence: high\n```\n\n随后，Shire 将会在处理数据时，自动对匹配到的数据进行脱敏处理。\n"
  },
  {
    "path": "docs/data-privacy/pipeline-guarding.md",
    "content": "---\nlayout: default\ntitle: Pipeline 函数\nnav_order: 1\nparent: Data Privacy\n---\n\n### 使用 Sed 函数保护数据\n\nBasic Sed Example\n\n```shire\n---\nvariables:\n  \"var2\": /.*ple.shire/ { cat | find(\"fileName\") | sed(\"\\\"fileName\\\"\", \"hello.kt\") }\n---\n\nSummary webpage: $var2\n```\n\nOpenAI Example:\n\n```shire\n---\nvariables:\n  \"openai\": \"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\"\n  \"var2\": /.*ple.shire/ { cat | find(\"openai\") | sed(\"(?i)\\b(sk-[a-zA-Z0-9]{20}T3BlbkFJ[a-zA-Z0-9]{20})(?:['|\\\"|\\n|\\r|\\s|\\x60|;]|${'$'})\", \"sk-***\") }\n---\n\nSummary webpage: $var2\n```\n\n## 相关资源\n\n### [Secrets Patterns DB](https://github.com/mazen160/secrets-patterns-db)\n\nSecrets Patterns DB 包含了用于检测秘密、API 密钥、密码、令牌等的正则表达式模式的最大开源数据库。\n\n示例：[db/pii-stable.yml](https://github.com/mazen160/secrets-patterns-db/blob/master/db/pii-stable.yml)\n\n部分内容如下：\n\n```yaml\npatterns:\n  - pattern:\n      name: times\n      regex: \\d{1,2}:\\d{2} ?(?:[ap]\\.?m\\.?)?|\\d[ap]\\.?m\\.?\n      confidence: high\n  - pattern:\n      name: phones\n      regex: ((?:(?<![\\d-])(?:\\+?\\d{1,3}[-.\\s*]?)?(?:\\(?\\d{3}\\)?[-.\\s*]?)?\\d{3}[-.\\s*]?\\d{4}(?![\\d-]))|(?:(?<![\\d-])(?:(?:\\(\\+?\\d{2}\\))|(?:\\+?\\d{2}))\\s*\\d{2}\\s*\\d{3}\\s*\\d{4}(?![\\d-])))\n      confidence: high\n  - pattern:\n      name: phones_with_exts\n      regex: ((?:(?:\\+?1\\s*(?:[.-]\\s*)?)?(?:\\(\\s*(?:[2-9]1[02-9]|[2-9][02-8]1|[2-9][02-8][02-9])\\s*\\)|(?:[2-9]1[02-9]|[2-9][02-8]1|[2-9][02-8][02-9]))\\s*(?:[.-]\\s*)?)?(?:[2-9]1[02-9]|[2-9][02-9]1|[2-9][02-9]{2})\\s*(?:[.-]\\s*)?(?:[0-9]{4})(?:\\s*(?:#|x\\.?|ext\\.?|extension)\\s*(?:\\d+)?))\n      confidence: high\n  - pattern:\n      name: emails\n      regex: ([a-z0-9!#$%&'*+\\/=?^_`{|.}~-]+@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)\n      confidence: high\n```\n\n"
  },
  {
    "path": "docs/development/design-principle.md",
    "content": "---\nlayout: default\ntitle: Design Principles\nnav_order: 1\nparent: Development\n---\n\n# Shire 设计原则\n\n## 原则 1：IDE 即上下文环境\n\n集成开发环境（IDE）不仅仅是一个文本编辑器或编程环境，它更是一个能够理解、处理和操作代码的高级工具。这种设计理念下，IDE\n的作用远不止于代码编辑，它还包括了代码的语法分析、语义理解以及操作执行等功能。 在 Shire 中，我们采纳了一系列的设计原则，以支持\nIDE 的智能化和自动化：\n\n- **上下文感知变量化**。我们定义了一系列上下文变量，例如选中的文本、光标前后的文本、文件名和路径等。这些变量能够辅助 IDE\n  理解和操作代码的不同部分，使得开发者与 AI 都能基于上下文信息进行代码处理和分析。\n- **模式-动作上下文构建**。我们借鉴 Unix 的 Shell 编程思想，引入了模式-动作概念。这种模式允许根据特定的模式（如文件类型或内容特征）执行相应的动作（如\n  find、sort、xargs）。通过此模式，IDE 可以根据用户定义的条件对文件和数据进行筛选和处理，显著提升了代码操作的灵活性和效率。\n- **查询语言 ShireQL**。这是一种用于描述和执行 PSI（程序结构接口）查询的语言。DSL（特定领域语言）定义了变量声明、条件查询和结果选择的结构。这种语言使得开发者和\n  AI 能够根据特定的语法结构和查询条件，从代码中提取和分析所需信息。这样的设计让 IDE 不仅仅是一个静态的文本编辑器，而是一个能够通过语义理解和查询执行来智能操作代码的工具。\n\n示例：\n\n```shire\n解释如下代码片段。\n\n项目相关的技术栈上下文： $frameworkContext\n\n$selection\n```    \n\n在此示例中，我们展示了如何利用 Shire 的设计原则和功能来解析代码和上下文信息。通过定义变量和执行特定的模式-动作操作，IDE\n可以根据条件筛选和处理代码文件，并通过 PSI 查询语言进行高级的代码结构分析和提取。这样的能力不仅提升了开发者的效率，还使得\nAI 能够更智能地参与到代码开发和分析过程中。\n\n## 原则 2：语言即抽象接口\n\n> 语言接口是一种使用自然语言作为领域特定语言（DSL）或与系统进行交互的接口。它通过解析、处理和分析自然语言，指导系统的设计、开发和执行。\n> 其设计目的是提高开发效率、准确性和用户体验，使用户能够使用自然语言描述系统需求、执行任务并获取系统生成的结果。\n\n在 Shire 中，我们采纳了一种语言接口设计原则，将自然语言作为领域特定语言（DSL）或与系统进行交互的接口。这种设计原则涵盖以下几个方面：\n\n- **领域特定语言（DSL）**。DSL 是一种专门用于解决特定领域问题的语言，其通过简单、直观的语法和语义，让开发者更容易地描述和执行特定任务。在\n  Shire 中，我们引入了一种类似于 YAML 的 HobbitHole，用于描述数据处理流程和 IDE 交互逻辑。通过这种\n  DSL，开发者可以定义数据处理流程、交互逻辑和输出结果。\n- **自然语言即 LLM 接口**。对于大型语言模型（LLM）而言，自然语言是一种重要的接口，用于描述和执行代码生成任务。开发者可以通过自然语言描述，\n  定义代码生成模板、变量和条件，以及执行代码生成任务。这种设计原则让开发者能够更直观、灵活地描述和执行代码生成任务，提升了代码生成的效率和准确性。\n  示例：\n\n```shire\n---\nname: \"AutoTest\"\ndescription: \"AutoTest\"\ninteraction: AppendCursor\nactionLocation: ContextMenu\n---\n\n为以下的 ${context.language} 代码编写单元测试。\n...\n```\n\n通过上述配置，我们定义了一个名为 \"AutoTest\" 的 HobbitHole，用于生成自动化测试代码。通过这种 DSL + 自然语言的结合，我们可以定义\nIDE 中的交互类型、操作位置和其他属性，进而实现代码生成任务的自动化和智能化。\n\n## 原则 3：原子功能单元（Atomic Functional Units）\n\n原子功能单元（Atomic Functional Units, AFUs）是一种设计方法，旨在将复杂系统分解为独立且功能明确的最小操作单元。这种设计原则受到\nLinux 设计思想的启发，强调模块化、独立性和简洁性。\n\n1. **原子性和模块化**。每个原子功能单元（AFU）都是独立且不可再分割的基本操作单元，采用模块化设计，能够独立执行特定任务，并可以自由组合和重用。\n2. **简单接口与管道式处理**。AFUs 暴露简单明确的输入和输出接口，通过管道连接，形成高效且连贯的数据处理流程，隐藏内部实现细节，简化用户操作。\n3. **高内聚低耦合**。每个 AFU 内部专注于特定任务（高内聚），与其他单元通过简单接口进行通信（低耦合），提升系统的灵活性、可维护性和扩展性。\n\n以下是一个示例，展示如何对Java文件进行Embedding，以提供模板中的变量，作为LLM（大型语言模型）的上下文：\n\n```shire\n---\nvariables:\n  \"searchResult\": /*.docx/ { splitting | embedding | searching(\"API 设计范式\") }\n---\n\n根据如下的内容，总结一下 API 如何设计：\n\n$searchResult\n```\n\n在这个示例中：\n\n- splitting：将文档拆分为独立的部分，每个部分可以单独处理。\n- embedding：对每个部分进行 Embedding，以便在 LLM 中使用。\n- searching：在 Embedding 的内容中搜索特定的关键词或概念。\n\n通过这种方式，我们可以将复杂的任务分解为独立的原子功能单元，每个单元都是独立的，可以自由组合和重用，从而提高系统的灵活性和可维护性。\n"
  },
  {
    "path": "docs/development/development.md",
    "content": "---\nlayout: default\ntitle: Development\nnav_order: 8\nhas_children: true\npermalink: /development\n---\n\n{: .note }\nDue to my Sabbatical Leave and responsibilities in caring for my child, updates to this document may not be as frequent.\n\n核心概念：\n\n- ShireCompiler\n- HobbitHole，the frontmatter config will convert to a HobbitHole, which is a data structure for the IDE action.\n\n## IDE Build issue\n\n### Notes: Kotlin 2.0 -> 1.9.24\n\n```bash\nInternal API usages (179): \n    #Internal class kotlinx.serialization.UnknownFieldException reference\n        Internal class kotlinx.serialization.UnknownFieldException is referenced in com.phodal.shirecore.guard.model.SecretPatternItem$.serializer.deserialize(Decoder) : SecretPatternItem. This class is marked with Kotlin `internal` visibility modifier, indicating that it is not supposed to be referenced in client code outside the declaring module.\n        Internal class kotlinx.serialization.UnknownFieldException is referenced in com.phodal.shirecore.llm.ChatMessage$.serializer.deserialize(Decoder) : ChatMessage. This class is marked with Kotlin `internal` visibility modifier, indicating that it is not supposed to be referenced in client code outside the declaring module.\n    #Internal class kotlinx.serialization.internal.StringSerializer reference\n        Internal class kotlinx.serialization.internal.StringSerializer is referenced in com.phodal.shirecore.agent.CustomFlowTransition$.serializer.childSerializers() : KSerializer[]. This class is marked with Kotlin `internal` visibility modifier, indicating that it is not supposed to be referenced in client code outside the declaring module.\n        Internal class kotlinx.serialization.internal.StringSerializer is referenced in com.phodal.shirecore.agent.CustomAgent$.serializer.childSerializers() : KSerializer[]. This class is marked with Kotlin `internal` visibility modifier, indicating that it is not supposed to be referenced in client code outside the declaring module.\n\n```\n\n- Kaml 0.60.0 -> build with Kotlin 2.0.0, but it's not compatible with Kotlin 1.9.24.\n"
  },
  {
    "path": "docs/development/ide-note.md",
    "content": "---\nlayout: default\ntitle: IDE Note\nnav_order: 999\nparent: Development\n---\n\n## CoroutineScope issue\n\n示例：\n\n```kotlin\nShireCoroutineScope.scope(context.project).launch {\n    val suggestion = StringBuilder()\n\n    flow?.cancelWithConsole(context.console)?.cancellable()?.collect { char ->\n        suggestion.append(char)\n\n        invokeLater {\n            context.console?.print(char, ConsoleViewContentType.NORMAL_OUTPUT)\n        }\n    }\n\n    postExecute.invoke(suggestion.toString(), null)\n}\n```\n\n## Data Context\n\n### 1. 从 AnAction 中获取 DataContext\n\n如果你正在实现一个继承自 `AnAction` 的动作类，可以通过 `AnActionEvent` 直接获取 `DataContext`。如下所示：\n\n```java\npublic class MyAction extends AnAction {\n    @Override\n    public void actionPerformed(AnActionEvent e) {\n        DataContext dataContext = e.getDataContext();\n        // 你可以从 DataContext 获取更多数据\n    }\n}\n```\n\n### 2. 从一个组件中获取 DataContext\n\n如果你有一个 UI 组件，比如一个按钮，你可以使用 `DataManager` 来获取该组件的 `DataContext`。例如：\n\n```java\nJComponent component = ...; // 你的组件\nDataContext dataContext = DataManager.getInstance().getDataContext(component);\n```\n\n### 3. 从当前焦点的组件获取 DataContext\n\n如果你需要从当前焦点的组件获取 `DataContext`，可以这样做：\n\n```java\nDataContext dataContext = DataManager.getInstance().getDataContextFromFocus().getResult();\n```\n\n### 4. 使用 PlatformDataKeys\n\n获取 `DataContext` 后，你可以使用 `PlatformDataKeys` 来提取特定的数据。例如，获取当前的 `Editor`：\n\n```java\nEditor editor = CommonDataKeys.EDITOR.getData(dataContext);\nProject project = CommonDataKeys.PROJECT.getData(dataContext);\nVirtualFile file = CommonDataKeys.VIRTUAL_FILE.getData(dataContext);\n```\n\n### 5. 存储 Event 的 DataContext\n\n在某些情况下，你可能需要在动作执行之前存储 `DataContext`。你可以使用 `VariableActionEventDataHolder` 来存储数据。例如：\n\n```kotlin\nclass ShireSonarLintAction : AnAction() {\n    // ...\n    override fun actionPerformed(e: AnActionEvent) {\n        val project = e.project ?: return\n\n        VariableActionEventDataHolder.putData(VariableActionEventDataHolder(e.dataContext))\n\n        val config = shireActionConfigs(project).firstOrNull() ?: return\n        ShireRunFileAction.executeShireFile(project, config, null)\n    }\n}\n```\n\n使用 `VariableActionEventDataHolder` 存储 `DataContext` 后，你可以在动作执行时获取数据。例如：\n\n```kotlin\nfun getCommitWorkflowUi(): CommitWorkflowUi? {\n    VariableActionEventDataHolder.getData()?.vcsDataContext?.let {\n        val commitWorkflowUi = it.getData(VcsDataKeys.COMMIT_WORKFLOW_UI)\n        return commitWorkflowUi as CommitWorkflowUi?\n    }\n\n    val dataContext = DataManager.getInstance().dataContextFromFocus.result\n    val commitWorkflowUi = dataContext?.getData(VcsDataKeys.COMMIT_WORKFLOW_UI)\n    return commitWorkflowUi\n}\n```\n\n\n## 自定义 ContextMenu 位置\n\n\n### 普通方式\n\n参考：`ShireActionStartupActivity` 中的实现，如：`attachTerminalAction` 方法\n\n```kotlin\nprivate fun attachTerminalAction() {\n    val actionManager = ActionManager.getInstance()\n    val toolsMenu = actionManager.getAction(\"TerminalToolwindowActionGroup\") as? DefaultActionGroup ?: return\n\n    val action = actionManager.getAction(\"ShireTerminalAction\")\n    if (!toolsMenu.containsAction(action)) {\n        toolsMenu.add(action)\n    }\n}\n```\n\n1. 从 ActionManager 中获取目标 ActionGroup\n2. 将自定义 Action 添加到目标 ActionGroup 中\n\n### 动态方式\n\n对于不对外提供的插件，可以尝试监听是否有对应的事件，诸如 Sonarlint 使用的是 Panel，因此可以监听 Panel 的事件，然后动态添加 Action。\n\n```kotlin\nprivate fun attachSonarLintAction(project: Project) {\n    project.messageBus.connect().subscribe(ToolWindowManagerListener.TOPIC, SonarLintToolWindowListener(project));\n}\n```\n\n对应的 `SonarLintToolWindowListener` 实现如下：\n\n```kotlin\nclass SonarLintToolWindowListener(private val project: Project) : ToolWindowManagerListener {\n    override fun toolWindowShown(toolWindow: ToolWindow) {\n        if (toolWindow.id != \"SonarLint\") return\n\n        val action = ActionManager.getInstance().getAction(\"ShireSonarLintAction\")\n\n        val contentManager = toolWindow.contentManager\n        val content = contentManager.getContent(0) ?: return\n\n        val simpleToolWindowPanel = content.component as? SimpleToolWindowPanel\n        val actionToolbar = simpleToolWindowPanel?.toolbar?.components?.get(0) as? ActionToolbar ?: return\n        val actionGroup = actionToolbar.actionGroup as? DefaultActionGroup\n\n        if (actionGroup?.containsAction(action) == false) {\n            actionGroup.add(action)\n        }\n    }\n}\n```\n\n\n\n"
  },
  {
    "path": "docs/development/language-spec.md",
    "content": "---\nlayout: default\ntitle: Language Specification\nparent: Development\nnav_order: 2\n---\n\nSee in [ShireParser.bnf] for latest version.\n\n## Hobbit Hole Design\n\n### Normal type\n\nExample:\n\n```shire\n---\nkey: \"value\"\n---\n```\n\nAll\n\n| ValueType  | Description                  |\n|------------|------------------------------|\n| String     | \"value\"                      |\n| IDENTIFIER | enum key in Shire soure code |\n| Number     | 123                          |\n| Boolean    | true                         |\n| Array      | [1, 2, 3]                    |\n| Object     | {key: \"value\"}               |\n\n### Function Type\n\nExample:\n\n```shire\n---\nwhen:  { $selection.length() >= 0 }\n---\n```\n\nAll\n\n| ValueType            | Description                       | Example                                                                     |\n|----------------------|-----------------------------------|-----------------------------------------------------------------------------|\n| Pattern Action       | /regex/ { functionBlock }         | /.*.java/ { $selection.length() >= 0 }                                      |\n| Function             | { functionBlock }                 | { $selection.length() >= 0 }                                                |\n| Ast Query Expression | use `from`, `select`, `where`,    | see in `Ast Query Expression`                                               |\n| Case Block           | case \"variable\" { functionBlock } | case \"$0\" { default  { find(\"ERROR\")   \\| sort \\| xargs(\"notify_admin\") } } |\n| Flags Block          | flags { flagBlock }               | flags { \"ignore\": { } }                                                     |\n\n#### Ast Query Expression\n\n```shire\n---\nvariables:\n  \"allController\": {\n    from {\n        PsiClass clazz // the class\n    }\n    where {\n        clazz.extends(\"org.springframework.web.bind.annotation.RestController\") and clazz.getAnAnnotation() == \"org.springframework.web.bind.annotation.RequestMapping\"\n    }\n\n    select {\n        clazz.id, clazz.name, \"code\"\n    }\n  }\n---\n```\n"
  },
  {
    "path": "docs/development/shire-sketch.md",
    "content": "---\nlayout: default\ntitle: Shire Sketch\nparent: Development\nnav_order: 3\n---\n\nShire Sketch 是 Shire 提供的 IDE 画布功能，旨在通过其丰富的文本代码（源码、Patch、UML、架构图等）二次处理、渲染组件，进一步简化交互成本，\n以提升开发者在 IDE 中的体验。\n无论是单个文件的显示、渲染操作，还是多文件协作、修复，Shire Sketch 都能提供强大的支持。\n\n- **实时流式代码高亮**：实时显示代码高亮的流式视图。\n- **内置差异（Patch 语言）**：显示代码差异的内置视图。\n- **实时流式差异（StreamDiff）**：实时显示代码差异的流式视图，基于 Continue 的 UI 修改。\n- **Mermaid 流程图**：支持 Mermaid 流程图的渲染，与双向绑定的代码编辑器。（要求启用 Mermaid 插件）\n- **PlantUML 图表**：支持 PlantUML 图表的渲染，与双向绑定的代码编辑器。（要求安装 `PlantUML integration` 插件）\n\n## Sketch 示例\n\n### Diff Sketch\n\n示例：Prompt：\n\n```shire\n请使用 patch 的方式完成需求，并使用 markdown patch 代码格式返回。\n```\n\n![](https://shire.run/images/shire-sketch-diff.png)\n\n### Mermaid Sketch\n\n示例 Prompt：\n\n```shire\n请使用 mermaid Code 格式，语言 `mermaid`，请根据用户故事绘制 mermaid 时序图，返回设计的 mermaid 代码。\n```\n\n![](https://shire.run/images/shire-sketch-mermaid.png)\n\n### PlantUml Sketch\n\n示例 Prompt：\n\n```shire\n请使用 plantuml Code 格式，语言 `plantuml`，请根据用户故事绘制 PUML 时序图，返回设计的 PUML 代码。\n```\n\n![](https://shire.run/images/shire-sketch-plantuml.png)\n\n## 创建新 Sketch\n\n```kotlin\ninterface LanguageSketchProvider {\n    fun isSupported(lang: String): Boolean\n\n    fun create(project: Project, content: String): ExtensionLangSketch\n\n    companion object {\n        private val EP_NAME: ExtensionPointName<LanguageSketchProvider> =\n            ExtensionPointName(\"com.phodal.shireLangSketchProvider\")\n\n        fun provide(language: String): LanguageSketchProvider? {\n            return EP_NAME.extensionList.firstOrNull {\n                it.isSupported(language)\n            }\n        }\n    }\n}\n```\n\n示例：\n\nXML 声明：\n\n```xml\n\n<extensions defaultExtensionNs=\"com.phodal\">\n    <shireLangSketchProvider implementation=\"com.phodal.shirecore.sketch.patch.DiffLangSketchProvider\"/>\n</extensions>\n```\n\n实现代码：\n\n```kotlin\nclass DiffLangSketchProvider : LanguageSketchProvider {\n    override fun isSupported(lang: String): Boolean = lang == \"diff\" || lang == \"patch\"\n    override fun create(project: Project, content: String): ExtensionLangSketch = DiffLangSketch(project, content)\n}\n```"
  },
  {
    "path": "docs/examples/auto-test.md",
    "content": "---\nlayout: default\ntitle: AI AutoTest\nparent: Shire Examples\nnav_order: 2\n---\n\n    ---\n    name: \"AutoTest\"\n    description: \"AutoTest\"\n    interaction: AppendCursor\n    actionLocation: ContextMenu\n    when: { $fileName.contains(\".java\") && $filePath.contains(\"src/main/java\") }\n    variables:\n      \"frameworkContext\": /.*/build\\.gradle\\.kts/ { find(\"org.springframework.boot:spring-boot-starter-jdbc\") | print(\"This project use Spring Framework\")}\n    ---\n    Write unit test for following ${context.language} code.\n    \n    ${frameworkContext}\n    \n    #if($context.relatedClasses.length() > 0 )\n    Here is the relate code maybe you can use\n    ${context.relatedClasses}\n    #end\n    \n    #if($context.currentClass.length() > 0 )\n    This is the class where the source code resides:\n    ${context.currentClass}\n    #end\n    \n    Here is the source code to be tested:\n    \n    ```${context.language}\n    ${context.imports}\n    ${context.selection}\n    ```\n    \n    #if($context.isNewFile)\n    Should include package and imports. Start method test code with Markdown code block here:\n    #else\n    Should include package and imports. Start ${context.targetTestFileName} test code with Markdown code block here:\n    #end\n\n"
  },
  {
    "path": "docs/examples/batch-execute.md",
    "content": "---\nlayout: default\ntitle: Batch Execute\nparent: Shire Examples\nnav_order: 10\n---\n\n`batch` 函数可以用来批量执行一个脚本。这个函数接受两个参数，第一个参数是要执行的脚本，第二个参数是要执行的文件列表。\n\n```shire\n---\nname: \"Generate Swagger Doc\"\nvariables:\n  \"controllers\": /.*.Controller.java/ { print }\n  \"gen-swagger\": /any/ { batch(\"controller-with-swagger.shire\", $controllers) }\nbeforeStreaming: { stop }\n---\n\n```\n\n"
  },
  {
    "path": "docs/examples/cli-copilot.md",
    "content": "---\nlayout: default\ntitle: CLI Copilot\nparent: Shire Examples\nnav_order: 5\n---\n\n```shire\n---\nname: \"Terminal\"\ndescription: \"Generate Cli\"\ninteraction: AppendCursor\nactionLocation: TerminalMenu\n---\n\nReturn only the command to be executed as a raw string, no string delimiters\nwrapping it, no yapping, no markdown, no fenced code blocks, what you return\nwill be passed to subprocess.check_output() directly.\n\n- Today is: $today, user system is: $os,\n- User current directory is: $cwd, user use is: $shellPath, according the tool to create the command.\n\nFor example, if the user asks: undo last git commit\n\nYou return only line command: git reset --soft HEAD~1\n\nUser asks: $input\n```\n\n注释：\n\n- `TerminalMenu`：在 Terminal Window 中添加 Shire 入口\n"
  },
  {
    "path": "docs/examples/code-comment.md",
    "content": "---\nlayout: default\ntitle: Code Comments\nparent: Shire Examples\nnav_order: 4\n---\n\n示例：生成注释\n\n    ---\n    name: \"生成注释\"\n    interaction: InsertBeforeSelection\n    actionLocation: ContextMenu\n    onStreamingEnd: { insertNewline | formatCode }\n    ---\n    \n    为如下的代码编写注释，使用 javadoc 风格：\n    \n    ```$language\n    $selection\n    ```\n    \n    只返回注释\n\n解释：\n\n- `InsertBeforeSelection`：在选中的代码之前插入注释\n- `insertNewLine`：在插入注释后换行\n- `formatCode`：格式化代码"
  },
  {
    "path": "docs/examples/code-refactoring.md",
    "content": "---\nlayout: default\ntitle: Code Refactoring\nparent: Shire Examples\nnav_order: 3\n---\n\n```shire\n---\nname: \"Refactoring\"\nactionLocation: ContextMenu\ninteraction: ReplaceSelection\n---\n\n请你这段代码建议适当的重构。提高代码的可读性、质量，使代码更加有组织和易懂。你的回答应包含重构描述和一个代码片段，展示重构后的结果。\n使用一些众所周知的重构技巧，比如以下列表中的一个：\n\n- 重命名\n- 修改签名、声明\n- 提取或引入变量、函数、常量、参数、类型参数\n- 提取类、接口、超类\n- 内联类、函数、变量等\n- 移动字段、函数、语句等\n- 上移构造函数、字段、方法\n- 下移字段、方法\n\n请勿生成多个代码片段，尝试将所有更改都整合到一个代码片段中。\n请勿生成包含虚构周围类、方法的代码。不要模拟缺失的依赖项。\n提供的代码已经整合到正确且可编译的代码中，不要在其周围添加额外的类。\n\n以下是静态代码分析的 Code Smell 结果：\n\n$codeSmell\n\n请重构以下代码：\n\n$selection\n```\n"
  },
  {
    "path": "docs/examples/commit-message-gen.md",
    "content": "---\nlayout: default\ntitle: AI Commit Message\nparent: Shire Examples\nnav_order: 2\n---\n\n```shire\n---\nname: \"Commit message\"\ninteraction: AppendCursor\nactionLocation: CommitMenu\n---\n\n为给定的变更（Diff）编写一个连贯但具有描述性的代码提交信息。\n\n- 确保包含修改了什么以及为什么。\n- 以不超过 50 个字符的祈使句形式开头。\n- 然后留下一个空行，如有必要，继续详细说明。\n- 说明应该少于 200 个字符。\n\n遵循常规提交规范，例如：\n\n- fix(authentication): 修复密码正则表达式模式问题\n- feat(storage): 添加对S3存储的支持\n- test(java): 修复用户控制器的测试用例\n- docs(architecture): 在主页添加架构图\n\nDiff：\n\n$currentChanges\n```\n"
  },
  {
    "path": "docs/examples/examples.md",
    "content": "---\nlayout: default\ntitle: Shire Examples\nnav_order: 9\nhas_children: true\npermalink: /examples\n---\n\nSample Projects:\n\n- [Shire Java/Spring Demo](https://github.com/shire-lang/shire-spring-java-demo)\n\n"
  },
  {
    "path": "docs/examples/inline-chat.md",
    "content": "---\nlayout: default\ntitle: Custom Inline Chat\nparent: Shire Examples\nnav_order: 11\n---\n\nInline Chat 可以在当用户选中内容时，在选中行的左侧显示一个 Icon，用户点击这个 Icon 后，可以在当前页面内进行对话。\n\n![Inline Chat](https://shire.run/images/shire-inline-chat.png)\n\n```shire\n---\nname: \"shire multiple file edit\"\ndescription: \"Shire Multiple File Edit\"\ninteraction: StreamDiff\nactionLocation: InlineChat\n---\n\n根据用户的要求和现有的代码编写 Java 代码。要求：\n\n1. 使用 diff patch 的方式。\n2. 如果是新文件也使用 patch 的格式。\n3. 每个文件的修改也请用 diff 的形式给出。\n\n现有代码如下：\n\n$all\n\n用户的需求如下：\n\n$chatPrompt\n```\n"
  },
  {
    "path": "docs/examples/multiple-file-edit.md",
    "content": "---\nlayout: default\ntitle: Multiple File Edit\nparent: Shire Examples\nnav_order: 12\n---\n\nChatBox 是在 Shire ToolWindow 中的一个输入框，用户可以在这里输入内容，然后调用大语言模型。使用事项如下：\n\n- 默认使用 `RigthPanel` 作为展示位置，使用 `ChatBox` 作为 `actionLocation`。\n- 当用户创建了 `actionLocation: ChatBox` 的 Shire 代码时，将会读取用户的输入作为提示词的一部分。\n\n```shire\n---\nname: \"shire multiple file edit\"\ndescription: \"Shire Multiple File Edit\"\nonStreaming: { logging }\ninteraction: RightPanel\nactionLocation: ChatBox\n---\n\n根据用户的要求和现有的代码编写 Java 代码。要求：\n\n1. 使用 diff patch 的方式。\n2. 如果是新文件也使用 patch 的格式。\n3. 每个文件的修改也请用 diff 的形式给出。\n\n用户的需求如下：\n\n$chatPrompt\n```"
  },
  {
    "path": "docs/examples/on-paste-modify.md",
    "content": "---\nlayout: default\ntitle: On Paste modify\nparent: Shire Examples\nnav_order: 9\n---\n\n`OnPaste` 可以在用户粘贴代码时，根据上下文，对用户粘贴的代码进行优化。\n\n```shire\n---\nname: \"PasteMaster\"\ninteraction: OnPaste\n---\n\n优化待复制代码。根据当前的代码上下文（光标前后），对用户复制的代码，生成新的代码。\n\n光标前的代码：\n\n$beforeCursor\n\n光标后的代码：\n\n$afterCursor\n\n用户复制的代码：\n\n$text\n\n只根据上下文，优化用户复制的代码\n```\n\n"
  },
  {
    "path": "docs/examples/search.md",
    "content": "---\nlayout: default\ntitle: Semantic Search\nparent: Shire Examples\nnav_order: 8\n---\n\n## Semantic Search\n\n- splitting, splitting the file\n  - splitting by the file extension\n  - support format: `*.docx`, `*.md`, `*.txt`, `*.pdf`, `*.xlsx`, `*.pptx` or other text-based file\n- embedding, embedding full path of the file\n  - embedding to InMemory: [InMemoryEmbeddingSearchIndex]\n- searching, searching for a specific string in the file, which will\n  - embedding the input string\n  - execute relevant search\n  - return result\n- caching, caching the search result (Should be in first of calling)\n  - support for local, remote, or InMemory cache?\n\n### Basic Example\n\n\n```shire\n---\nname: \"AutoTest\"\ndescription: \"AutoTest\"\ninteraction: AppendCursor\nvariables:\n  \"*.docx\": /.*/build\\.gradle\\.kts/ { splitting | embedding | searching(\"hello\") }\n---\nWrite unit test for following ${context.language} code.\n```\n\n### Cache Example\n\n```shire\n---\nname: \"Search\"\nvariables:\n  \"testTemplate\": /.*.java/ { caching(\"disk\") | splitting | embedding | searching(\"comment\") }\n---\n\n根据如下的代码，回答用户的问题：博客创建的流程\n\n$testTemplate\n```\n"
  },
  {
    "path": "docs/faq.md",
    "content": "---\nlayout: default\ntitle: FAQ\nnav_order: 999\nhas_children: true\npermalink: /faq\n---\n\n## `java.net.SocketException: No route to localhost:0 port is out of range`\n\n**原因分析**：\n\n此错误通常是因为IDE（例如IntelliJ IDEA）设置了手动代理，而错误信息中的“localhost:0”是配置项中的主机名和端口号参数所导致的。\n参考[Issue93](https://github.com/phodal/shire/issues/93)来解决该问题。\n\n**解决步骤**：\n\n- 检查IDE的代理设置是否正确。\n- 确认代理服务器的主机名和端口是否可用。\n- 如果不需要代理，尝试关闭IDE的代理设置。\n- 根据具体情况调整IDE配置或代理服务器配置。\n\n## `RunCode: No run service found for file`\n\n可能原因：\n\n- 缺少对应语言的 IDE 插件：诸如 [HttpClient](https://plugins.jetbrains.com/plugin/13121-http-client)、JavaScript 插件等。\n- 缺少对应语言的 FileRunService 实现。\n\n"
  },
  {
    "path": "docs/index.md",
    "content": "---\nlayout: default\ntitle: Home\ndescription: Shire offers a straightforward AI Coding Agent Language that enables communication between an LLM and control IDE for automated programming.\nnav_order: 1\npermalink: /\n---\n\n<p align=\"center\">\n  <img src=\"images/pluginIcon.svg\" width=\"160px\" height=\"160px\"  alt=\"logo\" />\n</p>\n<h1 align=\"center\">Shire - AI Coding Agent Language</h1>\n<p align=\"center\">\n  <a href=\"https://github.com/phodal/shire/actions/workflows/build.yml\">\n    <img src=\"https://github.com/phodal/shire/workflows/Build/badge.svg\" alt=\"Build\" />\n  </a>\n  <a href=\"https://plugins.jetbrains.com/plugin/24549\">\n    <img src=\"https://img.shields.io/jetbrains/plugin/v/24549.svg\" alt=\"Version\" />\n  </a>\n  <a href=\"https://plugins.jetbrains.com/plugin/24549\">\n    <img src=\"https://img.shields.io/jetbrains/plugin/d/24549.svg\" alt=\"Downloads\" />\n  </a>\n</p>\n\n## English Introduction\n\n**Shire** offers a seamless AI coding agent language, enabling large language models (LLMs) to engage in fluid dialogue\nwith integrated development environments (IDEs) to achieve automated programming.\n\n> The concept of Shire has its roots in [AutoDev](https://github.com/unit-mesh/auto-dev), a subproject of \n> [UnitMesh](https://unitmesh.cc/). Within AutoDev, we envisioned an AI-driven integrated development environment \n> for developers, which included Shire’s predecessor, DevIns. DevIns was designed to empower users to create custom AI \n> agents tailored to their own IDEs, thus forging a personalized AI-powered development realm.\n\n## Chinese Introduction\n\nPS：the Shire 一词来自于《魔戒》(LOTR）中的（夏尔）Shire，是霍比特人（Hobbit）的家园。\n\nShire 提供了一种简便的 AI 编码智能体语言，使大型语言模型（LLM）能够与集成开发环境（IDE）无缝交互，实现编程自动化。\n\n> Shire 的概念起源于 [AutoDev](https://github.com/unit-mesh/auto-dev)，这是 [UnitMesh](https://unitmesh.cc/) 的一个子项目。在\nAutoDev 中，我们设计了一个面向开发者的 AI 驱动集成开发环境（IDE），其中包括 Shire 的前身 DevIns。DevIns 旨在让用户能够为他们自己的\nIDE 创建定制的 AI 代理，从而构建个性化的 AI 驱动开发环境。\n\n\nShire example Project: [Java example](https://github.com/shire-lang/shire-spring-java-demo)\n\n### SDLC\n\n- [Code change analysis](https://github.com/shire-lang/shire-demo/blob/master/.shire/requirement/crud/analysis-requirements.shire) use LLM to analysis requirements, then choose the best files to change.\n- [Requirement + AutoCRUD](https://github.com/shire-lang/shire-demo/blob/master/.shire/requirement/analysis-requirements.shire) analysis requirements, then auto generate CRUD code.\n- [Dify + OpenAPI/Swagger](https://github.com/shire-lang/shire-demo/blob/master/.shire/api/design/design-rest-api.shire) interactive with Dify agent to design REST API\n- [Add Spring doc to project](https://github.com/shire-lang/shire-demo/blob/master/.shire/api/setup-dep/setup-spring-doc-openapi.shire) add Spring doc to project.\n- [Generate RestAssured Test](https://github.com/shire-lang/shire-demo/blob/master/.shire/api/verify/rest-assure.shire) AI to generate RestAssured test code.\n- [Generate JavaDoc](https://github.com/shire-lang/shire-demo/blob/master/.shire/documentation/javadoc.shire) use LLM to generate JavaDoc.\n- [Complexity Analysis](https://github.com/shire-lang/shire-demo/blob/master/.shire/git/complexity.shire) calculate code complexity.\n- [PlantUML: fetch Github issue for analysis](https://github.com/shire-lang/shire-demo/blob/master/.shire/requirement/visual/mindmap.shire) fetch GitHub issue to generate mindmap.\n\nFrontEnd:\n\n- [Frontend + HTML mockup](https://github.com/shire-lang/shire-demo/blob/master/.shire/frontend/html-mock-up.shire) use LLM to generate HTML mockup and show in WebView.\n- [Mobile + Ionic](https://github.com/shire-lang/shire-demo/blob/master/.shire/frontend/mobile-mock-up.shire) use LLM to generate mobile mockup with Ionic, show in WebView.\n- [Mobile + React](https://github.com/shire-lang/shire-demo/blob/master/.shire/frontend/react-mock-up.shire) use LLM to generate mobile mockup with React, show in WebView.\n- [JavaScript Auto Unittest](https://github.com/shire-lang/shire-demo/blob/master/.shire/frontend/js-test.shire) use LLM to generate JavaScript test code.\n\nTest:\n\n- [E2E Test: Playwright](https://github.com/shire-lang/shire-demo/blob/master/.shire/api/e2e/playwright.shire) AI to use Playwright to test the API and auto execute test.\n- [API Test: Java](https://github.com/shire-lang/shire-demo/blob/master/.shire/test/java/api-test.shire) use LLM to generate Java API test code.\n- [Unit Test: Java](https://github.com/shire-lang/shire-demo/blob/master/.shire/test/java/autotest.shire) use LLM to generate Java unit test code.\n- [Unit Test: Python](https://github.com/shire-lang/shire-demo/blob/master/.shire/test/python/AutoTest.shire) use LLM to generate Python unit test code.\n- [Unit Test: Golang](https://github.com/shire-lang/shire-demo/blob/master/.shire/test/go/AutoTest.shire) use LLM to generate Golang unit test code.\n\n### Workflow & IDE Integration\n\n- [Capture web pages and generate report](https://github.com/shire-lang/shire-demo/blob/master/.shire/research/research.shire) capture web pages and generate report.\n- [approvalExecute](https://github.com/shire-lang/shire-demo/blob/master/.shire/approve/approve.shire) waiting for approval to execute next shire code\n- [Custom InlineChat](https://github.com/shire-lang/shire-demo/blob/master/.shire/chatbox/inline-chat.shire)  custom inline chat\n- [Custom ChatBox](https://github.com/shire-lang/shire-demo/blob/master/.shire/chatbox/wrapper-chat.shire) custom prompt to use right panel chat box\n- [Python as Foreign Function Interface](https://github.com/shire-lang/shire-demo/blob/master/.shire/ffi/python-shell-thread.shire) use Python to run shell command in thread.\n- [Quick Input](https://github.com/shire-lang/shire-demo/blob/master/.shire/miscs/quick-input.shire) show quick input dialog.\n- [Terminal Agent](https://github.com/shire-lang/shire-demo/blob/master/.shire/miscs/terminal.shire) use terminal agent to run shell command.\n\n### EcoSystem\n\n- [Git: Auto push code](https://github.com/shire-lang/shire-demo/blob/master/.shire/git/auto-push.shire) auto commit and push code to server.\n- [Git: diff AI changed code](https://github.com/shire-lang/shire-demo/blob/master/.shire/git/diff-example.shire) diff AI changed code.\n- [Git: Commit message](https://github.com/shire-lang/shire-demo/blob/master/.shire/git/login-commit-message.shire) generate commit message.\n- [Git: Commit ID with Jira](https://github.com/shire-lang/shire-demo/blob/master/.shire/git/commit-message.shire) generate commit message with Jira ID.\n- [Database: GitHub issue + Design Database Schema](https://github.com/shire-lang/shire-demo/blob/master/.shire/database/design-db.shire) fetch GitHub issue as context to design database schema\n- [Database: Run SQL in Database](https://github.com/shire-lang/shire-demo/blob/master/.shire/database/command.shire) run SQL with `/database` command.\n- [OpenRewrite: generate refactoring code](https://github.com/shire-lang/shire-demo/blob/master/.shire/refactor/openRewrite.shire) use OpenRewrite to generate refactoring code.\n- [MockServer: WireMock](https://github.com/shire-lang/shire-demo/blob/master/.shire/api/mock/gen-mock.shire) AI to generate mock server with WireMock and auto start mock server.\n- [PlantUML: with remote Agent](https://github.com/shire-lang/shire-demo/blob/master/.shire/toolchain/puml/plantuml-remote.shire) use remote agent to generate PlantUML code.\n- [Mermaid: with remote Agent](https://github.com/shire-lang/shire-demo/blob/master/.shire/toolchain/mermaid.shire) use remote agent to generate Mermaid code.\n- [Sonarlint: fix issue](https://github.com/shire-lang/shire-demo/blob/master/.shire/toolchain/sonarfix.shire) use Sonarlint to fix issue.\n\n## Shire Resources\n\nShire Cheatsheet\n\n![Shire Cheatsheet](docs/images/shire-sheet.svg)\n\nShire Data Architecture:\n\n![Shire Data Architecture](docs/images/shire-data-flow.svg)\n\nShire Resources\n\n- Documentation: [Shire AI Coding Agent Language](https://shire.phodal.com/)\n- [Shire Book: AI for software-engineering](https://aise.phodal.com/) (Chinese only)\n- [Shire.Run - the shareable AI coding agent](https://shire.run/)\n\n## Demo Video\n\nYoutube:\n\n[![Shire AI Coding Agent Language](https://img.youtube.com/vi/z1ijWOL1rFY/0.jpg)](https://www.youtube.com/watch?v=z1ijWOL1rFY)\n\nBilibili\n\n[![Shire AI Coding Agent Language](https://img.youtube.com/vi/z1ijWOL1rFY/0.jpg)](https://www.bilibili.com/video/BV1Lf421q7S7/)\n"
  },
  {
    "path": "docs/lifecycle/after-streaming.md",
    "content": "---\nlayout: default\ntitle: afterStreaming\nparent: Lifecycle\nnav_order: 3\n---\n\n`afterStreaming` 会在执行完 `onStreamingEnd` 后执行，用于下一步的处理，因此也叫 `TaskRoutes`。 `TaskRoutes`\n顾名思义，是一系列的任务处理路由，将根据条件执行不同的任务。\n\n### 单条件\n\n如下的代码，用于在 `onStreamingEnd` 后输出结果：\n\n```shire\n---\nafterStreaming: { \n    case condition {\n      default { print($output) }\n    }\n  }\n---\n\nhi\n```\n\n其中的 `output` 值是 LLM  处理了 hi 后的结果，可能是：`Hello, I'm xxx ...`。\n\n### 多条件\n\n```shire\n---\nafterStreaming: {\n    condition {\n      \"error\"       { output.length < 1 }\n      \"json-result\" { jsonpath(\"${'$'}.store.*\") }\n    }\n    case condition {\n      \"error\"       { notify(\"Failed to Generate JSON\") }\n      \"json-result\" { execute(\"sample.shire\") } /* go to execute sample.shire */\n      default       { notify(\"Failed to Generate JSON\") /* mean nothing */ }\n    }\n  }\n---\n```\n\n当 LLM 返回的结果是 JSON 时:\n\n```json\n{\n  \"store\": {\n    \"book\": [\n      {\n        \"category\": \"reference\",\n        \"author\": \"Nigel Rees\",\n        \"title\": \"Sayings of the Century\",\n        \"price\": 8.95\n      }\n    ]\n  }\n}\n```\n\n可以匹配到 `json-result` 条件，然后执行 `sample.shire`。\n"
  },
  {
    "path": "docs/lifecycle/before-streaming.md",
    "content": "---\nlayout: default\ntitle: beforeStreaming\nparent: Lifecycle\nnav_order: 1\n---\n\n`beforeStreaming` 即在 Streaming 开始前对生成的内容进行处理。\n\n### 示例：启动 MockServer\n\n```shire\n---\nname: \"Blog.sample\"\nbeforeStreaming: { mock(\"docs/mock_v0-stubs.json\") }\n---\n\n```\n\n### 示例：执行 Gradle Task\n\n```shire\n---\nname: \"Blog.sample\"\nbeforeStreaming: { execute(\":bootRun\") }\nonStreamingEnd: { parseCode | saveFile | openFile | runCode }\n---\n\nhi\n```\n"
  },
  {
    "path": "docs/lifecycle/lifecycle.md",
    "content": "---\nlayout: default\ntitle: Lifecycle\nnav_order: 5\nhas_children: true\npermalink: /lifecycle\n---\n\n# Lifecycle\n\nShire 的生命周期是围绕 IDE 与 LLM 生成过程所设计的。Shire 的生命周期包括：\n\n- beforeStreaming: 在 Streaming 开始前对生成的内容进行处理。\n- onStreaming：在 Streaming 过程中对生成的内容进行处理。（TBD）\n- onStreamingDone：在 Streaming 完成后通过一系列的后处理器对生成的内容进行处理。\n- afterStreaming：在 Streaming 完成后，根据条件执行后续操作，诸如执行新的 Shire 指令、调用其它工具等等。\n"
  },
  {
    "path": "docs/lifecycle/on-streaming-done.md",
    "content": "---\nlayout: default\ntitle: onStreamingDone\nparent: Lifecycle\nnav_order: 4\n---\n\n`onStreamingDone` 即在 Streaming 完成后通过一系列的后处理器对生成的内容进行处理。\n\n## 内置 PostHandler\n\n内置的后处理器包括：\n\n| 后处理器             | 描述               |\n|------------------|------------------|\n| timeMetric       | 记录操作耗时。          |\n| verifyCode       | 检查代码错误或 PSI 问题。  |\n| runCode          | 运行生成的文本代码。       |\n| parseCode        | 将文本解析为代码块。       |\n| saveFile         | 将文件保存到磁盘。        |\n| openFile         | 在编辑器中打开文件。       |\n| insertCode       | 在当前光标位置插入代码。     |\n| formatCode       | 格式化代码。           |\n| parseComment     | 解析注释为注释块。        |\n| insertNewline    | 插入新行。            |\n| append           | 将文本追加到文件中。       |\n| updateEditorText | 更新编辑器文本。         |\n| patch            | 打补丁。             |\n| diff             | 生成 diff view 对比。 | \n| openWebpage      | 打开网页。            |\n| showWebView      | 显示 WebView。      |\n\n最新版本见源码：com.phodal.shirecore.middleware.PostProcessorType\n\n## 示例\n\n### Hello, world 示例\n\n```shire\n---\nonStreamingEnd: { parseCode | saveFile | openFile | verifyCode | runCode }\n---\n\n生成一个 python hello world，使用 markdown block  返回\n```\n\n该代码会调用 LLM 生成一个 python hello world，然后将生成的代码块解析，保存到文件，打开文件，检查代码错误或 PSI 问题，最后运行生成的代码。\n\n对应的后处理器有：\n\n| 后处理器       | 描述                              |\n|------------|---------------------------------|\n| parseCode  | 从生成的结果中解析生成的代码块。                |\n| saveFile   | 将保存生成的代码到文件。                    |\n| openFile   | 打开生成的文件。                        |\n| verifyCode | 检查代码错误或 PSI 问题。                 |\n| runCode    | 运行生成的代码（取决于是否存在对应的 RunService）。 |\n\n### 结合变量的示例\n\n```shire\n---\nname: Summary\ndescription: \"Generate Summary\"\ninteraction: AppendCursor\ndata: [\"a\", \"b\"]\nwhen: $fileName.matches(\"/.*.java/\")\nvariables:\n  \"var2\": /.*ple.shire/ { cat | find(\"fileName\") | sort }\nonStreamingEnd: { append($var2) | saveFile(\"summary.md\") }\n---\n\nSummary webpage: $fileName\n```\n\n这里的 `var2` 是一个正则表达式，用于匹配文件名中包含 `ple.shire` 的文件，然后将其追加到文件中。\n\n`onStreamingEnd` 会在 Streaming 完成后执行，这里会将 `var2` 的内容追加到 output 中，最终保存到 `summary.md` 文件中。\n\n### 提交信息生成示例\n\n```shire\n---\nname: \"Commit message\"\ninteraction: AppendCursor\nactionLocation: CommitMenu\nonStreamingEnd: { parseCode | updateEditorText }\n---\n\n请为给定的变更（Diff）编写一个连贯但具有描述性的代码提交信息。\n\n背景信息：我现在使用 Git 编写一本开源电子书《AI 辅助软件工程：AI IDE 插件与编程智能体示例》，我需要为每个提交编写一个简洁但具有描述性的提交信息。\n\n要求：\n\n- 确保包含修改了什么以及为什么。\n- 以不超过 50 个字符的祈使句形式开头。\n- 然后留下一个空行，如有必要，继续详细说明。\n- 如果变更是一个 .shire 文件，说明我添加了一个新的示例。\n\n遵循常规提交规范，例如：\n\n- fix(authentication): 修复密码正则表达式模式问题\n- feat(storage): 添加对S3存储的支持\n- test(java): 修复用户控制器的测试用例\n- docs(architecture): 在主页添加架构图\n\nDiff：\n\n$currentChanges\n```\n"
  },
  {
    "path": "docs/lifecycle/on-streaming.md",
    "content": "---\nlayout: default\ntitle: onStreaming\nparent: Lifecycle\nnav_order: 2\n---\n\n{: .note }\n> **Note:** This page is reserved for future use. It will be used to describe the `onStreaming` lifecycle event.\n\n\n示例：\n\n```shire\n---\nonStreaming: { logging }\n---\n```\n\n当前支持的函数：\n\n- logging,\n- timing,\n- profiling,\n\n### logging\n\n> 记录 LLM prompt 日志。\n\n- `.shire-output/logging.log` 是 LLM 日志文件\n- `.shire-output/logging.jsonl` 是含历史 LLM 日志的 JSON 格式文件。\n\n详细见：LoggingStreamingService\n\n### timing\n\n> 记录生成 LLM prompt 时间。\n\n### profiling\n\n> 记录 LLM prompt 的性能分析，主要包含内存。\n"
  },
  {
    "path": "docs/quick-start.md",
    "content": "---\nlayout: default\ntitle: Quick Start\nnav_order: 3\n---\n\n\n## Installation\n\n- Using the IDE built-in plugin system:\n\n  <kbd>Settings/Preferences</kbd> > <kbd>Plugins</kbd> > <kbd>Marketplace</kbd> > <kbd>Search for \"shire\"</kbd> >\n  <kbd>Install</kbd>\n\n- Get from Marketplace:\n\n  <iframe width=\"245px\" height=\"48px\" src=\"https://plugins.jetbrains.com/embeddable/install/24549\"></iframe>\n\n- Manually:\n\n  Download the [latest release](https://github.com/phodal/shire/releases/latest) and install it manually using\n  <kbd>Settings/Preferences</kbd> > <kbd>Plugins</kbd> > <kbd>⚙️</kbd> > <kbd>Install plugin from disk...</kbd>\n\nExamples: [https://github.com/shire-lang/shire-spring-java-demo](https://github.com/shire-lang/shire-spring-java-demo)\n\n## Usage\n\n### \"Hello World\" example\n\n1. create a new file, like `hi.shire`, with content:\n\n```shire\nhi\n```\n\n2. Run file with `Shire` plugin, and you will see AI response result.\n\n```\nPrepare for running hi.shire...\nShire Script: /Volumes/source/ai/shire/hi.shire\nShire Script Compile output:\nUsed model: gpt-4o\nhi\n\n--------------------\nHello! How's it going?\n\nProcess finished with exit code 0\n```\n\n### Example 2\n\n1. first create a new file, like `sample.shire`, with content:\n\n```shire\nExplain code /file:src/main/java/com/example/Controller.java\n```\n\n2. Run file with `Shire` plugin, and you will see AI response result.\n\n### Use in IDE\n\nExample:\n\n```shire\n---\nname: \"AutoTest\"\ndescription: \"AutoTest\"\nactionLocation: ContextMenu\ninteraction: AppendCursor\nwhen: { $fileName.contains(\".java\") && $filePath.contains(\"src/main/java\") }\n---\n\n@ext-context.autotest\n\nWrite unit test for following ${context.language} code.\n\n${context.frameworkContext}\n\n/file:src/main/kotlin/com/phodal/blog/controller/UserController.kt\n```\n\n## Config LLM\n\nCurrent we support OpenAI, GLM, 零一万物, Moonshot AI, DeepSeek AI. You need to configure your API Token and Model in\n`Settings` -> `Tools` -> `Shire`.\n\n### OpenAI\n\n- LLM API Host: Empty\n- ModelName: gpt-4o\n- Engine Token: your key\n\n### GLM 示例\n\n- LLM API Host: https://open.bigmodel.cn/api/paas/v4/chat/completions\n- ModelName: glm-4\n- Engine Token: your key\n\n### 零一万物 \n\n- LLM API Host: https://api.lingyiwangwu.com/v1/chat/completions\n- ModelName: yi-34b-chat\n- Engine Token: your key\n\n### Moonshot AI\n\n- LLM API Host: https://api.moonshot.cn/v1/chat/completions\n- ModelName: moonshot-v1-8k\n- Engine Token: your key\n\n### DeepSeek AI\n\n- LLM API Host: https://api.deepseek.com/v1/chat/completions\n- ModelName: deepseek-chat\n- Engine Token: your key\n\n\n"
  },
  {
    "path": "docs/scene/ai-for-doc.md",
    "content": "---\nlayout: default\ntitle: AI 增强技术文档写作\nparent: Shire Scene\nnav_order: 2\n---\n\n## Why: 经典文档工程的解决思路\n\n过去在编写文档的一些痛点，诸如于：\n\n- 文档代码不同步。即文档的 API 变化可能落后于代码，导致 API 与文档出现不一致。\n- 频繁的 API 变更。API 变更时，文档需要手动进行更新，不能自动化同步。\n- 概念不统一。对于同一个概念，文档的不同地方描述不一致。\n- 重复的文档块。文档需要重复引用某一部分的文档，不能像代码一样引用。\n- 代码无法运行。按照文档的步骤下来编写的代码、复制的代码，是不能运行的。\n\n几年前，为了提供技术框架文档的质量，我研究了市面上主流的文档生成工具、框架文档构建等，也总结了一些文档生成的最佳实践，诸如：\n\n- 《[文档代码化](https://www.phodal.com/blog/isomorphism-document/)》\n- 《[文档同构：如何实现文档与代码的双向绑定？](https://www.phodal.com/blog/isomorphism-document/)》\n- 《[文档工程体验设计：重塑开发者体验](https://www.phodal.com/blog/documentation-enginnering-experience-design/)》\n- 《[API 库的文档体系支持：主流编程语言的文档设计](https://www.phodal.com/blog/api-ducumentation-design-dsl-base/)》\n\n但是，这些工具都无法满足我的需求，所以在过去我也编写了一系列的文档生成工具，诸如：Forming （ https://github.com/inherd/forming ）\n\n```Rust\n// doc-code: file(\"src/lib.rs\").line()[2, 5]\n// 读取 \"src/lib.rs\" 文件的第 2 到第 5 行\n// doc-section: file(\"src/lib.rs\").section(\"section1\")\n// 读取 \"src/lib.rs\" 文件中的 section1 相关的代码块\n```\n\n但是，这并不是一个完美的解决方案，因为你经常要因为代码的变化而去更新文档。\n\n## WHAT: AI 增强技术文档写作体验\n\n> AI 增强的技术文档写作体验是一种创新的方法，将先进的人工智能技术与文档编写和管理深度融合。它通过自动化工具和智能分析，简化了文档创建、\n> 更新和维护的流程，显著提高了文档的质量、准确性和一致性。\n\n作为一个实验项目，我们开始使用 Shire 来生成和维护技术文档。以下是几个主要场景示例：\n\n- 代码注释生成：通过分析代码内容，自动生成相应的文档注释，确保文档与代码同步更新，并减少手动维护的需求。\n- 自动化内容生成：基于已有的代码注释，自动生成完整的文档内容，包括 API 说明、使用示例等，显著降低了手动编写和更新文档的工作量。\n- 代码示例生成：自动读取项目中的测试用例，并将其作为文档中的示例代码展示，帮助读者更好地理解代码的实际应用场景。\n- 动态内容检索：根据特定关键词，智能检索文档内容，帮助用户快速定位所需信息，并自动生成相关文档段落。\n\n通过智能自动化的介入，文档编写变得更加高效和轻松，开发者能够专注于核心开发任务，同时确保文档始终与最新的代码和功能保持同步。\n\n## HOW: Shire 智能体语言示例\n\n在这里，我们主要会使用 Shire 语言的三个基本能力：\n\n- 借助 IDE 与项目和 LLM 进行交互\n- 基于 pattern-action 的变量定义和生成\n- 基于 RAG 函数的内容检索\n\n相关的示例，可以直接阅读 Shire 中的代码：https://github.com/phodal/shire\n\n### 基础能力：生成自定义风格注释\n\n为了更好的让 LLM 理解代码的函数，我们需要先使用 Shire 编写一个生成注释的指令。如下代码所示：\n\n    ---\n    name: \"生成注释\"\n    interaction: InsertBeforeSelection\n    actionLocation: ContextMenu\n    when: $fileName.contains(\".kt\") && $filePath.contains(\"src/main/kotlin\")\n    onStreamingEnd: { insertNewline | formatCode }\n    ---\n    \n    为如下的代码编写注释，使用 KDoc 风格：\n    \n    ```$language\n    $selection\n    ```\n    \n    只返回注释\n\n在这里，我们定义了一个专用于生成 Kotlin 代码注释的指令，通过右键菜单触发。当用户在 Kotlin 文件中选择代码后，Shire\n会自动为选中的代码生成相应的注释，\n并插入到代码之前。\n\n### 读取与生成：借助 pipeline 函数，自动生成文档文件\n\n随后，我们就可以根据目标文档路径，诸如 `docs/shire/shire-builtin-variable.md` 编写对应的生成逻辑。诸如于：\n\n```shire\n---\nname: \"Context Variable\"\ndescription: \"Here is a description of the action.\"\ninteraction:  RunPanel\nvariables:\n  \"contextVariable\": /ContextVariable\\.kt/ { cat }\n  \"psiContextVariable\": /PsiContextVariable\\.kt/ { cat }\nonStreamingEnd: { parseCode | saveFile(\"docs/shire/shire-builtin-variable.md\") }\n---\n\n根据如下的信息，编写对应的 ContextVariable 相关信息的 markdown 文档。\n\n你所需要包含的 ContextVariable 信息如下：\n\n$contextVariable\n\n...\n```\n\n在这里，我们定义了一个变量 `contextVariable`，它的值是读取所有的 `ContextVariable.kt` 文件的结果。在运行的时候，Shire\n会将这个变量的值\n编译到 prompt 中，并发送给 LLM，以生成对应的文档。当 LLM 生成的文档返回后，我们会解析出其中的代码块，并保存到指定的文件中。\n\n除此，当代码库中包含有测试用例时，我们就可以配置示例作为代码示例：\n\n```shire\n---\nname: \"Hobbit Hole\"\ndescription: \"Here is a description of the action.\"\ninteraction:  RunPanel\nvariables:\n  \"currentCode\": /HobbitHole\\.kt/ { cat }\n  \"testCode\": /ShireCompileTest\\.kt/ { cat }\nonStreamingEnd: { saveFile(\"docs/shire/shire-hobbit-hole.md\")  }\n---\n\n根据如下的代码用例、文档，编写对应的 HobbitHole 相关信息的 markdown 文档。\n...\n\n```\n\n当然了，也可以直接读取原来的文档，然后进行更新。\n\n### 示例：结合 RAG 技术，自动化分析文档\n\n对于更复杂的场景，则可以直接结合 RAG 与 Shire 的 workflow 来实现。如下所示：\n\n```shire\n---\nname: \"Semantic Search\"\nvariables:\n  \"code\": /.*.kt/ { splitting | embedding }\n  \"input\": \"博客创建流程\"\n  \"lang\": \"java\"\nafterStreaming: {\n    case condition {\n      default { searching($output) | execute(\"SummaryQuestion.shire\", $output, $input, $lang) }\n    }\n }\n---\nYou are a coding assistant who helps the user answer questions about code in their workspace by providing a list of\n relevant keywords they can search for to answer the question.\n...\n```\n\n上述代码中，我们定义了一个变量 `code`，它的值是对所有的 `*.kt` 文件进行分割，并进行向量化。而这里的的 `input` 则是用户输入的问题，\n用于搜索相关的文档内容。\n\n在执行时，会将用户的问题发送给\nLLM，由其生成关键词，然后在本地进行检索，最后，将结果发送给下一个流程，即 `SummaryQuestion.shire`。\n在 `SummaryQuestion.shire` 中，会将检索结果进行总结，然后生成对应的文档。\n\n## 总结\n\n在这篇文档中，我们分享了使用 Shire 智能体语言来生成和维护技术文档的经验和思考。Shire 是我们在开发中不断探索和改进的一种智能语言工具，\n它不仅简化了文档编写的流程，还有效解决了传统文档编写中的诸多痛点。\n"
  },
  {
    "path": "docs/scene/scene.md",
    "content": "---\nlayout: default\ntitle: Shire Scene\nnav_order: 9\nhas_children: true\npermalink: /scene\n---\n\n"
  },
  {
    "path": "docs/scene/secondary-research.md",
    "content": "---\nlayout: default\ntitle: Secondary Research\nparent: Shire Scene\nnav_order: 1\n---\n\n> Desk Research（又称为二级研究）是一种利用已有的、已发布的信息进行分析和整理的研究方法。与初级研究不同，初级研究主要关注新数据的收集与生成，\n> 而二级研究则专注于对已有数据和研究成果的总结、整合和综合分析。典型的二级研究来源包括教科书、百科全书、新闻文章、评论文章和元分析等。\n\n这些文献通常不会包含详细的“方法”部分，因为它们依赖的是已有的研究数据，而非原始数据的生成。\n\n## WebResource\n\n```shire\n---\nvariables:\n  \"websites\": /*\\.md/ { capture(\"docs/crawlSample.md\", \"link\") | crawl() | thread(\"summary.shire\") }\n  \"confluence\": { thread(\"confluence.bash\", param1, param2) }\n  \"pythonNode.js\": { thread(\"python.py\", param1, param2) }  \n---\n\n[website]: it will extract all the .md files and crawl them, then thread them with summary.shire, then return the result.\n```\n\n- `capture` 函数, 参数 1: Language, 参数 2: AST Node Type\n- `crawl` 函数, 参数 1: URLs: List<String>\n- `thread` 函数, 参数 1: `Script File`, 参数...: Parameters：`Array<String>`\n    - 如果 crawl 返回的是一个数组，那么 thread 会对数组中的每一个元素执行一次\n\n## 真实世界示例：AI 辅助运维桌面研究\n\n详细见如下的代码示例：\n\n### Main.shire\n\n```shire\n---\nvariables:\n  \"crawl\": /crawlSample\\.md/ { capture(\"docs/crawlSample.md\", \"link\") | crawl() | thread(\".shire/research/summary.shire\") }\n  \"article\": /crawlSample\\.md/ { cat }\nonStreamingEnd: { saveFile(\"docs/output.md\") }\n---\n\n根据如下的草稿和对应的资料，编写一篇对应主题的文章。\n\n文章草稿如下：\n\n$article\n\n相关的资料如下：\n\n$crawl\n```\n\n### summary.shire\n\n```shire\n使用中文总结如下的开源项目。\n\n要求：\n\n1. 给出项目的基本介绍、首页和文档地址\n2. 列举 5 个关键特性\n\n$output\n````\n\n### crawlSample.md\n\n```md\n# AI 辅助软件工程：CLI 命令生成\n\n## 为什么需要 AI 来辅助 CLI？\n\n## 什么是 CLI 命令生成\n\n## 行业示例\n\n1. [nvtop](https://github.com/Syllo/nvtop) - <small>NVIDIA GPUs htop like monitoring tool</small>\n2. [nvitop](https://github.com/XuehaiPan/nvitop) - <small>An interactive NVIDIA-GPU process viewer and beyond.</small>\n3. [aichat](https://github.com/sigoden/aichat) - <small>all-in-one AI powered CLI chat and copilot.</small>\n4. [aider](https://github.com/paul-gauthier/aider) - <small>AI pair programming in your terminal</small>\n5. [elia](https://github.com/darrenburns/elia) - <small>A TUI ChatGPT client built with Textual</small>\n6. [gpterminator](https://github.com/AineeJames/ChatGPTerminator) - <small>A TUI for OpenAI's ChatGPT</small>\n7. [gtt](https://github.com/eeeXun/gtt) - <small>A TUI for Google Translate, ChatGPT, DeepL and other AI\n   services.</small>\n8. [ollama](https://github.com/ollama/ollama) - <small>get up and running with large language models locally.</small>\n9. [oterm](https://github.com/ggozad/oterm) - <small>A text-based terminal client for ollama.</small>\n10. [tgpt](https://github.com/aandrew-me/tgpt) - <small>AI Chatbots in the terminal without needing API keys.</small>\n11. [yai](https://github.com/ekkinox/yai) - <small>Your AI powered terminal assistant</small>\n```\n\n## API Resource by Bash\n\n- Confluence API\n- Jira API\n\n#### Confluence API\n\nhttps://developer.atlassian.com/cloud/confluence/using-the-rest-api/\n\n```bash\ncurl --request <method> '/rest/api/content/search?limit=1&cql=id!=0 order by lastmodified desc' \\\n--header 'Accept: application/json' \\\n--header 'Authorization: Basic <encoded credentials>'\n```\n\n#### Jira API\n\nhttps://developer.atlassian.com/cloud/jira/platform/rest/v3/intro/\n\n```bash\ncurl --request <method> '<url>?<parameters>' \\\n--header 'Accept: application/json' \\\nAuthorization: Basic <encoded credentials>'\n```\n\n## Design Pattern\n\n### AutoFeature\n\n- autoAnalysis\n- autoImage\n- autoDraft\n- autoSlide\n- block embedded\n- Notion like Block for Reference\n- Daily randomTip\n- toImage\n- Icon\n\n### Workflow Design\n\ntopic\n\n- capture\n- summary\n- insight\n- express\n"
  },
  {
    "path": "docs/shire/shire-builtin-variable.md",
    "content": "---\nlayout: default\ntitle: Shire Builtin Variable\nparent: Shire Language\nnav_order: 4\n---\n\n在 Shire 中，我们提供了一些内置变量，以便用户可以更方便地使用。\n\n当前支持的内置变量有：\n\n- [ContextVariable](#contextvariable) 用于提供当前文件的上下文信息。\n- [PsiContextVariable](#psicontextvariable) 用于提供当前 AST/PSI 语法树的上下文信息。\n- [Toolchain Variable](#toolchain-variable) 用于提供项目的工具链信息。\n\n## ContextVariable\n\n| 变量名              | 描述                                                     |\n|------------------|--------------------------------------------------------|\n| selection        | User selection code/element's in text                  |\n| selectionWithNum | User selection code/element's in text with line number |\n| beforeCursor     | All the text before the cursor                         |\n| afterCursor      | All the text after the cursor                          |\n| fileName         | The name of the file                                   |\n| filePath         | The path of the file                                   |\n| methodName       | The name of the method                                 |\n| language         | The language of the current file                       |\n| commentSymbol    | The comment symbol of the language                     |\n| all              | All the text                                           |\n\n## PsiContextVariable\n\n| 变量名                 | 描述                                                                                 |\n|---------------------|------------------------------------------------------------------------------------|\n| currentClassName    | The name of the current class                                                      |\n| currentClassCode    | The code of the current class                                                      |\n| currentMethodName   | The name of the current method                                                     |\n| currentMethodCode   | The code of the current method                                                     |\n| relatedClasses      | The related classes based on the AST analysis                                      |\n| similarTestCase     | The similar test cases based on the TfIDF analysis                                 |\n| imports             | The import statements required for the code structure                              |\n| isNeedCreateFile    | Flag indicating whether the code structure is being generated in a new file        |\n| targetTestFileName  | The name of the target test file where the code structure will be generated        |\n| underTestMethodCode | The code of the method under test                                                  |\n| frameworkContext    | The framework information in dependencies of current project                       |\n| codeSmell           | Include psi error and warning                                                      |\n| methodCaller        | The method that initiates the current call                                         |\n| calledMethod        | The method that is being called by the current method                              |\n| similarCode         | Recently 20 files similar code based on the tf-idf search                          |\n| structure           | The structure of the current class, for programming language will be in UML format |\n| changeCount         | The number of changes in the current file                                          |\n| lineCount           | The number of lines in the current element                                         |\n| complexityCount     | The complexity of the current element                                              |\n\n{: .note }\nSome languages may not support all the variables, depending on the language support."
  },
  {
    "path": "docs/shire/shire-custom-variable.md",
    "content": "---\nlayout: default\ntitle: Shire Custom Variable\nparent: Shire Language\nnav_order: 5\n---\n\nShire 自定义变量的核心是 Variable -> Pattern-Action 模型。在 Shire 中，Pattern-Action 模型是一种用于处理数据的模式匹配和动作执行模型。\n如下图所示：\n\n![](../images/shire-pattern-action.svg)\n\n- variable 是用户自定义的变量。\n- pattern 是用于筛选输入数据的规则或标准，可以是一组文件名模式、正则表达式，用于识别哪些数据需要进一步处理。\n- action 是当数据符合 pattern 时需要执行的任务，由一系列命令组成，描述了如何处理匹配的数据。\n    - function name 是 Shire 内置的函数，用于处理数据。\n    - arguments 是函数的参数，用于传递数据。\n    - pipe(`|`) 是用于连接多个操作的管道操作符，每个操作都是一个函数，其输出作为下一个函数的输入。\n\nCustom Variable 的三种实现方式：\n\n```shire\n---\nvariables:\n  \"var1\": \"demo\" // Value\n  \"var2\": /.*.java/ { find(\"error.log\") | sort | xargs(\"rm\")} // Pattern-Action\n  \"var3\": /.*.log/ {\n    case \"$0\" {\n      \"error\" { find(\"ERROR\") | sort | xargs(\"notify_admin\") }\n      \"warn\" { find(\"WARN\") | sort | xargs(\"notify_admin\") }\n      \"info\" { find(\"INFO\") | sort | xargs(\"notify_user\") }\n      default  { find(\"ERROR\") | sort | xargs(\"notify_admin\") }\n    }\n  }\n---\n```\n\n多变量示例：\n\n```shire\n---\nname: \"类图分析\"\nvariables:\n  \"controllers\": /.*.java/ { cat }\n  \"tokens\": /any/ { tokenizer($controllers, \"word\") }\n  \"chinese\": /any/ { tokenizer(\"孩子上了幼儿园 安全防拐教育要做好\", \"jieba\") }\n---\n\n$controllers\n```\n\n在这个示例中，我们在 tokens 变量中使用了 controllers 变量的值，这样就可以在变量之间传递数据。\n\n## Shire 常规自定义变量\n\n常规自定义变量，可以用于：\n\n- 对应不同类型文件，自定义 prompt。\n\n## Variable Pattern Action\n\n在Shire中，我们借鉴了 Unix/Linux 的设计理念和 Shell 编程模式，特别是 Pattern-Action 模型。该模型通过定义模式和动作来处理数据：\n\n1. **模式（Pattern）**：这代表用于筛选输入数据的规则或标准。在Unix/Linux中，这可以是一组文件名模式、正则表达式或其他条件，用于识别哪些数据需要进一步处理。\n2. **动作（Action）**：这是当数据符合模式时需要执行的任务。它由一系列命令组成，描述了如何处理匹配的数据。\n\n{: .note }\n注意：如果 pattern 为 `any` 或者 `null`，表示不进行筛选，直接执行动作。\n\n例如，在Shire中，我们可以这样定义一个 Pattern-Action：\n\n```text\n/.*.java/ { find(\"error.log\") | print }\n```\n\n这里，`/*.java/` 是模式部分，用于匹配所有以 `.java` 结尾的文件，而 `{ find(\"error.log\") | print }` 是动作部分，\n表示对匹配的文件执行一系列操作：首先搜索包含 \"error.log\" 的行，然后对这些行进行排序，最后将结果输出到标准输出。\n\n在 Shire 中，我们利用了 Intellij 的强大功能，如正则表达式匹配、代码高亮和语法检查，以帮助用户更高效地编写代码。例如，\n使用正则表达式 `.*.java` 可以轻松地匹配所有 Java 源文件。\n\n明白了！让我为您完整优化一下文档，包括对示例的详细解释：\n\n### 示例 1：Pattern-Action Pipeline\n\n```shire\n---\nvariables:\n  \"var2\": /.*.java/ { cat | find(\"error.log\") | sort | cat }\n  \"extContext\": /build\\.gradle\\.kts/ { cat | find(\"org.springframework.boot:spring-boot-starter-jdbc\") | print(\"This project use Spring Framework\") }\n---\n```\n\n在这个示例中：\n\n- **`var2` 变量**：匹配所有以 `.java` 结尾的文件。动作部分使用了管道操作符 `|`，依次执行了 `find(\"error.log\")`、`sort`\n  ，然后再次使用 `cat` 输出结果。\n\n- **`extContext` 变量**：匹配所有名为 `build.gradle.kts`\n  的文件。动作部分执行了 `find(\"org.springframework.boot:spring-boot-starter-jdbc\")`，并输出一条指示该项目使用 Spring\n  Framework 的信息。\n\n### 示例 2：Pattern-Action 多 CASE\n\n```shire\n---\nvariables:\n  \"testTemplate\": /\\(.*\\).java/ {\n    case \"$1\" {\n      \"Controller\" { cat(\".shire/templates/ControllerTest.java\") }\n      \"Service\" { cat(\".shire/templates/ServiceTest.java\") }\n      default  { cat(\".shire/templates/DefaultTest.java\") }\n    }\n  }\n---\n```\n\n在这个示例中：\n\n- **`testTemplate` 变量**：匹配所有以 `(.*)` 开头、`.java` 结尾的文件。根据不同的匹配结果执行不同的动作。\n    - 如果匹配到 `Controller`，则输出 `ControllerTest.java` 的内容。\n    - 如果匹配到 `Service`，则输出 `ServiceTest.java` 的内容。\n    - 如果没有匹配到上述任何值（`default`），则输出 `DefaultTest.java` 的内容。\n\n### 示例 3：变量二次处理\n\n用户自定义变量可以对 Shire 自带变量进行二次处理，例如：\n\n```shire\n---\nname: \"添加测试\"\nactionLocation: ContextMenu\nvariables:\n  \"sourceCode\": /any/ { print($filePath) | sed(\"src\\/test\\/\", \"src/main/\") | sed(\"Test.java\", \".java\") | cat }\nonStreamingEnd: { parseCode | patch($filePath, $output) }\n---\n\n```\n\n## Pattern Function\n\n| 函数类别      | 功能描述             | 参数                                                                          | 示例                                          |\n|-----------|------------------|-----------------------------------------------------------------------------|---------------------------------------------|\n| find      | 基于文本搜索           | `text`: 要搜索的文本                                                              | `find(\"error\")`                             |\n| grep      | 使用模式进行搜索         | `patterns`: 要搜索的模式                                                          | `grep(\"[a-zA-Z]+Controller\")`               |\n| sed       | 查找和替换操作          | `pattern`: 要查找的模式<br>`replacements`: 替换的字符串<br>`isRegex`: 是否为正则表达式          | `sed(\"s/old/new/g\")`                        |\n| sort      | 排序操作             | `arguments`: 排序所需的参数                                                        | `sort`                                      |\n| uniq      | 去除重复行            | `texts`: 要处理的文本                                                             | `uniq(\"line1\", \"line2\", \"line1\")`           |\n| head      | 获取文件的前几行         | `number`: 要获取的行数                                                            | `head(10)`                                  |\n| tail      | 获取文件的末尾几行        | `number`: 要获取的行数                                                            | `tail(5)`                                   |\n| xargs     | 处理变量             | `variables`: 要处理的变量                                                         | `xargs(\"arg1\", \"arg2\")`                     |\n| print     | 打印文本             | `texts`: 要打印的文本                                                             | `print(\"Hello\", \"World\")`                   |\n| cat       | 连接文件             | `paths`: 要连接的文件路径                                                           | `cat(\"file1.txt\", \"file2.txt\")`             |\n| notify    | 使用 IDE 通知        | `message`: 要显示的通知消息                                                         | `notify(\"Process completed successfully.\")` |\n| splitting | 分割文本或文件          | `paths`: 要分割的文本或文件路径                                                        | `splitting(\"file.txt\", \"file2.txt\")`        |\n| embedding | 嵌入文本             | `entries`: 要嵌入的文本条目                                                         | `embedding(\"entry1\", \"entry2\")`             |\n| searching | 搜索文本             | `text`: 要搜索的文本, threshold: 置信度阈值（string, 默认 0.5）                            | `searching(\"pattern\")`                      |\n| reranking | 重新排序             | `type`: 重排类型，默认  lostInTheMiddle                                            | `reranking(\"pattern\")`                      |\n| caching   | 缓存语义             | `text`: 要缓存的文本                                                              | `caching(\"data\")`                           |\n| redact    | 屏蔽敏感数据           |                                                                             | `redact()`                                  |\n| jsonpath  | 使用 JsonPath 选择数据 | `jsonPath`: JsonPath 表达式,其中 jsonString 为可选                                  | `jsonpath(jsonString, \"$.store.*\")`         |\n| batch     | 批处理操作            | `fileName`: Shire  文件名，: paths`: 要处理的文件路径                                   | `batch(\"file1.shire\", \"file2.txt\")`         |\n| tokenizer | 分词操作             | `text`: 待分词的文本, type: 分词类型（`word`, `naming`, `stopwords`，`jieba`，默认 `word`） | `tokenizer(\"text\") `                        |\n| lineNo    | 行号操作             | `text`: 待处理的文本                                                              | `lineNo(\"text\")`                            |\n\n编排函数\n\n| 函数类别           | 功能描述                       | 参数                 | 示例                               |\n|----------------|----------------------------|--------------------|----------------------------------|\n| execute        | 异步执行 Shire 脚本、GradleTask 等 | `string`: 要执行的脚本内容 | `execute(\"next-script.shire\")`   |\n| thread         | 线程执行（异步）                   | `path`: 要执行的脚本路径   | `thread(\"script.shire\")`         |\n| approveExecute | 等待确认再执行                    | `path`: 要执行的脚本路径   | `approveExecute(\"script.shire\")` |\n\nExecute\n\n- Shire 脚本执行: `execute(\"next-script.shire\")`\n- GradleTask 执行: `execute(\":bootRun\")`\n- 文件执行: `execute(\"next-script.sh\")`\n\n"
  },
  {
    "path": "docs/shire/shire-env.md",
    "content": "---\nlayout: default\ntitle: Shire Environment\nparent: Shire Language\nnav_order: 10\n---\n\nShire Environment 用于定义 Shire 的环境变量，用于存储一些敏感信息。 使用方式 `.shireEnv.json` 文件来存储环境变量，Shire\n将会自动加载这种文件。\n\n当前 Shire Env 支持两种变量：\n\n- `development`：配置 Token、API Key 等信息。\n- `models`：配置模型信息（在 `0.7.4` 版本后支持）。\n\n## `.shireEnv.json` 文件\n\n`.shireEnv.json` 用于存储环境变量，Shire 将会自动加载这种文件，当前只支持 `development` 环境。\n\n```json\n{\n  \"development\": {\n    \"apiKey\": \"xxx\"\n  },\n  \"models\": [\n    {\n      \"title\": \"quickModel\",\n      \"apiKey\": \"sk-xxx\",\n      \"model\": \"gpt-4o-mini\",\n      \"temperature\": 0.3\n    },\n    {\n      \"title\": \"gpt4o\",\n      \"apiKey\": \"sk-xxx\",\n      \"model\": \"gpt-4o\"\n    },\n    {\n      \"title\": \"glm-4-plus\",\n      \"apiKey\": \"xxx\",\n      \"model\": \"glm-4-plus\",\n      \"apiBase\": \"https://open.bigmodel.cn/api/paas/v4/chat/completions\"\n    }\n  ]\n}\n```\n\n### 使用你的  apiKey\n\n在 Shell 中放在 `${}` 中即可：\n\n```shell\ncurl -X POST 'https://api.dify.ai/v1/completion-messages' \\\n  --header \"Authorization: Bearer ${apiKey}\" \\\n  --header 'Content-Type: application/json' \\\n  --data-raw '{\n      \"inputs\": {\"feature\": \"Hello, world!\", \"story_list\": ${storyList}},\n      \"response_mode\": \"streaming\",\n      \"user\": \"phodal\"\n  }'\n```\n\n### Model 配置详细示例：\n\n```kotlin\nclass LlmConfig(\n    val title: String,\n    val provider: String = \"openai\",\n    val apiBase: String = \"https://api.openai.com/v1/chat/completions\",\n    val apiKey: String,\n    val model: String,\n    val temperature: Double = 0.0,\n    val maxTokens: Int? = 1024\n)\n```\n"
  },
  {
    "path": "docs/shire/shire-foreign-function.md",
    "content": "---\nlayout: default\ntitle: Shire Foreign Function\nparent: Shire Language\nnav_order: 8\n---\n\n# Foreign Function\n\n{: .note }\n注意：在 `0.9` 版本后支持\n\n示例：\n\n```shire\n---\nfunctions:\n  normal: \"defaultOutput.py\"(string)\n  output: \"multipleOutput.py\"(string) -> content, size // TODO, No Implemented\n  special: \"accessFunctionIfSupport.py\"::resize(string, number, number) -> image // TODO, No Implemented\n---\n```\n\n语言支持：\n\n- Node.js (`.js` 文件)\n- Shell (`.sh` 文件)\n- Python (`.py` 文件)\n- Kotlin Script (`.kts` 文件)\n\n## Quick Start\n\nThe Shire code:\n\n```shire\n---\nfunctions:\n  normal: \".shire/ffi/hello.js\"(string)\nvariables:\n  \"text\": /.*ple.shire/ { normal(\"world\") }\n---\n\nhello, $text\n```\n\n### JavaScript example\n\n```javascript\nconst args = process.argv.slice(2);\nconsole.log(args[0]);\n```\n\n### Kotlin Script example\n\nIn Kotlin Script mode, you can just use `args` without `main` function.\n\n```kotlin\nif (args.isNotEmpty()) {\n    println(\"${args[0]}!\")\n} else {\n    println(\"No args...\")\n}\n```"
  },
  {
    "path": "docs/shire/shire-hobbit-hole.md",
    "content": "---\nlayout: default\ntitle: Shire Config (Hobbit Hole)\nparent: Shire Language\nnav_order: 3\n---\n\nHobbit Hole 用于定义数据处理流程与 IDE 交互逻辑。\n\n## Hobbit Hole 概述\n\n```shire\n---\nname: \"Summary\"\ndescription: \"Generate Summary\"\ninteraction: AppendCursor\nactionLocation: ContextMenu\n---\n```\n\n### 属性说明\n\n- `name`：Shire 命令的显示名称，将显示在 IDE 的 UI 中，基于 [HobbitHole.interaction](#interaction)。\n- `description`：操作的提示信息，将显示在 UI 的 Hover tips 中。\n- `interaction`：操作的输出可以是编辑器中的流文本，当使用 [InteractionType.AppendCursorStream](#interaction) 时。\n- `actionLocation`：操作的位置，应该是 [ShireActionLocation](#actionlocation) 中的一个，默认为 [ShireActionLocation.RUN_PANEL]。\n- `selectionStrategy`：选择要应用操作的元素的策略。\n- `variables`：用于构建变量的带有 PatternAction 的变量列表。\n- `when`：用于 [com.intellij.codeInsight.intention.IntentionAction.isAvailable]、[com.intellij.openapi.project.DumbAwareAction.DumbAwareAction.update] 检查是否显示菜单的条件。\n- `ruleBasedFilter`：应用于操作的规则文件列表。\n- `onStreamingEnd`：在流处理结束后执行的后中间件操作列表，如日志记录、指标收集、代码验证、运行代码、解析代码等。\n- `afterStreaming`：流结束后执行任务的决策，路由到不同的任务。\n- `shortcut`：操作的 IDE 快捷键，使用 IntelliJ IDEA 的快捷键格式。\n- `enabled`：是否启用操作。\n- `model`: 使用的大语言模型，使用 `.shireEnv.json` 定义。\n- `userData`：其余数据。\n\n### 示例代码\n\n```shire\n---\nname: \"Summary\"\ndescription: \"Generate Summary\"\ninteraction: AppendCursor\nactionLocation: ContextMenu\nwhen: $fileName.matches(\"/.*.java/\")\nvariables:\n  \"var1\": \"demo\"\n  \"var2\": /**.java/ { find(\"error.log\") | sort | xargs(\"rm\")}\n---\n```\n\n## Hobbit Hole 属性\n\n### Interaction Type\n\n```kotlin\nenum class InteractionType(val description: String) {\n    AppendCursor(\"Append content at the current cursor position\"),\n    AppendCursorStream(\"Append content at the current cursor position, stream output\"),\n    OutputFile(\"Output to a new file\"),\n    ReplaceSelection(\"Replace the currently selected content\"),\n    ReplaceCurrentFile(\"Replace the content of the current file\"),\n    InsertBeforeSelection(\"Insert content before the currently selected content\"),\n    RunPanel(\"Show Result in Run panel which is the bottom of the IDE\"),\n    OnPaste(\"Copy the content to the clipboard\"),\n    RightPanel(\"Show Result in Right panel which is the right of the IDE\"),\n    StreamDiff(\"Use streaming diff to show the result\")\n    ;\n}\n```\n\n{: .note }\n由于性能原因，OnPaste 暂时只支持 Java 和 Kotlin 语言，并且需要行数多于 5 行。\n\n### Action Location\n\n```kotlin\nenum class ShireActionLocation(val location: String, val description: String) {\n    CONTEXT_MENU(\"ContextMenu\", \"Show in Context Menu by Right Click\"),\n    INTENTION_MENU(\"IntentionMenu\", \"Show in Intention Menu by Alt+Enter\"),\n    TERMINAL_MENU(\"TerminalMenu\", \"Show in Terminal panel menu bar\"),\n    COMMIT_MENU(\"CommitMenu\", \"Show in Commit panel menu bar\"),\n    RUN_PANEL(\"RunPanel\", \"Show in Run panel which is the bottom of the IDE\"),\n    INPUT_BOX(\"InputBox\", \"Show in Input Box\"),\n    DATABASE_MENU(\"DatabaseMenu\", \"Show in Database panel menu bar\"),\n    CONSOLE_MENU(\"ConsoleMenu\", \"Show in Console panel menu bar\"),\n    VCS_LOG_MENU(\"VcsLogMenu\", \"Show in VCS Log panel menu bar\"),\n    CHAT_BOX(\"ChatBox\", \"Show in Chat Box\"), // 将默认使用 RigthPanel 作为展示位置\n    INLINE_CHAT(\"InlineChat\", \"Show in Inline Chat\"),\n\n    /// external plugins\n    EXT_SONARQUBE_MENU(\"ExtSonarQubeMenu\", \"Show in SonarQube panel menu bar\"),\n    ;\n}\n```\n\n{: .note }\n当 COMMIT_MENU 项多于一个时，将会用 PopupMenu 显示；当只有一个时，将直接显示在 Commit 菜单中。\n\n#### ChatBox 示例\n\nChatBox 是在 Shire ToolWindow 中的一个输入框，用户可以在这里输入内容，然后调用大语言模型。使用事项如下：\n\n- 默认使用 `RigthPanel` 作为展示位置，使用 `ChatBox` 作为 `actionLocation`。\n- 当用户创建了 `actionLocation: ChatBox` 的 Shire 代码时，将会读取用户的输入作为提示词的一部分。 \n\n如下是一个自定义 ChatBox 的 Shire 示例：\n\n```shire\n---\nname: \"shire-chat-box\"\ndescription: \"Shire Chat Box\"\ninteraction: RightPanel\nactionLocation: ChatBox\n---\n\n根据用户的输入生成 Java 代码\n\n$chatPrompt\n\n```\n\n此时，当用户在 ChatBox 中输入 `create hello world` 时，会将 `hello world` 作为 `chatPrompt` 的值。生成最终的提示词：\n\n```shire\n根据用户的输入生成 Java 代码 \n\ncreate hello world\n```\n\n### Inline Chat 示例\n\nInline Chat 是在当前文件器的行号附近的一个由 Shire Icon 触发的输入框，用户可以在这里输入内容，然后调用大语言模型。使用事项如下：\n\n- 用户需要自行创建一个 Shire 文件，然后将 `actionLocation` 设置为 `InlineChat`。\n\n"
  },
  {
    "path": "docs/shire/shire-lang.md",
    "content": "---\nlayout: default\ntitle: Shire Language Introduction\nparent: Shire Language\nnav_order: 1\n---\n\nShire 主要由两部分组成：\n\n- Hobbit Hole，用于定义数据处理流程与 IDE 交互 逻辑。\n- Shire Template，用于编译和生成最终的提示词。\n\n## Hobbit Hole \n\nHere is the detail\n\n- Condition Visible: `when` condition to display the code block\n- Variables:\n  - Context Variables: `context` to get the context of the current file \n  - Pattern Action: use Pattern (Regex) to match the source data, and use Unix-like command to process the data.\n- PostMiddle code processor\n- Output Control Flow\n\n## Shire Template\n\nWe use Velocity Template to generate the final prompt, and you can access the context variables in the template.\n\n```shire\nExplain follow code\n\n$beforeCursor\n```\n"
  },
  {
    "path": "docs/shire/shire-template.md",
    "content": "---\nlayout: default\ntitle: Shire Command & Template\nparent: Shire Language\nnav_order: 2\n---\n\n## Shire Template\n\nWe use velocity template to generate code. Here is a simple example:\n    \n    Refactor code:\n    \n    ```${context.language}\n    ${context.selection}\n    ```\n\n## Shire Commands\n\nsee in: ShireCommand.kt\n\n- `/file`: read file content, format: `/file:<file-path>`, example: `/file:src/main/java/com/example/Controller.java`.\n- `/write`: write file content, format: `file#L1-L12`, example: `src/main/java/com/example/Controller.java#L1-L12`\n- `/rev`: read git change by git revision.\n- `/run`: run code, especially for test file, which is the best way to run code.\n- `/patch`: apply patches to file.\n- `/commit`: commit changes to git\n- `/symbol`: get child by symbol, like get Class by package name, format: `java.lang.String#length`,\n  example: `<package>.<class>#<method>`\n- `/shell`: run shell command or shell script, like `ls`, `pwd`, etc.\n- `/browse`: browse web page, like `https://ide.unitmesh.cc`\n- `/refactor`: refactor code, like `rename`, `delete`, `move` etc.\n- `/goto`: go to file, like `src/main/java/com/example/Controller.java#L1C1`\n- `/database`: query database, like `select * from table where id = 1`\n\n### File Command\n\nwe keep \"/\" as `File.separator` for macOS, Windows and Unix.\n\nRead file content:\n\n```shire\nExplain code /file:src/main/java/com/example/Controller.java\n```\n\nwill call LLM to handle it.\n\n### Write Command\n\nwrite content to file:\n\n    /write:src/main/java/com/example/Controller.java#L1-L12\n    ```java\n    public class Controller {\n        public void method() {\n            System.out.println(\"Hello, World!\");\n        }\n    }\n    ```\n\n### Rev Command\n\nRead git change by git revision:\n\n```shire\nExplain code /rev:HEAD~1\n```\n\nwill call LLM to handle it.\n\n### Run Command\n\nRun file:\n\n```shire\n/run:src/main/java/com/example/Controller.java\n```\n\nPS: current only support for TestFile, since UnitTest is the best way to run code.\n\n### Symbol Command\n\nGet child elements by symbol, like get Class by package name.\n\n```shire\n/symbol:com.phodal.shire.demo\n```\n\nThe output will be:\n\n    ```java\n    com.phodal.shire.demo.MathHelper\n    com.phodal.shire.demo.DemoApplication\n    com.phodal.shire.demo.MathHelperTest\n    com.phodal.shire.demo.DemoApplicationTests\n    ```\n\nGet method will return code:\n\n```shire\n/symbol:com.phodal.shire.demo.MathHelper.calculateInsurance\n```\n\nThe output will be:\n\n    ```java\n    public static double calculateInsurance(double income) {\n        if (income <= 10000) {\n            return income * 0.365;\n        } else {\n            return income * 0.365 + 1000;\n        }\n    }\n    ```\n\n### Browse Command\n\nBrowse web page:\n\n```shire\n/browse:https://ide.unitmesh.cc\n```\n\nIt will be text inside the body from web page.\n\n### Refactor Command\n\nRefactor code:\n\n```shire\n/refactor:rename /symbol:com.phodal.shire.demo.MathHelper.calculateInsurance to calculateInsuranceTax\n```\n\nIt will be handled in local.\n\n### Goto Command\n\nGo to file:\n\n```shire\n/goto:src/main/java/com/example/Controller.java#L1C1\n```\n\nGo to symbol:\n\n```shire\n/goto:com.phodal.shire.demo.MathHelper.calculateInsurance#L1C1\n```\n\n### Database Command\n\nList all tables:\n\n    /database:table\n    ```\n    autodev-index.sqlite\n    ```\n\nList all columns:\n    \n    /database:column\n    ```\n    chunks\n    ```\n\nExecute SQL:\n   \n    /database:query\n    ```sql\n    select * from table\n    ```\n\n"
  },
  {
    "path": "docs/shire/shire-toolchain-function.md",
    "content": "---\nlayout: default\ntitle: Shire Toolchain Function\nparent: Shire Language\nnav_order: 7\n---\n\n# Toolchain Function\n\n> Toolchain 函数默认遵循 Pattern-Action 模式，用于定义数据处理逻辑。在接受参数时，默认的第一个参数为上下文变量，即\n`lastResult`\n\n## Git\n\n支持的函数：\n\n- commit，提交代码，参数 1：`message`\n- push，推送代码\n\n### 示例：\n\n```shire\n---\nname: \"Auto Commit and Push\"\nafterStreaming: {\n  case condition {\n    default { print(\"feat: add auto commit and push sample\") | commit | push }\n  }\n}\n---\n\nhi\n```\n\n## Database\n\n支持的函数：\n\n- `table`，获取数据库表信息，参数 1：`databaseName`，默认获取第一个连接的数据库。\n- `column`，获取数据库列信息，参数 1：`tableName`，默认获取第一个表的列信息。\n- `query`，执行 SQL 查询，参数 1：`sql`，示例：`query(\"select * from user\")`\n\n### 示例 1\n\n```shire\n---\nvariables:\n  \"relatedTableInfo\": /./ { column(\"user\", \"post\", \"tag\") }\n---\n\n根据如下的信息，生成 SQL：\n\n$relatedTableInfo\n```\n\n## WireMock\n\n支持的函数：\n\n- `mock`，启动 WireMock 服务，参数 1：`filePath`。默认 8080 端口。\n\n### 示例\n\n```shire\n---\nname: \"sample\"\nvariables:\n  \"mock\": /any/ { mock(\"samples/mock/blog_v0-stubs.json\") }\n---\n```\n\n其中的 `samples/mock/blog_v0-stubs.json` 文件内容如下：\n\n```json\n{\n  \"mappings\": [\n    {\n      \"request\": {\n        \"method\": \"POST\",\n        \"url\": \"/blog\",\n        \"bodyPatterns\": [\n          {\n            \"matchesJsonPath\": \"$.title\"\n          },\n          {\n            \"matchesJsonPath\": \"$.content\"\n          },\n          {\n            \"matchesJsonPath\": \"$.author\"\n          }\n        ]\n      },\n      \"response\": {\n        \"status\": 201,\n        \"headers\": {\n          \"Content-Type\": \"application/json\"\n        },\n        \"body\": \"{\\\"message\\\": \\\"Blog post created successfully\\\", \\\"id\\\": 1}\"\n      }\n    }\n  ]\n}\n```\n"
  },
  {
    "path": "docs/shire/shire-toolchain-variable.md",
    "content": "---\nlayout: default\ntitle: Shire Toolchain Variable\nparent: Shire Language\nnav_order: 6\n---\n\n# Toolchain Variable\n\n工具链变量提供诸如语言、框架和其他工具等数据作为变量。这个数据可以在 Shire 变量和模板中使用。\n\n支持的工具链：\n\n- Git\n- SonarQube\n- Maven, Gradle（TODO）\n\n## Git\n\n{: .label .label-red }\nBuiltin plugin: Git4Idea\n\nGit 工具链提供以下变量：\n\n- `currentChanges`，当前分支的当前更改。\n- `currentBranch`，当前分支的名称。\n- `historyCommitMessages`，当前提交的提交消息。\n- `diff`，当前选中 commit 的 diff 信息。\n\n## SonarQube\n\n{: .label .label-red }\nRequire: [Sonarlint plugin](https://plugins.jetbrains.com/plugin/7973-sonarlint)\n\nSonarQube 工具链提供以下变量：\n\n- `sonarIssues`，当前文件的 SonarQube 中的问题列表。\n- `sonarResults`，当前文件的 SonarQube 中的结果，含有问题的详细信息。\n\n## ProjectDependencies (TBC)\n\n{: .label .label-red }\nBuiltin plugin: Java\n\nMaven, Gradle 工具链提供以下变量：\n\n- `projectDependencies`，当前文件的 Maven, Gradle 依赖列表。\n\n## Database\n\n{: .label .label-red }\nBuiltin plugin: Database\n\n数据库工具链提供以下变量：\n\n- `databases`，当前连接的数据库列表。\n- `tables`，当前数据库的表列表。\n- `columns`，当前数据库的表的列列表。\n\n## Protobuf\n\n{: .label .label-red }\nBuiltin plugin: [Protocol Buffers](https://plugins.jetbrains.com/plugin/14004-protocol-buffers)\n"
  },
  {
    "path": "docs/shire/shire.md",
    "content": "---\nlayout: default\ntitle: Shire Language\nnav_order: 4\nhas_children: true\npermalink: /language\n---\n\nShire 语法由三部分组成：\n\n- Hobbit Hole：用于定义数据结构。\n  - Pattern-Action：用于定义数据处理逻辑。\n  - Condition Visible：用于定义可见性条件。\n  - Output：用于定义输出。\n  - Variable：用于定义过程变量。\n- Template：用于定义 LLM 模板。\n  - Shire Template：用于定义代码生成模板。\n- Life Cycle：用于定义生命周期。\n"
  },
  {
    "path": "docs/shire/when.md",
    "content": "---\nlayout: default\ntitle: Condition Visible\nparent: Shire Language\nnav_order: 9\n---\n\nActivate Menu 是一类 `Condition Visible` 用于定义一个条件，当条件为真时，将显示对应的 Action。\n\n当前支持 Action 类型：ContextMenu（右键）和 Intention Action（意图感知，通过 Alt+Enter）。\n\n## 语法示例\n\n```shire\n---\nwhen: { $filePath.contains(\"src/main/java\") && $fileName.contains(\".java\") }\n---\n```\n\n当当前文件路径包含 `src/main/java` 且文件名包含 `.java` 时，显示对应的 Action。\n\n## 语法说明\n\n- `when`：条件表达式，当条件为真时，显示对应的 Action。\n- `$`：变量引用符号。\n- `.`：属性访问符号。\n- `&&`：逻辑与。\n- `contains`：字符串包含。\n\n### 支持的变量\n\n- PsiVariables\n    - `filePath`：文件路径\n    - `fileName`：文件名\n    - `fileExtension`：文件扩展名\n    - `fileContent`：文件内容\n\n### 支持的操作符\n\n- `==`：等于\n- `!=`：不等于\n- `>`：大于\n- `<`：小于\n- `>=`：大于等于\n- `<=`：小于等于\n\n### 支持的函数\n\n详细见：[com.phodal.shirelang.compiler.hobbit.ast.ExpressionBuiltInMethod]\n\n```Kotlin\nenum class ExpressionBuiltInMethod(\n    val methodName: String,\n    val description: String,\n    val postInsertString: String = \"()\",\n    val moveCaret: Int = 2,\n) {\n    LENGTH(\"length\", \"The length of the string\"),\n    TRIM(\"trim\", \"The trimmed string\"),\n    CONTAINS(\"contains\", \"Check if the string contains a substring\", \"(\\\"\\\")\", 2),\n    STARTS_WITH(\"startsWith\", \"Check if the string starts with a substring\", \"(\\\"\\\")\", 2),\n    ENDS_WITH(\"endsWith\", \"Check if the string ends with a substring\", \"(\\\"\\\")\", 2),\n    LOWERCASE(\"lowercase\", \"The lowercase version of the string\"),\n    UPPERCASE(\"uppercase\", \"The uppercase version of the string\"),\n    IS_EMPTY(\"isEmpty\", \"Check if the string is empty\"),\n    IS_NOT_EMPTY(\"isNotEmpty\", \"Check if the string is not empty\"),\n    FIRST(\"first\", \"The first character of the string\"),\n    LAST(\"last\", \"The last character of the string\"),\n    MATCHES(\"matches\", \"Check if the string matches a regex pattern\", \"(\\\"//\\\")\", 3);\n}\n```\n"
  },
  {
    "path": "docs/shireql/shire-database.md",
    "content": "---\nlayout: default\ntitle: ShireQL Database Query (TBC)\nparent: ShireQL\nnav_order: 4\n---\n\n## ShireQL 查询制品信息\n\n### Variable 方式 \n\n支持的 variables 或者 function\n\n- table()\n- columns()\n\n### ShireQL 示例\n\n```shire\n---\nvariables:\n  \"tables\": {\n    from {\n        DBTable table\n    }\n    where {\n        table.name == \"public\"\n    }\n    select {\n        table.name, table.columns\n    }\n  }\n---\n```\n\n"
  },
  {
    "path": "docs/shireql/shire-ql-basic.md",
    "content": "---\nlayout: default\ntitle: ShireQL Basic\nparent: ShireQL\nnav_order: 1\n---\n\n## ShireQL 基本语法\n\nDesign\n\n```shire\n---\nvariables\n  \"var1\": query {\n     // 变量声明部分\n     from  {} // datasource，如：`PsiClass`, `GitCommit`, `ProjectDependency` \n     // 条件部分 \n     where {} //  AST expand, and functions support for  regex, and methods: similar search, embedding search, tf-idf, and other advanced search\n     // 结果部分\n     select {} // Node, or Node's attribute, or Node's children \n  }\n---\n```\n\n## 通用函数\n\n### 常用函数\n\n- date 函数\n\n### NLP 函数 (待定)\n\n- similarSearch\n- embeddingSearch\n- tf-idf"
  },
  {
    "path": "docs/shireql/shire-ql-dependency.md",
    "content": "---\nlayout: default\ntitle: ShireQL Dependency Query (TBC)\nparent: ShireQL\nnav_order: 4\n---\n\n## ShireQL 查询制品信息\n\n### Maven 示例\n\n```shire\n---\nvariables:\n  \"mavenDependencies\": {\n    from {\n        ProjectDependency dependency\n    }\n    where {\n        dependency.groupId == \"org.springframework.boot\" and dependency.artifactId == \"spring-boot-starter-web\"\n    }\n    select {\n        dependency.groupId, dependency.artifactId, dependency.version\n    }\n  }\n---\n```\n"
  },
  {
    "path": "docs/shireql/shire-ql-git.md",
    "content": "---\nlayout: default\ntitle: ShireQL Git Query\nparent: ShireQL\nnav_order: 3\n---\n\n\n## ShireQL 查询版本管理\n\n### Git 示例\n\n详细见 [#41](https://github.com/phodal/shire/issues/41)\n\n```shire\n---\nvariables:\n  \"phodalCommits\": {\n    from {\n        GitCommit commit\n    }\n    where {\n        commit.authorName == \"Phodal Huang\"\n    }\n    select {\n        commit.authorName, commit.authorEmail, commit.message\n    }\n  }\n---\n```\n\nModel:\n\n```\ndata class ShireGitCommit(\n    val hash: String,\n    val authorName: String,\n    val authorEmail: String,\n    val authorDate: Long,\n    val committerName: String,\n    val committerEmail: String,\n    val committerDate: Long,\n    val message: String,\n    val fullMessage: String\n) : GitEntity()\n```\n\nModel design for #41\n\n- GitCommit\n    - Usage: support for git commit query\n    - Field: author, authorEmail, committer, committerEmail, hash, date, message, fullMessage\n- FileCommit\n    - Usage: support for file in history\n    - Field: commit, filename, status, path\n- Branch\n    - Usage: support for branch query\n    - Field: name, commitCount\n\nRef design: https://github.com/AmrDeveloper/GQL\n"
  },
  {
    "path": "docs/shireql/shire-ql-psi.md",
    "content": "---\nlayout: default\ntitle: ShireQL PSI Query\nparent: ShireQL\nnav_order: 2\n---\n\n示例:\n\n```shire\n---\nvariables:\n  \"allController\": {\n    from {\n        PsiClass clazz // here is a comment\n    }\n    where {\n        clazz.extends(\"org.springframework.web.bind.annotation.RestController\") and clazz.getAnAnnotation() == \"org.springframework.web.bind.annotation.RequestMapping\"\n    }\n    select {\n        clazz.id, clazz.name, \"code\"\n    }\n  }\n---\n```\n\n## ShireQL 查询抽象语法树\n\n### Java 语言示例\n\n```kotlin\nenum class JvmPsiPqlMethod(val methodName: String, val description: String) {\n    GET_NAME(\"getName\", \"Get class name\"),\n    NAME(\"name\", \"Get class name\"),\n    EXTENDS(\"extends\", \"Get class extends\"),\n    IMPLEMENTS(\"implements\", \"Get class implements\"),\n    METHOD_CODE_BY_NAME(\"methodCodeByName\", \"Get method code by name\"),\n    FIELD_CODE_BY_NAME(\"fieldCodeByName\", \"Get field code by name\"),\n\n    SUBCLASSES_OF(\"subclassesOf\", \"Get subclasses of class\"),\n    ANNOTATED_OF(\"annotatedOf\", \"Get annotated classes\"),\n    SUPERCLASS_OF(\"superclassOf\", \"Get superclass of class\"),\n    IMPLEMENTS_OF(\"implementsOf\", \"Get implemented interfaces of class\"),\n}\n```"
  },
  {
    "path": "docs/shireql/shire-ql.md",
    "content": "---\nlayout: default\ntitle: ShireQL\nnav_order: 5\nhas_children: true\npermalink: /shireql\n---\n\nShireQL 是一个基于 IDE 的数据查询语言，它允许你查询当前文件的 AST（抽象语法树）、Git、依赖信息等。它在 Shire\n中用于定义当前文件的上下文以及可以在当前文件上执行的操作。\n\n## 其它相关资源\n\n### GitQL\n\nGQL: [https://github.com/AmrDeveloper/GQL](https://github.com/AmrDeveloper/GQL)\n\n```sql\nSELECT author_name, COUNT(author_name) AS commit_num\nFROM commits\nGROUP BY author_name, author_email\nORDER BY commit_num DESC LIMIT 10\nSELECT commit_count\nFROM branches\nWHERE commit_count BETWEEN 0..10\n\nSELECT *\nFROM refs\nWHERE type = \"branch\"\nSELECT *\nFROM refs\nORDER BY type\n\nSELECT *\nFROM commits\nSELECT author_name, author_email\nFROM commits\nSELECT author_name, author_email\nFROM commits\nORDER BY author_name DESC, author_email ASC\nSELECT author_name, author_email\nFROM commits\nWHERE name LIKE \"%gmail%\"\nORDER BY author_name\nSELECT *\nFROM commits\nWHERE LOWER(name) = \"amrdeveloper\"\nSELECT author_name\nFROM commits\nGROUP By author_name\nSELECT author_name\nFROM commits\nGROUP By author_name\nhaving author_name = \"AmrDeveloper\"\n\nSELECT *\nFROM branches\nSELECT *\nFROM branches\nWHERE is_head = true\nSELECT name, LEN(name)\nFROM branches\n\nSELECT *\nFROM tags\nSELECT *\nFROM tags OFFSET 1 LIMIT 1\n```\n\n### GitHub CodeQL\n\nQL Language Reference: https://codeql.github.com/docs/ql-language-reference/queries/\n\n```codeql\nfrom /* ... variable declarations ... */\nwhere /* ... logical formula ... */\nselect /* ... expressions ... */\n```\n\nFor Example:\n\n```codeql\nimport java\n\nfrom Class c, Class superclass\nwhere superclass = c.getASupertype()\nselect c, \"This class extends the class $@.\", superclass, superclass.getName()\n```\n\nJava\n\n```codeql\nfrom Person p\nwhere parentOf(p) = parentOf(\"King Basil\") and\n  not p = \"King Basil\"\n  and not p.isDeceased()\nselect p\n```\n\nJavaScript\n\n```codeql\nimport javascript\n\nfrom Comment c\nwhere c.getText().regexpMatch(\"(?si).*\\\\bTODO\\\\b.*\")\nselect c\n```\n\nBetter Java Example:\n\n```codeql\nimport java\n\nfrom Constructor c, Annotation ann, AnnotationType anntp\nwhere ann = c.getAnAnnotation() and\n    anntp = ann.getType() and\n    anntp.hasQualifiedName(\"java.lang\", \"SuppressWarnings\")\nselect ann, ann.getValue(\"value\")\n```\n\nJava 2\n\n```codeql\nimport java\n\nfrom LTExpr expr\nwhere expr.getLeftOperand().getType().hasName(\"int\") and\n    expr.getRightOperand().getType().hasName(\"long\") and\n    exists(LoopStmt l | l.getCondition().getAChildExpr*() = expr) and\n    not expr.getAnOperand().isCompileTimeConstant()\nselect expr\n```\n\n### SourceGraph CodeSearch\n\nhttps://sourcegraph.com/docs/code-search/queries\n\n```query\nrepo:^github\\.com/sourcegraph/sourcegraph$ type:diff select:commit.diff.removed TODO\n\ntype:diff after:\"1 week ago\" \\.subscribe\\( lang:typescript\n\nrepo:github\\.com/sourcegraph/sourcegraph$ (test AND http AND NewRequest) lang:go\n\n```\n\nDate function\n\n```bash\nbefore:\"last thursday\"\nbefore:\"november 1 2019\"\n\nafter:\"6 weeks ago\"\nafter:\"november 1 2019\"\n\nrepo:vscode@*refs/heads/:^refs/heads/master type:diff task \n```\n\n"
  },
  {
    "path": "docs/workflow/custom-ai-agent.md",
    "content": "---\nlayout: default\ntitle: Custom AI Agent\nparent: Workflow\nnav_order: 3\n---\n\n## Custom AI Agent\n\n1. Create new file with end `.shireCustomAgent.json` in your project, for example: `demo.shireCustomAgent.json`.\n2. Fill JSON format config in `demo.shireCustomAgent.json` file.\n\n### Custom Agent Examples\n\n```json\n[\n  {\n    \"name\": \"内部 API 集成\",\n    \"description\": \"在一个组织或项目中，不同系统或组件之间的通信接口。\",\n    \"url\": \"http://127.0.0.1:8765/api/agent/api-market\",\n    \"responseAction\": \"Direct\"\n  },\n  {\n    \"name\": \"组件库查询\",\n    \"description\": \"从组件库中检索特定的 UI 组件，以便在开发的应用程序中使用。\",\n    \"url\": \"http://127.0.0.1:8765/api/agent/component-list\",\n    \"responseAction\": \"TextChunk\"\n  },\n  {\n    \"name\": \"页面生成\",\n    \"description\": \"使用 React 框架，基于组件和状态来生成页面。\",\n    \"url\": \"http://127.0.0.1:8765/api/agent/ux\",\n    \"auth\": {\n      \"type\": \"Bearer\",\n      \"token\": \"eyJhbGci\"\n    },\n    \"responseAction\": \"WebView\"\n  },\n  {\n    \"name\": \"DevInInsert\",\n    \"description\": \"Update，並指定20秒的timeout時間\",\n    \"url\": \"http://127.0.0.1:8765/api/agent/shire-sample\",\n    \"responseAction\": \"Shire\",\n    \"defaultTimeout\": 20\n  },\n  {\n    \"name\": \"内部 API 集成\",\n    \"description\": \"sample\",\n    \"url\": \"http://127.0.0.1:8765/api/agent/api-market\",\n    \"auth\": {\n      \"type\": \"Bearer\",\n      \"token\": \"eyJhbGci\"\n    },\n    \"connector\": {\n      \"requestFormat\": \"{\\\"customFields\\\": {\\\"model\\\": \\\"yi-34b-chat\\\", \\\"stream\\\": true}}\",\n      \"responseFormat\": \"$.choices[0].delta.content\"\n    },\n    \"responseAction\": \"Direct\"\n  }\n]\n```\n"
  },
  {
    "path": "docs/workflow/rag-flow.md",
    "content": "---\nlayout: default\ntitle: Shire Workflow 示例\nparent: Workflow\nnav_order: 4\n---\n\n## `execute` 代码解释示例\n\n### 步骤  1\n\n```shire\n---\nname: \"Search\"\nvariables:\n  \"testTemplate\": /.*.java/ { splitting | embedding }\n  \"lang\": \"java\"\n  \"input\": \"博客创建流程\"\nafterStreaming: {\n    case condition {\n      default { searching($output, \"0.1\") | execute(\"search.shire\") }\n    }\n }\n---\n\n```system```\nYou are a coding assistant who helps the user answer questions about code in their workspace by providing a list of relevant keywords they can search for to answer the question.\nThe user will provide you with potentially relevant information from the workspace. This information may be incomplete.\n\nDO NOT ask the user for additional information or clarification.\nDO NOT try to answer the user's question directly.\n\n**Additional Rules**\nThink step by step:\n\n1. Read the user's question to understand what they are asking about their workspace.\n2. If the question contains pronouns such as 'it' or 'that', try to understand what the pronoun refers to by looking at the rest of the question and the conversation history.\n3. If the question contains an ambiguous word such as 'this', try to understand what it refers to by looking at the rest of the question, the user's active selection, and the conversation history.\n4. Output a precise version of the question that resolves all pronouns and ambiguous words like 'this' to the specific nouns they stand for. Be sure to preserve the exact meaning of the question by only changing ambiguous pronouns and words like 'this'.\n5. Then output a short markdown list of up to 8 relevant keywords that the user could try searching for to answer their question. These keywords could be used as file names, symbol names, abbreviations, or comments in the relevant code. Put the most relevant keywords to the question first. Do not include overly generic keywords. Do not repeat keywords.\n6. For each keyword in the markdown list of related keywords, if applicable add a comma-separated list of variations after it. For example, for 'encode', possible variations include 'encoding', 'encoded', 'encoder', 'encoders'. Consider synonyms and plural forms. Do not repeat variations.\n7. keywords should be in English\n\n**Examples**\nUser: Where is the code for the function that calculates the average of a list of numbers?\n\nResponse:\nWhere is calculating the average of a list of numbers?\n\n- calculate average, average, average calculation // keywords should be in English\n- calculate, calculation, calculator // keywords should be in English\n- jisuan, pingjunshu, pingjun // keywords can be user's language\n\n```user```\nUser: $input\n\nResponse:\n```\n\n### 步骤 2：返回 Shire 代码\n\n```shire\nUse the above code to answer the following question. You should not reference any files outside of what is shown,\nunless they are commonly known files, like a .gitignore or package.json. Reference the filenames whenever possible.\nIf there isn't enough information to answer the question, suggest where the user might look to learn more.\n\ncode:\n\n$output\n\nUser's Question: $input\n```\n"
  },
  {
    "path": "docs/workflow/remote-ai-agent.md",
    "content": "---\nlayout: default\ntitle: Remote AI Agent\nparent: Workflow\nnav_order: 2\n---\n\nRemote AI Agent 是通过调用远程的 AI Agent 来执行任务。主要实现方式：\n\n- `thread` 来调用 `.curl.sh` 脚本，执行远程 AI Agent 的任务\n\n示例：\n\n```shire\n---\nvariables:\n  \"story\": /any/ { thread(\".shire/shell/dify-epic-story.curl.sh\") | jsonpath(\"$.answer\", true) }\n---\n```\n\n## 代码定位示例\n\n入口 Shire 文件\n\n```shire\n---\nname: \"定位待变更代码\"\nvariables:\n  \"story\": /any/ { thread(\".shire/shell/dify-user-story-workflow.curl.sh\") | jsonpath(\"$.answer\", true) }\n  \"controllers\": /.*.java/ { cat | grep(\"class\\s+([a-zA-Z]*Controller)\")  }\n  \"services\": /.*.java/ { cat | grep(\"class\\s+([a-zA-Z]*Service)\")  }\n  \"firstController\": /CinemaController\\.java/ { print }\n  \"firstService\": /CinemaController\\.java/ { print }\n  \"domainLanguage\": /domain-language\\.csv/ { cat }\nonStreamingEnd: { parseCode | openFile }\n---\n\n你是一个网站资深的开发人员，能帮助我定位到代码文件。请根据如下的用户故事，以及对应的 controller, service 名称，选择最合适修改的代码文件\n\n用户故事：\n\n$story\n\nController 列表：\n\n$controllers\n\nService 列表：\n\n$services\n\n这个网站的一些专有名词如下：\n\n$domainLanguage\n\n要求：\n\n如果没有合适的 controller，请给出最合适的 controller 和 service 路径。\n\nController 示例路径在：\n\n$firstController\n\nservice 示例路径在：\n\n$firstService\n\n你只返回文件名，格式如：`src/main/xxx/DemoController.java`\n\n请严格按格式返回，只返回存在的代码文件，只返回文件路径。\n```\n\n`dify-user-story-workflow.curl.sh` 代码：\n\n```bash\ncurl -X POST 'https://api.dify.ai/v1/completion-messages' \\\n  --header \"Authorization: Bearer ${singleStoryKey}\" \\\n  --header 'Content-Type: application/json' \\\n  --data-raw '{\n      \"inputs\": {\"feature\": \"Hello, world!\", \"story_list\": \"作为购物中心电影观众，我想要提前预订电影场次相关的食物，以便于节省购买食物的时间，更好地安排观影时间。\", \"story\": \"添加零食和饮料至购物车影院观众在购票时添加零食和饮料提前准备好观影期间的零食和饮料\"},\n      \"response_mode\": \"streaming\",\n      \"user\": \"phodal\"\n  }'\n```\n\n其中的 `singleStoryKey` 可以通过在项目中创建 `xx.shireEnv.json` 来支持，示例：\n\n```json\n{\n  \"development\": {\n    \"singleStoryKey\": \"xxxx\"\n  }\n}\n```"
  },
  {
    "path": "docs/workflow/response-routing-function.md",
    "content": "---\nlayout: default\ntitle: Response Routing - afterStreaming\nparent: Workflow\nnav_order: 1\n---\n\n在 LLM 处理完后，可以返回后续的路由处理函数，以便在不同的场景下，对返回的结果进行处理。\n\n1. 返回 Shire 代码，可以作为 Shire 语言块来处理。\n2. 返回 JSON 数据（待支持）\n\n示例：\n\n```shire\n---\nafterStreaming: {\n    case condition {\n      default { searching($output) | execute(\"search.shire\") }\n    }\n }\n---\n\n```"
  },
  {
    "path": "docs/workflow/workflow.md",
    "content": "---\nlayout: default\ntitle: Workflow\nnav_order: 7\nhas_children: true\npermalink: /workflow\n---\n\n# Workflow\n\n主要通过：\n\n- `thread` 函数来在多个线程中执行任务\n- `execute` 函数，在执行完后，调用额外的函数\n- `batch` 函数，批量执行任务\n\n其它：\n\n- `mock` 函数，启动 WireMock 服务\n\n"
  },
  {
    "path": "gradle/libs.versions.toml",
    "content": "[versions]\n# libraries\n\n# plugins\nkotlin = \"2.1.20\"\nchangelog = \"2.2.1\"\ngradleIntelliJPlugin = \"2.0.1\"\nqodana = \"2025.1.1\"\nkover = \"0.9.0\"\n\n[libraries]\njson-pathkt = \"com.nfeld.jsonpathkt:jsonpathkt:2.0.1\"\n\nokhttp = \"com.squareup.okhttp3:okhttp:4.4.1\"\nokhttp-sse = \"com.squareup.okhttp3:okhttp-sse:4.12.0\"\nkaml = \"com.charleskorn.kaml:kaml:0.98.0\"\n\n# org.reflections:reflections:0.10.2\nreflections = \"org.reflections:reflections:0.10.2\"\n# \"org.jetbrains.kotlinx:kotlinx-serialization-json:1.8.1\"\nserialization-json = \"org.jetbrains.kotlinx:kotlinx-serialization-json:1.8.1\"\n\n[plugins]\nchangelog = { id = \"org.jetbrains.changelog\", version.ref = \"changelog\" }\ngradleIntelliJPlugin = { id = \"org.jetbrains.intellij.platform\", version.ref = \"gradleIntelliJPlugin\" }\nkotlin = { id = \"org.jetbrains.kotlin.jvm\", version.ref = \"kotlin\" }\nkover = { id = \"org.jetbrains.kotlinx.kover\", version.ref = \"kover\" }\nqodana = { id = \"org.jetbrains.qodana\", version.ref = \"qodana\" }\nserialization = { id = \"org.jetbrains.kotlin.plugin.serialization\", version.ref = \"kotlin\" }"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-8.10.2-bin.zip\nnetworkTimeout=10000\nvalidateDistributionUrl=true\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "gradle-241.properties",
    "content": "# IntelliJ Platform Artifacts Repositories -> https://plugins.jetbrains.com/docs/intellij/intellij-artifacts.html\nideaPlatformVersion=241\nideaVersion=IU-2024.1\n\n# Supported build number ranges and IntelliJ Platform versions -> https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html\npluginSinceBuild = 232\npluginUntilBuild = 252.*\n\n# 3rd party plugins\n# Plugin Dependencies -> https://plugins.jetbrains.com/docs/intellij/plugin-dependencies.html\nplatformPlugins = PythonCore:241.14494.240,PlantUML integration:7.10.1-IJ2023.2,com.intellij.mermaid:0.0.22+IJ.232\n#platformPlugins = PlantUML integration:7.10.1-IJ2023.2,com.intellij.mermaid:0.0.22+IJ.232\npythonPlugins = PythonCore:241.14494.240\ngoPlugin=org.jetbrains.plugins.go:241.14494.240\nplantUmlPlugin = PlantUML integration:7.10.1-IJ2023.2\nwireMockPlugin=com.intellij.wiremock:241.14494.150\ntestAutomationPlugin=com.intellij.aqua:241.14494.240\nmermaidPlugin=com.intellij.mermaid:0.0.22+IJ.232\njsonPlugin=\n\n# 3rd party plugins\nnodejsPlugin=NodeJS:241.14494.155\nsonarPlugin = org.sonarlint.idea:10.8.1.79205\nprotoPlugin = idea.plugin.protoeditor:241.14494.150"
  },
  {
    "path": "gradle-243.properties",
    "content": "# IntelliJ Platform Artifacts Repositories -> https://plugins.jetbrains.com/docs/intellij/intellij-artifacts.html\nideaPlatformVersion=243\nideaVersion=IU-2024.3\n\n# Supported build number ranges and IntelliJ Platform versions -> https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html\npluginSinceBuild = 243\npluginUntilBuild = 243.*\n\n# official plugins\nplatformPlugins=PythonCore:243.21565.211,com.intellij.mermaid:0.0.24+IJ.243\npythonPlugins=PythonCore:243.21565.211\nmermaidPlugin=com.intellij.mermaid:0.0.24+IJ.243\ngoPlugin=org.jetbrains.plugins.go:243.15521.24\nnodejsPlugin=NodeJS:243.15521.24\nwireMockPlugin=com.intellij.wiremock:243.15521.24\ntestAutomationPlugin=com.intellij.aqua:243.15521.24\njsonPlugin=com.intellij.modules.json:243.15521.24\n\n# 3rd party plugins\nsonarPlugin=org.sonarlint.idea:10.8.1.79205\nplantUmlPlugin=PlantUML integration:7.10.1-IJ2023.2\nprotoPlugin = idea.plugin.protoeditor:243.15521.24"
  },
  {
    "path": "gradle.properties",
    "content": "# IntelliJ Platform Artifacts Repositories -> https://plugins.jetbrains.com/docs/intellij/intellij-artifacts.html\npropertiesPluginEnvironmentNameProperty=ideaPlatformVersion\nideaPlatformVersion=241\n\npluginGroup = com.phodal.shire\npluginName = Shire - AI Coding Agent Language\npluginRepositoryUrl = https://github.com/phodal/shire\n# SemVer format -> https://semver.org\npluginVersion = 1.3.6\n\n# IntelliJ Platform Properties -> https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html#configuration-intellij-extension\nplatformType = IU\nplatformVersion = 2024.1\n\n# Gradle Releases -> https://github.com/gradle/gradle/releases\ngradleVersion = 8.10\n\n# Opt-out flag for bundling Kotlin standard library -> https://jb.gg/intellij-platform-kotlin-stdlib\nkotlin.stdlib.default.dependency = false\n\n# Enable Gradle Configuration Cache -> https://docs.gradle.org/current/userguide/configuration_cache.html\norg.gradle.configuration-cache = true\n\n# Enable Gradle Build Cache -> https://docs.gradle.org/current/userguide/build_cache.html\norg.gradle.caching = true\n\njavaVersion = 17\n\nkotlin.daemon.jvmargs=-Xmx2g\norg.gradle.jvmargs =-Xmx2g\n"
  },
  {
    "path": "gradlew",
    "content": "#!/bin/sh\n\n#\n# Copyright © 2015-2021 the original authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n##############################################################################\n#\n#   Gradle start up script for POSIX generated by Gradle.\n#\n#   Important for running:\n#\n#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is\n#       noncompliant, but you have some other compliant shell such as ksh or\n#       bash, then to run this script, type that shell name before the whole\n#       command line, like:\n#\n#           ksh Gradle\n#\n#       Busybox and similar reduced shells will NOT work, because this script\n#       requires all of these POSIX shell features:\n#         * functions;\n#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,\n#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;\n#         * compound commands having a testable exit status, especially «case»;\n#         * various built-in commands including «command», «set», and «ulimit».\n#\n#   Important for patching:\n#\n#   (2) This script targets any POSIX shell, so it avoids extensions provided\n#       by Bash, Ksh, etc; in particular arrays are avoided.\n#\n#       The \"traditional\" practice of packing multiple parameters into a\n#       space-separated string is a well documented source of bugs and security\n#       problems, so this is (mostly) avoided, by progressively accumulating\n#       options in \"$@\", and eventually passing that to Java.\n#\n#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,\n#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;\n#       see the in-line comments for details.\n#\n#       There are tweaks for specific operating systems such as AIX, CygWin,\n#       Darwin, MinGW, and NonStop.\n#\n#   (3) This script is generated from the Groovy template\n#       https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt\n#       within the Gradle project.\n#\n#       You can find Gradle at https://github.com/gradle/gradle/.\n#\n##############################################################################\n\n# Attempt to set APP_HOME\n\n# Resolve links: $0 may be a link\napp_path=$0\n\n# Need this for daisy-chained symlinks.\nwhile\n    APP_HOME=${app_path%\"${app_path##*/}\"}  # leaves a trailing /; empty if no leading path\n    [ -h \"$app_path\" ]\ndo\n    ls=$( ls -ld \"$app_path\" )\n    link=${ls#*' -> '}\n    case $link in             #(\n      /*)   app_path=$link ;; #(\n      *)    app_path=$APP_HOME$link ;;\n    esac\ndone\n\n# This is normally unused\n# shellcheck disable=SC2034\nAPP_BASE_NAME=${0##*/}\n# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)\nAPP_HOME=$( cd \"${APP_HOME:-./}\" > /dev/null && pwd -P ) || exit\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=maximum\n\nwarn () {\n    echo \"$*\"\n} >&2\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n} >&2\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"$( uname )\" in                #(\n  CYGWIN* )         cygwin=true  ;; #(\n  Darwin* )         darwin=true  ;; #(\n  MSYS* | MINGW* )  msys=true    ;; #(\n  NONSTOP* )        nonstop=true ;;\nesac\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=$JAVA_HOME/jre/sh/java\n    else\n        JAVACMD=$JAVA_HOME/bin/java\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=java\n    if ! command -v java >/dev/null 2>&1\n    then\n        die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nfi\n\n# Increase the maximum file descriptors if we can.\nif ! \"$cygwin\" && ! \"$darwin\" && ! \"$nonstop\" ; then\n    case $MAX_FD in #(\n      max*)\n        # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        MAX_FD=$( ulimit -H -n ) ||\n            warn \"Could not query maximum file descriptor limit\"\n    esac\n    case $MAX_FD in  #(\n      '' | soft) :;; #(\n      *)\n        # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        ulimit -n \"$MAX_FD\" ||\n            warn \"Could not set maximum file descriptor limit to $MAX_FD\"\n    esac\nfi\n\n# Collect all arguments for the java command, stacking in reverse order:\n#   * args from the command line\n#   * the main class name\n#   * -classpath\n#   * -D...appname settings\n#   * --module-path (only if needed)\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.\n\n# For Cygwin or MSYS, switch paths to Windows format before running java\nif \"$cygwin\" || \"$msys\" ; then\n    APP_HOME=$( cygpath --path --mixed \"$APP_HOME\" )\n    CLASSPATH=$( cygpath --path --mixed \"$CLASSPATH\" )\n\n    JAVACMD=$( cygpath --unix \"$JAVACMD\" )\n\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    for arg do\n        if\n            case $arg in                                #(\n              -*)   false ;;                            # don't mess with options #(\n              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath\n                    [ -e \"$t\" ] ;;                      #(\n              *)    false ;;\n            esac\n        then\n            arg=$( cygpath --path --ignore --mixed \"$arg\" )\n        fi\n        # Roll the args list around exactly as many times as the number of\n        # args, so each arg winds up back in the position where it started, but\n        # possibly modified.\n        #\n        # NB: a `for` loop captures its iteration list before it begins, so\n        # changing the positional parameters here affects neither the number of\n        # iterations, nor the values presented in `arg`.\n        shift                   # remove old arg\n        set -- \"$@\" \"$arg\"      # push replacement arg\n    done\nfi\n\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS='\"-Xmx64m\" \"-Xms64m\"'\n\n# Collect all arguments for the java command:\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,\n#     and any embedded shellness will be escaped.\n#   * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be\n#     treated as '${Hostname}' itself on the command line.\n\nset -- \\\n        \"-Dorg.gradle.appname=$APP_BASE_NAME\" \\\n        -classpath \"$CLASSPATH\" \\\n        org.gradle.wrapper.GradleWrapperMain \\\n        \"$@\"\n\n# Stop when \"xargs\" is not available.\nif ! command -v xargs >/dev/null 2>&1\nthen\n    die \"xargs is not available\"\nfi\n\n# Use \"xargs\" to parse quoted args.\n#\n# With -n1 it outputs one arg per line, with the quotes and backslashes removed.\n#\n# In Bash we could simply go:\n#\n#   readarray ARGS < <( xargs -n1 <<<\"$var\" ) &&\n#   set -- \"${ARGS[@]}\" \"$@\"\n#\n# but POSIX shell has neither arrays nor command substitution, so instead we\n# post-process each arg (as a line of input to sed) to backslash-escape any\n# character that might be a shell metacharacter, then use eval to reverse\n# that process (while maintaining the separation between arguments), and wrap\n# the whole thing up as a single \"set\" statement.\n#\n# This will of course break if any of these variables contains a newline or\n# an unmatched quote.\n#\n\neval \"set -- $(\n        printf '%s\\n' \"$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\" |\n        xargs -n1 |\n        sed ' s~[^-[:alnum:]+,./:=@_]~\\\\&~g; ' |\n        tr '\\n' ' '\n    )\" '\"$@\"'\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "gradlew.bat",
    "content": "@rem\n@rem Copyright 2015 the original author or authors.\n@rem\n@rem Licensed under the Apache License, Version 2.0 (the \"License\");\n@rem you may not use this file except in compliance with the License.\n@rem You may obtain a copy of the License at\n@rem\n@rem      https://www.apache.org/licenses/LICENSE-2.0\n@rem\n@rem Unless required by applicable law or agreed to in writing, software\n@rem distributed under the License is distributed on an \"AS IS\" BASIS,\n@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n@rem See the License for the specific language governing permissions and\n@rem limitations under the License.\n@rem\n\n@if \"%DEBUG%\"==\"\" @echo off\n@rem ##########################################################################\n@rem\n@rem  Gradle startup script for Windows\n@rem\n@rem ##########################################################################\n\n@rem Set local scope for the variables with windows NT shell\nif \"%OS%\"==\"Windows_NT\" setlocal\n\nset DIRNAME=%~dp0\nif \"%DIRNAME%\"==\"\" set DIRNAME=.\n@rem This is normally unused\nset APP_BASE_NAME=%~n0\nset APP_HOME=%DIRNAME%\n\n@rem Resolve any \".\" and \"..\" in APP_HOME to make it shorter.\nfor %%i in (\"%APP_HOME%\") do set APP_HOME=%%~fi\n\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nset DEFAULT_JVM_OPTS=\"-Xmx64m\" \"-Xms64m\"\n\n@rem Find java.exe\nif defined JAVA_HOME goto findJavaFromJavaHome\n\nset JAVA_EXE=java.exe\n%JAVA_EXE% -version >NUL 2>&1\nif %ERRORLEVEL% equ 0 goto execute\n\necho. 1>&2\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2\necho. 1>&2\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\necho location of your Java installation. 1>&2\n\ngoto fail\n\n:findJavaFromJavaHome\nset JAVA_HOME=%JAVA_HOME:\"=%\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\n\nif exist \"%JAVA_EXE%\" goto execute\n\necho. 1>&2\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2\necho. 1>&2\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\necho location of your Java installation. 1>&2\n\ngoto fail\n\n:execute\n@rem Setup the command line\n\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\n\n\n@rem Execute Gradle\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %*\n\n:end\n@rem End local scope for the variables with windows NT shell\nif %ERRORLEVEL% equ 0 goto mainEnd\n\n:fail\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\nrem the _cmd.exe /c_ return code!\nset EXIT_CODE=%ERRORLEVEL%\nif %EXIT_CODE% equ 0 set EXIT_CODE=1\nif not \"\"==\"%GRADLE_EXIT_CONSOLE%\" exit %EXIT_CODE%\nexit /b %EXIT_CODE%\n\n:mainEnd\nif \"%OS%\"==\"Windows_NT\" endlocal\n\n:omega\n"
  },
  {
    "path": "languages/README.md",
    "content": ""
  },
  {
    "path": "languages/shire-go/src/main/kotlin/com/phodal/shirelang/go/codemodel/GoClassStructureProvider.kt",
    "content": "package com.phodal.shirelang.go.codemodel\n\nimport com.goide.psi.GoMethodDeclaration\nimport com.goide.psi.GoTypeDeclaration\nimport com.goide.psi.GoTypeSpec\nimport com.intellij.psi.PsiElement\nimport com.phodal.shirecore.provider.codemodel.ClassStructureProvider\nimport com.phodal.shirecore.provider.codemodel.model.ClassStructure\nimport com.phodal.shirelang.go.util.GoPsiUtil\n\nclass GoClassStructureProvider : ClassStructureProvider {\n    override fun build(psiElement: PsiElement, gatherUsages: Boolean): ClassStructure? {\n        if (psiElement !is GoTypeDeclaration && psiElement !is GoTypeSpec) {\n            return null\n        }\n\n        val typeSpecs: List<GoTypeSpec> = when (psiElement) {\n            is GoTypeSpec -> listOf(psiElement)\n            is GoTypeDeclaration -> psiElement.typeSpecList\n            else -> emptyList()\n        }\n\n        val methodPairs = typeSpecs.flatMap { type ->\n            val methods = type.methods\n            methods.map { method -> method to type.name }\n        }\n\n        val methods = methodPairs.map { it.first }\n            .filterIsInstance<GoMethodDeclaration>()\n\n        val name = GoPsiUtil.getDeclarationName(psiElement)\n\n        return ClassStructure(\n            psiElement, psiElement.text, name, name, methods, emptyList(), emptyList(), emptyList()\n        )\n    }\n}\n"
  },
  {
    "path": "languages/shire-go/src/main/kotlin/com/phodal/shirelang/go/codemodel/GoFileStructureProvider.kt",
    "content": "package com.phodal.shirelang.go.codemodel\n\nimport com.goide.psi.GoFile\nimport com.goide.psi.GoFunctionOrMethodDeclaration\nimport com.goide.psi.GoType\nimport com.intellij.psi.PsiFile\nimport com.intellij.psi.util.PsiTreeUtil\nimport com.phodal.shirecore.provider.codemodel.FileStructureProvider\nimport com.phodal.shirecore.provider.codemodel.model.FileStructure\nimport com.phodal.shirecore.relativePath\n\nclass GoFileStructureProvider : FileStructureProvider {\n    override fun build(psiFile: PsiFile): FileStructure? {\n        if (psiFile !is GoFile) return null\n\n        val packageString = psiFile.packageName\n        val path = psiFile.virtualFile.relativePath(psiFile.project)\n        val imports = psiFile.imports\n        val classes = PsiTreeUtil.getChildrenOfTypeAsList(psiFile, GoType::class.java)\n        val methods = PsiTreeUtil.getChildrenOfTypeAsList(psiFile, GoFunctionOrMethodDeclaration::class.java)\n\n        return FileStructure(psiFile, psiFile.name, path, packageString, imports, classes, methods)\n    }\n}\n"
  },
  {
    "path": "languages/shire-go/src/main/kotlin/com/phodal/shirelang/go/codemodel/GoMethodStructureProvider.kt",
    "content": "package com.phodal.shirelang.go.codemodel\n\nimport com.goide.psi.GoFunctionDeclaration\nimport com.goide.psi.GoFunctionOrMethodDeclaration\nimport com.goide.psi.GoMethodDeclaration\nimport com.intellij.psi.PsiElement\nimport com.phodal.shirecore.provider.codemodel.MethodStructureProvider\nimport com.phodal.shirecore.provider.codemodel.model.MethodStructure\n\nclass GoMethodStructureProvider : MethodStructureProvider {\n    override fun build(psiElement: PsiElement, includeClassContext: Boolean, gatherUsages: Boolean): MethodStructure? {\n        if (psiElement !is GoFunctionOrMethodDeclaration) {\n            return null\n        }\n\n\n        val funcName = psiElement.name ?: \"\"\n\n        val functionSignature: String = when (psiElement) {\n            is GoMethodDeclaration -> {\n                psiElement.signature?.text ?: \"\"\n            }\n\n            is GoFunctionDeclaration -> {\n                psiElement.signature?.text ?: \"\"\n            }\n\n            else -> \"\"\n        }\n        val returnType = psiElement.signature?.resultType?.text\n        val languages = psiElement.language.displayName\n        val returnTypeText = returnType\n        val parameterNames = psiElement.signature?.parameters?.parameterDeclarationList?.mapNotNull {\n            it.paramDefinitionList.firstOrNull()?.text\n        }.orEmpty()\n\n        return MethodStructure(\n            psiElement, psiElement.text, funcName, functionSignature, null, languages,\n            returnTypeText, parameterNames, includeClassContext, emptyList()\n        )\n\n    }\n}\n"
  },
  {
    "path": "languages/shire-go/src/main/kotlin/com/phodal/shirelang/go/codemodel/GoVariableStructureProvider.kt",
    "content": "package com.phodal.shirelang.go.codemodel\n\nimport com.goide.psi.GoVarOrConstDefinition\nimport com.intellij.psi.PsiElement\nimport com.phodal.shirecore.provider.codemodel.VariableStructureProvider\nimport com.phodal.shirecore.provider.codemodel.model.VariableStructure\n\nclass GoVariableStructureProvider : VariableStructureProvider {\n    override fun build(\n        psiElement: PsiElement,\n        withMethodContext: Boolean,\n        withClassContext: Boolean,\n        gatherUsages: Boolean,\n    ): VariableStructure? {\n        if (psiElement !is GoVarOrConstDefinition) {\n            return null\n        }\n\n        val name = psiElement.name\n\n        return VariableStructure(\n            psiElement, psiElement.text, name, null, null, emptyList(), false, false\n        )\n    }\n}\n"
  },
  {
    "path": "languages/shire-go/src/main/kotlin/com/phodal/shirelang/go/util/GoPsiUtil.kt",
    "content": "package com.phodal.shirelang.go.util\n\nimport com.goide.psi.*\nimport com.goide.psi.impl.GoPackage.GoPomTargetPsiElement\nimport com.goide.psi.impl.GoPsiImplUtil\nimport com.intellij.openapi.roots.ProjectFileIndex\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.util.PsiTreeUtil\nimport com.intellij.util.concurrency.annotations.RequiresReadLock\n\nobject GoPsiUtil {\n    fun getDeclarationName(psiElement: PsiElement): String? {\n        return singleNamedDescendant(psiElement)?.name\n    }\n\n    /**\n     * Returns the single named descendant of the given [element].\n     *\n     * @param element the PsiElement to find the single named descendant from\n     * @return the single named descendant of the given [element], or null if there is none\n     */\n    fun singleNamedDescendant(element: PsiElement): GoNamedElement? {\n        return when (element) {\n            is GoNamedElement -> element\n            is GoTypeDeclaration -> element.typeSpecList.singleOrNull()\n            is GoVarOrConstSpec<*> -> element.definitionList.singleOrNull()\n            is GoVarOrConstDeclaration<*> -> {\n                (element.specList.singleOrNull() as? GoVarOrConstSpec)?.definitionList?.singleOrNull()\n            }\n\n            is GoImportDeclaration -> element.importSpecList.singleOrNull()\n            else -> null\n        }\n    }\n\n    @RequiresReadLock\n    fun findRelatedTypes(declaration: GoFunctionOrMethodDeclaration): List<GoTypeSpec> {\n        val signature = declaration.signature ?: return emptyList()\n\n        val parameterTypes = signature.parameters.parameterDeclarationList\n            .mapNotNull { it.type }\n\n        val resultTypes = when (val resultType = signature.resultType) {\n            is GoTypeList -> resultType.typeList\n            else -> listOf(resultType)\n        }\n\n        val mentionedTypes = parameterTypes + resultTypes\n\n        val genericTypes = mentionedTypes\n            .flatMap { it.typeArguments?.types ?: emptyList() }\n\n        val relatedTypes = genericTypes + mentionedTypes\n\n        return relatedTypes\n            .mapNotNull { it.resolve(declaration) as? GoTypeSpec }\n            .filter { isProjectContent(it) }\n    }\n\n    private fun isProjectContent(element: PsiElement): Boolean {\n        val virtualFile = element.containingFile.virtualFile ?: return true\n        return ProjectFileIndex.getInstance(element.project).isInContent(virtualFile)\n    }\n\n    private fun notInLibrary(element: PsiElement): Boolean {\n        return !(element is GoPomTargetPsiElement ||\n                ProjectFileIndex.getInstance(element.project).isInLibrary(element.containingFile.virtualFile))\n    }\n\n    private fun parentWithContext(element: PsiElement): PsiElement? {\n        return when (element) {\n            is GoTypeSpec, is GoMethodSpec, is GoFieldDefinition -> parentTypeSpecOrDeclaration(element)\n            is GoMethodDeclaration -> {\n                val resolveTypeSpec = element.resolveTypeSpec()\n                resolveTypeSpec?.let { parentTypeSpecOrDeclaration(it) }\n            }\n\n            is GoVarOrConstDefinition -> parentVarOrConstSpecOrDeclaration(element)\n            is GoImportSpec -> parentImportList(element)\n            else -> element\n        }\n    }\n\n    private fun parentImportList(importSpec: GoImportSpec): PsiElement {\n        val importList = PsiTreeUtil.getParentOfType(importSpec, GoImportList::class.java, true)\n        return if (importList?.importDeclarationList?.size == 1) importList else importSpec\n    }\n\n    private fun parentTypeSpecOrDeclaration(element: PsiElement): PsiElement {\n        val typeSpec = PsiTreeUtil.getParentOfType(element, GoTypeSpec::class.java, false)\n            ?: return element\n        val typeDeclaration = PsiTreeUtil.getParentOfType(typeSpec, GoTypeDeclaration::class.java, true)\n        return if (typeDeclaration?.typeSpecList?.size == 1) typeDeclaration else typeSpec\n    }\n\n    private fun parentVarOrConstSpecOrDeclaration(element: PsiElement): PsiElement {\n        val varOrConstSpec = PsiTreeUtil.getParentOfType(element, GoVarOrConstSpec::class.java, false)\n            ?: return element\n\n        val varOrConstDeclaration =\n            PsiTreeUtil.getParentOfType(varOrConstSpec, GoVarOrConstDeclaration::class.java, true)\n                ?: return varOrConstSpec\n\n        return when {\n            varOrConstDeclaration.specList.size == 1 -> varOrConstDeclaration\n            varOrConstDeclaration is GoConstDeclaration && varOrConstDeclaration.containsIota() -> varOrConstDeclaration\n            else -> varOrConstSpec\n        }\n    }\n\n    private fun GoExpression.containsIota(): Boolean {\n        val traverser = GoPsiTreeUtil.goTraverser().withRoot(this)\n        for (element in traverser.traverse()) {\n            if (GoPsiImplUtil.isIota(element)) {\n                return true\n            }\n        }\n        return false\n    }\n\n    private fun GoConstDeclaration.containsIota(): Boolean {\n        return constSpecList\n            .asSequence()\n            .flatMap { it.expressionList.asSequence() }\n            .any { it.containsIota() }\n    }\n\n}"
  },
  {
    "path": "languages/shire-go/src/main/kotlin/com/phodal/shirelang/go/variable/GoLanguageProvider.kt",
    "content": "package com.phodal.shirelang.go.variable\n\nimport com.goide.GoLanguage\nimport com.goide.sdk.GoSdkService\nimport com.goide.sdk.GoTargetSdkVersionProvider\nimport com.goide.util.GoUtil\nimport com.intellij.openapi.application.ReadAction\nimport com.intellij.openapi.project.Project\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.PsiFile\nimport com.phodal.shirecore.provider.context.LanguageToolchainProvider\nimport com.phodal.shirecore.provider.context.ToolchainContextItem\nimport com.phodal.shirecore.provider.context.ToolchainPrepareContext\nimport java.lang.reflect.Method\n\nclass GoLanguageProvider : LanguageToolchainProvider {\n    override fun isApplicable(project: Project, context: ToolchainPrepareContext): Boolean {\n        return context.sourceFile?.language is GoLanguage\n    }\n\n    override suspend fun collect(project: Project, context: ToolchainPrepareContext): List<ToolchainContextItem> {\n        if (context.element == null) {\n            return emptyList()\n        }\n\n        val sourceFile = context.sourceFile ?: return emptyList()\n\n        return ReadAction.compute<List<ToolchainContextItem>, Throwable> {\n            val goVersion = GoSdkService.getInstance(project).getSdk(GoUtil.module(sourceFile)).version\n            val targetVersion = getGoVersion(sourceFile)\n\n            val prompt = \"Go Version: $goVersion, Target Version: $targetVersion\"\n            val element = ToolchainContextItem(GoLanguageProvider::class, prompt)\n            listOf(element)\n        }\n    }\n\n    private fun getGoVersion(sourceFile: PsiFile): String {\n        return try {\n            val clazz = Class.forName(\"com.goide.sdk.GoTargetSdkVersionProvider\")\n            val method: Method = clazz.getMethod(\"getTargetGoSdkVersion\", PsiElement::class.java)\n            val result = method.invoke(null, sourceFile)\n            result?.toString()\n        } catch (e: Exception) {\n            e.printStackTrace()\n            null\n        } ?: \"\"\n    }\n}\n"
  },
  {
    "path": "languages/shire-go/src/main/kotlin/com/phodal/shirelang/go/variable/GoPsiContextVariableProvider.kt",
    "content": "package com.phodal.shirelang.go.variable\n\nimport com.goide.GoLanguage\nimport com.goide.psi.*\nimport com.intellij.openapi.editor.Editor\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.roots.TestSourcesFilter\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.util.PsiTreeUtil\nimport com.intellij.testIntegration.TestFinderHelper\nimport com.phodal.shirecore.provider.variable.PsiContextVariableProvider\nimport com.phodal.shirecore.psi.CodeSmellCollector\nimport com.phodal.shirecore.provider.variable.model.PsiContextVariable\nimport com.phodal.shirecore.search.similar.SimilarChunksSearch\nimport com.phodal.shirelang.go.codemodel.GoClassStructureProvider\nimport com.phodal.shirelang.go.codemodel.GoMethodStructureProvider\nimport com.phodal.shirelang.go.util.GoPsiUtil\n\nclass GoPsiContextVariableProvider : PsiContextVariableProvider {\n    override fun resolve(\n        variable: PsiContextVariable,\n        project: Project,\n        editor: Editor,\n        psiElement: PsiElement?,\n    ): Any {\n        if (psiElement?.language !is GoLanguage) return \"\"\n\n        val underTestElement = getElementForTests(psiElement)\n        val underTestFile = underTestElement?.containingFile as? GoFile ?: return \"\"\n\n        return when (variable) {\n            PsiContextVariable.CURRENT_CLASS_NAME -> \"\"\n            PsiContextVariable.CURRENT_CLASS_CODE -> {\n                when (underTestElement) {\n                    is GoTypeDeclaration,\n                    is GoTypeSpec,\n                        -> {\n                        GoClassStructureProvider().build(underTestElement, false)?.format()\n                    }\n\n                    is GoFunctionOrMethodDeclaration -> GoMethodStructureProvider()\n                        .build(underTestElement, false, false)\n                        ?.format()\n\n                    else -> null\n                }\n            }\n\n            PsiContextVariable.CURRENT_METHOD_NAME -> {\n                when (psiElement) {\n                    is GoFunctionOrMethodDeclaration -> psiElement.name\n                    else -> psiElement.text\n                }\n            }\n\n            PsiContextVariable.CURRENT_METHOD_CODE -> psiElement.text\n            PsiContextVariable.RELATED_CLASSES -> {\n                when (underTestElement) {\n                    is GoFunctionOrMethodDeclaration -> {\n                        GoPsiUtil.findRelatedTypes(underTestElement).map { it.text}\n                    }\n\n                    is GoFile -> {\n                        val functions = underTestElement.functions\n                        val methods = underTestElement.methods\n\n                        (functions + methods).flatMap {\n                            GoPsiUtil.findRelatedTypes(it)\n                        }.map { it.text}\n                    }\n\n                    else -> emptyList()\n                }\n            }\n\n            PsiContextVariable.SIMILAR_TEST_CASE -> TODO()\n\n            PsiContextVariable.IMPORTS -> {\n                val importList = PsiTreeUtil.getChildrenOfTypeAsList(underTestFile, GoImportDeclaration::class.java)\n                importList.map { it.text }\n            }\n\n            PsiContextVariable.IS_NEED_CREATE_FILE -> TODO()\n            PsiContextVariable.TARGET_TEST_FILE_NAME -> {\n                val name = GoPsiUtil.getDeclarationName(underTestElement) ?: return \"\"\n                toTestFileName(name)\n            }\n\n            PsiContextVariable.UNDER_TEST_METHOD_CODE -> {\n                when (underTestElement) {\n                    is GoFunctionOrMethodDeclaration -> psiElement.text\n                    else -> psiElement.text\n                }\n            }\n\n            PsiContextVariable.FRAMEWORK_CONTEXT -> return collectFrameworkContext(psiElement, project)\n            PsiContextVariable.CODE_SMELL -> CodeSmellCollector.collectElementProblemAsSting(\n                underTestElement,\n                project,\n                editor\n            )\n\n            PsiContextVariable.METHOD_CALLER -> {\n                if (psiElement !is GoFunctionOrMethodDeclaration) return \"\"\n                \"\"\n            }\n\n            PsiContextVariable.CALLED_METHOD -> return SimilarChunksSearch.createQuery(psiElement) ?: \"\"\n            PsiContextVariable.SIMILAR_CODE -> TODO()\n            PsiContextVariable.STRUCTURE -> {\n                when (underTestElement) {\n                    is GoTypeDeclaration,\n                    is GoTypeSpec,\n                        -> {\n                        GoClassStructureProvider().build(underTestElement, true)?.toString() ?: \"\"\n                    }\n\n                    is GoFunctionOrMethodDeclaration -> GoMethodStructureProvider()\n                        .build(underTestElement, true, true)\n                        ?.toString() ?: \"\"\n                    else -> \"\"\n                }\n            }\n            PsiContextVariable.CHANGE_COUNT -> calculateChangeCount(psiElement)\n            PsiContextVariable.LINE_COUNT -> calculateLineCount(psiElement)\n            PsiContextVariable.COMPLEXITY_COUNT -> calculateComplexityCount(psiElement)\n        } ?: \"\"\n    }\n\n    private fun toTestFileName(underTestFileName: String): String = underTestFileName + \"_test.go\"\n\n    private fun getElementForTests(elementAtCaret: PsiElement): PsiElement? {\n        val parent = PsiTreeUtil.getParentOfType(elementAtCaret, GoFunctionOrMethodDeclaration::class.java, false)\n        if (parent == null) {\n            val goFile: GoFile = elementAtCaret as? GoFile ?: return null\n            return if (goFile.functions.isNotEmpty() || goFile.methods.isNotEmpty()) {\n                goFile\n            } else {\n                null\n            }\n        }\n\n        val virtualFile = elementAtCaret.containingFile?.virtualFile ?: return null\n\n        val project = elementAtCaret.project\n        if (TestSourcesFilter.isTestSources(virtualFile, project) || TestFinderHelper.isTest(parent)) return null\n\n        return parent\n    }\n}\n\n"
  },
  {
    "path": "languages/shire-go/src/main/resources/com.phodal.shirelang.go.xml",
    "content": "<idea-plugin package=\"com.phodal.shirelang.go\">\n    <!--suppress PluginXmlValidity -->\n    <dependencies>\n        <plugin id=\"org.jetbrains.plugins.go\"/>\n        <plugin id=\"com.intellij.modules.go-capable\"/>\n        <plugin id=\"com.intellij.modules.platform\"/>\n    </dependencies>\n\n    <extensions defaultExtensionNs=\"com.phodal\">\n        <fileStructureProvider language=\"go\"\n                               implementationClass=\"com.phodal.shirelang.go.codemodel.GoFileStructureProvider\"/>\n        <classStructureProvider language=\"go\"\n                                implementationClass=\"com.phodal.shirelang.go.codemodel.GoClassStructureProvider\"/>\n        <methodStructureProvider language=\"go\"\n                                 implementationClass=\"com.phodal.shirelang.go.codemodel.GoMethodStructureProvider\"/>\n        <variableStructureProvider language=\"go\"\n                                   implementationClass=\"com.phodal.shirelang.go.codemodel.GoVariableStructureProvider\"/>\n\n        <shireLanguageToolchainProvider\n                language=\"go\"\n                implementationClass=\"com.phodal.shirelang.go.variable.GoLanguageProvider\"/>\n\n\n        <shirePsiVariableProvider\n                language=\"go\"\n                implementationClass=\"com.phodal.shirelang.go.variable.GoPsiContextVariableProvider\"/>\n    </extensions>\n</idea-plugin>\n"
  },
  {
    "path": "languages/shire-java/src/main/kotlin/com/phodal/shirelang/java/archmeta/SpringLayerCharacteristic.kt",
    "content": "package com.phodal.shirelang.java.archmeta\n\nimport com.intellij.psi.PsiClass\nimport kotlinx.serialization.Serializable\n\n@Serializable\nclass SpringLayerCharacteristic(val annotation: String, val imports: List<String>, val codeRegex: String, val fileName: String? = null) {\n    companion object {\n        private val controllerCharacteristic = SpringLayerCharacteristic(\n            annotation = \"@Controller\",\n            imports = listOf(\n                \"org.springframework.stereotype.Controller\",\n                \"org.springframework.web.bind.annotation.RestController\"\n            ),\n            codeRegex = \"public\\\\s+class\\\\s+\\\\w+Controller\",\n            fileName = \".*Controller\\\\.java\"\n        )\n\n        private val serviceCharacteristic = SpringLayerCharacteristic(\n            annotation = \"@Service\",\n            imports = listOf(\"org.springframework.stereotype.Service\"),\n            codeRegex = \"public\\\\s+(class|interface)\\\\s+\\\\w+Service\",\n            fileName = \".*(Service|ServiceImpl)\\\\.java\"\n        )\n\n        private val entityCharacteristic = SpringLayerCharacteristic(\n            annotation = \"@Entity\",\n            imports = listOf(\"javax.persistence.Entity\"),\n            codeRegex = \"public\\\\s+class\\\\s+\\\\w+Entity\",\n        )\n\n        private val dtoCharacteristic = SpringLayerCharacteristic(\n            annotation = \"@Data\",\n            imports = listOf(\"lombok.Data\"),\n            codeRegex = \"public\\\\s+class\\\\s+\\\\w+(Dto|DTO|Request|Response|Res|Req)\",\n            fileName = \".*(Dto|DTO|Request|Response|Res|Req)\\\\.java\"\n        )\n\n        private val repositoryCharacteristic = SpringLayerCharacteristic(\n            annotation = \"org.springframework.stereotype.Repository\",\n            imports = listOf(\"org.springframework.stereotype.Repository\"),\n            codeRegex = \"public\\\\s+(class|interface)\\\\s+\\\\w+Repository\",\n            fileName = \".*Repository\\\\.java\"\n        )\n\n        private val allCharacteristics = mapOf(\n            \"controller\" to controllerCharacteristic,\n            \"service\" to serviceCharacteristic,\n            \"entity\" to entityCharacteristic,\n            \"dto\" to dtoCharacteristic,\n            \"repository\" to repositoryCharacteristic\n        )\n\n\n        fun check(code: String, type: String): Boolean {\n            val characteristic = allCharacteristics[type] ?: return false\n            if (code.contains(characteristic.annotation)) {\n                return true\n            }\n\n            characteristic.imports.forEach {\n                if (code.contains(it)) {\n                    return true\n                }\n            }\n\n            val regex = Regex(characteristic.codeRegex)\n            return regex.containsMatchIn(code)\n        }\n\n        fun check(code: PsiClass, type: String): Boolean {\n            val characteristic = allCharacteristics[type] ?: return false\n            code.annotations.forEach {\n                if (characteristic.imports.contains(it.qualifiedName)) {\n                    return true\n                }\n            }\n\n            if (characteristic.fileName != null) {\n                val regex = Regex(characteristic.fileName)\n                code.name?.lowercase()?.let {\n                    if (regex.containsMatchIn(it)) {\n                        return true\n                    }\n                }\n            }\n\n            val regex = Regex(characteristic.codeRegex)\n            return regex.containsMatchIn(code.text)\n        }\n    }\n}"
  },
  {
    "path": "languages/shire-java/src/main/kotlin/com/phodal/shirelang/java/archmeta/SpringLibrary.kt",
    "content": "package com.phodal.shirelang.java.archmeta\n\ndata class SpringDataLibraryDescriptor(val shortText: String, val coords: List<String>)\ndata class LibraryDescriptor(val shortText: String, val coords: String)\n\nobject SpringLibrary {\n    // Spring\n    private const val SPRING_MVC_MAVEN = \"org.springframework:spring-webmvc\"\n    private const val SPRING_WEBFLUX_MAVEN = \"org.springframework:spring-webflux\"\n\n    // Spring Data\n    private const val REACTOR_MAVEN = \"io.projectreactor:reactor-core\"\n    private const val MONGO_REACTIVE_STREAMS_MAVEN = \"org.mongodb:mongodb-driver-reactivestreams\"\n    private const val SPRING_DATA_COMMONS_MAVEN = \"org.springframework.data:spring-data-commons\"\n    private const val JPA_MAVEN = \"org.springframework.data:spring-data-jpa\"\n    private const val CASSANDRA_MAVEN = \"org.springframework.data:spring-data-cassandra\"\n    private const val COUCHBASE_MAVEN = \"org.springframework.data:spring-data-couchbase\"\n    private const val JDBC_MAVEN = \"org.springframework.data:spring-data-jdbc\"\n    private const val MONGO_MAVEN = \"org.springframework.data:spring-data-mongodb\"\n    private const val NEO4J_MAVEN = \"org.springframework.data:spring-data-neo4j\"\n    private const val R2DBC_MAVEN = \"org.springframework.data:spring-data-r2dbc\"\n    private const val REDIS_MAVEN = \"org.springframework.data:spring-data-redis\"\n\n    val SPRING_DATA = listOf(\n        SpringDataLibraryDescriptor(\"JPA \", listOf(JPA_MAVEN)),\n        SpringDataLibraryDescriptor(\"CASSANDRA\", listOf(CASSANDRA_MAVEN)),\n        SpringDataLibraryDescriptor(\"REACTIVE CASSANDRA\", listOf(CASSANDRA_MAVEN, REACTOR_MAVEN)),\n        SpringDataLibraryDescriptor(\"COUCHBASE\", listOf(COUCHBASE_MAVEN)),\n        SpringDataLibraryDescriptor(\"REACTIVE COUCHBASE\", listOf(COUCHBASE_MAVEN, REACTOR_MAVEN)),\n        SpringDataLibraryDescriptor(\"JDBC\", listOf(JDBC_MAVEN)),\n        SpringDataLibraryDescriptor(\"MONGO\", listOf(MONGO_MAVEN)),\n        SpringDataLibraryDescriptor(\n            \"REACTIVE MONGO\",\n            listOf(MONGO_MAVEN, REACTOR_MAVEN, MONGO_REACTIVE_STREAMS_MAVEN)\n        ),\n        SpringDataLibraryDescriptor(\"NEO4J\", listOf(NEO4J_MAVEN)),\n        SpringDataLibraryDescriptor(\"R2DBC\", listOf(R2DBC_MAVEN)),\n        SpringDataLibraryDescriptor(\"REDIS\", listOf(REDIS_MAVEN))\n    )\n\n    fun canApplySpringData(libName: String) = libName == SPRING_DATA_COMMONS_MAVEN\n\n    val SPRING_MVC = listOf(\n        LibraryDescriptor(\"Spring MVC\", SPRING_MVC_MAVEN),\n        LibraryDescriptor(\"Spring WebFlux\", SPRING_WEBFLUX_MAVEN)\n    )\n\n    fun canApplySpringMvc(libName: String) = libName == SPRING_MVC_MAVEN || libName == SPRING_WEBFLUX_MAVEN\n}"
  },
  {
    "path": "languages/shire-java/src/main/kotlin/com/phodal/shirelang/java/codeedit/JavaAutoTestingService.kt",
    "content": "package com.phodal.shirelang.java.codeedit\n\nimport com.intellij.codeInsight.daemon.DaemonCodeAnalyzer\nimport com.intellij.execution.RunManager\nimport com.intellij.execution.configurations.RunConfiguration\nimport com.intellij.execution.configurations.RunProfile\nimport com.intellij.lang.java.JavaLanguage\nimport com.intellij.openapi.application.ReadAction\nimport com.intellij.openapi.application.runReadAction\nimport com.intellij.openapi.command.WriteCommandAction\nimport com.intellij.openapi.diagnostic.logger\nimport com.intellij.openapi.externalSystem.service.project.ProjectDataManager\nimport com.intellij.openapi.fileEditor.FileDocumentManager\nimport com.intellij.openapi.fileEditor.FileEditorManager\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.project.guessProjectDir\nimport com.intellij.openapi.roots.ProjectFileIndex\nimport com.intellij.openapi.vfs.LocalFileSystem\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.intellij.openapi.vfs.VirtualFileManager\nimport com.intellij.psi.*\nimport com.intellij.psi.util.PsiTreeUtil\nimport com.phodal.shirecore.provider.TestingService\nimport com.phodal.shirecore.provider.codemodel.ClassStructureProvider\nimport com.phodal.shirecore.provider.codemodel.model.ClassStructure\nimport com.phodal.shirecore.psi.collectPsiError\nimport com.phodal.shirecore.variable.toolchain.unittest.AutoTestingPromptContext\nimport com.phodal.shirelang.java.util.JavaTypeResolver\nimport org.jetbrains.idea.maven.execution.MavenRunConfiguration\nimport org.jetbrains.idea.maven.execution.MavenRunConfigurationType\nimport org.jetbrains.idea.maven.execution.MavenRunnerParameters\nimport org.jetbrains.idea.maven.project.MavenProject\nimport org.jetbrains.idea.maven.project.MavenProjectsManager\nimport org.jetbrains.plugins.gradle.service.execution.GradleExternalTaskConfigurationType\nimport org.jetbrains.plugins.gradle.service.execution.GradleRunConfiguration\nimport org.jetbrains.plugins.gradle.util.GradleConstants\nimport java.io.File\n\nclass JavaAutoTestService : TestingService() {\n    private val maxLevelOneClass = 8\n    override fun runConfigurationClass(project: Project): Class<out RunProfile> = GradleRunConfiguration::class.java\n    override fun isApplicable(element: PsiElement): Boolean = element.language is JavaLanguage\n    override fun isApplicable(project: Project, file: VirtualFile): Boolean {\n        return file.extension == \"java\" && PsiManager.getInstance(project).findFile(file) is PsiJavaFile\n    }\n\n    override fun createConfiguration(project: Project, virtualFile: VirtualFile): RunConfiguration? {\n        val psiFile =\n            runReadAction { PsiManager.getInstance(project).findFile(virtualFile) } as? PsiJavaFile ?: return null\n\n        if (psiFile.collectPsiError().isNotEmpty()) {\n            return null\n        }\n\n        return createConfigForJava(virtualFile, project)\n    }\n\n    override fun findOrCreateTestFile(\n        sourceFile: PsiFile,\n        project: Project,\n        psiElement: PsiElement,\n    ): AutoTestingPromptContext? {\n        val sourceFilePath = sourceFile.virtualFile\n        val parentDir = sourceFilePath.parent\n        val testFileName = sourceFile.name.replace(\".java\", \"\") + \"Test\"\n\n        val parentDirPath = ReadAction.compute<String, Throwable> { parentDir?.path }\n\n        val relatedModels = lookupRelevantClass(project, psiElement).distinctBy { it.name }\n\n        // Check if the source file is in the src/main/java directory\n        if (!parentDirPath?.contains(\"/src/main/java/\")!!) {\n            log.error(\"Source file is not in the src/main/java directory: $parentDirPath\")\n            return null\n        }\n\n        var isNewFile = false\n\n        // Find the test directory\n        val testDirPath = parentDirPath.replace(\"/src/main/java/\", \"/src/test/java/\")\n        var testDir = LocalFileSystem.getInstance().findFileByPath(testDirPath)\n\n        if (testDir == null || !testDir.isDirectory) {\n            isNewFile = true\n            // Create the test directory if it doesn't exist\n            val testDirFile = File(testDirPath)\n            if (!testDirFile.exists()) {\n                testDirFile.mkdirs()\n\n                LocalFileSystem.getInstance().refreshAndFindFileByPath(testDirPath)?.let { refreshedDir ->\n                    testDir = refreshedDir\n                }\n            }\n        }\n\n        val testDirCreated: VirtualFile? =\n            VirtualFileManager.getInstance().refreshAndFindFileByUrl(\"file://$testDirPath\")\n\n        if (testDirCreated == null) {\n            log.error(\"Failed to create test directory: $testDirPath\")\n            return null\n        }\n\n        // Test directory already exists, find the corresponding test file\n        val testFilePath = testDirPath + \"/\" + sourceFile.name.replace(\".java\", \"Test.java\")\n        val testFile = LocalFileSystem.getInstance().findFileByPath(testFilePath)\n\n        project.guessProjectDir()?.refresh(true, true)\n\n        val imports = runReadAction {\n            val importList = PsiTreeUtil.getChildrenOfTypeAsList(sourceFile, PsiImportList::class.java)\n            importList.flatMap { it.allImportStatements.map { import -> import.text } }\n        }\n\n        val currentClass = extracted(psiElement)\n\n\n        val relatedClasses = relatedModels.map { it.format() }\n        return if (testFile != null) {\n            AutoTestingPromptContext(\n                isNewFile,\n                testFile,\n                relatedClasses,\n                testFileName,\n                sourceFile.language,\n                currentClass,\n                imports\n            )\n        } else {\n            val targetFile = createTestFile(sourceFile, testDir!!, project)\n            AutoTestingPromptContext(\n                isNewFile = true,\n                targetFile,\n                relatedClasses,\n                \"\",\n                sourceFile.language,\n                currentClass,\n                imports\n            )\n        }\n    }\n\n    private fun extracted(psiElement: PsiElement): String? {\n        var currentClass: ClassStructure? = null;\n        when (psiElement) {\n            is PsiJavaFile -> {\n                currentClass = runReadAction { psiElement.classes.firstOrNull()?.let {  ClassStructureProvider.from(it) } }\n            }\n\n            is PsiClass -> {\n                currentClass = runReadAction { ClassStructureProvider.from(psiElement) }\n            }\n\n            is PsiMethod -> {\n                currentClass = runReadAction { psiElement.containingClass?.let { ClassStructureProvider.from(it) } }\n            }\n        }\n\n        return currentClass?.format();\n    }\n\n    override fun lookupRelevantClass(project: Project, element: PsiElement): List<ClassStructure> {\n        return ReadAction.compute<List<ClassStructure>, Throwable> {\n            val elements = mutableListOf<ClassStructure>()\n            val projectPath = project.guessProjectDir()?.path\n\n            val resolvedClasses: MutableMap<String, PsiClass> = mutableMapOf()\n            resolvedClasses.putAll(JavaTypeResolver.resolveByField(element))\n\n            when (element) {\n                is PsiJavaFile -> {\n                    element.classes.forEach { psiClass ->\n                        resolvedClasses.putAll(JavaTypeResolver.resolveByClass(psiClass))\n                    }\n                }\n\n                is PsiClass -> {\n                    element.methods.forEach { method ->\n                        resolvedClasses.putAll(JavaTypeResolver.resolveByMethod(method))\n                    }\n                }\n\n                is PsiMethod -> {\n                    resolvedClasses.putAll(JavaTypeResolver.resolveByMethod(element))\n                }\n            }\n\n            if (resolvedClasses.isEmpty()) {\n                return@compute elements\n            }\n\n            if ((resolvedClasses.size <= maxLevelOneClass) || element is PsiMethod) {\n                // load all second childrens\n                val childClasses: MutableMap<String, PsiClass> = mutableMapOf()\n                resolvedClasses.forEach { (key, value) ->\n                    value.fields.forEach { field ->\n                        childClasses.putAll(JavaTypeResolver.resolveByType(field.type))\n                    }\n                }\n\n                resolvedClasses.putAll(childClasses)\n            }\n\n            // find the class in the same project\n            resolvedClasses.forEach { (_, psiClass) ->\n                val classPath = psiClass.containingFile?.virtualFile?.path\n                if (classPath?.contains(projectPath!!) == true) {\n                    elements += ClassStructureProvider.from(psiClass, false) ?: return@forEach\n                }\n            }\n\n            elements\n        }\n    }\n\n    override fun tryFixSyntaxError(outputFile: VirtualFile, project: Project, issues: List<String>) {\n        val sourceFile: PsiJavaFile =\n            runReadAction { PsiManager.getInstance(project).findFile(outputFile) as? PsiJavaFile } ?: return\n\n        val editor = FileEditorManager.getInstance(project).selectedTextEditor ?: return\n        DaemonCodeAnalyzer.getInstance(project).autoImportReferenceAtCursor(editor, sourceFile)\n    }\n\n    private fun createTestFile(sourceFile: PsiFile, testDir: VirtualFile, project: Project): VirtualFile {\n        val sourceFileName = sourceFile.name\n        val testFileName = sourceFileName.replace(\".java\", \"Test.java\")\n        val testFileContent = \"\"\n\n        return WriteCommandAction.runWriteCommandAction<VirtualFile>(project) {\n            val testFile = testDir.createChildData(this, testFileName)\n            val document = FileDocumentManager.getInstance().getDocument(testFile)\n            document?.setText(testFileContent)\n            testFile\n        }\n    }\n\n    companion object {\n        private val log = logger<JavaAutoTestService>()\n    }\n}\n\nfun createConfigForJava(virtualFile: VirtualFile, project: Project): RunConfiguration? {\n    val gradleLibraryData = ProjectDataManager.getInstance().getExternalProjectData(\n        project, GradleConstants.SYSTEM_ID, project.basePath!!\n    )\n\n    if (gradleLibraryData == null) {\n        return createConfigForMaven(virtualFile, project)\n    }\n\n    return createConfigForGradle(virtualFile, project)\n}\n\nfun createConfigForGradle(virtualFile: VirtualFile, project: Project): GradleRunConfiguration? {\n    val name = virtualFile.name\n\n    val canonicalName = runReadAction {\n        val psiFile: PsiJavaFile =\n            PsiManager.getInstance(project).findFile(virtualFile) as? PsiJavaFile ?: return@runReadAction null\n        // skip for non-test files\n        (psiFile.packageName + \".\" + virtualFile.nameWithoutExtension).removePrefix(\".\")\n    } ?: return null\n\n    val runManager = RunManager.getInstance(project)\n\n    var moduleName = \"\"\n    val moduleForFile = runReadAction { ProjectFileIndex.getInstance(project).getModuleForFile(virtualFile) }\n    // a moduleForFile.name will be like <project>.<module>.<testModule>, so we need to remove the last part and first part\n    if (moduleForFile != null) {\n        val moduleNameSplit = moduleForFile.name.split(\".\").drop(1).dropLast(1).joinToString(\":\")\n        if (moduleNameSplit.isNotEmpty()) {\n            moduleName = \"$moduleNameSplit:\"\n        }\n    }\n\n    // todo: add maven ??\n    val configuration = runManager.createConfiguration(name, GradleExternalTaskConfigurationType::class.java)\n    val runConfiguration = configuration.configuration as GradleRunConfiguration\n\n    runConfiguration.isDebugServerProcess = false\n    runConfiguration.settings.externalProjectPath = project.guessProjectDir()?.path\n    // todo: add module for test\n    runConfiguration.rawCommandLine = moduleName + \"test --tests \\\"${canonicalName}\\\"\"\n\n    runManager.addConfiguration(configuration)\n    runManager.selectedConfiguration = configuration\n\n    return runConfiguration\n}\n\nfun createConfigForMaven(virtualFile: VirtualFile, project: Project): MavenRunConfiguration? {\n    val projectsManager = MavenProjectsManager.getInstance(project);\n\n    val mavenProject: MavenProject = projectsManager.findProject(virtualFile) ?: return null\n    val module = runReadAction { projectsManager.findModule(mavenProject) } ?: return null\n\n    var trulyMavenProject = projectsManager.projects.firstOrNull {\n        it.mavenId.artifactId == module.name\n    }\n\n    if (trulyMavenProject == null) {\n        trulyMavenProject = projectsManager.projects.first() ?: return null\n    }\n\n    val pomFile = trulyMavenProject.file.name\n\n    val parameters = MavenRunnerParameters(\n        true, trulyMavenProject.directory, pomFile, listOf(\"test\"),\n        projectsManager.explicitProfiles.enabledProfiles, arrayListOf()\n    )\n\n    // $MODULE_WORKING_DIR$\n    //\n    // -ea Method: com.example.demo.MathHelperTest should_ReturnSum_When_GivenTwoPositiveNumbers\n    // /Users/phodal/Library/Java/JavaVirtualMachines/corretto-18.0.2/Contents/Home/bin/java\n    // -ea -Didea.test.cyclic.buffer.size=1048576\n    // -javaagent:ideaIU-2024.1/lib/idea_rt.jar=54637:1/bin -Dfile.encoding=UTF-8\n    // -Dsun.stdout.encoding=UTF-8 -Dsun.stderr.encoding=UTF-8\n\n    val runnerAndConfigurationSettings =\n        MavenRunConfigurationType.createRunnerAndConfigurationSettings(null, null, parameters, project)\n\n    val runManager = RunManager.getInstance(project)\n\n    val configuration = runnerAndConfigurationSettings.configuration\n\n    runManager.addConfiguration(runnerAndConfigurationSettings)\n    runManager.selectedConfiguration = runnerAndConfigurationSettings\n\n    return configuration as MavenRunConfiguration\n}\n"
  },
  {
    "path": "languages/shire-java/src/main/kotlin/com/phodal/shirelang/java/codeedit/JavaCodeModifier.kt",
    "content": "package com.phodal.shirelang.java.codeedit\n\nimport com.intellij.lang.Language\nimport com.intellij.lang.java.JavaLanguage\nimport com.intellij.openapi.application.ReadAction\nimport com.intellij.openapi.application.runReadAction\nimport com.intellij.openapi.command.WriteCommandAction\nimport com.intellij.openapi.diagnostic.logger\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.project.guessProjectDir\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.intellij.psi.*\nimport com.intellij.psi.codeStyle.CodeStyleManager\nimport com.phodal.shirecore.provider.codeedit.CodeModifier\n\nopen class JavaCodeModifier : CodeModifier {\n    private val log = logger<JavaCodeModifier>()\n\n    override fun isApplicable(language: Language) = language is JavaLanguage\n\n    override fun smartInsert(sourceFile: VirtualFile, project: Project, code: String): PsiElement? {\n        val isTestFile = sourceFile.name.endsWith(\"Test.java\")\n        if (!isTestFile) {\n            return insertTestCode(sourceFile, project, code)\n        }\n\n        return insertMethod(sourceFile, project, code)\n    }\n\n    private fun lookupFile(project: Project, sourceFile: VirtualFile) =\n        PsiManager.getInstance(project).findFile(sourceFile) as PsiJavaFile\n\n    /**\n     * This function is used to insert test code into a specified source file in a Kotlin project.\n     * It takes the source file, project, and the test code as parameters.\n     *\n     * The function first trims the test code by removing leading and trailing whitespaces, as well as any surrounding triple backticks and \"java\" prefix.\n     * If the trimmed code does not contain the \"@Test\" annotation, a warning is logged and the method code is inserted into the source file.\n     *\n     * It then checks if the trimmed code is a full class code (starts with \"import\" or \"package\" and contains \"class \").\n     * If the source file already contains classes, the function inserts the test code into an existing class.\n     *\n     * If the trimmed code is a full class code, the function inserts a new class into the source file.\n     *\n     * If none of the above conditions are met, the function inserts the test code as a method into the source file.\n     *\n     * @param sourceFile The VirtualFile representing the source file where the test code will be inserted.\n     * @param project The Project to which the source file belongs.\n     * @param code The test code to be inserted into the source file.\n     * @return Boolean value indicating whether the test code was successfully inserted.\n     */\n    override fun insertTestCode(sourceFile: VirtualFile, project: Project, code: String): PsiElement? {\n        val trimCode = code.trim().removeSurrounding(\"```\").removePrefix(\"java\").trim()\n\n        val isFullTestCode =\n            (trimCode.startsWith(\"import\") || trimCode.startsWith(\"package\")) && trimCode.contains(\"class \")\n\n        val existTestFileClasses = runReadAction { lookupFile(project, sourceFile).classes }\n        val alreadyExtTestFile = existTestFileClasses.isNotEmpty()\n\n        return when {\n            alreadyExtTestFile -> return insertToExistClass(existTestFileClasses, project, trimCode)\n            isFullTestCode -> return insertClass(sourceFile, project, trimCode)\n            trimCode.contains(\"@Test\") -> insertMethod(sourceFile, project, trimCode)\n            else -> {\n                log.warn(\"methodCode does not contain @Test annotation: $trimCode\")\n                insertMethod(sourceFile, project, trimCode)\n            }\n        }\n    }\n\n    private fun insertToExistClass(\n        testFileClasses: Array<out PsiClass>,\n        project: Project,\n        trimCode: String,\n    ): PsiElement? {\n        // todo: check to naming testFile, but since Java only has One Class under file\n        val lastClass = testFileClasses.last()\n        val classEndOffset = runReadAction { lastClass.textRange.endOffset }\n\n        val psiFile = try {\n            PsiFileFactory.getInstance(project)\n                .createFileFromText(\"Test.java\", JavaLanguage.INSTANCE, trimCode)\n        } catch (e: Throwable) {\n            log.warn(\"Failed to create file from text: $trimCode\", e)\n            null\n        }\n\n        val newCode = psiFile?.text ?: trimCode\n        try {\n            val newClassMethods = runReadAction {\n                psiFile?.children?.firstOrNull { it is PsiClass }?.children?.filterIsInstance<PsiMethod>()\n            }\n\n            WriteCommandAction.runWriteCommandAction(project) {\n                newClassMethods?.forEach {\n                    lastClass.add(it)\n                }\n            }\n\n            return lastClass\n        } catch (e: Throwable) {\n            WriteCommandAction.runWriteCommandAction(project) {\n                val document = PsiDocumentManager.getInstance(project).getDocument(lastClass.containingFile)\n                document?.insertString(classEndOffset - 1, \"\\n    $newCode\")\n            }\n\n            return lastClass\n        }\n    }\n\n    override fun insertMethod(sourceFile: VirtualFile, project: Project, code: String): PsiElement? {\n        val rootElement = runReadAction {\n            val psiJavaFile = lookupFile(project, sourceFile)\n            val psiClass = psiJavaFile.classes.firstOrNull()\n            if (psiClass == null) {\n                log.error(\"Failed to find PsiClass in the source file: $psiJavaFile, code: $code\")\n                return@runReadAction null\n            }\n\n            return@runReadAction psiClass\n        } ?: return null\n\n        val newTestMethod = ReadAction.compute<PsiMethod, Throwable> {\n            val psiElementFactory = PsiElementFactory.getInstance(project)\n            try {\n                val methodCode = psiElementFactory.createMethodFromText(code, rootElement)\n                if (rootElement.findMethodsByName(methodCode.name, false).isNotEmpty()) {\n                    log.error(\"Method already exists in the class: ${methodCode.name}\")\n                }\n\n                methodCode\n            } catch (e: Throwable) {\n                log.error(\"Failed to create method from text: $code\", e)\n                return@compute null\n            }\n        }\n\n        WriteCommandAction.runWriteCommandAction(project) {\n            try {\n                rootElement.add(newTestMethod)\n            } catch (e: Throwable) {\n                val classEndOffset = rootElement.textRange.endOffset\n                val document = PsiDocumentManager.getInstance(project).getDocument(rootElement.containingFile)\n                document?.insertString(classEndOffset - 1, \"\\n    \")\n                document?.insertString(classEndOffset - 1 + \"\\n    \".length, newTestMethod.text)\n            }\n        }\n\n        project.guessProjectDir()?.refresh(true, true)\n\n        return newTestMethod\n    }\n\n    override fun insertClass(sourceFile: VirtualFile, project: Project, code: String): PsiElement? {\n        return WriteCommandAction.runWriteCommandAction<PsiElement?>(project) {\n            val psiFile = lookupFile(project, sourceFile)\n            val document = psiFile.viewProvider.document!!\n            document.insertString(document.textLength, code)\n\n            psiFile.classes.firstOrNull()\n        }\n    }\n}\n"
  },
  {
    "path": "languages/shire-java/src/main/kotlin/com/phodal/shirelang/java/codemodel/JavaClassStructureProvider.kt",
    "content": "package com.phodal.shirelang.java.codemodel\n\nimport com.intellij.openapi.application.ApplicationManager\nimport com.intellij.openapi.application.runReadAction\nimport com.intellij.openapi.progress.ProgressManager\nimport com.intellij.openapi.progress.util.ProgressIndicatorBase\nimport com.intellij.psi.*\nimport com.intellij.psi.search.GlobalSearchScope\nimport com.intellij.psi.search.SearchScope\nimport com.intellij.psi.search.searches.MethodReferencesSearch\nimport com.intellij.psi.search.searches.ReferencesSearch\nimport com.phodal.shirecore.provider.codemodel.ClassStructureProvider\nimport com.phodal.shirecore.provider.codemodel.model.ClassStructure\n\n\nclass JavaClassStructureProvider : ClassStructureProvider {\n    override fun build(psiElement: PsiElement, gatherUsages: Boolean): ClassStructure? {\n        if (psiElement !is PsiClass) return null\n\n        val supers = runReadAction {\n            psiElement.extendsList?.referenceElements?.mapNotNull {\n                it.text\n            }\n        }\n\n        return runReadAction {\n            val fields = psiElement.fields.toList()\n            val methods = psiElement.methods.toList()\n\n            val usages =\n                if (gatherUsages) findUsages(psiElement as PsiNameIdentifierOwner) else emptyList()\n\n            val annotations: List<String> = psiElement.annotations.mapNotNull {\n                it.text\n            }\n\n            ClassStructure(\n                psiElement, psiElement.text, psiElement.name,\n                displayName = psiElement.qualifiedName,\n                methods, fields, supers,\n                annotations,\n                usages\n            )\n        }\n    }\n\n    companion object {\n        /**\n         * This method is used to find usages of a given PsiNameIdentifierOwner in the project.\n         *\n         * @param nameIdentifierOwner the PsiNameIdentifierOwner for which usages need to be found\n         * @return a list of PsiReference objects representing the usages of the given PsiNameIdentifierOwner\n         */\n        fun findUsages(nameIdentifierOwner: PsiNameIdentifierOwner): List<PsiReference> {\n            val project = nameIdentifierOwner.project\n            val searchScope = GlobalSearchScope.allScope(project)\n\n            var results = emptyList<PsiReference>()\n            ProgressManager.getInstance().runProcess(Runnable {\n                results = when (nameIdentifierOwner) {\n                    is PsiMethod -> {\n                        MethodReferencesSearch.search(nameIdentifierOwner, searchScope, true)\n                    }\n\n                    else -> {\n                        ReferencesSearch.search((nameIdentifierOwner as PsiElement), searchScope, true)\n                    }\n                }.findAll().map { it as PsiReference }\n            }, ProgressIndicatorBase())\n            return results\n        }\n    }\n}\n"
  },
  {
    "path": "languages/shire-java/src/main/kotlin/com/phodal/shirelang/java/codemodel/JavaFileStructureProvider.kt",
    "content": "package com.phodal.shirelang.java.codemodel\n\nimport com.intellij.psi.*\nimport com.intellij.psi.util.PsiTreeUtil.getChildrenOfTypeAsList\nimport com.phodal.shirecore.provider.codemodel.FileStructureProvider\nimport com.phodal.shirecore.provider.codemodel.model.FileStructure\nimport com.phodal.shirecore.relativePath\n\nclass JavaFileStructureProvider : FileStructureProvider {\n    override fun build(psiFile: PsiFile): FileStructure {\n        val packageStatement = getChildrenOfTypeAsList(psiFile, PsiPackageStatement::class.java).firstOrNull()\n        val importLists = getChildrenOfTypeAsList(psiFile, PsiImportList::class.java)\n        val classDeclarations = getChildrenOfTypeAsList(psiFile, PsiClass::class.java)\n\n        val imports = mutableListOf<PsiElement>()\n        for (it in importLists) imports.addAll(it.allImportStatements)\n\n        val packageString = packageStatement?.text\n        val path = psiFile.virtualFile.relativePath(psiFile.project)\n\n        return FileStructure(psiFile, psiFile.name, path, packageString, imports, classDeclarations, emptyList())\n    }\n}\n"
  },
  {
    "path": "languages/shire-java/src/main/kotlin/com/phodal/shirelang/java/codemodel/JavaMethodStructureProvider.kt",
    "content": "package com.phodal.shirelang.java.codemodel\n\nimport com.intellij.openapi.application.ApplicationManager\nimport com.intellij.openapi.application.runReadAction\nimport com.intellij.psi.PsiClass\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.PsiMethod\nimport com.intellij.psi.PsiNameIdentifierOwner\nimport com.phodal.shirecore.provider.codemodel.MethodStructureProvider\nimport com.phodal.shirecore.provider.codemodel.model.MethodStructure\nimport com.phodal.shirelang.java.util.JavaTypeResolver\nimport java.util.concurrent.Future\n\nclass JavaMethodStructureProvider : MethodStructureProvider {\n    override fun build(psiElement: PsiElement, includeClassContext: Boolean, gatherUsages: Boolean): MethodStructure? {\n        if (psiElement !is PsiMethod) {\n            return null\n        }\n\n        val parameterList = runReadAction { psiElement.parameters.mapNotNull { it.name } }\n        val variableContextList = parameterList.map { it }\n\n        val usagesList = if (gatherUsages) {\n            JavaClassStructureProvider.findUsages(psiElement as PsiNameIdentifierOwner)\n        } else {\n            emptyList()\n        }\n\n        val ios: List<PsiElement> = try {\n            val executeOnPooledThread: Future<List<PsiElement>> =\n                ApplicationManager.getApplication().executeOnPooledThread<List<PsiElement>> {\n                    return@executeOnPooledThread JavaTypeResolver.resolveByMethod(psiElement).values.map<PsiClass, PsiClass> { it }\n                }\n\n            executeOnPooledThread.get()\n        } catch (e: Exception) {\n            emptyList()\n        }\n\n        return ApplicationManager.getApplication().executeOnPooledThread<MethodStructure> {\n            runReadAction {\n                MethodStructure(\n                    psiElement,\n                    text = psiElement.text,\n                    name = psiElement.name,\n                    signature = getSignatureString(psiElement),\n                    enclosingClass = psiElement.containingClass,\n                    language = psiElement.language.displayName,\n                    returnType = processReturnTypeText(psiElement.returnType?.presentableText),\n                    variableContextList,\n                    includeClassContext,\n                    usagesList,\n                    ios\n                )\n            }\n        }.get()\n    }\n\n    private fun processReturnTypeText(returnType: String?): String? {\n        return if (returnType == \"void\") null else returnType\n    }\n\n    companion object {\n        fun getSignatureString(method: PsiMethod): String {\n            val bodyStart = runReadAction { method.body?.startOffsetInParent ?: method.textLength }\n            val text = runReadAction { method.text }\n            val substring = text.substring(0, bodyStart)\n            val trimmed = substring.replace('\\n', ' ').trim()\n            return trimmed\n        }\n    }\n}\n"
  },
  {
    "path": "languages/shire-java/src/main/kotlin/com/phodal/shirelang/java/codemodel/JavaVariableStructureProvider.kt",
    "content": "package com.phodal.shirelang.java.codemodel\n\nimport com.intellij.openapi.application.runReadAction\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.PsiNameIdentifierOwner\nimport com.intellij.psi.PsiVariable\nimport com.phodal.shirecore.provider.codemodel.VariableStructureProvider\nimport com.phodal.shirecore.provider.codemodel.model.VariableStructure\nimport com.phodal.shirelang.java.util.getContainingClass\nimport com.phodal.shirelang.java.util.getContainingMethod\n\nclass JavaVariableStructureProvider : VariableStructureProvider {\n    override fun build(\n        psiElement: PsiElement,\n        withMethodContext: Boolean,\n        withClassContext: Boolean,\n        gatherUsages: Boolean,\n    ): VariableStructure? {\n        if (psiElement !is PsiVariable) return null\n\n        val containingMethod = runReadAction {psiElement.getContainingMethod()  }\n        val containingClass = runReadAction {  psiElement.getContainingClass()}\n\n        val references =\n            if (gatherUsages) JavaClassStructureProvider.findUsages(psiElement as PsiNameIdentifierOwner) else emptyList()\n\n        return runReadAction {  VariableStructure(\n            psiElement,\n            psiElement.text ?: \"\",\n            psiElement.name,\n            containingMethod,\n            containingClass,\n            references,\n            withMethodContext,\n            withClassContext\n        )\n        }\n    }\n}\n"
  },
  {
    "path": "languages/shire-java/src/main/kotlin/com/phodal/shirelang/java/complexity/JavaComplexityProvider.kt",
    "content": "package com.phodal.shirelang.java.complexity\n\nimport com.intellij.psi.PsiElement\nimport com.phodal.shirecore.ast.ComplexitySink\nimport com.phodal.shirecore.ast.ComplexityVisitor\nimport com.phodal.shirecore.provider.complexity.ComplexityProvider\n\nclass JavaComplexityProvider : ComplexityProvider {\n    override fun process(element: PsiElement): Int {\n        val sink = ComplexitySink()\n        val visitor = visitor(sink)\n        element.accept(visitor)\n        return sink.getComplexity()\n    }\n\n    override fun visitor(sink: ComplexitySink): ComplexityVisitor {\n        return JavaLanguageVisitor(sink)\n    }\n}"
  },
  {
    "path": "languages/shire-java/src/main/kotlin/com/phodal/shirelang/java/complexity/JavaLanguageVisitor.kt",
    "content": "/**\n * The MIT License (MIT)\n * <p>\n *     https://github.com/nikolaikopernik/code-complexity-plugin\n *  </p>\n */\npackage com.phodal.shirelang.java.complexity\n\nimport com.intellij.psi.JavaTokenType\nimport com.intellij.psi.PsiBreakStatement\nimport com.intellij.psi.PsiCatchSection\nimport com.intellij.psi.PsiConditionalExpression\nimport com.intellij.psi.PsiContinueStatement\nimport com.intellij.psi.PsiDoWhileStatement\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.PsiExpression\nimport com.intellij.psi.PsiForStatement\nimport com.intellij.psi.PsiForeachStatement\nimport com.intellij.psi.PsiIfStatement\nimport com.intellij.psi.PsiJavaToken\nimport com.intellij.psi.PsiKeyword\nimport com.intellij.psi.PsiLambdaExpression\nimport com.intellij.psi.PsiMethod\nimport com.intellij.psi.PsiMethodCallExpression\nimport com.intellij.psi.PsiParenthesizedExpression\nimport com.intellij.psi.PsiPolyadicExpression\nimport com.intellij.psi.PsiPrefixExpression\nimport com.intellij.psi.PsiSwitchStatement\nimport com.intellij.psi.PsiWhileStatement\nimport com.intellij.psi.PsiWhiteSpace\nimport com.intellij.psi.tree.IElementType\nimport com.phodal.shirecore.ast.ComplexitySink\nimport com.phodal.shirecore.ast.ComplexityVisitor\nimport com.phodal.shirecore.ast.PointType\nimport com.phodal.shirecore.ast.PointType.*\n\nclass JavaLanguageVisitor(private val sink: ComplexitySink) : ComplexityVisitor() {\n    override fun processElement(element: PsiElement) {\n        when (element) {\n            is PsiWhileStatement -> sink.increaseComplexityAndNesting(LOOP_WHILE)\n            is PsiDoWhileStatement -> sink.increaseComplexityAndNesting(LOOP_WHILE)\n            is PsiIfStatement -> element.processIfExpression()\n            is PsiKeyword -> {\n                if (element.text == PsiKeyword.ELSE && element.parent is PsiIfStatement) {\n                    sink.increaseComplexity(ELSE)\n                }\n            }\n\n            is PsiConditionalExpression -> {\n                sink.increaseComplexityAndNesting(IF)\n                element.calculateBinaryComplexity()\n            }\n\n            is PsiSwitchStatement -> sink.increaseComplexityAndNesting(SWITCH)\n            is PsiForStatement -> sink.increaseComplexityAndNesting(LOOP_FOR)\n            is PsiForeachStatement -> sink.increaseComplexityAndNesting(LOOP_FOR)\n            is PsiCatchSection -> sink.increaseComplexityAndNesting(CATCH)\n            is PsiBreakStatement -> if (element.labelIdentifier != null) sink.increaseComplexity(BREAK)\n            is PsiContinueStatement -> if (element.labelIdentifier != null) sink.increaseComplexity(CONTINUE)\n            is PsiLambdaExpression -> sink.increaseNesting()\n            is PsiPolyadicExpression -> {\n                // this method will go over all the nested elements as well\n                // we don't want that so we accept only the top-level expressions\n                // and the entire expression will be processed recursively in [calculateBinaryComplexity]\n                if (element.parent !is PsiExpression) {\n                    element.calculateBinaryComplexity()\n                }\n            }\n\n            is PsiMethodCallExpression -> if (element.isRecursion()) sink.increaseComplexity(RECURSION)\n        }\n    }\n\n    private fun PsiExpression.calculateBinaryComplexity() {\n        var prevOperand: IElementType? = null\n        this.children.forEach { element ->\n            when (element) {\n                is PsiJavaToken -> if (element.tokenType in listOf(JavaTokenType.ANDAND, JavaTokenType.OROR)) {\n                    if (prevOperand == null || element.tokenType != prevOperand) {\n                        sink.increaseComplexity(element.tokenType.toPointType())\n                    }\n                    prevOperand = element.tokenType\n                }\n\n                is PsiParenthesizedExpression -> {\n                    element.calculateBinaryComplexity()\n                    prevOperand = null\n                }\n\n                is PsiPrefixExpression -> {\n                    element.calculateBinaryComplexity()\n                    prevOperand = null\n                }\n\n                is PsiPolyadicExpression -> element.calculateBinaryComplexity()\n            }\n        }\n    }\n\n    override fun postProcess(element: PsiElement) {\n        if (element is PsiWhileStatement ||\n            element is PsiDoWhileStatement ||\n            element is PsiConditionalExpression ||\n            element is PsiForStatement ||\n            element is PsiForeachStatement ||\n            element is PsiCatchSection ||\n            element is PsiSwitchStatement ||\n            element is PsiLambdaExpression\n        ) {\n            sink.decreaseNesting()\n        } else if (element is PsiIfStatement && !element.isElseIf()) {\n            sink.decreaseNesting()\n        }\n    }\n\n    override fun shouldVisitElement(element: PsiElement): Boolean = true\n\n    private fun PsiIfStatement.processIfExpression() {\n        // if exists `else` that is not a plain IF -> ignoring\n        if (this.isElseIf()) {\n            return\n        }\n        sink.increaseComplexityAndNesting(IF)\n    }\n}\n\n/**\n * Checking if recursion is used.\n * Same problems as in [KtLanguageVisitor]\n */\nprivate fun PsiMethodCallExpression.isRecursion(): Boolean {\n    val parentMethod: PsiMethod = this.findCurrentMethod() ?: return false\n    if (this.methodExpression.text != parentMethod.nameIdentifier?.text) return false\n    if (this.argumentList.expressionCount != parentMethod.parameterList.parametersCount) return false\n    return true\n}\n\nprivate fun PsiElement.findCurrentMethod(): PsiMethod? {\n    var element: PsiElement? = this\n    while (element != null && element !is PsiMethod) element = element.parent\n    return element?.let { it as PsiMethod }\n}\n\nprivate fun PsiIfStatement.isElseIf(): Boolean =\n    this.prevNotWhitespace().isElse()\n\nprivate fun PsiElement?.isElse(): Boolean = this?.let {\n    it is PsiKeyword && it.text == PsiKeyword.ELSE\n} ?: false\n\nprivate fun PsiIfStatement.prevNotWhitespace(): PsiElement? {\n    var prev: PsiElement = this\n    while (prev.prevSibling != null) {\n        prev = prev.prevSibling\n        if (prev !is PsiWhiteSpace) {\n            return prev\n        }\n    }\n    return null\n}\n\n\nprivate fun IElementType.toPointType(): PointType =\n    when (this) {\n        JavaTokenType.OROR -> LOGICAL_OR\n\n        JavaTokenType.ANDAND -> LOGICAL_AND\n\n        else -> UNKNOWN\n    }\n"
  },
  {
    "path": "languages/shire-java/src/main/kotlin/com/phodal/shirelang/java/impl/JavaElementStrategyBuilder.kt",
    "content": "package com.phodal.shirelang.java.impl\n\nimport com.intellij.openapi.project.Project\nimport com.intellij.psi.*\nimport com.intellij.psi.search.GlobalSearchScope\nimport com.intellij.psi.util.PsiTreeUtil\nimport com.phodal.shirecore.provider.codemodel.model.ClassStructure\nimport com.phodal.shirecore.provider.psi.PsiElementStrategyBuilder\nimport com.phodal.shirelang.java.codemodel.JavaClassStructureProvider\n\nclass JavaElementStrategyBuilder : PsiElementStrategyBuilder {\n    override fun lookupElement(project: Project, canonicalName: String): ClassStructure? {\n        val psiClass: PsiClass = JavaPsiFacade.getInstance(project)\n            .findClass(canonicalName, GlobalSearchScope.projectScope(project))\n            ?: return null\n\n        return JavaClassStructureProvider().build(psiClass, false)\n    }\n\n    override fun relativeElement(project: Project, givenElement: PsiElement, type: PsiComment): PsiElement? {\n        return PsiTreeUtil.getParentOfType(givenElement, type::class.java)\n    }\n\n    override fun findNearestTarget(psiElement: PsiElement): PsiNameIdentifierOwner? {\n        if (psiElement is PsiMethod || psiElement is PsiClass) return psiElement as PsiNameIdentifierOwner\n\n        val closestIdentifierOwner = PsiTreeUtil.getParentOfType(psiElement, PsiNameIdentifierOwner::class.java)\n        if (closestIdentifierOwner !is PsiMethod) {\n            return PsiTreeUtil.getParentOfType(psiElement, PsiMethod::class.java) ?: closestIdentifierOwner\n        }\n\n        return closestIdentifierOwner\n    }\n}\n"
  },
  {
    "path": "languages/shire-java/src/main/kotlin/com/phodal/shirelang/java/impl/JavaPsiElementDataBuilder.kt",
    "content": "package com.phodal.shirelang.java.impl\n\nimport com.intellij.openapi.diagnostic.logger\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.util.NlsSafe\nimport com.intellij.psi.*\nimport com.intellij.psi.impl.source.PsiClassReferenceType\nimport com.intellij.psi.search.GlobalSearchScope\nimport com.phodal.shirecore.provider.codemodel.ClassStructureProvider\nimport com.phodal.shirecore.provider.codemodel.model.ClassStructure\nimport com.phodal.shirecore.provider.psi.PsiElementDataBuilder\nimport com.phodal.shirelang.java.util.JavaContextCollection\n\nopen class JavaPsiElementDataBuilder : PsiElementDataBuilder {\n    /**\n     * Returns the base route of a given Kotlin language method.\n     *\n     * This method takes a PsiElement as input and checks if it is an instance of PsiMethod. If it is not, an empty string is returned.\n     * If the input element is a PsiMethod, the method checks if its containing class has the annotation \"@RequestMapping\" from the Spring Framework.\n     * If the annotation is found, the method retrieves the value attribute of the annotation and returns it as a string.\n     * If the value attribute is not a PsiLiteralExpression, an empty string is returned.\n     *\n     * @param element the PsiElement representing the Kotlin language method\n     * @return the base route of the method as a string, or an empty string if the method does not have a base route or if the input element is not a PsiMethod\n     */\n    override fun baseRoute(element: PsiElement): String {\n        if (element !is PsiMethod) return \"\"\n\n        val containingClass = element.containingClass ?: return \"\"\n        val requestMappingAnnotation = containingClass.annotations.firstOrNull {\n            it.qualifiedName?.endsWith(\"RequestMapping\") == true\n        } ?: return \"\"\n\n        val value = requestMappingAnnotation.findAttributeValue(\"value\") as? PsiLiteralExpression\n        return value?.value as? String ?: \"\"\n    }\n\n    override fun inboundData(element: PsiElement): Map<String, String> {\n        if (element !is PsiMethod) return emptyMap()\n\n        val result = mutableMapOf<String, String>()\n        val parameters = element.parameterList.parameters\n        for (parameter in parameters) {\n            result += handleFromType(parameter)\n        }\n        return result\n    }\n\n    private fun handleFromType(parameter: PsiParameter): Map<@NlsSafe String, String> {\n        when (val type = parameter.type) {\n            is PsiClassType -> processingClassType(type)\n        }\n\n        return emptyMap()\n    }\n\n    private fun processing(returnType: PsiType): Map<@NlsSafe String, String> {\n        when {\n            returnType is PsiClassType -> {\n                return processingClassType(returnType)\n            }\n        }\n\n        return mapOf()\n    }\n\n    private fun processingClassType(type: PsiClassType): Map<@NlsSafe String, String> {\n        val result = mutableMapOf<String, String>()\n        when (type) {\n            is PsiClassReferenceType -> {\n                type.reference.typeParameters.forEach {\n                    result += processing(it)\n                }\n            }\n        }\n\n        type.resolve()?.let {\n            val qualifiedName = it.qualifiedName!!\n            JavaContextCollection.dataStructure(it)?.let { simpleClassStructure ->\n                result += mapOf(qualifiedName to simpleClassStructure.toString())\n            }\n        }\n\n        return result\n    }\n\n    override fun outboundData(element: PsiElement): Map<String, String> {\n        if (element !is PsiMethod) return emptyMap()\n\n        val result = mutableMapOf<String, String>()\n        val returnType = element.returnType ?: return emptyMap()\n\n        result += processing(returnType)\n\n        return result\n    }\n\n    override fun lookupElement(project: Project, canonicalName: String): ClassStructure? {\n        val psiFacade = JavaPsiFacade.getInstance(project)\n\n        val psiClass: PsiClass = psiFacade.findClass(canonicalName, GlobalSearchScope.projectScope(project))\n                ?: return null\n\n        return ClassStructureProvider.from(psiClass, false)\n    }\n\n    override fun parseComment(project: Project, code: String): String? {\n        val psiElementFactory = JavaPsiFacade.getInstance(project).elementFactory\n\n        try {\n            val docComment = psiElementFactory.createDocCommentFromText(code)\n\n            return docComment.text\n        } catch (e: Exception) {\n            logger<JavaPsiElementDataBuilder>().warn(\"Failed to parse comment: $code\", e)\n        }\n        return code\n    }\n}"
  },
  {
    "path": "languages/shire-java/src/main/kotlin/com/phodal/shirelang/java/impl/JavaRefactoringTool.kt",
    "content": "package com.phodal.shirelang.java.impl\n\nimport com.intellij.codeInsight.daemon.impl.quickfix.RenameElementFix\nimport com.intellij.codeInsight.daemon.impl.quickfix.SafeDeleteFix\nimport com.intellij.codeInspection.MoveToPackageFix\nimport com.intellij.ide.highlighter.JavaFileType\nimport com.intellij.openapi.application.runReadAction\nimport com.intellij.openapi.project.ProjectManager\nimport com.intellij.psi.*\nimport com.intellij.psi.search.FileTypeIndex\nimport com.intellij.psi.search.ProjectScope\nimport com.phodal.shirecore.provider.shire.RefactoringTool\nimport com.phodal.shirecore.variable.toolchain.refactoring.RefactorInstElement\n\nclass JavaRefactoringTool : RefactoringTool {\n    private val project = ProjectManager.getInstance().openProjects.firstOrNull()\n\n    override fun lookupFile(path: String): PsiFile? {\n        if (project == null) return null\n\n        val elementInfo = getElementInfo(path, null) ?: return null\n        val searchScope = ProjectScope.getProjectScope(project)\n        val javaFiles: List<PsiJavaFile> = FileTypeIndex.getFiles(JavaFileType.INSTANCE, searchScope)\n            .mapNotNull { PsiManager.getInstance(project).findFile(it) as? PsiJavaFile }\n\n        val className = elementInfo.className\n        val packageName = elementInfo.pkgName\n\n        val sourceFile = javaFiles.firstOrNull {\n            it.packageName == packageName && it.name == \"$className.java\"\n        } ?: return null\n\n        return sourceFile\n    }\n\n    override fun rename(sourceName: String, targetName: String, psiFile: PsiFile?): Boolean {\n        if (project == null) return false\n        val elementInfo = getElementInfo(sourceName, psiFile) ?: return false\n\n        val element: PsiNamedElement =\n            if (psiFile != null) {\n                if (psiFile is PsiJavaFile) {\n                    val methodName = elementInfo.methodName\n                    val className = elementInfo.className\n\n                    val psiMethod: PsiMethod? =\n                        psiFile.classes.firstOrNull { it.name == className }\n                            ?.methods?.firstOrNull { it.name == methodName }\n\n                    psiMethod ?: psiFile\n                } else {\n                    psiFile\n                }\n\n            } else {\n                searchPsiElementByName(elementInfo, sourceName) ?: return false\n            }\n\n        try {\n            RenameElementFix(element, targetName)\n                .invoke(project, element.containingFile, element, element)\n\n            performRefactoringRename(project, element, targetName)\n        } catch (e: Exception) {\n            return false\n        }\n\n        return true\n    }\n\n    /**\n     * Deletes the given PsiElement in a safe manner, ensuring that no syntax errors or unexpected behavior occur as a result.\n     * The method performs checks before deletion to confirm that it is safe to remove the element from the code structure.\n     *\n     * @param element The PsiElement to be deleted. This should be a valid element within the PSI tree structure.\n     * @return true if the element was successfully deleted without any issues, false otherwise. This indicates whether\n     * the deletion was performed and considered safe.\n     */\n    override fun safeDelete(element: PsiElement): Boolean {\n        val delete = SafeDeleteFix(element)\n        try {\n            delete.invoke(element.project, element.containingFile, element, element)\n        } catch (e: Exception) {\n            return false\n        }\n\n        return true\n    }\n\n    /**\n     * In Java the canonicalName is the fully qualified name of the target package.\n     * In Kotlin the canonicalName is the fully qualified name of the target package or class.\n     */\n    override fun move(element: PsiElement, canonicalName: String): Boolean {\n        val file = element.containingFile\n        val fix = MoveToPackageFix(file, canonicalName)\n\n        try {\n            fix.invoke(file.project, file, element, element)\n        } catch (e: Exception) {\n            return false\n        }\n\n        return true\n    }\n\n    private fun searchPsiElementByName(refactorInstElement: RefactorInstElement, sourceName: String): PsiNamedElement? = runReadAction {\n        when {\n            refactorInstElement.isMethod -> {\n                val className = refactorInstElement.className\n                val javaFile = this.lookupFile(sourceName) as? PsiJavaFile ?: return@runReadAction null\n\n                val psiMethod: PsiMethod =\n                    javaFile.classes.firstOrNull { it.name == className }\n                        ?.methods?.firstOrNull { it.name == refactorInstElement.methodName }\n                        ?: return@runReadAction null\n\n                psiMethod\n            }\n\n            refactorInstElement.isClass -> {\n                val javaFile = this.lookupFile(sourceName) as? PsiJavaFile ?: return@runReadAction null\n                javaFile.classes.firstOrNull { it.name == refactorInstElement.className }\n            }\n\n            else -> {\n                val javaFile = this.lookupFile(sourceName) as? PsiJavaFile ?: return@runReadAction null\n                javaFile\n            }\n        }\n    }\n\n    /**\n     * input will be canonicalName#methodName or just methodName\n     */\n    private fun getElementInfo(input: String, psiFile: PsiFile?): RefactorInstElement? {\n        if (!input.contains(\"#\") && psiFile != null) {\n            val javaFile = psiFile as? PsiJavaFile ?: return null\n            val className = javaFile.classes.firstOrNull()?.name ?: return null\n            val canonicalName = javaFile.packageName + \".\" + className\n            return RefactorInstElement(\n                true,\n                isMethod = true,\n                methodName = input,\n                canonicalName = canonicalName,\n                className = className,\n                pkgName = javaFile.packageName,\n            )\n        }\n\n        val isMethod = input.contains(\"#\")\n        val methodName = input.substringAfter(\"#\")\n        val canonicalName = input.substringBefore(\"#\")\n        val maybeClassName = canonicalName.substringAfterLast(\".\")\n        // the clasName should be Uppercase, or it will be the package\n        var isClass = false\n        var pkgName = canonicalName.substringBeforeLast(\".\")\n        if (maybeClassName[0].isLowerCase()) {\n            pkgName = \"$pkgName.$maybeClassName\"\n        } else {\n            isClass = true\n        }\n\n        return RefactorInstElement(isClass, isMethod, methodName, canonicalName, maybeClassName, pkgName)\n    }\n}\n"
  },
  {
    "path": "languages/shire-java/src/main/kotlin/com/phodal/shirelang/java/impl/JavaShireQLInterpreter.kt",
    "content": "package com.phodal.shirelang.java.impl\n\nimport com.intellij.lang.Language\nimport com.intellij.openapi.diagnostic.logger\nimport com.intellij.openapi.project.Project\nimport com.intellij.psi.JavaPsiFacade\nimport com.intellij.psi.PsiClass\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.search.GlobalSearchScope\nimport com.intellij.psi.search.searches.AnnotatedElementsSearch\nimport com.intellij.psi.search.searches.ClassInheritorsSearch\nimport com.phodal.shirecore.provider.variable.ShireQLInterpreter\nimport com.phodal.shirecore.function.shireql.JvmShireQLFuncType\n\n\nclass JavaShireQLInterpreter : ShireQLInterpreter {\n    override fun supportsMethod(language: Language, methodName: String): List<String> {\n        if (language.id != \"JAVA\") return emptyList()\n\n        return JvmShireQLFuncType.entries.map { it.methodName }\n    }\n\n    /**\n     * clazz.getName() or clazz.extensions\n     */\n    override fun resolveCall(element: PsiElement, methodName: String, arguments: List<Any>): Any {\n        // is of method\n        if (methodName.endsWith(\"Of\")) {\n            return this.resolveOfTypedCall(element.project, methodName, arguments)\n        }\n\n        return when (element) {\n            is PsiClass -> {\n\n                when (methodName) {\n                    JvmShireQLFuncType.GET_NAME.methodName -> element.name!!\n                    JvmShireQLFuncType.NAME.methodName -> element.name!!\n                    JvmShireQLFuncType.EXTENDS.methodName -> element\n                        .extendsList?.referencedTypes?.map { it.resolve() }\n                        ?: emptyList<PsiClass>()\n\n                    JvmShireQLFuncType.IMPLEMENTS.methodName -> element\n                        .implementsList?.referencedTypes?.map { it.resolve() }\n                        ?: emptyList<PsiClass>()\n\n                    JvmShireQLFuncType.METHOD_CODE_BY_NAME.methodName -> element\n                        .methods\n                        .filter { it.name == arguments.first() }\n\n                    JvmShireQLFuncType.FIELD_CODE_BY_NAME.methodName -> element\n                        .fields\n                        .filter { it.name == arguments.first() }\n\n                    else -> \"\"\n                }\n            }\n\n            else -> \"\"\n        }\n    }\n\n    override fun resolveOfTypedCall(project: Project, methodName: String, arguments: List<Any>): Any {\n        // get first argument for infer type\n        val firstArgument = arguments.firstOrNull().toString()\n        if (firstArgument.isBlank()) {\n            logger<JavaShireQLInterpreter>().warn(\"Cannot find first argument\")\n            return \"\"\n        }\n\n        return when (methodName) {\n            JvmShireQLFuncType.SUBCLASSES_OF.methodName -> {\n                val facade = JavaPsiFacade.getInstance(project)\n\n                val psiClass = facade.findClass(firstArgument, GlobalSearchScope.projectScope(project))\n                if (psiClass == null) {\n                    logger<JavaShireQLInterpreter>().warn(\"Cannot find class: $firstArgument\")\n                    return \"\"\n                }\n\n                val map: List<PsiClass> =\n                    ClassInheritorsSearch.search(psiClass, GlobalSearchScope.projectScope(project), true).map { it }\n                map\n            }\n\n            JvmShireQLFuncType.ANNOTATED_OF.methodName -> {\n                val facade = JavaPsiFacade.getInstance(project)\n                val annotationClass =\n                    facade.findClass(firstArgument, GlobalSearchScope.allScope(project))\n\n                if (annotationClass == null) {\n                    logger<JavaShireQLInterpreter>().warn(\"Cannot find annotation class: $firstArgument\")\n                    return \"\"\n                }\n\n                val classes = AnnotatedElementsSearch\n                    .searchPsiClasses(annotationClass, GlobalSearchScope.projectScope(project))\n                    .findAll()\n\n                classes.toList()\n            }\n\n            JvmShireQLFuncType.SUPERCLASS_OF.methodName -> {\n                val psiClass = searchClass(project, firstArgument) ?: return \"\"\n                psiClass.superClass ?: \"\"\n            }\n\n            JvmShireQLFuncType.IMPLEMENTS_OF.methodName -> {\n                val psiClass = searchClass(project, firstArgument) ?: return emptyList<String>()\n                psiClass.implementsList?.referencedTypes ?: emptyList<String>()\n            }\n\n            else -> {\n                logger<JavaShireQLInterpreter>().error(\"Cannot find method: $methodName\")\n            }\n        }\n    }\n\n    private fun searchClass(project: Project, className: String): PsiClass? {\n        val scope = GlobalSearchScope.allScope(project)\n        val psiFacade = JavaPsiFacade.getInstance(project)\n        return psiFacade.findClass(className, scope)\n    }\n}\n"
  },
  {
    "path": "languages/shire-java/src/main/kotlin/com/phodal/shirelang/java/impl/JavaSymbolProvider.kt",
    "content": "package com.phodal.shirelang.java.impl\n\nimport com.intellij.codeInsight.completion.CompletionParameters\nimport com.intellij.codeInsight.completion.CompletionResultSet\nimport com.intellij.codeInsight.completion.CompletionUtil\nimport com.intellij.codeInsight.completion.CompletionUtilCore\nimport com.intellij.codeInsight.lookup.LookupElement\nimport com.intellij.codeInsight.lookup.LookupElementBuilder\nimport com.intellij.ide.highlighter.JavaFileType\nimport com.intellij.lang.java.JavaLanguage\nimport com.intellij.openapi.application.ApplicationManager\nimport com.intellij.openapi.application.runReadAction\nimport com.intellij.openapi.project.Project\nimport com.intellij.psi.*\nimport com.intellij.psi.impl.file.impl.JavaFileManagerImpl\nimport com.intellij.psi.search.FileTypeIndex\nimport com.intellij.psi.search.GlobalSearchScope\nimport com.intellij.psi.search.ProjectScope\nimport com.intellij.psi.search.PsiShortNamesCache\nimport com.intellij.psi.util.PsiTreeUtil\nimport com.intellij.util.SmartList\nimport com.phodal.shirecore.provider.shire.ShireSymbolProvider\n\nclass JavaSymbolProvider : ShireSymbolProvider {\n    override val language: String = JavaLanguage.INSTANCE.displayName\n\n    override fun lookupSymbol(\n        project: Project,\n        parameters: CompletionParameters,\n        result: CompletionResultSet,\n    ): List<LookupElement> {\n        val lookupElements: MutableList<LookupElement> = SmartList()\n        val searchScope = ProjectScope.getProjectScope(project)\n        val javaFiles = FileTypeIndex.getFiles(JavaFileType.INSTANCE, searchScope)\n        if (javaFiles.isEmpty()) return lookupElements\n\n        val prefixMatcher = CompletionUtil.findReferenceOrAlphanumericPrefix(parameters)\n        result.withPrefixMatcher(prefixMatcher)\n\n        val text = parameters.position.text.removePrefix(CompletionUtilCore.DUMMY_IDENTIFIER_TRIMMED)\n\n        val packageStatements = javaFiles.mapNotNull {\n            val psi = PsiManager.getInstance(project).findFile(it) ?: return@mapNotNull null\n            PsiTreeUtil.getChildrenOfTypeAsList(psi, PsiPackageStatement::class.java).firstOrNull()\n        }\n\n        packageStatements.forEach {\n            if (it.packageName.startsWith(text)) {\n                val element = LookupElementBuilder.create(it.packageName).withIcon(JavaFileType.INSTANCE.icon)\n                lookupElements.add(element)\n            }\n        }\n\n        return lookupElements\n    }\n\n    override fun lookupElementByName(project: Project, name: String): List<PsiElement>? {\n        val searchScope = ProjectScope.getProjectScope(project)\n        val virtualFiles = FileTypeIndex.getFiles(JavaFileType.INSTANCE, searchScope)\n        val psiManager = PsiManager.getInstance(project)\n\n        return when (name) {\n            \"PsiFile\" -> virtualFiles.mapNotNull(psiManager::findFile).toList()\n            \"PsiPackage\" -> virtualFiles.mapNotNull { psiManager.findFile(it) }\n                .flatMap { PsiTreeUtil.getChildrenOfTypeAsList(it, PsiPackageStatement::class.java) }\n                .toList()\n\n            \"PsiClass\" -> virtualFiles.mapNotNull { psiManager.findFile(it) as PsiJavaFile }\n                .flatMap { it.classes.toList() }\n                .toList()\n\n            \"PsiMethod\" -> virtualFiles.mapNotNull { psiManager.findFile(it) as PsiJavaFile }\n                .flatMap { it.classes.toList() }\n                .flatMap { it.methods.toList() }\n\n            \"PsiField\" -> virtualFiles.mapNotNull { psiManager.findFile(it) as PsiJavaFile }\n                .flatMap { it.classes.toList() }\n                .flatMap { it.fields.toList() }\n\n            else -> {\n                null\n            }\n        }\n    }\n\n    override fun resolveSymbol(project: Project, symbol: String): List<PsiNamedElement> {\n        val scope = GlobalSearchScope.allScope(project)\n\n        if (symbol.isEmpty()) return emptyList()\n\n        // className only, like `String` not Dot\n        if (symbol.contains(\".\").not()) {\n            val psiClasses = PsiShortNamesCache.getInstance(project).getClassesByName(symbol, scope)\n            if (psiClasses.isNotEmpty()) {\n                return psiClasses.toList()\n            }\n        }\n\n        // for package name only, like `cc.unitmesh`\n        JavaFileManagerImpl(project).findPackage(symbol)?.let { pkg ->\n            return pkg.classes.toList()\n        }\n\n        // for single class, with function name, like `cc.unitmesh.idea.provider.JavaCustomShireSymbolProvider`\n        val clazz = JavaFileManagerImpl(project).findClass(symbol, scope)\n        if (clazz != null) {\n            return clazz.methods.toList()\n        }\n\n        // for lookup for method\n        val method = symbol.split(\"#\")\n        if (method.size == 2) {\n            val clazzName = method[0]\n            val methodName = method[1]\n            return lookupWithMethodName(project, clazzName, scope, methodName)\n        }\n\n        // may by not our format, like <package>.<class>.<method> split last\n        val lastDotIndex = symbol.lastIndexOf(\".\")\n        if (lastDotIndex != -1) {\n            val clazzName = symbol.substring(0, lastDotIndex)\n            val methodName = symbol.substring(lastDotIndex + 1)\n            return lookupWithMethodName(project, clazzName, scope, methodName)\n        }\n\n        return emptyList()\n    }\n\n    private fun lookupWithMethodName(\n        project: Project,\n        clazzName: String,\n        scope: GlobalSearchScope,\n        methodName: String,\n    ): List<PsiMethod> {\n        val psiClass = JavaFileManagerImpl(project).findClass(clazzName, scope) ?: return emptyList()\n        val psiMethod = ApplicationManager.getApplication().executeOnPooledThread<PsiMethod?> {\n            runReadAction { psiClass.findMethodsByName(methodName, true).firstOrNull() }\n        }.get() ?: return emptyList()\n\n        return listOf(psiMethod)\n    }\n}\n\n"
  },
  {
    "path": "languages/shire-java/src/main/kotlin/com/phodal/shirelang/java/impl/JvmBuildSystemProvider.kt",
    "content": "package com.phodal.shirelang.java.impl\n\nimport com.intellij.openapi.externalSystem.service.project.ProjectDataManager\nimport com.intellij.openapi.project.Project\nimport com.phodal.shirecore.variable.toolchain.buildsystem.BuildSystemContext\nimport com.phodal.shirecore.provider.context.BuildSystemProvider\nimport com.phodal.shirelang.java.toolchain.*\nimport org.jetbrains.plugins.gradle.util.GradleConstants\n\nopen class JvmBuildSystemProvider : BuildSystemProvider() {\n    override fun collect(project: Project): BuildSystemContext {\n        val projectDataManager = ProjectDataManager.getInstance()\n        val javaVersion = JvmLanguageDetector.detectLanguageLevel(project, null)\n        val gradleInfo = projectDataManager.getExternalProjectsData(project, GradleConstants.SYSTEM_ID)\n\n        when {\n            gradleInfo.isNotEmpty() -> {\n                val buildTool = GradleBuildTool()\n                return BuildSystemContext(\n                    buildToolName = buildTool.toolName(),\n                    buildToolVersion = \"\",\n                    languageName = \"Java\",\n                    languageVersion = \"$javaVersion\",\n                    taskString = buildTool.collectTasks(project).joinToString(\" \") { it.text },\n                    libraries = buildTool.prepareLibraryData(project)?.map {\n                        it.prettyString()\n                    }  ?: emptyList()\n                )\n            }\n\n            else -> {\n                val buildTool = MavenBuildTool()\n                val buildToolName = buildTool.toolName()\n                val libraryData = buildTool.prepareLibraryData(project)\n                val collectTasks = buildTool.collectTasks(project)\n                val taskString = collectTasks.joinToString(\" \") { it.text }\n\n                return BuildSystemContext(\n                    buildToolName = buildToolName,\n                    buildToolVersion = \"\",\n                    languageName = \"Java\",\n                    languageVersion = \"$javaVersion\",\n                    taskString = taskString,\n                    libraries = libraryData.map {\n                        it.prettyString()\n                    }\n                )\n            }\n        }\n    }\n\n}\n\nval JAVA_TASK_COMPLETION_COMPARATOR = Comparator<String> { o1, o2 ->\n    when {\n        o1.startsWith(\"--\") && o2.startsWith(\"--\") -> o1.compareTo(o2)\n        o1.startsWith(\"-\") && o2.startsWith(\"--\") -> -1\n        o1.startsWith(\"--\") && o2.startsWith(\"-\") -> 1\n        o1.startsWith(\":\") && o2.startsWith(\":\") -> o1.compareTo(o2)\n        o1.startsWith(\":\") && o2.startsWith(\"-\") -> -1\n        o1.startsWith(\"-\") && o2.startsWith(\":\") -> 1\n        o2.startsWith(\"-\") -> -1\n        o2.startsWith(\":\") -> -1\n        o1.startsWith(\"-\") -> 1\n        o1.startsWith(\":\") -> 1\n        else -> o1.compareTo(o2)\n    }\n}"
  },
  {
    "path": "languages/shire-java/src/main/kotlin/com/phodal/shirelang/java/provider/JavaRelatedClassesProvider.kt",
    "content": "package com.phodal.shirelang.java.provider\n\nimport com.intellij.openapi.application.ApplicationManager\nimport com.intellij.openapi.application.runReadAction\nimport com.intellij.openapi.roots.ProjectFileIndex\nimport com.intellij.psi.*\nimport com.intellij.psi.util.*\nimport com.intellij.testIntegration.TestFinderHelper\nimport com.phodal.shirecore.provider.psi.RelatedClassesProvider\nimport com.phodal.shirelang.java.util.JavaContextCollection.isJavaBuiltin\nimport com.phodal.shirelang.java.util.JavaContextCollection.isPopularFramework\n\nclass JavaRelatedClassesProvider : RelatedClassesProvider {\n    override fun lookup(element: PsiElement): List<PsiClass> {\n        return when (element) {\n            is PsiMethod -> findRelatedClasses(element)\n                .flatMap { findSuperClasses(it) }\n                .map { cleanUp(it) }\n                .toList()\n\n            is PsiClass -> findRelatedClasses(element)\n            else -> emptyList()\n        }\n    }\n\n    override fun lookup(element: PsiFile): List<PsiElement> {\n        return when (element) {\n            is PsiJavaFile -> findRelatedClasses(element.classes.first()) + lookupTestFile(element.classes.first())\n            else -> emptyList()\n        }\n    }\n\n    private fun lookupTestFile(psiElement: PsiElement): List<PsiElement> {\n        if (!psiElement.isValid) return emptyList()\n\n        return ApplicationManager.getApplication().executeOnPooledThread<List<PsiElement>> {\n            runReadAction {\n                val isTest = TestFinderHelper.isTest(psiElement)\n                if (isTest) return@runReadAction emptyList()\n\n                TestFinderHelper.findTestsForClass(psiElement)\n            }?.toList() ?: emptyList()\n        }.get() ?: emptyList()\n    }\n\n    private fun findRelatedClasses(clazz: PsiClass): List<PsiClass> {\n        if (!clazz.isValid) return emptyList()\n\n        val qualifiedName = clazz.qualifiedName\n        return ApplicationManager.getApplication().executeOnPooledThread<List<PsiClass>?> {\n            runReadAction {\n                val methods = clazz.allMethods.flatMap { findRelatedClasses(it) }\n                val fieldsTypes: List<PsiClass> = clazz.fields.mapNotNull {\n                    when (it.type) {\n                        is PsiClassType -> {\n                            val resolve = (it.type as PsiClassType).resolve() ?: return@mapNotNull null\n                            if (resolve.qualifiedName == qualifiedName) return@mapNotNull null\n\n                            if (isJavaBuiltin(resolve.qualifiedName) == true || isPopularFramework(resolve.qualifiedName) == true) {\n                                return@mapNotNull null\n                            }\n\n                            resolve\n                        }\n\n                        else -> null\n                    }\n                }\n                return@runReadAction (fieldsTypes + methods).distinct()\n            }\n        }?.get() ?: emptyList()\n    }\n\n    /**\n     * Finds related classes to the given PsiMethod by analyzing its parameters, return type, and generic types.\n     *\n     * @param method the PsiMethod for which related classes need to be found\n     * @return a list of PsiClass instances that are related to the given PsiMethod, filtered to include only classes that are part of the project content\n     */\n    private fun findRelatedClasses(method: PsiMethod): List<PsiClass> = runReadAction {\n        if (!method.isValid) return@runReadAction emptyList()\n\n        val parameters = method.parameterList.parameters\n        val parameterTypes = parameters.map { it.type }\n\n        val genericTypes = parameters.flatMap { (it.type as? PsiClassType)?.parameters?.toList() ?: emptyList() }\n        val mentionedTypes = parameterTypes + genericTypes\n\n        val filterIsInstance = mentionedTypes.filterIsInstance<PsiClassType>()\n            .distinct()\n\n        return@runReadAction ApplicationManager.getApplication().executeOnPooledThread<List<PsiClass>> {\n            return@executeOnPooledThread filterIsInstance\n                .mapNotNull { runReadAction { it.resolve() } }\n                .filter { isProjectContent(it) }\n                .toList()\n        }.get()\n    }\n\n    /**\n     * Cleans up a given PsiClass by removing unnecessary elements such as method bodies, method comments, and any other removable members.\n     *\n     * @param psiClass the PsiClass to be cleaned up\n     * @return a new PsiClass with the unnecessary elements removed\n     */\n    private fun cleanUp(psiClass: PsiClass): PsiClass {\n        val psiElement = psiClass.copy() as PsiClass\n        psiElement.containingFile.setName(psiClass.containingFile.name)\n\n        val members = PsiTreeUtil.findChildrenOfType(psiElement, PsiMember::class.java)\n\n        members.filterIsInstance<PsiMethod>().forEach {\n            it.body?.delete()\n            it.docComment?.delete()\n        }\n        members.filter { canBeRemoved(it) }.forEach { it.delete() }\n\n        psiElement.docComment?.delete()\n        return psiElement\n    }\n\n    private fun findSuperClasses(psiClass: PsiClass): List<PsiClass> {\n        val superClass = psiClass.superClass ?: return emptyList()\n        if (isProjectContent(superClass)) {\n            return listOf(psiClass.superClass!!, psiClass)\n        }\n\n        if (isProjectContent(psiClass)) {\n            return listOf(psiClass)\n        }\n\n        return emptyList()\n    }\n\n    private fun canBeRemoved(member: PsiMember): Boolean {\n        if (member.modifierList?.hasModifierProperty(\"public\") == true) return false\n        return member.annotations.isEmpty()\n    }\n\n    private fun isProjectContent(element: PsiElement): Boolean {\n        val virtualFile = PsiUtil.getVirtualFile(element) ?: return false\n        return ApplicationManager.getApplication().executeOnPooledThread<Boolean> {\n            runReadAction {\n                ProjectFileIndex.getInstance(element.project).isInSourceContent(virtualFile)\n            }\n        }.get()\n    }\n}"
  },
  {
    "path": "languages/shire-java/src/main/kotlin/com/phodal/shirelang/java/toolchain/GradleBuildTool.kt",
    "content": "package com.phodal.shirelang.java.toolchain\n\nimport com.intellij.execution.RunManager\nimport com.intellij.execution.configurations.LocatableConfigurationBase\nimport com.intellij.openapi.externalSystem.model.project.LibraryData\nimport com.intellij.openapi.externalSystem.service.project.ProjectDataManager\nimport com.intellij.openapi.externalSystem.service.ui.completion.TextCompletionInfo\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.project.guessProjectDir\nimport com.intellij.openapi.util.NlsSafe\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.phodal.shirecore.provider.context.BuildTool\nimport com.phodal.shirecore.provider.context.CommonLibraryData\nimport com.phodal.shirelang.java.impl.JAVA_TASK_COMPLETION_COMPARATOR\nimport org.jetbrains.plugins.gradle.service.execution.GradleExternalTaskConfigurationType\nimport org.jetbrains.plugins.gradle.service.execution.GradleRunConfiguration\nimport org.jetbrains.plugins.gradle.service.project.GradleTasksIndices\nimport org.jetbrains.plugins.gradle.util.GradleConstants\nimport org.jetbrains.plugins.gradle.util.GradleTaskData\n\nclass GradleBuildTool: BuildTool {\n    override fun toolName(): String = \"Gradle\"\n\n    override fun prepareLibraryData(project: Project): List<CommonLibraryData>? {\n        val basePath = project.basePath ?: return null\n        val projectData = ProjectDataManager.getInstance().getExternalProjectData(\n            project, GradleConstants.SYSTEM_ID, basePath\n        )\n\n        val libraryDataList: List<LibraryData>? = projectData?.externalProjectStructure?.children?.filter {\n            it.data is LibraryData\n        }?.map {\n            it.data as LibraryData\n        }\n\n        return libraryDataList?.map {\n            CommonLibraryData(it.groupId, it.artifactId, it.version)\n        }\n    }\n\n    override fun collectTasks(project: Project): List<TextCompletionInfo> {\n        val indices = GradleTasksIndices.getInstance(project)\n        val tasks = indices.findTasks(project.guessProjectDir()!!.path)\n            .filterNot { it.isInherited }\n            .groupBy { it.name }\n            .map { TextCompletionInfo(it.key, it.value.first().description) }\n            .sortedWith(\n                Comparator.comparing<TextCompletionInfo?, @NlsSafe String?>(\n                    { it.text },\n                    JAVA_TASK_COMPLETION_COMPARATOR\n                )\n            )\n        return tasks\n    }\n\n    override fun configureRun(\n        project: Project,\n        taskName: String,\n        virtualFile: VirtualFile?,\n    ): LocatableConfigurationBase<*> {\n        val runManager = RunManager.getInstance(project)\n        val configuration = runManager.createConfiguration(\n            taskName,\n            GradleExternalTaskConfigurationType::class.java\n        )\n        val runConfiguration = configuration.configuration as GradleRunConfiguration\n        runConfiguration.isDebugServerProcess = false\n        runConfiguration.settings.externalProjectPath = project.guessProjectDir()?.path\n        runConfiguration.rawCommandLine = taskName\n        runManager.addConfiguration(configuration)\n        runManager.selectedConfiguration = configuration\n        return runConfiguration\n    }\n\n    companion object {\n        fun collectGradleTasksData(project: Project): List<GradleTaskData> {\n            val indices = GradleTasksIndices.getInstance(project)\n            val tasks = indices.findTasks(project.guessProjectDir()!!.path)\n            return tasks\n        }\n    }\n}"
  },
  {
    "path": "languages/shire-java/src/main/kotlin/com/phodal/shirelang/java/toolchain/JvmLanguageDetector.kt",
    "content": "package com.phodal.shirelang.java.toolchain\n\nimport com.intellij.openapi.module.LanguageLevelUtil\nimport com.intellij.openapi.module.ModuleManager\nimport com.intellij.openapi.module.ModuleUtilCore\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.projectRoots.JavaSdkType\nimport com.intellij.openapi.roots.ModuleRootManager\nimport com.intellij.openapi.roots.ProjectRootManager\nimport com.intellij.pom.java.LanguageLevel\nimport com.intellij.psi.PsiFile\nimport com.intellij.psi.util.PsiUtil\n\nobject JvmLanguageDetector {\n    fun detectLanguageLevel(project: Project, sourceFile: PsiFile?): LanguageLevel? {\n        val projectSdk = ProjectRootManager.getInstance(project).projectSdk\n        if (projectSdk != null) {\n            if (projectSdk.sdkType !is JavaSdkType) return null\n            return PsiUtil.getLanguageLevel(project)\n        }\n\n        val moduleForFile = ModuleUtilCore.findModuleForFile(sourceFile)\n            ?: ModuleManager.getInstance(project).modules.firstOrNull()\n            ?: return null\n\n        if (ModuleRootManager.getInstance(moduleForFile).sdk !is JavaSdkType) return null\n\n        return LanguageLevelUtil.getEffectiveLanguageLevel(moduleForFile)\n    }\n}"
  },
  {
    "path": "languages/shire-java/src/main/kotlin/com/phodal/shirelang/java/toolchain/JvmRunProjectService.kt",
    "content": "package com.phodal.shirelang.java.toolchain\n\nimport com.intellij.codeInsight.completion.CompletionParameters\nimport com.intellij.codeInsight.completion.CompletionResultSet\nimport com.intellij.codeInsight.completion.PrioritizedLookupElement\nimport com.intellij.codeInsight.lookup.LookupElement\nimport com.intellij.codeInsight.lookup.LookupElementBuilder\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.projectRoots.JavaSdk\nimport com.intellij.openapi.roots.ProjectRootManager\nimport com.intellij.util.SmartList\nimport com.phodal.shirecore.provider.shire.ProjectRunService\nimport com.phodal.shirecore.runner.ConfigurationRunner\nimport icons.GradleIcons\n\nclass JvmRunProjectService : ProjectRunService, ConfigurationRunner {\n    override fun isAvailable(project: Project): Boolean {\n        return ProjectRootManager.getInstance(project).projectSdk is JavaSdk\n    }\n\n    override fun run(project: Project, taskName: String) {\n        val runConfiguration = GradleBuildTool().configureRun(project, taskName, null)\n        executeRunConfigurations(project, runConfiguration)\n    }\n\n    override fun lookupAvailableTask(\n        project: Project,\n        parameters: CompletionParameters,\n        result: CompletionResultSet,\n    ): List<LookupElement> {\n        val lookupElements: MutableList<LookupElement> = SmartList()\n        GradleBuildTool.collectGradleTasksData(project)\n            .filter { !it.isTest && !it.isJvmTest }\n            .forEach {\n                val element = LookupElementBuilder.create(it.getFqnTaskName())\n                    .withTypeText(it.description)\n                    .withIcon(GradleIcons.Gradle)\n\n                lookupElements.add(PrioritizedLookupElement.withPriority(element, 99.0))\n            }\n\n        return lookupElements\n    }\n\n    override fun tasks(project: Project): List<String> {\n        return GradleBuildTool.collectGradleTasksData(project).map { it.getFqnTaskName() }\n    }\n}\n"
  },
  {
    "path": "languages/shire-java/src/main/kotlin/com/phodal/shirelang/java/toolchain/MavenBuildTool.kt",
    "content": "package com.phodal.shirelang.java.toolchain\n\nimport com.intellij.execution.RunManager\nimport com.intellij.execution.configurations.LocatableConfigurationBase\nimport com.intellij.openapi.application.runReadAction\nimport com.intellij.openapi.externalSystem.service.ui.completion.TextCompletionInfo\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.phodal.shirecore.provider.context.BuildTool\nimport com.phodal.shirecore.provider.context.CommonLibraryData\nimport com.phodal.shirelang.java.impl.JAVA_TASK_COMPLETION_COMPARATOR\nimport org.jetbrains.idea.maven.execution.MavenRunConfiguration\nimport org.jetbrains.idea.maven.execution.MavenRunConfigurationType\nimport org.jetbrains.idea.maven.execution.MavenRunnerParameters\nimport org.jetbrains.idea.maven.project.MavenProject\nimport org.jetbrains.idea.maven.project.MavenProjectsManager\n\nclass MavenBuildTool() : BuildTool {\n    override fun toolName(): String = \"Maven\"\n\n    override fun prepareLibraryData(project: Project): List<CommonLibraryData> {\n        val projectDependencies: List<org.jetbrains.idea.maven.model.MavenArtifact> =\n            MavenProjectsManager.getInstance(project).projects.flatMap {\n                it.dependencies\n            }\n\n        return projectDependencies.map {\n            CommonLibraryData(it.groupId, it.artifactId, it.version)\n        }\n    }\n\n    override fun collectTasks(project: Project): List<TextCompletionInfo> {\n        val projectsManager = MavenProjectsManager.getInstance(project)\n        val mavenProjects: List<MavenProject> = projectsManager.projects\n        val tasks = mavenProjects.flatMap { it.plugins }.flatMap { it.executions }\n            .map { TextCompletionInfo(it.executionId, it.phase) }\n            .sortedWith(Comparator.comparing({ it.text }, JAVA_TASK_COMPLETION_COMPARATOR))\n\n        return tasks\n    }\n\n    override fun configureRun(\n        project: Project,\n        taskName: String,\n        virtualFile: VirtualFile?,\n    ): LocatableConfigurationBase<*>? {\n        if (virtualFile == null) return null\n\n        val projectsManager = MavenProjectsManager.getInstance(project);\n\n        val mavenProject: MavenProject = projectsManager.findProject(virtualFile) ?: return null\n        val module = runReadAction { projectsManager.findModule(mavenProject) } ?: return null\n\n        var trulyMavenProject = projectsManager.projects.firstOrNull {\n            it.mavenId.artifactId == module.name\n        }\n\n        if (trulyMavenProject == null) {\n            trulyMavenProject = projectsManager.projects.first() ?: return null\n        }\n\n        val pomFile = trulyMavenProject.file.name\n\n        val parameters = MavenRunnerParameters(\n            true, trulyMavenProject.directory, pomFile, listOf(\"test\"),\n            projectsManager.explicitProfiles.enabledProfiles, arrayListOf()\n        )\n\n        // $MODULE_WORKING_DIR$\n        //\n        // -ea Method: com.example.demo.MathHelperTest should_ReturnSum_When_GivenTwoPositiveNumbers\n        // /Users/phodal/Library/Java/JavaVirtualMachines/corretto-18.0.2/Contents/Home/bin/java\n        // -ea -Didea.test.cyclic.buffer.size=1048576\n        // -javaagent:ideaIU-2024.1/lib/idea_rt.jar=54637:1/bin -Dfile.encoding=UTF-8\n        // -Dsun.stdout.encoding=UTF-8 -Dsun.stderr.encoding=UTF-8\n\n        val runnerAndConfigurationSettings =\n            MavenRunConfigurationType.createRunnerAndConfigurationSettings(null, null, parameters, project)\n\n        val runManager = RunManager.getInstance(project)\n\n        val configuration = runnerAndConfigurationSettings.configuration\n\n        runManager.addConfiguration(runnerAndConfigurationSettings)\n        runManager.selectedConfiguration = runnerAndConfigurationSettings\n\n        return configuration as MavenRunConfiguration\n    }\n\n}"
  },
  {
    "path": "languages/shire-java/src/main/kotlin/com/phodal/shirelang/java/util/JavaContextCollection.kt",
    "content": "package com.phodal.shirelang.java.util\n\nimport com.intellij.openapi.diagnostic.logger\nimport com.intellij.psi.*\nimport com.intellij.psi.impl.source.PsiClassReferenceType\n\nobject JavaContextCollection {\n    private val logger = logger<JavaContextCollection>()\n\n    /**\n     * This method takes a PsiClass object as input and builds a tree of the class and its fields, including the fields of the fields, and so on. The resulting tree is represented as a HashMap where the keys are the PsiClass objects and the values are ArrayLists of PsiField objects.\n     *\n     * @param clazz the PsiClass object for which the tree needs to be built\n     * @return a HashMap where the keys are the PsiClass objects, and the values are ArrayLists of PsiField objects\n     *\n     * For example, if a BlogPost class includes a Comment class, and the Comment class includes a User class, then the resulting tree will be:\n     *\n     * ```\n     * parent: BlogPost Psi\n     *    child: id\n     *    child: Comment\n     *        child: User\n     *          child: name\n     *```\n     */\n    fun dataStructure(clazz: PsiClass): SimpleClassStructure? {\n        return simpleStructure(clazz)\n    }\n\n    private val psiStructureCache = mutableMapOf<PsiClass, SimpleClassStructure?>()\n\n    /**\n     * Creates a simple class structure for the given PsiClass and search scope.\n     *\n     * @param clazz the PsiClass for which the simple class structure needs to be created.\n     * @return a SimpleClassStructure object representing the simple class structure of the given PsiClass.\n     * The object contains the name of the class, the name of the fields, their types, and whether they are built-in or not.\n     * If the field type is a primitive type or a boxed type, it is marked as built-in.\n     * If the field type is a custom class, the method recursively creates a SimpleClassStructure object for that class.\n     * If the field type cannot be resolved, it is skipped.\n     */\n    private fun simpleStructure(clazz: PsiClass): SimpleClassStructure? {\n        // skip for generic\n        if (clazz.name?.uppercase() == clazz.name && clazz.name?.length == 1) return null\n\n        val qualifiedName = clazz.qualifiedName\n        if ((qualifiedName != null) && psiStructureCache.containsKey(clazz)) {\n            return psiStructureCache[clazz]!!\n        }\n\n        if (isJavaBuiltin(qualifiedName) == true || isPopularFramework(qualifiedName)) return null\n\n        val classStructures = clazz.fields.mapNotNull { field ->\n            // if current field same to parent class, skip it\n            if (field.type == clazz) return@mapNotNull null\n            if (field.type is PsiTypeParameter) return@mapNotNull null\n\n            val simpleClassStructure = when {\n                // like: int, long, boolean, etc.\n                field.type is PsiPrimitiveType -> {\n                    SimpleClassStructure(field.name, field.type.presentableText, emptyList(), builtIn = true)\n                }\n\n                // like: String, List, etc.\n                isPsiBoxedType(field.type) -> {\n                    SimpleClassStructure(field.name, field.type.presentableText, emptyList(), builtIn = true)\n                }\n\n                field.type is PsiTypeParameter -> {\n                    null\n                }\n\n                field.type is PsiClassType -> {\n                    // skip for some frameworks like, org.springframework, etc.\n                    val resolve = (field.type as PsiClassType).resolve() ?: return@mapNotNull null\n                    if (resolve.qualifiedName == qualifiedName) return@mapNotNull null\n\n                    if (isJavaBuiltin(resolve.qualifiedName) == true || isPopularFramework(resolve.qualifiedName) == true) {\n                        return@mapNotNull null\n                    }\n\n                    val classStructure = simpleStructure(resolve) ?: return@mapNotNull null\n                    classStructure.fieldName = field.name\n                    classStructure.builtIn = false\n                    classStructure\n                }\n\n                else -> {\n                    logger.warn(\"Unknown supported type: ${field.type}\")\n                    return@mapNotNull null\n                }\n            }\n\n            simpleClassStructure\n        }\n\n        val simpleClassStructure = SimpleClassStructure(clazz.name ?: \"\", clazz.name ?: \"\", classStructures)\n        psiStructureCache[clazz] = simpleClassStructure\n        return simpleClassStructure\n    }\n\n    private val popularFrameworks = listOf(\n        \"org.springframework\",\n        \"org.apache\",\n        \"org.hibernate\",\n        \"org.slf4j\",\n        \"org.junit\",\n        \"org.mockito\"\n    )\n\n    fun isPopularFramework(qualifiedName: String?): Boolean {\n        return popularFrameworks.any { qualifiedName?.startsWith(it) == true }\n    }\n\n    /**\n     * Checks if the given PsiType is a boxed type.\n     *\n     * A boxed type refers to a type that is represented by a PsiClassReferenceType and its resolve() method returns null.\n     * This typically occurs when the type is a generic type parameter or a type that cannot be resolved in the current context.\n     *\n     * @param type the PsiType to be checked\n     * @return true if the given type is a boxed type, false otherwise\n     */\n    private fun isPsiBoxedType(type: PsiType): Boolean {\n        if (type !is PsiClassReferenceType) return false\n\n        val resolve = try {\n            type.resolve() ?: return true\n        } catch (e: Exception) {\n            return false\n        }\n\n        return isJavaBuiltin(resolve.qualifiedName) == true\n    }\n\n    fun isJavaBuiltin(qualifiedName: String?) = qualifiedName?.startsWith(\"java.\")\n}\n"
  },
  {
    "path": "languages/shire-java/src/main/kotlin/com/phodal/shirelang/java/util/JavaTestHelper.kt",
    "content": "package com.phodal.shirelang.java.util\n\nimport com.intellij.openapi.progress.ProgressManager\nimport com.intellij.openapi.progress.util.ProgressIndicatorBase\nimport com.intellij.openapi.project.Project\nimport com.intellij.psi.*\nimport com.intellij.psi.search.GlobalSearchScope\nimport com.intellij.psi.search.ProjectScope\nimport com.intellij.psi.search.PsiShortNamesCache\nimport com.intellij.psi.search.searches.ClassInheritorsSearch\nimport com.intellij.psi.search.searches.MethodReferencesSearch\nimport com.intellij.psi.util.*\nimport com.phodal.shirecore.search.algorithm.TfIdf\nimport com.phodal.shirecore.search.tokenizer.CodeNamingTokenizer\n\n\nobject JavaTestHelper {\n    fun extractMethodCalls(project: Project, psiElement: PsiElement): String {\n        val searchScope = GlobalSearchScope.allScope(project)\n\n        return when (psiElement) {\n            is PsiFile -> {\n                PsiTreeUtil.findChildrenOfAnyType(psiElement, PsiClass::class.java)\n                    .joinToString(\"\\n\") { extractMethodCalls(project, it) }\n            }\n\n            is PsiClass -> {\n                PsiTreeUtil.findChildrenOfAnyType(psiElement, PsiMethod::class.java)\n                    .joinToString(\"\\n\") { extractMethodCalls(project, it) }\n            }\n\n            is PsiMethod -> {\n                PsiTreeUtil.findChildrenOfAnyType(psiElement.body, PsiMethodCallExpression::class.java)\n                    .filter { isMethodCallFromInheritedClass(it, searchScope) }\n                    .joinToString(\"\\n\") { it.text }\n            }\n\n            else -> \"\"\n        }\n    }\n\n    private fun isMethodCallFromInheritedClass(\n        callExpression: PsiMethodCallExpression,\n        searchScope: GlobalSearchScope,\n    ): Boolean {\n        val resolvedMethod = callExpression.resolveMethod() ?: return false\n        val containingClass = resolvedMethod.containingClass ?: return false\n\n        return ClassInheritorsSearch.search(containingClass, searchScope, true).findAll().isNotEmpty()\n    }\n\n    fun searchSimilarTestCases(psiElement: PsiElement, minScore: Double = 1.0): List<PsiMethod> {\n        val project = psiElement.project\n        val psiMethod = psiElement as? PsiMethod ?: return emptyList()\n        val methodName = psiMethod.name\n\n        // 使用缓存机制获取所有测试方法\n        val allTestMethods = getAllTestMethods(project)\n\n        // 使用 TfIdf 进行相似度计算\n        val tfIdf = TfIdf<String, List<PsiMethod>>()\n        tfIdf.setTokenizer(CodeNamingTokenizer())\n\n        allTestMethods.forEach { tfIdf.addDocument(it.name, it) }\n\n        return tfIdf.tfidfs(methodName)\n            .mapIndexedNotNull { index, measure ->\n                if (measure > minScore) allTestMethods[index] else null\n            }\n    }\n\n    /**\n     * Retrieves all test methods from the given project.\n     *\n     * @param project the project from which to retrieve the test methods\n     * @return a list of PsiMethod objects representing all test methods found in the project\n     */\n    private fun getAllTestMethods(project: Project): List<PsiMethod> {\n        val cachedValue: CachedValue<List<PsiMethod>> = CachedValuesManager.getManager(project).createCachedValue {\n            val testMethods = mutableListOf<PsiMethod>()\n            val scope = GlobalSearchScope.projectScope(project)\n\n            PsiShortNamesCache.getInstance(project).allClassNames\n                .filter { it.contains(\"Test\") }\n                .forEach { className ->\n                    PsiShortNamesCache.getInstance(project).getClassesByName(className, scope)\n                        .filter { it.containingFile.name.endsWith(\"Test.java\") }\n                        .forEach { psiClass ->\n                            testMethods.addAll(psiClass.methods)\n                        }\n                }\n\n            CachedValueProvider.Result.create(testMethods, PsiModificationTracker.MODIFICATION_COUNT)\n        }\n\n        return cachedValue.value\n    }\n\n    /**\n     * Finds all the methods called by the given method.\n     *\n     * @param method the method for which callees need to be found\n     * @return a list of PsiMethod objects representing the methods called by the given method\n     */\n    fun findCallees(project: Project, method: PsiMethod): List<String> {\n        val calledMethods = mutableSetOf<PsiMethod>()\n        method.accept(object : JavaRecursiveElementVisitor() {\n            override fun visitMethodCallExpression(expression: PsiMethodCallExpression) {\n                super.visitMethodCallExpression(expression)\n                calledMethods.add(expression.resolveMethod() ?: return)\n            }\n        })\n\n        return calledMethods\n            .mapNotNull {\n                val containingClass = it.containingClass ?: return@mapNotNull null\n                if (!ProjectScope.getProjectScope(project).contains(containingClass.containingFile.virtualFile)) {\n                    return@mapNotNull null\n                }\n\n                \"ClassName: ${containingClass.qualifiedName}\\nMethodText:\\n${it.text}\"\n            }\n    }\n\n    /**\n     * Finds all the callers of a given method.\n     *\n     * @param method the method for which callers need to be found\n     * @return a list of PsiMethod objects representing the callers of the given method\n     */\n    fun findCallers(project: Project, method: PsiMethod): List<String> {\n        val callers: MutableList<PsiMethod> = ArrayList()\n\n        ProgressManager.getInstance().runProcess(Runnable {\n            val references = MethodReferencesSearch.search(method, method.useScope, true).findAll()\n            for (reference in references) {\n                PsiTreeUtil.getParentOfType(reference.element, PsiMethod::class.java)?.let {\n                    callers.add(it)\n                }\n            }\n        }, ProgressIndicatorBase())\n\n        val psiMethods = callers.distinct()\n\n        return psiMethods\n            .mapNotNull {\n                val containingClass = it.containingClass ?: return@mapNotNull null\n                \"ClassName: ${containingClass.qualifiedName}\\nMethodText:\\n${it.text}\"\n            }\n    }\n}\n"
  },
  {
    "path": "languages/shire-java/src/main/kotlin/com/phodal/shirelang/java/util/JavaTypeResolver.kt",
    "content": "package com.phodal.shirelang.java.util\n\nimport com.intellij.openapi.application.runReadAction\nimport com.intellij.openapi.roots.ProjectFileIndex\nimport com.intellij.psi.*\nimport com.intellij.psi.impl.source.PsiClassReferenceType\nimport com.intellij.psi.util.PsiUtil\n\nobject JavaTypeResolver {\n    fun resolveByType(outputType: PsiType?): Map<String, PsiClass> {\n        val resolvedClasses = mutableMapOf<String, PsiClass>()\n        if (outputType is PsiClassReferenceType) {\n            resolvedClasses.putAll(resolveTypeReferences(outputType))\n        }\n\n        return resolvedClasses.filter { isProjectContent(it.value) }.toMap()\n    }\n\n    private fun resolveTypeReferences(outputType: PsiClassReferenceType): MutableMap<String, PsiClass> {\n        val resolvedClasses = mutableMapOf<String, PsiClass>()\n\n        fun resolveRecursively(type: PsiClassReferenceType) {\n            val resolvedClass = type.resolve()\n            if (resolvedClass != null) {\n                resolvedClasses[type.canonicalText] = resolvedClass\n            }\n\n            type.parameters.filterIsInstance<PsiClassReferenceType>().forEach { childType ->\n                resolveRecursively(childType)\n            }\n        }\n\n        resolveRecursively(outputType)\n        return resolvedClasses\n    }\n\n    fun resolveByField(element: PsiElement): Map<String, PsiClass> {\n        val psiFile = element.containingFile as PsiJavaFile\n\n        val resolvedClasses = mutableMapOf<String, PsiClass>()\n        psiFile.classes.forEach { psiClass ->\n            psiClass.fields.forEach { field ->\n                resolvedClasses.putAll(resolveByType(field.type))\n            }\n        }\n\n        return resolvedClasses.filter { isProjectContent(it.value) }.toMap()\n    }\n\n    fun resolveByClass(element: PsiElement): Map<String, PsiClass> {\n        val resolvedClasses = mutableMapOf<String, PsiClass>()\n        if (element !is PsiClass) {\n            return emptyMap()\n        }\n\n        return runReadAction {\n            element.fields.forEach { field ->\n                resolvedClasses.putAll(resolveByType(field.type))\n            }\n\n            element.methods.forEach { method ->\n                resolvedClasses.putAll(resolveByMethod(method))\n            }\n\n            resolvedClasses.filter { isProjectContent(it.value) }.toMap()\n        }\n    }\n\n    /**\n     * The resolved classes include all the classes in the method signature. For example, if the method signature is\n     * Int, will return Int, but if the method signature is List<Int>, will return List and Int.\n     * So, remember to filter out the classes that are not needed.\n     */\n    fun resolveByMethod(element: PsiElement): Map<String, PsiClass> {\n        val resolvedClasses = mutableMapOf<String, PsiClass>()\n        if (element !is PsiMethod) {\n            return emptyMap()\n        }\n\n        return runReadAction {\n            element.parameterList.parameters\n                .filter { it.type is PsiClassReferenceType }\n                .map { parameter ->\n                    val type = parameter.type as PsiClassReferenceType\n                    val resolve: PsiClass = type.resolve() ?: return@map null\n                    val typeParametersTypeList: List<PsiType> = getTypeParametersType(type)\n\n                    val relatedClass = mutableListOf(parameter.type)\n                    relatedClass.addAll(typeParametersTypeList)\n\n                    relatedClass\n                        .filter { isProjectContent((it as PsiClassReferenceType).resolve() ?: return@filter false) }\n                        .forEach { resolvedClasses.putAll(resolveByType(it)) }\n\n                    // class kotlin.Unit cannot be cast to class java.lang.Void\n                    if (resolve is PsiClass) {\n                        resolvedClasses[parameter.name] = resolve\n                    }\n\n                    resolvedClasses\n                }\n\n            val outputType = element.returnTypeElement?.type\n            resolvedClasses.putAll(resolveByType(outputType))\n\n            resolvedClasses.filter { isProjectContent(it.value) }.toMap()\n        }\n    }\n\n    private fun getTypeParametersType(\n        psiType: PsiClassReferenceType,\n    ): List<PsiType> {\n        val result = psiType.resolveGenerics()\n        val psiClass = result.element ?: return emptyList();\n\n        return runReadAction {\n            val substitutor = result.substitutor\n            psiClass.typeParameters.mapNotNull {\n                substitutor.substitute(it)\n            }\n        }\n    }\n}\n\nfun isProjectContent(element: PsiElement): Boolean {\n    val virtualFile = PsiUtil.getVirtualFile(element)\n    val project = runReadAction { element.project }\n    return virtualFile == null || ProjectFileIndex.getInstance(project).isInContent(virtualFile)\n}\n\nfun PsiElement.getContainingClass(): PsiClass? {\n    var context: PsiElement? = this.context\n    while (context != null) {\n        if (context is PsiClass) return context\n        if (context is PsiMember) return context.containingClass\n\n        context = context.context\n    }\n\n    return null\n}\n\nfun PsiElement.getContainingMethod(): PsiMethod? {\n    var context: PsiElement? = this.context\n    while (context != null) {\n        if (context is PsiMethod) return context\n\n        context = context.context\n    }\n\n    return null\n}"
  },
  {
    "path": "languages/shire-java/src/main/kotlin/com/phodal/shirelang/java/util/SimpleClassStructure.kt",
    "content": "package com.phodal.shirelang.java.util\n\ndata class SimpleClassStructure(\n    var fieldName: String,\n    var fieldType: String,\n    val children: List<SimpleClassStructure>,\n    var builtIn: Boolean = false\n) {\n    private val childrenUml: MutableMap<String, String> = mutableMapOf()\n\n    /**\n     * Returns a PlantUML string representation of the class structure.\n     *\n     * This method generates a PlantUML string representation of the class structure based on the current object and its child objects. The resulting string is built using the buildPuml() method and the childPuml map. The resulting string will be a tree-like structure that shows the relationships between the classes.\n     *\n     * @return the PlantUML string representation of the class structure\n     *\n     * For example, if a BlogPost class includes a Comment class, and the Comment class includes a User class, then the resulting tree will be:\n     *\n     * ```puml\n     * class BlogPost {\n     *  id: long\n     *  comment: Comment\n     *}\n     *\n     * class Comment {\n     *   user: User\n     * }\n     *\n     * class User {\n     *  name: String\n     * }\n     *```\n     */\n    override fun toString(): String {\n        val diagramBuilder = StringBuilder()\n        diagramBuilder.append(classStructureToUml(this))\n\n        buildChildUmlHierarchy(children)\n\n        childrenUml.forEach {\n            diagramBuilder.append(\"\\n\")\n            diagramBuilder.append(it.value)\n        }\n\n        return diagramBuilder.toString()\n    }\n\n\n    /**\n     * Returns a PlantUML string representation of the class structure\n     * for example:\n     * ```\n     * class BlogPost {\n     *     long id;\n     *     Comment comment;\n     * }\n     * class Comment {\n     *     User user;\n     * }\n     * class User {\n     *     String name;\n     * }\n     *```\n     *\n     * will be represented as:\n     *\n     * ```puml\n     * class BlogPost {\n     *    long id;\n     *    Comment comment;\n     *}\n     *```\n     */\n    private fun classStructureToUml(simpleClassStructure: SimpleClassStructure): String {\n        val children = simpleClassStructure.children.joinToString(\"\\n\") { \"  ${it.fieldName}: ${it.fieldType}\" }\n        return \"class ${simpleClassStructure.fieldType} {\\n\" +\n                children +\n                \"\\n}\\n\"\n    }\n\n    private fun buildChildUmlHierarchy(data: List<SimpleClassStructure>) {\n        data.filter { !it.builtIn }.forEach {\n            childrenUml[it.fieldType] = classStructureToUml(it)\n            buildChildUmlHierarchy(it.children)\n        }\n    }\n\n}"
  },
  {
    "path": "languages/shire-java/src/main/kotlin/com/phodal/shirelang/java/variable/JavaLanguageToolchainProvider.kt",
    "content": "package com.phodal.shirelang.java.variable\n\nimport com.intellij.lang.java.JavaLanguage\nimport com.intellij.openapi.module.Module\nimport com.intellij.openapi.module.ModuleUtilCore\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.projectRoots.JavaSdkType\nimport com.intellij.openapi.roots.ModuleRootManager\nimport com.intellij.openapi.roots.ProjectRootManager\nimport com.intellij.psi.PsiJavaFile\nimport com.phodal.shirecore.provider.context.LanguageToolchainProvider\nimport com.phodal.shirecore.provider.context.ToolchainContextItem\nimport com.phodal.shirecore.provider.context.ToolchainPrepareContext\nimport com.phodal.shirelang.java.toolchain.JvmLanguageDetector\n\nclass JavaLanguageToolchainProvider : LanguageToolchainProvider {\n    override fun isApplicable(project: Project, context: ToolchainPrepareContext): Boolean {\n        val sourceFile = context.sourceFile ?: return false\n        if (sourceFile.language != JavaLanguage.INSTANCE) return false\n        val projectSdk = ProjectRootManager.getInstance(project).projectSdk\n        if (projectSdk?.sdkType !is JavaSdkType) return false\n\n        val module: Module = try {\n            ModuleUtilCore.findModuleForFile(sourceFile)\n        } catch (e: Exception) {\n            return false\n        } ?: return false\n\n        return ModuleRootManager.getInstance(module).sdk?.sdkType is JavaSdkType\n    }\n\n    override suspend fun collect(\n        project: Project,\n        context: ToolchainPrepareContext,\n    ): List<ToolchainContextItem> {\n        return collectJavaVersion(context, project)?.let { listOf(it) } ?: emptyList()\n    }\n\n    private fun collectJavaVersion(\n        context: ToolchainPrepareContext,\n        project: Project,\n    ): ToolchainContextItem? {\n        val psiFile = context.sourceFile as? PsiJavaFile ?: return null\n\n        val languageLevel = JvmLanguageDetector.detectLanguageLevel(project, psiFile) ?: return null\n\n        val prompt = \"You are working on a project that uses Java SDK version $languageLevel.\"\n\n        return ToolchainContextItem(JavaLanguageToolchainProvider::class, prompt)\n    }\n}\n"
  },
  {
    "path": "languages/shire-java/src/main/kotlin/com/phodal/shirelang/java/variable/JavaPsiContextVariableProvider.kt",
    "content": "package com.phodal.shirelang.java.variable\n\nimport com.intellij.lang.java.JavaLanguage\nimport com.intellij.openapi.editor.Editor\nimport com.intellij.openapi.project.Project\nimport com.intellij.psi.PsiClass\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.PsiJavaFile\nimport com.intellij.psi.PsiMethod\nimport com.intellij.testIntegration.TestFinderHelper\nimport com.phodal.shirecore.provider.variable.PsiContextVariableProvider\nimport com.phodal.shirecore.psi.CodeSmellCollector\nimport com.phodal.shirecore.provider.variable.model.PsiContextVariable\nimport com.phodal.shirecore.provider.variable.model.PsiContextVariable.*\nimport com.phodal.shirecore.search.similar.SimilarChunksSearch\nimport com.phodal.shirelang.java.codemodel.JavaClassStructureProvider\nimport com.phodal.shirelang.java.util.JavaTestHelper\nimport com.phodal.shirelang.java.util.getContainingClass\nimport com.phodal.shirelang.java.provider.JavaRelatedClassesProvider\n\nclass JavaPsiContextVariableProvider : PsiContextVariableProvider {\n    override fun resolve(variable: PsiContextVariable, project: Project, editor: Editor, psiElement: PsiElement?): Any {\n        if (psiElement?.language != JavaLanguage.INSTANCE) return \"\"\n\n        val clazz: PsiClass? = psiElement as? PsiClass ?: psiElement.getContainingClass()\n        val sourceFile: PsiJavaFile = psiElement.containingFile as PsiJavaFile\n\n        return when (variable) {\n            IMPORTS -> sourceFile.importList?.text ?: \"\"\n            CURRENT_CLASS_NAME -> clazz?.name ?: \"\"\n            CURRENT_CLASS_CODE -> sourceFile.text\n            CURRENT_METHOD_NAME -> (psiElement as? PsiMethod)?.name ?: \"\"\n            CURRENT_METHOD_CODE -> (psiElement as? PsiMethod)?.text ?: \"\"\n            RELATED_CLASSES -> JavaRelatedClassesProvider().lookup(psiElement.parent).joinToString(\"\\n\") { it.text }\n            SIMILAR_TEST_CASE -> JavaTestHelper.searchSimilarTestCases(psiElement).joinToString(\"\\n\") { it.text }\n            IS_NEED_CREATE_FILE -> TestFinderHelper.findClassesForTest(psiElement).isEmpty()\n            TARGET_TEST_FILE_NAME -> sourceFile.name.replace(\".java\", \"\") + \"Test.java\"\n            UNDER_TEST_METHOD_CODE -> JavaTestHelper.extractMethodCalls(project, psiElement)\n            CODE_SMELL -> CodeSmellCollector.collectElementProblemAsSting(psiElement, project, editor)\n            METHOD_CALLER -> {\n                if (psiElement !is PsiMethod) return \"\"\n                return JavaTestHelper.findCallers(project, psiElement).joinToString(\"\\n\\n\")\n            }\n\n            CALLED_METHOD -> {\n                if (psiElement !is PsiMethod) return \"\"\n                return JavaTestHelper.findCallees(project, psiElement).joinToString(\"\\n\\n\")\n            }\n\n            SIMILAR_CODE -> return SimilarChunksSearch.createQuery(psiElement) ?: \"\"\n            STRUCTURE -> clazz?.let {\n                JavaClassStructureProvider().build(it, true)?.format() ?: \"\"\n            } ?: \"\"\n\n            FRAMEWORK_CONTEXT -> return collectFrameworkContext(psiElement, project)\n            CHANGE_COUNT -> calculateChangeCount(psiElement)\n            LINE_COUNT -> calculateLineCount(psiElement)\n            COMPLEXITY_COUNT -> calculateComplexityCount(psiElement)\n        }\n    }\n}\n\n"
  },
  {
    "path": "languages/shire-java/src/main/kotlin/com/phodal/shirelang/java/variable/JavaVariableProvider.kt",
    "content": "package com.phodal.shirelang.java.variable\n\nimport com.intellij.openapi.editor.Editor\nimport com.intellij.openapi.project.Project\nimport com.intellij.psi.PsiElement\nimport com.phodal.shirecore.provider.variable.ToolchainVariableProvider\nimport com.phodal.shirecore.provider.variable.model.ToolchainVariable\nimport com.phodal.shirecore.provider.variable.model.toolchain.BuildToolchainVariable\nimport com.phodal.shirelang.java.impl.JvmBuildSystemProvider\nimport com.phodal.shirelang.java.toolchain.GradleBuildTool\n\nclass JavaVariableProvider : ToolchainVariableProvider {\n    override fun isResolvable(variable: ToolchainVariable, psiElement: PsiElement?, project: Project): Boolean {\n        return variable is BuildToolchainVariable && GradleBuildTool().prepareLibraryData(project)?.isNotEmpty() == true\n    }\n\n    override fun resolve(variable: ToolchainVariable, project: Project, editor: Editor, psiElement: PsiElement?): Any {\n        return when (variable) {\n            BuildToolchainVariable.ProjectDependencies -> JvmBuildSystemProvider().collect(project)\n            else -> \"\"\n        } ?: \"\"\n    }\n}\n"
  },
  {
    "path": "languages/shire-java/src/main/resources/com.phodal.shirelang.java.xml",
    "content": "<idea-plugin package=\"com.phodal.shirelang.java\">\n    <!--suppress PluginXmlValidity -->\n    <dependencies>\n        <plugin id=\"com.intellij.modules.java\"/>\n        <plugin id=\"org.jetbrains.plugins.gradle\"/>\n    </dependencies>\n\n    <extensions defaultExtensionNs=\"com.phodal\">\n        <fileStructureProvider language=\"JAVA\"\n                               implementationClass=\"com.phodal.shirelang.java.codemodel.JavaFileStructureProvider\"/>\n        <classStructureProvider language=\"JAVA\"\n                                implementationClass=\"com.phodal.shirelang.java.codemodel.JavaClassStructureProvider\"/>\n        <methodStructureProvider language=\"JAVA\"\n                                 implementationClass=\"com.phodal.shirelang.java.codemodel.JavaMethodStructureProvider\"/>\n        <variableStructureProvider language=\"JAVA\"\n                                   implementationClass=\"com.phodal.shirelang.java.codemodel.JavaVariableStructureProvider\"/>\n\n        <shireRefactoringTool\n                language=\"JAVA\"\n                implementationClass=\"com.phodal.shirelang.java.impl.JavaRefactoringTool\"/>\n\n        <shireBuildSystemProvider\n                implementation=\"com.phodal.shirelang.java.impl.JvmBuildSystemProvider\"/>\n\n        <shireAutoTesting language=\"JAVA\"\n                          implementationClass=\"com.phodal.shirelang.java.codeedit.JavaAutoTestService\"/>\n\n        <shireToolchainVariableProvider\n                implementation=\"com.phodal.shirelang.java.variable.JavaVariableProvider\"/>\n\n        <shireLanguageToolchainProvider\n                language=\"JAVA\"\n                implementationClass=\"com.phodal.shirelang.java.variable.JavaLanguageToolchainProvider\"/>\n\n        <shireRunProjectService\n                implementation=\"com.phodal.shirelang.java.toolchain.JvmRunProjectService\"/>\n\n        <shireSymbolProvider implementation=\"com.phodal.shirelang.java.impl.JavaSymbolProvider\"/>\n\n        <shirePsiVariableProvider\n                language=\"JAVA\"\n                implementationClass=\"com.phodal.shirelang.java.variable.JavaPsiContextVariableProvider\"/>\n\n        <shireCodeModifier language=\"JAVA\"\n                           implementationClass=\"com.phodal.shirelang.java.codeedit.JavaCodeModifier\"/>\n\n        <shireElementStrategyBuilder implementation=\"com.phodal.shirelang.java.impl.JavaElementStrategyBuilder\"/>\n\n        <shirePsiElementDataBuilder language=\"JAVA\"\n                                    implementationClass=\"com.phodal.shirelang.java.impl.JavaPsiElementDataBuilder\"/>\n\n        <shirePsiQLInterpreter language=\"JAVA\"\n                               implementationClass=\"com.phodal.shirelang.java.impl.JavaShireQLInterpreter\"/>\n\n        <shireComplexityProvider language=\"JAVA\"\n                                 implementationClass=\"com.phodal.shirelang.java.complexity.JavaComplexityProvider\"/>\n\n        <shireRelatedClass language=\"JAVA\"\n                                   implementationClass=\"com.phodal.shirelang.java.provider.JavaRelatedClassesProvider\"/>\n    </extensions>\n</idea-plugin>\n"
  },
  {
    "path": "languages/shire-java/src/test/kotlin/com/phodal/shirelang/java/complexity/JavaComplexityProviderTest.kt",
    "content": "package com.phodal.shirelang.java.complexity\n\nimport com.intellij.psi.PsiJavaFile\nimport com.intellij.testFramework.fixtures.BasePlatformTestCase\nimport org.intellij.lang.annotations.Language\n\nclass JavaComplexityProviderTest: BasePlatformTestCase() {\n    fun testShouldCalculateComplexitySize2() {\n        @Language(\"Java\")\n        val code = \"\"\"\n            public class TestClass {\n                @Complexity(4)\n                public void parenthesisInCenterSplitTheGroup() {\n                    if (                          // +1 if\n                        a || b ||                 // +1 OR\n                            !(c || d)             // +1 OR separate\n                                || e || f) {      // +1 new OR\n                        return;\n                    }\n                }\n\n            }\n        \"\"\".trimIndent()\n\n        val psiFile = myFixture.addFileToProject(\"TestClass.java\", code) as PsiJavaFile\n        val psiClass = psiFile.classes[0]\n\n        val complexityProvider = JavaComplexityProvider()\n        val result = complexityProvider.process(psiClass)\n        assertEquals(4, result)\n    }\n}\n"
  },
  {
    "path": "languages/shire-java/src/test/kotlin/com/phodal/shirelang/java/impl/JavaBuildSystemProviderTest.kt",
    "content": "package com.phodal.shirelang.java.impl\n\nimport org.junit.Assert.assertEquals\nimport org.junit.Test\n\nclass JavaTaskCompletionComparatorTest {\n\n    @Test\n    fun should_compare_when_both_start_with_double_dash() {\n        // Given\n        val o1 = \"--task1\"\n        val o2 = \"--task2\"\n\n        // When\n        val result = JAVA_TASK_COMPLETION_COMPARATOR.compare(o1, o2)\n\n        // Then\n        assertEquals(o1.compareTo(o2), result)\n    }\n\n    @Test\n    fun should_compare_when_first_starts_with_dash_and_second_with_double_dash() {\n        // Given\n        val o1 = \"-task1\"\n        val o2 = \"--task2\"\n\n        // When\n        val result = JAVA_TASK_COMPLETION_COMPARATOR.compare(o1, o2)\n\n        // Then\n        assertEquals(-1, result)\n    }\n\n    @Test\n    fun should_compare_when_first_starts_with_double_dash_and_second_with_dash() {\n        // Given\n        val o1 = \"--task1\"\n        val o2 = \"-task2\"\n\n        // When\n        val result = JAVA_TASK_COMPLETION_COMPARATOR.compare(o1, o2)\n\n        // Then\n        assertEquals(1, result)\n    }\n\n    @Test\n    fun should_compare_when_both_start_with_colon() {\n        // Given\n        val o1 = \":task1\"\n        val o2 = \":task2\"\n\n        // When\n        val result = JAVA_TASK_COMPLETION_COMPARATOR.compare(o1, o2)\n\n        // Then\n        assertEquals(o1.compareTo(o2), result)\n    }\n\n    @Test\n    fun should_compare_when_first_starts_with_colon_and_second_with_dash() {\n        // Given\n        val o1 = \":task1\"\n        val o2 = \"-task2\"\n\n        // When\n        val result = JAVA_TASK_COMPLETION_COMPARATOR.compare(o1, o2)\n\n        // Then\n        assertEquals(-1, result)\n    }\n\n    @Test\n    fun should_compare_when_first_starts_with_dash_and_second_with_colon() {\n        // Given\n        val o1 = \"-task1\"\n        val o2 = \":task2\"\n\n        // When\n        val result = JAVA_TASK_COMPLETION_COMPARATOR.compare(o1, o2)\n\n        // Then\n        assertEquals(1, result)\n    }\n\n    @Test\n    fun should_compare_when_second_starts_with_dash() {\n        // Given\n        val o1 = \"task1\"\n        val o2 = \"-task2\"\n\n        // When\n        val result = JAVA_TASK_COMPLETION_COMPARATOR.compare(o1, o2)\n\n        // Then\n        assertEquals(-1, result)\n    }\n\n    @Test\n    fun should_compare_when_second_starts_with_colon() {\n        // Given\n        val o1 = \"task1\"\n        val o2 = \":task2\"\n\n        // When\n        val result = JAVA_TASK_COMPLETION_COMPARATOR.compare(o1, o2)\n\n        // Then\n        assertEquals(-1, result)\n    }\n\n    @Test\n    fun should_compare_when_first_starts_with_dash() {\n        // Given\n        val o1 = \"-task1\"\n        val o2 = \"task2\"\n\n        // When\n        val result = JAVA_TASK_COMPLETION_COMPARATOR.compare(o1, o2)\n\n        // Then\n        assertEquals(1, result)\n    }\n\n    @Test\n    fun should_compare_when_first_starts_with_colon() {\n        // Given\n        val o1 = \":task1\"\n        val o2 = \"task2\"\n\n        // When\n        val result = JAVA_TASK_COMPLETION_COMPARATOR.compare(o1, o2)\n\n        // Then\n        assertEquals(1, result)\n    }\n\n    @Test\n    fun should_compare_when_neither_starts_with_dash_or_colon() {\n        // Given\n        val o1 = \"task1\"\n        val o2 = \"task2\"\n\n        // When\n        val result = JAVA_TASK_COMPLETION_COMPARATOR.compare(o1, o2)\n\n        // Then\n        assertEquals(o1.compareTo(o2), result)\n    }\n}\n"
  },
  {
    "path": "languages/shire-java/src/test/kotlin/com/phodal/shirelang/java/impl/JavaPsiQLInterpreterTest.kt",
    "content": "package com.phodal.shirelang.java.impl\n\nimport com.intellij.psi.PsiClass\nimport com.intellij.psi.PsiJavaFile\nimport com.intellij.testFramework.fixtures.BasePlatformTestCase\n\n\nclass JavaPsiQLInterpreterTest: BasePlatformTestCase() {\n\n    fun testShouldSuccessGetClassName() {\n        val runnableCode = \"\"\"\n            public class Runnable {\n                public String getName() {\n                    return \"Runnable\";\n                }\n            }\n        \"\"\".trimIndent()\n        myFixture.addFileToProject(\"Runnable.java\", runnableCode)\n\n        val shireObjectCode = \"\"\"\n            public class ShireObject {\n                public String getName() {\n                    return \"ShireObject\";\n                }\n            }\n        \"\"\".trimIndent()\n        myFixture.addFileToProject(\"ShireObject.java\", shireObjectCode)\n\n        val javaClassCode = \"\"\"\n            public class TestClass extends ShireObject implements Runnable {\n                public String getName() {\n                    return \"TestClass\";\n                }\n            }\n        \"\"\".trimIndent()\n\n        val psiFile = myFixture.addFileToProject(\"TestClass.java\", javaClassCode) as PsiJavaFile\n        val psiClass = psiFile.classes[0]\n\n        val interpreter = JavaShireQLInterpreter()\n        val result = interpreter.resolveCall(psiClass, \"getName\", emptyList())\n        val extendsClasses = interpreter.resolveCall(psiClass, \"extends\", emptyList())\n        // implements\n        val implementsClass = interpreter.resolveCall(psiClass, \"implements\", emptyList())\n\n        assertEquals(\"TestClass\", result)\n        assertEquals(\"ShireObject\", (extendsClasses as List<PsiClass>).first().name)\n        assertEquals(\"Runnable\", (implementsClass as List<PsiClass>).first().name)\n    }\n\n    fun testShouldResolveParentOf() {\n        val javaClassCode = \"\"\"\n            public class TestClass extends ShireObject implements Runnable {\n                public String getName() {\n                    return \"TestClass\";\n                }\n            }\n        \"\"\".trimIndent()\n\n        val shireObjectCode = \"\"\"\n            public class ShireObject {\n                public String getName() {\n                    return \"ShireObject\";\n                }\n            }\n        \"\"\".trimIndent()\n\n        val psiFile = myFixture.addFileToProject(\"TestClass.java\", javaClassCode) as PsiJavaFile\n        myFixture.addFileToProject(\"ShireObject.java\", shireObjectCode)\n\n        val psiClass = psiFile.classes[0]\n\n        val interpreter = JavaShireQLInterpreter()\n        val result = interpreter.resolveCall(psiClass, \"superclassOf\", listOf(\"TestClass\"))\n        // extendsOf\n        val extendsClasses = interpreter.resolveOfTypedCall(project, \"subclassesOf\", listOf(\"ShireObject\"))\n\n        assertEquals(\"ShireObject\", (result as PsiClass).name)\n        assertEquals(\"TestClass\", (extendsClasses as List<PsiClass>).first().name)\n    }\n}\n"
  },
  {
    "path": "languages/shire-java/src/test/kotlin/com/phodal/shirelang/java/toolchain/SpringLayerCharacteristicTest.kt",
    "content": "package com.phodal.shirelang.java.toolchain\n\nimport com.phodal.shirelang.java.archmeta.SpringLayerCharacteristic\nimport org.junit.Test\n\nclass SpringLayerCharacteristicTest {\n\n    @Test\n    fun should_return_true_when_spring_controller() {\n        val code = \"\"\"\n            package cc.unitmesh.devti.flow;\n            \n            import org.springframework.stereotype.Controller;\n            import org.springframework.web.bind.annotation.RestController;\n            \n            @Controller\n            public class UserController {\n            }\n        \"\"\".trimIndent()\n        val result = SpringLayerCharacteristic.check(code, \"controller\")\n        assert(result)\n    }\n\n    @Test\n    fun should_return_true_when_is_a_mvc_service() {\n        val serviceCode = \"\"\"\n            @Service\n            public class HelloWorldService {\n            }\n        \"\"\".trimIndent()\n\n        val result = SpringLayerCharacteristic.check(serviceCode, \"service\")\n        assert(result)\n    }\n\n    @Test\n    fun should_return_true_when_given_a_dto_code() {\n        val dtoCode = \"\"\"\n            @Data\n            public class UserDto {\n            }\n        \"\"\".trimIndent()\n\n        val result = SpringLayerCharacteristic.check(dtoCode, \"dto\")\n        assert(result)\n    }\n\n    @Test\n    fun should_return_true_when_given_a_repository_code() {\n        val repositoryCode = \"\"\"\n            @Repository\n            public class UserRepository {\n            }\n        \"\"\".trimIndent()\n\n        val result = SpringLayerCharacteristic.check(repositoryCode, \"repository\")\n        assert(result)\n    }\n}"
  },
  {
    "path": "languages/shire-java/src/test/kotlin/com/phodal/shirelang/java/variable/JavaTestHelperTest.kt",
    "content": "package com.phodal.shirelang.java.variable\n\nimport com.intellij.psi.PsiJavaFile\nimport com.intellij.psi.PsiMethod\nimport com.intellij.testFramework.fixtures.BasePlatformTestCase\nimport com.phodal.shirelang.java.util.JavaTestHelper\nimport junit.framework.TestCase\n\nclass JavaTestHelperTest : BasePlatformTestCase() {\n    private val code: String = \"\"\"\n        import org.junit.Test;\n\npublic class MathHelperTest {\n\n    @Test\n    public void testAdditionWithPositiveNumbers() {\n\n    }\n\n    @Test\n    public void testAdditionWithNegativeNumbers() {\n\n    }\n\n    @Test\n    public void testSubtractionWithPositiveNumbers() {\n\n    }\n\n    @Test\n    public void testSubtractionWithNegativeNumbers() {\n\n    }\n\n    @Test\n    public void testMultiplicationWithPositiveNumbers() {\n\n    }\n\n    @Test\n    public void testMultiplicationWithNegativeNumbers() {\n\n    }\n\n    @Test\n    public void testDivisionWithPositiveNumbers() {\n\n    }\n\n    @Test\n    public void testDivisionWithNegativeNumbers() {\n\n    }\n}\n    \"\"\".trimIndent()\n\n    fun testShouldReturnCorrectJavaMethodName() {\n        val code2 = \"\"\"\n            class MathHelper {\n                public int AdditionWith(int a, int b) {\n                    return a + b;\n                }\n            }\n        \"\"\".trimIndent()\n\n        myFixture.addFileToProject(\"MathHelperTest.java\", code)\n        val psiFile2 = myFixture.addFileToProject(\"MathHelper.java\", code2) as PsiJavaFile\n\n        val addMethod = psiFile2.classes.first().methods.first() as PsiMethod\n\n        val testCases = JavaTestHelper.searchSimilarTestCases(addMethod)\n        TestCase.assertEquals(2, testCases.size)\n    }\n}\n"
  },
  {
    "path": "languages/shire-javascript/src/main/kotlin/com/phodal/shirelang/javascript/JSTypeResolver.kt",
    "content": "package com.phodal.shirelang.javascript\n\nimport com.intellij.lang.javascript.psi.JSFunction\nimport com.intellij.lang.javascript.psi.ecma6.TypeScriptInterface\nimport com.intellij.lang.javascript.psi.ecma6.TypeScriptSingleType\nimport com.intellij.lang.javascript.psi.ecmal4.JSClass\nimport com.intellij.lang.javascript.psi.util.JSStubBasedPsiTreeUtil\nimport com.intellij.openapi.application.ReadAction\nimport com.intellij.psi.PsiElement\n\nobject JSTypeResolver {\n    fun resolveByElement(element: PsiElement): List<JSClass> =\n        ReadAction.compute<List<JSClass>, Throwable> {\n            val elements = mutableListOf<JSClass>()\n            when (element) {\n                is JSClass -> {\n                    element.functions.map {\n                        elements += resolveByFunction(it).values\n                    }\n                }\n\n                is JSFunction -> {\n                    elements += resolveByFunction(element).values\n                }\n\n                else -> {}\n            }\n\n            return@compute elements\n        }\n\n    private fun resolveByFunction(jsFunction: JSFunction): Map<String, JSClass> {\n        val result = mutableMapOf<String, JSClass>()\n        jsFunction.parameterList?.parameters?.map {\n            it.typeElement?.let { typeElement ->\n                result += resolveByType(typeElement, it.typeElement!!.text)\n            }\n        }\n\n        result += jsFunction.returnTypeElement?.let {\n            resolveByType(it, jsFunction.returnType!!.resolvedTypeText)\n        } ?: emptyMap()\n\n        return result\n    }\n\n    private fun resolveByType(\n        returnType: PsiElement?,\n        typeName: String,\n    ): MutableMap<String, JSClass> {\n        val result = mutableMapOf<String, JSClass>()\n        when (returnType) {\n            is TypeScriptSingleType -> {\n                when (val referenceLocally = JSStubBasedPsiTreeUtil.resolveLocally(typeName, returnType)) {\n                    is TypeScriptInterface -> {\n                        result += mapOf(typeName to referenceLocally)\n                    }\n                }\n            }\n        }\n\n        return result\n    }\n}"
  },
  {
    "path": "languages/shire-javascript/src/main/kotlin/com/phodal/shirelang/javascript/codeedit/JSAutoTestingService.kt",
    "content": "package com.phodal.shirelang.javascript.codeedit\n\nimport com.intellij.execution.configurations.RunProfile\nimport com.intellij.lang.javascript.buildTools.npm.rc.NpmRunConfiguration\nimport com.intellij.lang.javascript.psi.JSFile\nimport com.intellij.lang.javascript.psi.ecmal4.JSImportStatement\nimport com.intellij.openapi.application.ReadAction\nimport com.intellij.openapi.application.runReadAction\nimport com.intellij.openapi.command.WriteCommandAction\nimport com.intellij.openapi.diagnostic.logger\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.vfs.LocalFileSystem\nimport com.intellij.openapi.vfs.VfsUtil\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.PsiFile\nimport com.intellij.psi.PsiManager\nimport com.intellij.psi.util.PsiTreeUtil\nimport com.phodal.shirecore.provider.TestingService\nimport com.phodal.shirecore.provider.codemodel.model.ClassStructure\nimport com.phodal.shirecore.variable.toolchain.unittest.AutoTestingPromptContext\nimport com.phodal.shirelang.javascript.JSTypeResolver\nimport com.phodal.shirelang.javascript.codemodel.JavaScriptClassStructureProvider\nimport com.phodal.shirelang.javascript.codemodel.JavaScriptMethodStructureProvider\nimport com.phodal.shirelang.javascript.util.JSPsiUtil\nimport com.phodal.shirelang.javascript.util.LanguageApplicableUtil\nimport kotlin.io.path.Path\n\nclass JSAutoTestingService : TestingService() {\n    private val log = logger<JSAutoTestingService>()\n    override fun runConfigurationClass(project: Project): Class<out RunProfile> = NpmRunConfiguration::class.java\n\n    override fun isApplicable(element: PsiElement): Boolean {\n        val sourceFile: PsiFile = element.containingFile ?: return false\n        return LanguageApplicableUtil.isWebChatCreationContextSupported(sourceFile)\n    }\n\n    override fun isApplicable(project: Project, file: VirtualFile): Boolean {\n        val psiFile = PsiManager.getInstance(project).findFile(file) as? JSFile ?: return false\n        return LanguageApplicableUtil.isWebChatCreationContextSupported(psiFile)\n    }\n\n    override fun findOrCreateTestFile(\n        sourceFile: PsiFile,\n        project: Project,\n        psiElement: PsiElement,\n    ): AutoTestingPromptContext? {\n        val language = sourceFile.language\n        val testFilePath = JSPsiUtil.getTestFilePath(psiElement)?.toString()\n        if (testFilePath == null) {\n            log.warn(\"Failed to find test file path for: $psiElement\")\n            return null\n        }\n\n        val elementToTest = runReadAction { JSPsiUtil.getElementToTest(psiElement) }\n        if (elementToTest == null) {\n            log.warn(\"Failed to find element to test for: ${psiElement}, check your function is exported.\")\n            return null\n        }\n\n        val elementName = JSPsiUtil.elementName(elementToTest)\n        if (elementName == null) {\n            log.warn(\"Failed to find element name for: $psiElement\")\n            return null\n        }\n\n        var testFile = LocalFileSystem.getInstance().findFileByPath(testFilePath)\n        if (testFile != null) {\n            return AutoTestingPromptContext(false, testFile, emptyList(), null, language, null)\n        }\n\n        WriteCommandAction.writeCommandAction(sourceFile.project).withName(\"Generate Unit Tests\")\n            .compute<Unit, Throwable> {\n                val parentDir = VfsUtil.createDirectoryIfMissing(Path(testFilePath).parent.toString())\n                testFile = parentDir?.createChildData(this, Path(testFilePath).fileName.toString())\n            }\n\n        val underTestObj = ReadAction.compute<String, Throwable> {\n            val underTestObj = JavaScriptClassStructureProvider()\n                .build(elementToTest, false)?.format()\n\n            if (underTestObj == null) {\n                val funcObj = JavaScriptMethodStructureProvider()\n                    .build(elementToTest, false, false)?.format()\n\n                return@compute funcObj ?: \"\"\n            } else {\n                return@compute underTestObj\n            }\n        }\n\n        val imports: List<String> = (sourceFile as? JSFile)?.let {\n            PsiTreeUtil.findChildrenOfType(it, JSImportStatement::class.java)\n        }?.map {\n            it.text\n        } ?: emptyList()\n\n        return AutoTestingPromptContext(true, testFile!!, emptyList(), elementName, language, underTestObj, imports)\n    }\n\n    override fun lookupRelevantClass(project: Project, element: PsiElement): List<ClassStructure> {\n        return JSTypeResolver.resolveByElement(element).mapNotNull {\n            JavaScriptClassStructureProvider().build(it, false)\n        }\n    }\n}"
  },
  {
    "path": "languages/shire-javascript/src/main/kotlin/com/phodal/shirelang/javascript/codeedit/JSFileRunService.kt",
    "content": "package com.phodal.shirelang.javascript.codeedit\n\nimport com.intellij.execution.RunManager\nimport com.intellij.execution.configurations.RunConfiguration\nimport com.intellij.execution.configurations.RunProfile\nimport com.intellij.lang.javascript.psi.JSFile\nimport com.intellij.openapi.application.runReadAction\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.intellij.psi.PsiManager\nimport com.jetbrains.nodejs.run.NodeJsRunConfiguration\nimport com.jetbrains.nodejs.run.NodeJsRunConfigurationType\nimport com.phodal.shirecore.provider.shire.FileRunService\n\nclass JSFileRunService : FileRunService {\n    override fun isApplicable(project: Project, file: VirtualFile): Boolean {\n        return PsiManager.getInstance(project).findFile(file) is JSFile && file.name.endsWith(\".js\")\n    }\n\n    override fun runConfigurationClass(project: Project): Class<out RunProfile>? {\n        return NodeJsRunConfiguration::class.java\n    }\n\n    override fun createConfiguration(project: Project, virtualFile: VirtualFile): RunConfiguration? {\n        val configurationSetting = runReadAction {\n            val runManager = RunManager.getInstance(project)\n            val configurationType = NodeJsRunConfigurationType.getInstance()\n            val configuration = runManager.createConfiguration(\"Node.js\", configurationType.configurationFactories[0])\n            runManager.addConfiguration(configuration)\n            configuration\n        }\n\n        val runConfiguration = configurationSetting.configuration as NodeJsRunConfiguration\n        runConfiguration.name = virtualFile.nameWithoutExtension\n        runConfiguration.mainScriptFilePath = virtualFile.path\n        runConfiguration.workingDirectory = virtualFile.parent.path\n        runConfiguration.nodeOptions = \"--harmony\"\n\n        return runConfiguration\n    }\n}\n"
  },
  {
    "path": "languages/shire-javascript/src/main/kotlin/com/phodal/shirelang/javascript/codeedit/JavaScriptTestCodeModifier.kt",
    "content": "package com.phodal.shirelang.javascript.codeedit\n\nimport com.intellij.lang.Language\nimport com.intellij.lang.javascript.psi.JSFile\nimport com.intellij.lang.javascript.psi.impl.JSPsiElementFactory\nimport com.intellij.openapi.application.runReadAction\nimport com.intellij.openapi.command.WriteCommandAction\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.PsiFileFactory\nimport com.intellij.psi.PsiManager\nimport com.phodal.shirecore.provider.codeedit.CodeModifier\nimport com.phodal.shirelang.javascript.util.LanguageApplicableUtil\n\nopen class JavaScriptTestCodeModifier : CodeModifier {\n    override fun isApplicable(language: Language): Boolean {\n        return LanguageApplicableUtil.isJavaScriptApplicable(language)\n    }\n\n    override fun smartInsert(\n        sourceFile: VirtualFile,\n        project: Project,\n        code: String,\n    ): PsiElement? {\n        if (sourceFile !is JSFile) {\n            return insertClass(sourceFile, project, code)\n        }\n\n        return insertMethod(sourceFile, project, code)\n    }\n\n    override fun insertTestCode(sourceFile: VirtualFile, project: Project, code: String): PsiElement? {\n        if (sourceFile !is JSFile) return insertClass(sourceFile, project, code)\n        return insertMethod(sourceFile, project, code)\n    }\n\n    override fun insertMethod(sourceFile: VirtualFile, project: Project, code: String): PsiElement? {\n        // todo: spike for insert different method type, like named function, arrow function, etc.\n        val jsFile = PsiManager.getInstance(project).findFile(sourceFile) as JSFile\n        val psiElement = jsFile.lastChild\n\n        val element = PsiFileFactory.getInstance(project).createFileFromText(jsFile.language, \"\")\n        val codeElement = JSPsiElementFactory.createJSStatement(code, element)\n\n        return runReadAction {\n            psiElement?.parent?.addAfter(codeElement, psiElement)\n        }\n    }\n\n    override fun insertClass(sourceFile: VirtualFile, project: Project, code: String): PsiElement? {\n        return WriteCommandAction.runWriteCommandAction<PsiElement?>(project) {\n            val psiFile = PsiManager.getInstance(project).findFile(sourceFile) as JSFile\n            val document = psiFile.viewProvider.document!!\n            document.insertString(document.textLength, code)\n            psiFile.lastChild\n        }\n    }\n\n}\n"
  },
  {
    "path": "languages/shire-javascript/src/main/kotlin/com/phodal/shirelang/javascript/codeedit/JestCodeModifier.kt",
    "content": "package com.phodal.shirelang.javascript.codeedit\n\nimport com.intellij.lang.Language\nimport com.phodal.shirelang.javascript.util.LanguageApplicableUtil\n\nclass JestCodeModifier : JavaScriptTestCodeModifier() {\n    override fun isApplicable(language: Language): Boolean {\n        return LanguageApplicableUtil.isJavaScriptApplicable(language)\n    }\n}"
  },
  {
    "path": "languages/shire-javascript/src/main/kotlin/com/phodal/shirelang/javascript/codemodel/JavaScriptClassStructureProvider.kt",
    "content": "package com.phodal.shirelang.javascript.codemodel\n\nimport com.intellij.lang.javascript.psi.ecmal4.JSClass\nimport com.intellij.openapi.application.runReadAction\nimport com.intellij.psi.*\nimport com.intellij.psi.search.GlobalSearchScope\nimport com.intellij.psi.search.searches.ReferencesSearch\nimport com.phodal.shirecore.provider.codemodel.ClassStructureProvider\nimport com.phodal.shirecore.provider.codemodel.model.ClassStructure\n\nclass JavaScriptClassStructureProvider : ClassStructureProvider {\n    override fun build(psiElement: PsiElement, gatherUsages: Boolean): ClassStructure? {\n        when (psiElement) {\n            is JSClass -> {\n                val methods: List<PsiElement> = psiElement.functions.toList()\n                val fields: List<PsiElement> = psiElement.fields.toList()\n\n                val usages =\n                    if (gatherUsages) findUsages(psiElement as PsiNameIdentifierOwner) else emptyList()\n\n                val supers = psiElement.supers\n                val superClasses = supers.filterIsInstance<JSClass>().mapNotNull { it.name }\n\n                val annotations: List<String> = mutableListOf()\n\n                return ClassStructure(\n                    psiElement,\n                    psiElement.text,\n                    psiElement.name,\n                    displayName = runReadAction { psiElement.qualifiedName },\n                    methods,\n                    fields,\n                    superClasses,\n                    annotations,\n                    usages\n                )\n            }\n            else -> return null\n        }\n    }\n\n    companion object {\n        fun findUsages(psiElement: PsiElement): List<PsiReference> {\n            val globalSearchScope = GlobalSearchScope.allScope(psiElement.project)\n\n            return ReferencesSearch.search(psiElement, globalSearchScope, true)\n                .findAll()\n                .toList()\n        }\n    }\n}"
  },
  {
    "path": "languages/shire-javascript/src/main/kotlin/com/phodal/shirelang/javascript/codemodel/JavaScriptFileStructureProvider.kt",
    "content": "package com.phodal.shirelang.javascript.codemodel\n\nimport com.intellij.lang.ecmascript6.psi.impl.ES6ImportPsiUtil\nimport com.intellij.lang.javascript.psi.JSFunction\nimport com.intellij.lang.javascript.psi.ecmal4.JSClass\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.PsiFile\nimport com.intellij.psi.util.PsiTreeUtil\nimport com.phodal.shirecore.provider.codemodel.FileStructureProvider\nimport com.phodal.shirecore.provider.codemodel.model.FileStructure\nimport com.phodal.shirecore.relativePath\n\nclass JavaScriptFileStructureProvider : FileStructureProvider {\n    override fun build(psiFile: PsiFile): FileStructure? {\n        val file = if (psiFile.virtualFile != null) psiFile.virtualFile!!.relativePath(psiFile.project) else \"\"\n        val importDeclarations = ES6ImportPsiUtil.getImportDeclarations((psiFile as PsiElement))\n        val classes =\n            PsiTreeUtil.getChildrenOfTypeAsList(psiFile as PsiElement, JSClass::class.java)\n        val functions =\n            PsiTreeUtil.getChildrenOfTypeAsList(psiFile as PsiElement, JSFunction::class.java)\n\n        return FileStructure(\n            psiFile,\n            psiFile.name,\n            file,\n            null,\n            importDeclarations,\n            classes,\n            functions\n        )\n    }\n}"
  },
  {
    "path": "languages/shire-javascript/src/main/kotlin/com/phodal/shirelang/javascript/codemodel/JavaScriptMethodStructureProvider.kt",
    "content": "package com.phodal.shirelang.javascript.codemodel\n\nimport com.intellij.lang.javascript.presentable.JSFormatUtil\nimport com.intellij.lang.javascript.psi.JSFunction\nimport com.intellij.lang.javascript.psi.JSType\nimport com.intellij.lang.javascript.psi.util.JSUtils\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.PsiNameIdentifierOwner\nimport com.phodal.shirecore.provider.codemodel.MethodStructureProvider\nimport com.phodal.shirecore.provider.codemodel.model.MethodStructure\n\nclass JavaScriptMethodStructureProvider : MethodStructureProvider {\n    override fun build(psiElement: PsiElement, includeClassContext: Boolean, gatherUsages: Boolean): MethodStructure? {\n        if (psiElement !is JSFunction) return null\n\n        val functionSignature = JSFormatUtil.buildFunctionSignaturePresentation(psiElement)\n        val containingClass: PsiElement? = JSUtils.getMemberContainingClass(psiElement)\n        val languageDisplayName = psiElement.language.displayName\n        val returnType = psiElement.returnType\n        val returnTypeText = returnType?.substitute()?.getTypeText(JSType.TypeTextFormat.CODE)\n\n        val parameterNames = psiElement.parameters.mapNotNull { it.name }\n\n        val usages =\n            if (gatherUsages) JavaScriptClassStructureProvider.findUsages(psiElement as PsiNameIdentifierOwner) else emptyList()\n\n        return MethodStructure(\n            psiElement, psiElement.text, psiElement.name!!, psiElement.name + functionSignature, containingClass, languageDisplayName,\n            returnTypeText, parameterNames, includeClassContext, usages\n        )\n    }\n}"
  },
  {
    "path": "languages/shire-javascript/src/main/kotlin/com/phodal/shirelang/javascript/codemodel/JavaScriptVariableStructureProvider.kt",
    "content": "package com.phodal.shirelang.javascript.codemodel\n\nimport com.intellij.lang.javascript.psi.JSFieldVariable\nimport com.intellij.lang.javascript.psi.JSFunction\nimport com.intellij.lang.javascript.psi.util.JSUtils\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.PsiNameIdentifierOwner\nimport com.intellij.psi.PsiReference\nimport com.intellij.psi.util.PsiTreeUtil\nimport com.phodal.shirecore.provider.codemodel.VariableStructureProvider\nimport com.phodal.shirecore.provider.codemodel.model.VariableStructure\n\nclass JavaScriptVariableStructureProvider : VariableStructureProvider {\n    override fun build(\n        psiElement: PsiElement,\n        withMethodContext: Boolean,\n        withClassContext: Boolean,\n        gatherUsages: Boolean\n    ): VariableStructure? {\n        if (psiElement !is JSFieldVariable) {\n            return null\n        }\n\n        val parentOfType: PsiElement? = PsiTreeUtil.getParentOfType(psiElement, JSFunction::class.java, true)\n        val memberContainingClass: PsiElement = JSUtils.getMemberContainingClass(psiElement)\n        val psiReferences: List<PsiReference> = if (gatherUsages) {\n            JavaScriptClassStructureProvider.findUsages(psiElement as PsiNameIdentifierOwner)\n        } else {\n            emptyList()\n        }\n\n        return VariableStructure(\n            psiElement,\n            psiElement.text,\n            psiElement.name!!,\n            parentOfType,\n            memberContainingClass,\n            psiReferences,\n            withMethodContext,\n            withClassContext\n        )\n    }\n}"
  },
  {
    "path": "languages/shire-javascript/src/main/kotlin/com/phodal/shirelang/javascript/framework/ReactPage.kt",
    "content": "package com.phodal.shirelang.javascript.framework\n\nimport com.intellij.lang.javascript.JavaScriptFileType\nimport com.intellij.lang.javascript.dialects.ECMA6LanguageDialect\nimport com.intellij.lang.javascript.dialects.TypeScriptJSXLanguageDialect\nimport com.intellij.lang.javascript.psi.JSFile\nimport com.intellij.openapi.diagnostic.logger\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.project.guessProjectDir\nimport com.intellij.psi.PsiManager\nimport com.intellij.psi.search.FileTypeIndex\nimport com.intellij.psi.search.GlobalSearchScope\nimport com.intellij.psi.search.ProjectScope\nimport com.phodal.shirecore.variable.frontend.Component\nimport com.phodal.shirecore.variable.frontend.ComponentProvider\nimport kotlinx.serialization.json.Json\n\nenum class RouterFile(val filename: String) {\n    UMI(\".umirc.ts\"),\n    NEXT(\"next.config.js\"),\n    VITE(\"vite.config.js\"),\n}\n\nclass ReactPage(private val project: Project): ComponentProvider {\n    private val logger = logger<ReactPage>()\n    private val routes: MutableMap<RouterFile, JSFile> = mutableMapOf()\n    private val pages: MutableList<Component> = mutableListOf()\n    private val components: MutableList<Component> = mutableListOf()\n\n    // config files\n    private val configs: MutableList<JSFile> = mutableListOf()\n\n    init {\n        val searchScope: GlobalSearchScope = ProjectScope.getContentScope(project)\n        val psiManager = PsiManager.getInstance(project)\n\n        val virtualFiles =\n            FileTypeIndex.getFiles(JavaScriptFileType.INSTANCE, searchScope)\n//                    FileTypeIndex.getFiles(TypeScriptJSXFileType.INSTANCE, searchScope)\n//                    FileTypeIndex.getFiles(JSXHarmonyFileType.INSTANCE, searchScope)\n\n        val root = project.guessProjectDir()!!\n\n        virtualFiles.forEach { file ->\n            val path = file.canonicalFile?.path ?: return@forEach\n\n            val jsFile = (psiManager.findFile(file) ?: return@forEach) as? JSFile ?: return@forEach\n            if (jsFile.isTestFile) return@forEach\n\n            when {\n                path.contains(\"pages\") -> buildComponent(jsFile)?.let {\n                    pages += it\n                }\n\n                path.contains(\"components\") -> buildComponent(jsFile)?.let {\n                    components += it\n                }\n\n                else -> {\n                    if (root.findChild(file.name) != null) {\n                        RouterFile.entries.filter { it.filename == file.name }.map {\n                            routes += it to jsFile\n                        }\n\n                        configs.add(jsFile)\n                    }\n                }\n            }\n        }\n    }\n\n    override fun getPages(): List<Component> = pages\n\n    override fun getComponents(): List<Component> = components\n\n    private fun buildComponent(jsFile: JSFile): List<Component>? {\n        return when (jsFile.language) {\n            is TypeScriptJSXLanguageDialect,\n            is ECMA6LanguageDialect,\n                -> {\n//                val Components = ReactPsiUtil.tsxComponentToComponent(jsFile)\n//                if (Components.isEmpty()) {\n//                    logger.warn(\"no component found in ${jsFile.name}\")\n//                }\n//                Components\n                null\n            }\n\n            else -> {\n                logger.warn(\"unknown language: ${jsFile.language}\")\n                null\n            }\n        }\n    }\n\n    override fun getRoutes(): Map<String, String> {\n        return this.routes.map {\n            when (it.key) {\n                RouterFile.UMI -> emptyMap()\n                RouterFile.NEXT -> {\n                    pages.associate { page ->\n                        val route = page.name.replace(Regex(\"([A-Z])\"), \"-$1\").lowercase()\n                        route to route\n                    }\n                }\n\n                RouterFile.VITE -> emptyMap()\n            }\n        }.reduce { acc, map -> acc + map }\n    }\n\n    /**\n     * Retrieves a list of design system components from the ds.json file located in the prompts/context directory.\n     * The method first attempts to locate the ds.json file by traversing the project directory structure.\n     * If the file is found, it reads its content and decodes it as a JSON string into a list of [Component] objects.\n     * In case of any exception during the reading or decoding process, an empty list is returned.\n     *\n     * @return a list of design system components parsed from the ds.json file, or an empty list if the file is not found\n     *         or an error occurs during parsing.\n     */\n    fun getDesignSystemComponents(): List<Component> {\n        val rootConfig = project.guessProjectDir()\n            ?.findChild(\"prompts\")\n            ?.findChild(\"context\")\n            ?.findChild(\"ds.json\") ?: return emptyList()\n\n        val json = rootConfig.inputStream.reader().readText()\n        return try {\n            val result: List<Component> = Json.decodeFromString(json)\n            result\n        } catch (e: Exception) {\n            emptyList()\n        }\n    }\n\n    fun filterComponents(components: List<String>): List<Component> {\n        val comps = this.pages + this.components\n        return components.mapNotNull { component ->\n            comps.find { it.name == component }\n        }\n    }\n}\n\n"
  },
  {
    "path": "languages/shire-javascript/src/main/kotlin/com/phodal/shirelang/javascript/framework/ReactPsiUtil.kt",
    "content": "package com.phodal.shirelang.javascript.framework\n\nimport com.intellij.lang.ecmascript6.psi.ES6ExportDeclaration\nimport com.intellij.lang.ecmascript6.psi.ES6ExportDefaultAssignment\nimport com.intellij.lang.javascript.presentable.JSFormatUtil\nimport com.intellij.lang.javascript.psi.JSFile\nimport com.intellij.lang.javascript.psi.JSFunctionExpression\nimport com.intellij.lang.javascript.psi.JSReferenceExpression\nimport com.intellij.lang.javascript.psi.JSVariable\nimport com.intellij.lang.javascript.psi.ecma6.TypeScriptClass\nimport com.intellij.lang.javascript.psi.ecma6.TypeScriptFunction\nimport com.intellij.lang.javascript.psi.ecma6.TypeScriptSingleType\nimport com.intellij.lang.javascript.psi.ecma6.TypeScriptVariable\nimport com.intellij.lang.javascript.psi.resolve.JSResolveResult\nimport com.intellij.psi.PsiNameIdentifierOwner\nimport com.intellij.psi.util.PsiTreeUtil\nimport com.phodal.shirecore.variable.frontend.Component\n\nobject ReactPsiUtil {\n    private fun getExportElements(file: JSFile): List<PsiNameIdentifierOwner> {\n        val exportDeclarations =\n            PsiTreeUtil.getChildrenOfTypeAsList(file, ES6ExportDeclaration::class.java)\n\n        val map = exportDeclarations.map { exportDeclaration ->\n            exportDeclaration.exportSpecifiers\n                .asSequence()\n                .mapNotNull {\n                    it.resolve()?.originalElement ?: it.alias?.findAliasedElement()\n                }\n                .filterIsInstance<PsiNameIdentifierOwner>()\n                .toList()\n        }.flatten()\n\n        val defaultAssignments = PsiTreeUtil.getChildrenOfTypeAsList(file, ES6ExportDefaultAssignment::class.java)\n        val defaultAssignment = defaultAssignments.mapNotNull {\n            val jsReferenceExpression = it.expression as? JSReferenceExpression ?: return@mapNotNull null\n            val resolveReference = JSResolveResult.resolveReference(jsReferenceExpression)\n            resolveReference.firstOrNull() as? PsiNameIdentifierOwner\n        }\n\n        return map + defaultAssignment\n    }\n\n//    fun tsxComponentToComponent(jsFile: JSFile): List<Component> = getExportElements(jsFile).map { psiElement ->\n//        val name = psiElement.name ?: return@map null\n//\n//        val projectPath = jsFile.project.basePath ?: \"\"\n//        val path = jsFile.virtualFile.path.removePrefix(projectPath)\n//            .replace(\"\\\\\", \"/\")\n//            .removePrefix(\"/\")\n//\n//        return@map when (psiElement) {\n//            is TypeScriptFunction -> Component(name = name, path)\n//            is TypeScriptClass -> Component(name = name, path)\n//            is TypeScriptVariable, is JSVariable -> {\n//                val funcExpr = PsiTreeUtil.findChildrenOfType(psiElement, JSFunctionExpression::class.java)\n//                    .firstOrNull() ?: return@map null\n//\n//                val signature = JSFormatUtil.buildFunctionSignaturePresentation(funcExpr)\n//                val props: List<String> = funcExpr.parameterList?.parameters?.mapNotNull { parameter ->\n//                    val typeElement = parameter.typeElement ?: return@mapNotNull null\n//                    when (typeElement) {\n//                        is TypeScriptSingleType -> {\n//                            val resolve = typeElement.referenceExpression?.resolve()\n//                            resolve?.text\n//                        }\n//\n//                        else -> null\n//                    }\n//                } ?: emptyList()\n//\n//                Component(name = name, path, props = props, signature = signature)\n//            }\n//\n//            else -> null\n//        }\n//    }.filterNotNull()\n}"
  },
  {
    "path": "languages/shire-javascript/src/main/kotlin/com/phodal/shirelang/javascript/impl/JavaScriptBuildSystemProvider.kt",
    "content": "package com.phodal.shirelang.javascript.impl\n\nimport com.intellij.lang.javascript.buildTools.npm.NpmScriptsUtil\nimport com.intellij.lang.javascript.buildTools.npm.PackageJsonUtil\nimport com.intellij.openapi.application.runReadAction\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.project.guessProjectDir\nimport com.phodal.shirecore.provider.context.BuildSystemProvider\nimport com.phodal.shirecore.variable.toolchain.buildsystem.BuildSystemContext\nimport com.phodal.shirelang.javascript.util.JsDependenciesSnapshot\nimport com.phodal.shirelang.javascript.variable.JsWebFrameworks\n\nopen class JavaScriptBuildSystemProvider : BuildSystemProvider() {\n    override fun collect(project: Project): BuildSystemContext? {\n        val snapshot = JsDependenciesSnapshot.create(project, null)\n        if (snapshot.packageJsonFiles.isEmpty()) {\n            return null\n        }\n\n        var language = \"JavaScript\"\n        var languageVersion = \"ES5\"\n        val buildTool = \"NPM\"\n\n        val packageJson = snapshot.packages[\"typescript\"]\n        val tsVersion = packageJson?.parseVersion()\n        if (tsVersion != null) {\n            language = \"TypeScript\"\n            languageVersion = tsVersion.rawVersion\n        }\n\n        JsWebFrameworks.entries.forEach { framework ->\n            if (snapshot.packages[framework.packageName] != null) {\n                language += \" with ${framework.presentation}\"\n            }\n        }\n\n        var taskString = \"\"\n        runReadAction {\n            val root = PackageJsonUtil.findChildPackageJsonFile(project.guessProjectDir()) ?: return@runReadAction\n            NpmScriptsUtil.listTasks(project, root).scripts.forEach { task ->\n                taskString += task.name + \" \"\n            }\n        }\n\n        return BuildSystemContext(\n            buildToolName = buildTool,\n            buildToolVersion = \"\",\n            languageName = language,\n            languageVersion = languageVersion,\n            taskString = taskString,\n            libraries = snapshot.mostPopularFrameworks()\n        )\n    }\n}\n"
  },
  {
    "path": "languages/shire-javascript/src/main/kotlin/com/phodal/shirelang/javascript/impl/TypeScriptRefactoringTool.kt",
    "content": "package com.phodal.shirelang.javascript.impl\n\nimport com.intellij.codeInsight.daemon.impl.quickfix.RenameElementFix\nimport com.intellij.codeInsight.daemon.impl.quickfix.SafeDeleteFix\nimport com.intellij.codeInspection.MoveToPackageFix\nimport com.intellij.lang.javascript.JavaScriptFileType\nimport com.intellij.lang.javascript.psi.JSFile\nimport com.intellij.lang.javascript.psi.JSFunction\nimport com.intellij.lang.javascript.psi.ecmal4.JSClass\nimport com.intellij.openapi.project.ProjectManager\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.PsiFile\nimport com.intellij.psi.PsiManager\nimport com.intellij.psi.PsiNamedElement\nimport com.intellij.psi.search.FileTypeIndex\nimport com.intellij.psi.search.ProjectScope\nimport com.intellij.psi.util.PsiTreeUtil\nimport com.phodal.shirecore.provider.shire.RefactoringTool\nimport com.phodal.shirecore.variable.toolchain.refactoring.RefactorInstElement\n\n\nclass TypeScriptRefactoringTool : RefactoringTool {\n    private val identifierPattern = Regex(\"^[a-zA-Z_][a-zA-Z0-9_]*$\")\n\n    val project = ProjectManager.getInstance().openProjects.firstOrNull()\n\n    override fun lookupFile(path: String): PsiFile? {\n        if (project == null) return null\n\n        val searchScope = ProjectScope.getProjectScope(project)\n\n        val jsFiles = FileTypeIndex.getFiles(JavaScriptFileType.INSTANCE, searchScope)\n            .mapNotNull { PsiManager.getInstance(project).findFile(it) as? JSFile }\n\n        val tsFiles = FileTypeIndex.getFiles(JavaScriptFileType.INSTANCE, searchScope)\n            .mapNotNull { PsiManager.getInstance(project).findFile(it) as? JSFile }\n\n        val files = jsFiles + tsFiles\n\n        val sourceFile = files.firstOrNull {\n            it.virtualFile.path == path\n        } ?: return null\n\n        return sourceFile\n    }\n\n\n    /**\n     * Deletes the given PsiElement in a safe manner, ensuring that no syntax errors or unexpected behavior occur as a result.\n     * The method performs checks before deletion to confirm that it is safe to remove the element from the code structure.\n     *\n     * @param element The PsiElement to be deleted. This should be a valid element within the PSI tree structure.\n     * @return true if the element was successfully deleted without any issues, false otherwise. This indicates whether\n     * the deletion was performed and considered safe.\n     */\n    override fun safeDelete(element: PsiElement): Boolean {\n        val delete = SafeDeleteFix(element)\n        try {\n            delete.invoke(element.project, element.containingFile, element, element)\n        } catch (e: Exception) {\n            return false\n        }\n\n        return true\n    }\n\n    /**\n     * In Java the canonicalName is the fully qualified name of the target package.\n     * In Kotlin the canonicalName is the fully qualified name of the target package or class.\n     */\n    override fun move(element: PsiElement, canonicalName: String): Boolean {\n        val file = element.containingFile\n        val fix = MoveToPackageFix(file, canonicalName)\n\n        try {\n            fix.invoke(file.project, file, element, element)\n        } catch (e: Exception) {\n            return false\n        }\n\n        return true\n    }\n\n\n    private fun findNamedElement(psiFile: PsiFile?, elementInfo: RefactorInstElement): PsiNamedElement? {\n        return when (psiFile) {\n            is JSFile -> findElementInJSFile(psiFile, elementInfo)\n            else -> null\n        }\n    }\n\n    private fun findElementInJSFile(jsFile: JSFile, elementInfo: RefactorInstElement): PsiNamedElement? {\n        val classes = PsiTreeUtil.getChildrenOfTypeAsList(jsFile, JSClass::class.java)\n        val functions = PsiTreeUtil.getChildrenOfTypeAsList(jsFile, JSFunction::class.java)\n\n        return when {\n            elementInfo.isClass -> findClassByName(classes, elementInfo.className)\n            elementInfo.isMethod -> findMethodByName(classes, functions, elementInfo.methodName)\n            else -> null\n        }\n    }\n\n    private fun findClassByName(classes: List<JSClass>, className: String): PsiNamedElement? {\n        return classes.firstOrNull { it.name == className }\n    }\n\n    private fun findMethodByName(\n        classes: List<JSClass>,\n        functions: List<JSFunction>,\n        methodName: String\n    ): PsiNamedElement? {\n        return classes.firstNotNullOfOrNull {\n            it.findFunctionByName(methodName)\n        } ?: functions.firstOrNull { it.name == methodName }\n    }\n\n    override fun rename(sourceName: String, targetName: String, psiFile: PsiFile?): Boolean {\n        if (project == null) return false\n        // if targetElement is not a valid function name, return false\n        if (!identifierPattern.matches(targetName)) {\n            return false\n        }\n\n        val elementInfo = getElementInfo(sourceName, psiFile) ?: return false\n        val element = findNamedElement(psiFile, elementInfo) ?: return false\n\n        try {\n            var target = targetName\n            if (element is JSFile) {\n                target += element.name.substringAfterLast(\".\")\n            }\n\n            RenameElementFix(element, target)\n                .invoke(project, element.containingFile, element, element)\n\n            performRefactoringRename(project, element, targetName)\n        } catch (e: Exception) {\n            return false\n        }\n\n        return false\n    }\n\n    private fun getElementInfo(input: String, psiFile: PsiFile?): RefactorInstElement? {\n        if (!input.contains(\"#\") && psiFile != null) {\n            val jsFile = psiFile as? JSFile ?: return null\n            // check input name is uppercase\n            val isClass = input[0].isUpperCase()\n            val isMethod = input[0].isLowerCase()\n\n            return RefactorInstElement(isClass, isMethod, input, input, input, jsFile.name)\n        }\n\n        val isMethod = input.contains(\"#\")\n        val methodName = input.substringAfter(\"#\")\n        val canonicalName = input.substringBefore(\"#\")\n        val maybeClassName = canonicalName.substringAfterLast(\".\")\n        // the clasName should be Uppercase or it will be the package\n        var isClass = false\n        var pkgName = canonicalName.substringBeforeLast(\".\")\n        if (maybeClassName[0].isLowerCase()) {\n            pkgName = \"$pkgName.$maybeClassName\"\n        } else {\n            isClass = true\n        }\n\n        return RefactorInstElement(isClass, isMethod, methodName, canonicalName, maybeClassName, pkgName)\n    }\n\n}\n"
  },
  {
    "path": "languages/shire-javascript/src/main/kotlin/com/phodal/shirelang/javascript/provider/JavaScriptRelatedClassesProvider.kt",
    "content": "package com.phodal.shirelang.javascript.provider\n\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.PsiFile\nimport com.phodal.shirecore.provider.psi.RelatedClassesProvider\nimport com.phodal.shirelang.javascript.JSTypeResolver\n\nclass JavaScriptRelatedClassesProvider : RelatedClassesProvider {\n    override fun lookup(element: PsiElement): List<PsiElement> {\n        return JSTypeResolver.resolveByElement(element)\n    }\n\n    override fun lookup(element: PsiFile): List<PsiElement> {\n        return JSTypeResolver.resolveByElement(element)\n    }\n}\n"
  },
  {
    "path": "languages/shire-javascript/src/main/kotlin/com/phodal/shirelang/javascript/util/JSPsiUtil.kt",
    "content": "package com.phodal.shirelang.javascript.util\n\nimport com.intellij.lang.ecmascript6.psi.ES6ExportDeclaration\nimport com.intellij.lang.ecmascript6.psi.ES6ExportDefaultAssignment\nimport com.intellij.lang.javascript.frameworks.commonjs.CommonJSUtil\nimport com.intellij.lang.javascript.psi.*\nimport com.intellij.lang.javascript.psi.ecma6.TypeScriptGenericOrMappedTypeParameter\nimport com.intellij.lang.javascript.psi.ecmal4.JSAttributeList\nimport com.intellij.lang.javascript.psi.ecmal4.JSAttributeListOwner\nimport com.intellij.lang.javascript.psi.ecmal4.JSClass\nimport com.intellij.lang.javascript.psi.ecmal4.JSQualifiedNamedElement\nimport com.intellij.lang.javascript.psi.resolve.JSResolveResult\nimport com.intellij.lang.javascript.psi.stubs.JSImplicitElement\nimport com.intellij.lang.javascript.psi.util.JSDestructuringUtil\nimport com.intellij.lang.javascript.psi.util.JSStubBasedPsiTreeUtil\nimport com.intellij.lang.javascript.psi.util.JSUtils\nimport com.intellij.openapi.application.ReadAction\nimport com.intellij.openapi.application.runReadAction\nimport com.intellij.openapi.diagnostic.logger\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.roots.ProjectFileIndex\nimport com.intellij.psi.*\nimport com.intellij.psi.util.PsiTreeUtil\nimport com.intellij.psi.util.parents\nimport com.phodal.shirecore.project.isInProject\nimport java.io.File\nimport java.nio.file.Path\n\nobject JSPsiUtil {\n    fun resolveReference(node: JSReferenceExpression, scope: PsiElement): PsiElement? {\n        val resolveReference = JSResolveResult.resolveReference(node)\n        var resolved = resolveReference.firstOrNull() as? JSImplicitElement\n\n        if (resolved != null) {\n            resolved = resolved.parent as? JSImplicitElement\n        }\n\n        if (resolved is JSFunction && resolved.isConstructor) {\n            resolved = JSUtils.getMemberContainingClass(resolved) as? JSImplicitElement\n        }\n\n        if (resolved == null || skipDeclaration(resolved)) {\n            return null\n        }\n\n        val virtualFile = resolved.containingFile?.virtualFile\n\n        if (virtualFile == null ||\n            !node.project.isInProject(virtualFile) ||\n            ProjectFileIndex.getInstance(node.project).isInLibrary(virtualFile)\n        ) {\n            return JSStubBasedPsiTreeUtil.resolveReferenceLocally(node as PsiPolyVariantReference, node.referenceName)\n        }\n\n        val jSImplicitElement = resolved\n\n        return if (jSImplicitElement.textLength == 0 || !PsiTreeUtil.isAncestor(scope, jSImplicitElement, true)) {\n            jSImplicitElement\n        } else {\n            null\n        }\n    }\n\n    private fun skipDeclaration(element: PsiElement): Boolean {\n        return when (element) {\n            is JSParameter, is TypeScriptGenericOrMappedTypeParameter -> true\n            is JSField -> {\n                element.initializerOrStub !is JSFunctionExpression\n            }\n\n            is JSVariable -> {\n                var initializer = JSDestructuringUtil.getNearestDestructuringInitializer(element)\n                if (initializer == null) {\n                    initializer = element.initializerOrStub ?: return true\n                }\n\n                !(initializer is JSCallExpression\n                        || initializer is JSFunctionExpression\n                        || initializer is JSObjectLiteralExpression\n                        )\n            }\n\n            else -> false\n        }\n    }\n\n    fun isExportedFileFunction(element: PsiElement): Boolean {\n        when (val parent = element.parent) {\n            is JSFile, is JSEmbeddedContent -> {\n                return when (element) {\n                    is JSVarStatement -> {\n                        val variables = element.variables\n                        val variable = variables.firstOrNull() ?: return false\n                        variable.initializerOrStub is JSFunction && exported(variable)\n                    }\n\n                    is JSFunction -> exported(element)\n                    else -> false\n                }\n            }\n\n            is JSVariable -> {\n                val varStatement = parent.parent as? JSVarStatement ?: return false\n                return varStatement.parent is JSFile && exported(parent)\n            }\n\n            else -> {\n                return parent is ES6ExportDefaultAssignment\n            }\n        }\n    }\n\n    fun isExportedClass(elementForTests: PsiElement?): Boolean {\n        return elementForTests is JSClass && elementForTests.isExported\n    }\n\n    fun isExportedClassPublicMethod(psiElement: PsiElement): Boolean {\n        val jsClass = PsiTreeUtil.getParentOfType(psiElement, JSClass::class.java, true) ?: return false\n        if (!exported(jsClass as PsiElement)) return false\n\n        val parentElement = psiElement.parents(true).firstOrNull() ?: return false\n        if (isPrivateMember(parentElement)) return false\n\n        return when (parentElement) {\n            is JSFunction -> !parentElement.isConstructor\n            is JSVarStatement -> {\n                val variables = parentElement.variables\n                val jSVariable = variables.firstOrNull()\n                (jSVariable?.initializerOrStub as? JSFunction) != null\n            }\n\n            else -> false\n        }\n    }\n\n    private fun exported(element: PsiElement): Boolean {\n        if (element !is JSElementBase) return false\n\n        if (element.isExported || element.isExportedWithDefault) {\n            return true\n        }\n\n        if (element is JSPsiElementBase && CommonJSUtil.isExportedWithModuleExports(element)) {\n            return true\n        }\n\n        val containingFile = element.containingFile ?: return false\n        val exportDeclarations =\n            PsiTreeUtil.getChildrenOfTypeAsList(containingFile, ES6ExportDeclaration::class.java)\n\n        return exportDeclarations.any { exportDeclaration ->\n            exportDeclaration.exportSpecifiers\n                .asSequence()\n                .any { it.alias?.findAliasedElement() == element }\n        }\n    }\n\n    fun elementName(psiElement: PsiElement): String? {\n        if (psiElement !is JSVarStatement) {\n            if (psiElement !is JSNamedElement) return null\n\n            return psiElement.name\n        }\n\n        val jSVariable = psiElement.variables.firstOrNull() ?: return null\n        return jSVariable.name\n    }\n\n    /**\n     * Determines whether the given [element] is a private member.\n     *\n     * @param element the PSI element to check\n     * @return `true` if the element is a private member, `false` otherwise\n     */\n    private fun isPrivateMember(element: PsiElement): Boolean {\n        if (element is JSQualifiedNamedElement && element.isPrivateName) {\n            return true\n        }\n\n        if (element !is JSAttributeListOwner) return false\n\n        val attributeList = element.attributeList\n        return attributeList?.accessType == JSAttributeList.AccessType.PRIVATE\n    }\n\n    /**\n     * In JavaScript/TypeScript a testable element is a function, a class or a variable.\n     *\n     * Function:\n     * ```javascript\n     * function testableFunction() {}\n     * export testableFunction\n     * ```\n     *\n     * Class:\n     * ```javascript\n     * export class TestableClass {}\n     * ```\n     *\n     * Variable:\n     * ```javascript\n     * var functionA = function() {}\n     * export functionA\n     * ```\n     */\n    fun getElementToTest(psiElement: PsiElement): PsiElement? {\n        if (psiElement is JSFile) return psiElement\n        if (psiElement is JSClass) return psiElement\n\n        val jsFunc = PsiTreeUtil.getParentOfType(psiElement, JSFunction::class.java, false)\n        val jsVarStatement = PsiTreeUtil.getParentOfType(psiElement, JSVarStatement::class.java, false)\n        val jsClazz = PsiTreeUtil.getParentOfType(psiElement, JSClass::class.java, false)\n\n        val elementForTests: PsiElement? = when {\n            jsFunc != null -> jsFunc\n            jsVarStatement != null -> jsVarStatement\n            jsClazz != null -> jsClazz\n            else -> null\n        }\n\n        if (elementForTests == null) return null\n\n        return when {\n            isExportedClassPublicMethod(elementForTests) -> elementForTests\n            isExportedFileFunction(elementForTests) -> elementForTests\n            isExportedClass(elementForTests) -> elementForTests\n            else -> {\n                null\n            }\n        }\n    }\n\n    fun getTestFilePath(element: PsiElement): Path? {\n        val testDirectory = suggestTestDirectory(element)\n        if (testDirectory == null) {\n            logger<JSPsiUtil>().warn(\"Failed to find test directory for: $element\")\n            return null\n        }\n\n        val containingFile: PsiFile = runReadAction { element.containingFile } ?: return null\n        val extension = containingFile.virtualFile?.extension ?: return null\n        val elementName = elementName(element) ?: return null\n        val testFile: Path = generateUniqueTestFile(elementName, containingFile, testDirectory, extension).toPath()\n        return testFile\n    }\n\n    /**\n     * Todo: since in JavaScript has different test framework, we need to find the test directory by the framework.\n     */\n    fun suggestTestDirectory(element: PsiElement): PsiDirectory? =\n        ReadAction.compute<PsiDirectory?, Throwable> {\n            val project: Project = element.project\n            val elementDirectory = element.containingFile\n\n            val parentDir = elementDirectory?.virtualFile?.parent ?: return@compute null\n            val psiManager = PsiManager.getInstance(project)\n\n            val findDirectory = psiManager.findDirectory(parentDir)\n            if (findDirectory != null) {\n                return@compute findDirectory\n            }\n\n            val createChildDirectory = parentDir.createChildDirectory(this, \"test\")\n            return@compute psiManager.findDirectory(createChildDirectory)\n        }\n\n    fun generateUniqueTestFile(\n        elementName: String?,\n        containingFile: PsiFile,\n        testDirectory: PsiDirectory,\n        extension: String,\n    ): File {\n        val testPath = testDirectory.virtualFile.path\n        val prefix = elementName ?: containingFile.name.substringBefore('.', \"\")\n        val nameCandidate = \"$prefix.test.$extension\"\n        var testFile = File(testPath, nameCandidate)\n\n        var i = 1\n        while (testFile.exists()) {\n            val nameCandidateWithIndex = \"$prefix${i}.test.$extension\"\n            i++\n            testFile = File(testPath, nameCandidateWithIndex)\n        }\n\n        return testFile\n    }\n}\n"
  },
  {
    "path": "languages/shire-javascript/src/main/kotlin/com/phodal/shirelang/javascript/util/JsDependeciesSnapshot.kt",
    "content": "package com.phodal.shirelang.javascript.util\n\n\nimport com.intellij.javascript.nodejs.PackageJsonData\nimport com.intellij.javascript.nodejs.packageJson.PackageJsonFileManager\nimport com.intellij.lang.javascript.buildTools.npm.PackageJsonUtil\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.project.guessProjectDir\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.intellij.psi.PsiFile\nimport com.phodal.shirelang.javascript.variable.MOST_POPULAR_PACKAGES\n\n/**\n * Represents a snapshot of JavaScript dependencies in a Kotlin language project.\n *\n * This class provides information about the JavaScript dependencies in the project, including the package.json files,\n * whether the package.json files have been resolved, the tsconfig.json files, and the packages defined in the package.json files.\n *\n * @property packageJsonFiles The set of package.json files in the project.\n * @property resolvedPackageJson A flag indicating whether the package.json files have been resolved.\n * @property tsConfigs The set of tsconfig.json files in the project.\n * @property packages The map of package names to their corresponding PackageJsonDependencyEntry objects.\n */\nclass JsDependenciesSnapshot(\n    val packageJsonFiles: Set<VirtualFile>,\n    private val resolvedPackageJson: Boolean,\n    private val tsConfigs: Set<VirtualFile>,\n    val packages: Map<String, PackageJsonData.PackageJsonDependencyEntry>\n) {\n    fun mostPopularFrameworks(): List<String> {\n        val dependencies = this.packages\n            .asSequence()\n            .filter { entry -> MOST_POPULAR_PACKAGES.contains(entry.key) && !entry.key.startsWith(\"@type\") }\n            .map { entry ->\n                val dependency = entry.key\n                val version = entry.value.parseVersion()\n                if (version != null) \"$dependency: $version\" else dependency\n            }\n            .toList()\n        return dependencies\n    }\n\n    fun language(): String {\n        var language = \"JavaScript\"\n        var languageVersion = \"ES5\"\n\n        val packageJson = this.packages[\"typescript\"]\n        val tsVersion = packageJson?.parseVersion()\n        if (tsVersion != null) {\n            language = \"TypeScript\"\n            languageVersion = tsVersion.rawVersion\n        }\n\n        return \"$language: $languageVersion\"\n    }\n\n    companion object {\n        fun create(project: Project, psiFile: PsiFile?): JsDependenciesSnapshot {\n            var packageJsonFiles = emptySet<VirtualFile>()\n            var resolvedPackageJson = false\n\n            val virtualFile = psiFile?.virtualFile\n            if (virtualFile != null) {\n                val packageJson = PackageJsonUtil.findUpPackageJson(virtualFile)\n                if (packageJson != null) {\n                    packageJsonFiles = setOf(packageJson)\n                    resolvedPackageJson = true\n                }\n            }\n\n            if (packageJsonFiles.isEmpty()) {\n                packageJsonFiles = PackageJsonFileManager.getInstance(project).validPackageJsonFiles\n            }\n\n            val tsConfigs = findTsConfigs(project, packageJsonFiles)\n            val packages = enumerateAllPackages(packageJsonFiles)\n            return JsDependenciesSnapshot(packageJsonFiles, resolvedPackageJson, tsConfigs, packages)\n        }\n\n        private fun enumerateAllPackages(set: Set<VirtualFile>): Map<String, PackageJsonData.PackageJsonDependencyEntry> {\n            return set.asSequence()\n                .map { PackageJsonData.getOrCreate(it) }\n                .flatMap { it.allDependencyEntries.entries }\n                .associateBy({ it.key }, { it.value })\n        }\n\n        private fun findTsConfigs(project: Project, set: Set<VirtualFile>): Set<VirtualFile> {\n            val mapNotNull = set.asSequence().mapNotNull {\n                it.parent?.findChild(\"tsconfig.json\")\n            }\n\n            val rootConfig = project.guessProjectDir()?.findChild(\"tsconfig.json\")\n\n            return mapNotNull.plus(rootConfig).filterNotNull().toSet()\n        }\n    }\n}\n"
  },
  {
    "path": "languages/shire-javascript/src/main/kotlin/com/phodal/shirelang/javascript/util/LanguageApplicationUtil.kt",
    "content": "package com.phodal.shirelang.javascript.util\n\nimport com.intellij.lang.Language\nimport com.intellij.lang.html.HTMLLanguage\nimport com.intellij.lang.javascript.DialectDetector\nimport com.intellij.lang.javascript.JavascriptLanguage\nimport com.intellij.lang.javascript.buildTools.npm.PackageJsonUtil\nimport com.intellij.psi.PsiFile\nimport com.phodal.shirecore.provider.context.ToolchainPrepareContext\n\nobject LanguageApplicableUtil {\n    fun isJavaScriptApplicable(language: Language) =\n        language.isKindOf(JavascriptLanguage.INSTANCE) || language.isKindOf(HTMLLanguage.INSTANCE)\n\n    fun isPreferTypeScript(context: ToolchainPrepareContext): Boolean {\n        val sourceFile = context.sourceFile ?: return false\n        return DialectDetector.isTypeScript(sourceFile)\n    }\n\n    fun isWebChatCreationContextSupported(psiFile: PsiFile?): Boolean {\n        return isWebLLMContext(psiFile)\n    }\n\n    fun isWebLLMContext(psiFile: PsiFile?): Boolean {\n        if (psiFile == null) return false\n        if (PackageJsonUtil.isPackageJsonFile(psiFile)) return true\n\n        return isJavaScriptApplicable(psiFile.language)\n    }\n}"
  },
  {
    "path": "languages/shire-javascript/src/main/kotlin/com/phodal/shirelang/javascript/variable/JSPsiContextVariableProvider.kt",
    "content": "package com.phodal.shirelang.javascript.variable\n\nimport com.intellij.lang.ecmascript6.psi.ES6ImportDeclaration\nimport com.intellij.lang.ecmascript6.psi.ES6ImportSpecifier\nimport com.intellij.lang.ecmascript6.psi.ES6ImportSpecifierAlias\nimport com.intellij.lang.javascript.JavascriptLanguage\nimport com.intellij.lang.javascript.psi.JSFile\nimport com.intellij.lang.javascript.psi.JSFunction\nimport com.intellij.lang.javascript.psi.ecmal4.JSClass\nimport com.intellij.lang.javascript.psi.ecmal4.JSImportStatement\nimport com.intellij.openapi.editor.Editor\nimport com.intellij.openapi.project.Project\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.util.PsiTreeUtil\nimport com.phodal.shirecore.provider.variable.PsiContextVariableProvider\nimport com.phodal.shirecore.psi.CodeSmellCollector\nimport com.phodal.shirecore.provider.variable.model.PsiContextVariable\nimport com.phodal.shirecore.provider.variable.model.PsiContextVariable.*\nimport com.phodal.shirecore.search.similar.SimilarChunksSearch\nimport com.phodal.shirelang.javascript.JSTypeResolver\nimport com.phodal.shirelang.javascript.codemodel.JavaScriptClassStructureProvider\nimport com.phodal.shirelang.javascript.codemodel.JavaScriptMethodStructureProvider\nimport com.phodal.shirelang.javascript.util.JSPsiUtil\n\nclass JSPsiContextVariableProvider : PsiContextVariableProvider {\n    override fun resolve(variable: PsiContextVariable, project: Project, editor: Editor, psiElement: PsiElement?): Any {\n        if (psiElement == null) return \"\"\n        if(!psiElement.language.isKindOf(JavascriptLanguage.INSTANCE)) return \"\"\n\n        val underTestElement = JSPsiUtil.getElementToTest(psiElement) ?: return \"\"\n        val sourceFile = underTestElement.containingFile as? JSFile ?: return \"\"\n\n        return when (variable) {\n            CURRENT_CLASS_NAME -> {\n                when (underTestElement) {\n                    is JSClass -> underTestElement.name ?: \"\"\n                    else -> \"\"\n                }\n            }\n\n            CURRENT_CLASS_CODE -> {\n                val underTestObj = JavaScriptClassStructureProvider()\n                    .build(underTestElement, false)?.format()\n\n                if (underTestObj == null) {\n                    val funcObj = JavaScriptMethodStructureProvider()\n                        .build(underTestElement, false, false)?.format()\n\n                    funcObj ?: \"\"\n                } else {\n                    underTestObj\n                }\n            }\n\n            CURRENT_METHOD_NAME -> {\n                when (underTestElement) {\n                    is JSFunction -> underTestElement.name ?: \"\"\n                    else -> \"\"\n                }\n            }\n\n            CURRENT_METHOD_CODE -> {\n                when (underTestElement) {\n                    is JSFunction -> underTestElement.text ?: \"\"\n                    else -> \"\"\n                }\n            }\n\n            RELATED_CLASSES -> JSTypeResolver.resolveByElement(underTestElement)\n            SIMILAR_TEST_CASE -> \"\"\n            IMPORTS -> {\n                return PsiTreeUtil.findChildrenOfAnyType(sourceFile,\n                    JSImportStatement::class.java,\n                    ES6ImportDeclaration::class.java,\n                    ES6ImportSpecifier::class.java,\n                    ES6ImportSpecifierAlias::class.java\n                )\n                    .map { it.text }\n            }\n\n            IS_NEED_CREATE_FILE -> TODO()\n            TARGET_TEST_FILE_NAME -> JSPsiUtil.getTestFilePath(psiElement) ?: \"\"\n            UNDER_TEST_METHOD_CODE -> {\n                when (underTestElement) {\n                    is JSFunction -> underTestElement.text ?: \"\"\n                    else -> \"\"\n                }\n            }\n\n            FRAMEWORK_CONTEXT -> {\n                collectFrameworkContext(underTestElement, project)\n            }\n\n            CODE_SMELL -> CodeSmellCollector.collectElementProblemAsSting(psiElement, project, editor)\n            METHOD_CALLER -> TODO()\n            CALLED_METHOD -> TODO()\n            SIMILAR_CODE -> return SimilarChunksSearch.createQuery(psiElement) ?: \"\"\n            STRUCTURE -> {\n                when (underTestElement) {\n                    is JSClass -> JavaScriptClassStructureProvider().build(underTestElement, true)?.toString() ?: \"\"\n                    is JSFunction -> JavaScriptMethodStructureProvider().build(underTestElement, false, false)\n                    else -> null\n                } ?: \"\"\n            }\n            CHANGE_COUNT -> calculateChangeCount(psiElement)\n            LINE_COUNT -> calculateLineCount(psiElement)\n            COMPLEXITY_COUNT -> calculateComplexityCount(psiElement)\n        }\n    }\n\n}\n"
  },
  {
    "path": "languages/shire-javascript/src/main/kotlin/com/phodal/shirelang/javascript/variable/JavaScriptFrameworks.kt",
    "content": "package com.phodal.shirelang.javascript.variable\n\ninterface Framework {\n    val presentation: String\n    val packageName: String\n}\n\nenum class JsWebFrameworks(override val presentation: String, override val packageName: String) : Framework {\n    React(\"React\", \"react\"),\n    Vue(\"Vue\", \"vue\"),\n    Angular(\"Angular\", \"@angular/core\"),\n    AngularJS(\"AngularJS\", \"angular\"),\n    Svelte(\"Svelte\", \"svelte\"),\n    Astro(\"Astro\", \"astro\"),\n    Lit(\"Lit\", \"lit\"),\n    Solid(\"Solid\", \"solid-js\"),\n    Preact(\"Preact\", \"preact\"),\n    Next(\"Next\", \"next\"),\n    Nuxt(\"Nuxt\", \"nuxt\"),\n}\n\nenum class JsTestFrameworks(override val presentation: String, override val packageName: String) : Framework {\n    Jest(\"Jest\", \"jest\"),\n    Mocha(\"Mocha\", \"mocha\"),\n    Jasmine(\"Jasmine\", \"jasmine\"),\n    Karma(\"Karma\", \"karma\"),\n    Ava(\"Ava\", \"ava\"),\n    Tape(\"Tape\", \"tape\"),\n    Qunit(\"Qunit\", \"qunit\"),\n    Tap(\"Tap\", \"tap\"),\n    Cypress(\"Cypress\", \"cypress\"),\n    Protractor(\"Protractor\", \"protractor\"),\n    Nightwatch(\"Nightwatch\", \"nightwatch\"),\n    Vitest(\"Vitest\", \"vitest\")\n}\n\nprivate const val TYPESCRIPT_PACKAGE = \"typescript\"\n\nval MOST_POPULAR_PACKAGES = setOf(\n    \"lodash\",\n    \"request\",\n    \"commander\",\n    \"react\",\n    \"express\",\n    \"async\",\n    \"moment\",\n    \"prop-types\",\n    \"react-dom\",\n    \"bluebird\",\n    \"underscore\",\n    \"vue\",\n    \"axios\",\n    \"tslib\",\n    \"glob\",\n    \"yargs\",\n    \"colors\",\n    \"webpack\",\n    \"uuid\",\n    \"classnames\",\n    \"minimist\",\n    \"body-parser\",\n    \"rxjs\",\n    \"babel-runtime\",\n    \"jquery\",\n    \"babel-core\",\n    \"core-js\",\n    \"babel-loader\",\n    \"cheerio\",\n    \"rimraf\",\n    \"eslint\",\n    \"dotenv\",\n    TYPESCRIPT_PACKAGE,\n    \"@types/node\",\n    \"@angular/core\",\n    \"@angular/common\",\n    \"redux\",\n    \"gulp\",\n    \"node-fetch\",\n    \"@angular/platform-browser\",\n    \"@babel/runtime\",\n    \"handlebars\",\n    \"@angular/compiler\",\n    \"aws-sdk\",\n    \"@angular/forms\",\n    \"webpack-dev-server\",\n    \"@angular/platform-browser-dynamic\",\n    \"mocha\",\n    \"socket.io\",\n    \"ws\",\n    \"node-sass\",\n    \"@angular/router\",\n    \"ramda\",\n    \"react-redux\",\n    \"@babel/core\",\n    \"@angular/http\",\n    \"ejs\",\n    \"coffee-script\",\n    \"mongodb\",\n    \"chai\",\n    \"mongoose\",\n    \"xml2js\",\n    \"bootstrap\",\n    \"jest\",\n    \"redis\",\n    \"vue-router\",\n    \"optimist\",\n    \"promise\",\n    \"@angular/animations\",\n    \"postcss\",\n    \"morgan\",\n    \"less\",\n    \"immutable\"\n)\n"
  },
  {
    "path": "languages/shire-javascript/src/main/kotlin/com/phodal/shirelang/javascript/variable/JavaScriptLanguageToolchainProvider.kt",
    "content": "package com.phodal.shirelang.javascript.variable\n\nimport com.intellij.javascript.nodejs.PackageJsonDependency\nimport com.intellij.openapi.project.Project\nimport com.phodal.shirecore.provider.context.LanguageToolchainProvider\nimport com.phodal.shirecore.provider.context.ToolchainContextItem\nimport com.phodal.shirecore.provider.context.ToolchainPrepareContext\nimport com.phodal.shirelang.javascript.util.JsDependenciesSnapshot\nimport com.phodal.shirelang.javascript.util.LanguageApplicableUtil\n\nclass JavaScriptLanguageToolchainProvider : LanguageToolchainProvider {\n    override fun isApplicable(project: Project, context: ToolchainPrepareContext): Boolean {\n        return LanguageApplicableUtil.isWebChatCreationContextSupported(context.sourceFile)\n    }\n\n    override suspend fun collect(project: Project, context: ToolchainPrepareContext): List<ToolchainContextItem> {\n        val results = mutableListOf<ToolchainContextItem>()\n        val snapshot = JsDependenciesSnapshot.create(project, context.sourceFile)\n\n        val preferType = if (LanguageApplicableUtil.isPreferTypeScript(context)) \"TypeScript\" else \"JavaScript\"\n\n        results.add(\n            ToolchainContextItem(\n                JavaScriptLanguageToolchainProvider::class,\n                \"You are working on a project that uses $preferType language\"\n            )\n        )\n\n        if (preferType == \"TypeScript\") {\n            getTypeScriptLanguageContext(snapshot)?.let { results.add(it) }\n        }\n\n        getMostPopularPackagesContext(snapshot)?.let { results.add(it) }\n        getJsWebFrameworksContext(snapshot)?.let { results.add(it) }\n        getTestFrameworksContext(snapshot)?.let { results.add(it) }\n\n        return results\n    }\n\n    private fun getTypeScriptLanguageContext(snapshot: JsDependenciesSnapshot): ToolchainContextItem? {\n        val packageJson = snapshot.packages[\"typescript\"] ?: return null\n        val version = packageJson.parseVersion()\n        return ToolchainContextItem(\n            JavaScriptLanguageToolchainProvider::class,\n            \"The project uses TypeScript language\" + (version?.let { \", version: $version\" } ?: \"\")\n        )\n    }\n\n    private fun getMostPopularPackagesContext(snapshot: JsDependenciesSnapshot): ToolchainContextItem? {\n        val dependencies = snapshot.mostPopularFrameworks()\n        return dependencies.takeIf { it.isNotEmpty() }?.let {\n            ToolchainContextItem(\n                JavaScriptLanguageToolchainProvider::class,\n                \"The project uses the following JavaScript packages: ${it.joinToString(\", \")}\"\n            )\n        }\n    }\n\n    private fun getJsWebFrameworksContext(snapshot: JsDependenciesSnapshot): ToolchainContextItem? {\n        val frameworks = collectFrameworks(snapshot, JsWebFrameworks.entries)\n        return frameworks.takeIf { it.isNotEmpty() }?.let {\n            ToolchainContextItem(\n                JavaScriptLanguageToolchainProvider::class,\n                \"The project uses the following JavaScript component frameworks: $it\"\n            )\n        }\n    }\n\n    private fun getTestFrameworksContext(snapshot: JsDependenciesSnapshot): ToolchainContextItem? {\n        val frameworks = collectFrameworks(snapshot, JsTestFrameworks.entries)\n        return frameworks.takeIf { it.isNotEmpty() }?.let {\n            ToolchainContextItem(\n                JavaScriptLanguageToolchainProvider::class,\n                \"The project uses $it to test.\"\n            )\n        }\n    }\n\n    private fun collectFrameworks(\n        snapshot: JsDependenciesSnapshot,\n        frameworks: List<Framework>,\n    ): Map<String, Boolean> {\n        return snapshot.packages.filter { (_, entry) ->\n            entry.dependencyType == PackageJsonDependency.dependencies ||\n                    entry.dependencyType == PackageJsonDependency.devDependencies\n        }.mapNotNull { (name, _) ->\n            frameworks.find { name.startsWith(it.packageName) || name == it.packageName }?.packageName\n        }.associateWith { true }\n    }\n}"
  },
  {
    "path": "languages/shire-javascript/src/main/resources/com.phodal.shirelang.javascript.xml",
    "content": "<idea-plugin package=\"com.phodal.shirelang.javascript\">\n    <!--suppress PluginXmlValidity -->\n    <dependencies>\n        <plugin id=\"JavaScript\"/>\n        <plugin id=\"NodeJS\"/>\n    </dependencies>\n\n    <extensions defaultExtensionNs=\"com.phodal\">\n        <classStructureProvider language=\"JavaScript\"\n                                implementationClass=\"com.phodal.shirelang.javascript.codemodel.JavaScriptClassStructureProvider\"/>\n        <classStructureProvider language=\"TypeScript\"\n                                implementationClass=\"com.phodal.shirelang.javascript.codemodel.JavaScriptClassStructureProvider\"/>\n\n        <fileStructureProvider language=\"JavaScript\"\n                               implementationClass=\"com.phodal.shirelang.javascript.codemodel.JavaScriptFileStructureProvider\"/>\n        <fileStructureProvider language=\"TypeScript\"\n                               implementationClass=\"com.phodal.shirelang.javascript.codemodel.JavaScriptFileStructureProvider\"/>\n\n        <methodStructureProvider language=\"JavaScript\"\n                                 implementationClass=\"com.phodal.shirelang.javascript.codemodel.JavaScriptMethodStructureProvider\"/>\n        <methodStructureProvider language=\"TypeScript\"\n                                 implementationClass=\"com.phodal.shirelang.javascript.codemodel.JavaScriptMethodStructureProvider\"/>\n\n        <variableStructureProvider language=\"JavaScript\"\n                                   implementationClass=\"com.phodal.shirelang.javascript.codemodel.JavaScriptVariableStructureProvider\"/>\n        <variableStructureProvider language=\"TypeScript\"\n                                   implementationClass=\"com.phodal.shirelang.javascript.codemodel.JavaScriptVariableStructureProvider\"/>\n\n        <shireLanguageToolchainProvider language=\"TypeScript\"\n                                        implementationClass=\"com.phodal.shirelang.javascript.variable.JavaScriptLanguageToolchainProvider\"/>\n        <shireRefactoringTool\n                language=\"TypeScript\"\n                implementationClass=\"com.phodal.shirelang.javascript.impl.TypeScriptRefactoringTool\"/>\n\n        <shireBuildSystemProvider\n                implementation=\"com.phodal.shirelang.javascript.impl.JavaScriptBuildSystemProvider\"/>\n\n        <shireCodeModifier language=\"JavaScript\"\n                           implementationClass=\"com.phodal.shirelang.javascript.codeedit.JestCodeModifier\"/>\n        <shireCodeModifier language=\"TypeScript\"\n                           implementationClass=\"com.phodal.shirelang.javascript.codeedit.JestCodeModifier\"/>\n\n        <shireAutoTesting language=\"JavaScript\"\n                          implementationClass=\"com.phodal.shirelang.javascript.codeedit.JSAutoTestingService\"/>\n        <shireAutoTesting language=\"TypeScript\"\n                          implementationClass=\"com.phodal.shirelang.javascript.codeedit.JSAutoTestingService\"/>\n\n        <shireFileRunService\n                implementation=\"com.phodal.shirelang.javascript.codeedit.JSFileRunService\"/>\n\n        <shirePsiVariableProvider\n                language=\"TypeScript\"\n                implementationClass=\"com.phodal.shirelang.javascript.variable.JSPsiContextVariableProvider\"/>\n        <shirePsiVariableProvider\n                language=\"JavaScript\"\n                implementationClass=\"com.phodal.shirelang.javascript.variable.JSPsiContextVariableProvider\"/>\n\n        <shireRelatedClass language=\"TypeScript\"\n                           implementationClass=\"com.phodal.shirelang.javascript.provider.JavaScriptRelatedClassesProvider\"/>\n\n    </extensions>\n</idea-plugin>"
  },
  {
    "path": "languages/shire-json/src/main/kotlin/com/phodal/shire/json/PsiJsonUtil.kt",
    "content": "package com.phodal.shire.json\n\nimport com.intellij.json.JsonUtil\nimport com.intellij.json.psi.*\n\nfun JsonProperty.valueAsString(obj: JsonObject): String? {\n    val value = JsonUtil.getPropertyValueOfType(obj, name, JsonLiteral::class.java)\n    return when (value) {\n        is JsonStringLiteral -> value.value\n        is JsonBooleanLiteral -> value.value.toString()\n        else -> value?.text\n    }\n}\n\nfun JsonObject.findString(name: String): String? {\n    val property = findProperty(name) ?: return null\n    return property.valueAsString(this)\n}\n\nfun JsonObject.findNumber(name: String): Number? {\n    val property = findProperty(name) ?: return null\n    return JsonUtil.getPropertyValueOfType(this, name, JsonNumberLiteral::class.java)?.value\n}\n"
  },
  {
    "path": "languages/shire-json/src/main/kotlin/com/phodal/shire/json/ShireEnvReader.kt",
    "content": "package com.phodal.shire.json\n\nimport com.intellij.json.psi.JsonFile\nimport com.intellij.json.psi.JsonObject\nimport com.intellij.openapi.project.DumbService\nimport com.intellij.openapi.project.Project\nimport com.intellij.psi.PsiFile\nimport com.intellij.psi.PsiManager\nimport com.intellij.psi.search.GlobalSearchScope\nimport com.intellij.util.indexing.FileBasedIndex\n\nobject ShireEnvReader {\n    const val DEFAULT_ENV_NAME = \"development\"\n    /**\n     * This function attempts to retrieve a JSON file associated with a given environment name within the specified scope and project.\n     *\n     * @param envName The name of the environment for which to find the associated JSON file.\n     * @param scope The GlobalSearchScope to limit the search for the JSON file.\n     * @param project The Project within which to search for the JSON file.\n     *\n     * @return A JsonFile object if a file with the environment name is found, or null if no such file exists within the given scope and project.\n     */\n    private fun getEnvJsonFile(\n        envName: String,\n        scope: GlobalSearchScope,\n        project: Project,\n    ): JsonFile? {\n        return DumbService.getInstance(project).runReadActionInSmartMode<JsonFile?> {\n            FileBasedIndex.getInstance().getContainingFiles(ShireEnvironmentIndex.id(), envName, scope)\n                .firstOrNull()\n                ?.let {\n                    (PsiManager.getInstance(project).findFile(it) as? JsonFile)\n                }\n        }\n    }\n\n    fun getEnvObject(\n        envName: String,\n        scope: GlobalSearchScope,\n        project: Project,\n    ): JsonObject? {\n        val psiFile = getEnvJsonFile(envName, scope, project)\n        val envObject = getEnvObject(envName, psiFile)\n        return envObject\n    }\n\n    /**\n     * Read Shire env file object\n     */\n    fun getEnvObject(envName: String, psiFile: PsiFile?): JsonObject? {\n        val rootObject = (psiFile as? JsonFile)?.topLevelValue as? JsonObject ?: return null\n        return rootObject.propertyList.firstOrNull { it.name == envName }?.value as? JsonObject\n    }\n\n    fun fetchEnvironmentVariables(envName: String, scope: GlobalSearchScope): List<Set<String>> {\n        return FileBasedIndex.getInstance().getValues(\n            ShireEnvironmentIndex.id(),\n            envName,\n            scope\n        )\n    }\n\n    fun getAllEnvironments(project: Project, scope: GlobalSearchScope): Collection<String> {\n        try {\n            return DumbService.getInstance(project).runReadActionInSmartMode<Collection<String>> {\n                val index = FileBasedIndex.getInstance()\n                index.getAllKeys(ShireEnvironmentIndex.id(), project).stream()\n                    .filter {\n                        it != ShireEnvironmentIndex.MODEL_LIST && index.getContainingFiles(\n                            ShireEnvironmentIndex.id(),\n                            it,\n                            scope\n                        ).isNotEmpty()\n                    }\n                    .toList()\n            }\n        } catch (e: Exception) {\n            return emptyList()\n        }\n    }\n\n}"
  },
  {
    "path": "languages/shire-json/src/main/kotlin/com/phodal/shire/json/ShireEnvVariableFiller.kt",
    "content": "package com.phodal.shire.json\n\nimport com.intellij.json.JsonUtil\nimport com.intellij.json.psi.*\nimport com.intellij.openapi.util.TextRange\n\nobject ShireEnvVariableFiller {\n    private fun getVariableValue(jsonObject: JsonObject, name: String, processVars: Map<String, String>): String? {\n        val value = JsonUtil.getPropertyValueOfType(jsonObject, name, JsonLiteral::class.java)\n        val jsonResult = getValueAsString(value)\n        if (jsonResult != null) {\n            return jsonResult\n        }\n\n        return processVars[name]\n    }\n\n    private fun getValueAsString(value: JsonLiteral?): String? {\n        return when (value) {\n            is JsonStringLiteral -> value.value\n            is JsonBooleanLiteral -> value.value.toString()\n            else -> value?.text\n        }\n    }\n\n    fun fillVariables(\n        messageBody: String,\n        variables: List<Set<String>>,\n        obj: JsonObject?,\n        processVars: Map<String, String>\n    ): String {\n        if (obj == null) return messageBody\n        if (variables.isEmpty()) return messageBody\n\n        val envRanges = collectVariablesRangesInMessageBody(messageBody)\n\n        val result = StringBuilder(messageBody.length)\n        var lastVariableRangeEndOffset = 0\n\n        for (variableRange in envRanges) {\n            result.append(messageBody as CharSequence, lastVariableRangeEndOffset, variableRange.startOffset)\n            val variableValue = getVariableValue(obj, getVariableKey(variableRange, messageBody), processVars)\n\n            result.append(variableValue)\n            lastVariableRangeEndOffset = variableRange.endOffset\n        }\n\n        result.append(messageBody as CharSequence, lastVariableRangeEndOffset, messageBody.length)\n        val sb = result.toString()\n        return sb\n    }\n\n    private fun getVariableKey(variableRange: TextRange, messageBody: String) =\n        variableRange.substring(messageBody).removePrefix(\"\\${\").removeSuffix(\"}\")\n\n    private fun collectVariablesRangesInMessageBody(body: String): List<TextRange> {\n        val ranges = mutableListOf<TextRange>()\n        var startIndex = 0\n\n        while (startIndex < body.length) {\n            val openBraceIndex = body.indexOf(\"\\${\", startIndex)\n            val closeBraceIndex = body.indexOf(\"}\", openBraceIndex)\n\n            if (openBraceIndex == -1 || closeBraceIndex == -1) {\n                break\n            }\n\n            val range = TextRange(openBraceIndex, closeBraceIndex + 1)\n            val contentInsideBraces = body.substring(openBraceIndex + 2, closeBraceIndex)\n\n            if (contentInsideBraces.isNotBlank()) {\n                ranges.add(range)\n            }\n\n            startIndex = closeBraceIndex + 1\n        }\n\n        return ranges\n    }\n}"
  },
  {
    "path": "languages/shire-json/src/main/kotlin/com/phodal/shire/json/ShireEnvironmentIndex.kt",
    "content": "package com.phodal.shire.json\n\nimport com.intellij.json.psi.*\nimport com.intellij.openapi.util.text.StringUtil\nimport com.intellij.util.indexing.*\nimport com.intellij.util.io.DataExternalizer\nimport com.intellij.util.io.EnumeratorStringDescriptor\nimport com.intellij.util.io.KeyDescriptor\n\n\nclass ShireEnvironmentIndex : FileBasedIndexExtension<String, Set<String>>() {\n    companion object {\n        val SHIRE_ENV_ID: ID<String, Set<String>> = ID.create(\"shire.environment\")\n        const val MODEL_TITLE = \"title\"\n        const val MODEL_LIST = \"models\"\n\n        fun id(): ID<String, Set<String>> {\n            return SHIRE_ENV_ID\n        }\n    }\n\n    override fun getValueExternalizer(): DataExternalizer<Set<String>> = ShireStringsExternalizer()\n    override fun getVersion(): Int = 2\n    override fun getInputFilter(): FileBasedIndex.InputFilter = ShireEnvironmentInputFilter()\n    override fun dependsOnFileContent(): Boolean = true\n    override fun getName(): ID<String, Set<String>> = SHIRE_ENV_ID\n    override fun getKeyDescriptor(): KeyDescriptor<String> = EnumeratorStringDescriptor.INSTANCE\n\n    override fun getIndexer(): DataIndexer<String, Set<String>, FileContent> {\n        return DataIndexer { inputData: FileContent ->\n            val file = inputData.psiFile\n            require(file is JsonFile) { AssertionError() }\n\n            val variablesFromFile = getVariablesFromFile(file)\n            variablesFromFile\n        }\n    }\n\n    private fun getVariablesFromFile(file: JsonFile): Map<String, Set<String>> {\n        val result: MutableMap<String, Set<String>> = HashMap()\n        when (val topLevelValue = file.topLevelValue) {\n            is JsonObject -> {\n                for (property in topLevelValue.propertyList) {\n                    when (val value = property.value) {\n                        is JsonObject -> {\n                            result[property.name] = readEnvVariables(value, file.name)\n                        }\n\n                        is JsonArray -> {\n                            // the prop key should be \"models\"\n                            if (property.name != MODEL_LIST) {\n                                continue\n                            }\n\n                            // the child elements of the array are objects, which should have prop call \"name\"\n                            val envVariables = value.children\n                                .filterIsInstance<JsonObject>()\n                                .mapNotNull { obj ->\n                                    val name = obj.findProperty(MODEL_TITLE)?.value?.text\n                                    name?.let { StringUtil.unquoteString(it) }\n                                }\n                                .toSet()\n\n                            result[property.name] = envVariables\n                        }\n                    }\n                }\n            }\n        }\n\n        return result\n    }\n\n    private fun readEnvVariables(obj: JsonObject, fileName: String): Set<String> {\n        val properties = obj.propertyList\n        return if (properties.isEmpty()) {\n            emptySet()\n        } else {\n            val set = properties.stream()\n                .map { property ->\n                    StringUtil.nullize(property.name)\n                }\n                .toList()\n                .mapNotNull { it }\n                .toSet()\n\n            set\n        }\n    }\n}\n"
  },
  {
    "path": "languages/shire-json/src/main/kotlin/com/phodal/shire/json/ShireEnvironmentInputFilter.kt",
    "content": "package com.phodal.shire.json\n\nimport com.intellij.json.JsonFileType\nimport com.intellij.openapi.fileTypes.FileType\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.intellij.util.indexing.DefaultFileTypeSpecificInputFilter\n\nclass ShireEnvironmentInputFilter : DefaultFileTypeSpecificInputFilter(*arrayOf<FileType>(JsonFileType.INSTANCE)) {\n    override fun acceptInput(file: VirtualFile): Boolean {\n        return super.acceptInput(file) && isShireEnvFile(file)\n    }\n\n    private fun isShireEnvFile(file: VirtualFile?): Boolean {\n        return file?.name?.endsWith(\".shireEnv.json\") ?: false\n    }\n}"
  },
  {
    "path": "languages/shire-json/src/main/kotlin/com/phodal/shire/json/ShireStringsExternalizer.kt",
    "content": "package com.phodal.shire.json\n\nimport com.intellij.util.io.DataExternalizer\nimport java.io.DataInput\nimport java.io.DataOutput\n\nclass ShireStringsExternalizer : DataExternalizer<Set<String>> {\n    override fun save(out: DataOutput, value: Set<String>) {\n        out.writeInt(value.size)\n        for (s in value) {\n            out.writeUTF(s)\n        }\n    }\n\n    override fun read(input: DataInput): Set<String> {\n        val size = input.readInt()\n        val result: MutableSet<String> = HashSet(size)\n        for (i in 0 until size) {\n            result.add(input.readUTF())\n        }\n\n        return result\n    }\n\n}"
  },
  {
    "path": "languages/shire-json/src/main/kotlin/com/phodal/shire/json/llm/LlmEnv.kt",
    "content": "package com.phodal.shire.json.llm\n\nimport com.intellij.json.psi.JsonArray\nimport com.intellij.json.psi.JsonFile\nimport com.intellij.json.psi.JsonObject\nimport com.intellij.openapi.application.runReadAction\nimport com.intellij.openapi.project.Project\nimport com.intellij.psi.PsiManager\nimport com.intellij.psi.search.GlobalSearchScope\nimport com.intellij.util.indexing.FileBasedIndex\nimport com.phodal.shire.json.ShireEnvironmentIndex\nimport com.phodal.shire.json.valueAsString\n\nclass LlmEnv {\n    companion object {\n        private fun configFromFile(modelName: String, psiFile: JsonFile?): JsonObject? {\n            val rootObject = psiFile?.topLevelValue as? JsonObject ?: return null\n            val envObject = rootObject.propertyList.firstOrNull { it.name == ShireEnvironmentIndex.MODEL_LIST }?.value as? JsonArray\n            return envObject?.children?.firstOrNull {\n                it is JsonObject && it.findProperty(ShireEnvironmentIndex.MODEL_TITLE)?.valueAsString(it) == modelName\n            } as? JsonObject\n        }\n\n        fun configFromFile(modelName: String, scope: GlobalSearchScope, project: Project): JsonObject? {\n            val jsonFile = runReadAction {\n                FileBasedIndex.getInstance().getContainingFiles(ShireEnvironmentIndex.id(), ShireEnvironmentIndex.MODEL_LIST, scope)\n                    .firstOrNull()\n                    ?.let {\n                        (PsiManager.getInstance(project).findFile(it) as? JsonFile)\n                    }\n            }\n\n            return configFromFile(modelName, jsonFile)\n        }\n    }\n}"
  },
  {
    "path": "languages/shire-json/src/main/resources/com.phodal.shire.json.xml",
    "content": "<idea-plugin package=\"com.phodal.shire.json\">\n    <extensions defaultExtensionNs=\"com.intellij\">\n        <fileBasedIndex implementation=\"com.phodal.shire.json.ShireEnvironmentIndex\"/>\n    </extensions>\n</idea-plugin>\n"
  },
  {
    "path": "languages/shire-json/src/test/kotlin/com/phodal/shire/json/ShireEnvVariableFillerTest.kt",
    "content": "package com.phodal.shire.json\n\nimport com.intellij.json.psi.JsonObject\nimport org.assertj.core.api.Assertions.assertThat\nimport org.junit.Test\n\nclass ShireEnvVariableFillerTest {\n    @Test\n    fun should_return_original_message_body_when_json_object_is_null() {\n        // Given\n        val messageBody = \"Hello, \\${name}!\"\n        val variables = listOf(setOf(\"name\"))\n        val jsonObject: JsonObject? = null\n        val processVars = mapOf(\"name\" to \"John\")\n\n        // When\n        val result = ShireEnvVariableFiller.fillVariables(messageBody, variables, jsonObject, processVars)\n\n        // Then\n        assertThat(result).isEqualTo(messageBody)\n    }\n}\n"
  },
  {
    "path": "languages/shire-kotlin/src/main/kotlin/com/phodal/shirelang/kotlin/KotlinContextCollector.kt",
    "content": "package com.phodal.shirelang.kotlin\n\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.roots.ProjectFileIndex\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.PsiElementFactory\nimport com.intellij.psi.PsiReference\nimport com.intellij.psi.util.PsiTreeUtil\nimport org.jetbrains.kotlin.analysis.decompiler.psi.file.KtDecompiledFile\nimport org.jetbrains.kotlin.idea.KotlinLanguage\nimport org.jetbrains.kotlin.idea.references.mainReference\nimport org.jetbrains.kotlin.psi.*\n\nobject KotlinContextCollector {\n    /**\n     * Resolves the given reference to a PSI element.\n     *\n     * @param reference the reference to resolve\n     * @return the resolved PSI element, or null if the reference cannot be resolved\n     */\n    fun resolveReference(reference: PsiReference?): PsiElement? {\n        val resolvedElement: PsiElement? = reference?.resolve()\n\n        if (resolvedElement is KtPrimaryConstructor) {\n            return PsiTreeUtil.getParentOfType(resolvedElement, KtClass::class.java, false)\n        }\n\n        if (resolvedElement == null) return null\n        val virtualFile = resolvedElement.containingFile.virtualFile\n\n        if (virtualFile != null && !ProjectFileIndex.getInstance(resolvedElement.project).isInLibrary(virtualFile)) {\n            if (resolvedElement is KtClass || resolvedElement is KtFunction) {\n                return resolvedElement\n            }\n        }\n\n        return null\n    }\n\n    /**\n     * Finds and returns a list of used variables within the given scope.\n     *\n     * @param scope the scope within which to search for used variables\n     * @return a list of KtValVarKeywordOwner objects representing the used variables found within the scope\n     */\n    fun findUsedVariables(scope: PsiElement): List<KtValVarKeywordOwner> {\n        val referenceExpressions = PsiTreeUtil.findChildrenOfType(scope, KtReferenceExpression::class.java)\n\n        val resolvedReferences = referenceExpressions.mapNotNull {\n            it.mainReference.resolve() as? KtValVarKeywordOwner\n        }\n\n        return resolvedReferences.toList()\n    }\n\n    /**\n     * Returns the text representation of the return type of a Kotlin function.\n     *\n     * @param function The Kotlin function for which the return type text is to be retrieved.\n     *\n     * @return The text representation of the return type of the function, preceded by a colon (\":\").\n     *         If the function does not have a return type, an empty string is returned.\n     *\n     * When given a class, this method does not return the code representation of the class.\n     * Instead, it returns the comment representation of the class, enclosed in /** ... */.\n     *\n     * Note: This documentation does not include @author and @version tags.\n     */\n    fun returnTypeText(function: KtFunction?): String {\n        val typeReference = function?.typeReference\n        val typeText = typeReference?.getTypeText() ?: return \"\"\n        return \": $typeText\"\n    }\n\n    /**\n     * Replaces all occurrences of a reference expression with a new name in the given element.\n     *\n     * @param element the element in which to replace the reference expressions\n     * @param oldName the old name of the reference expression to be replaced\n     * @param newName the new name to replace the reference expression with\n     * @return a new PsiElement with the replaced reference expressions, or the original element if newName is null\n     */\n    private fun replaceReferenceExpressions(element: PsiElement, oldName: String, newName: String?): PsiElement {\n        if (newName == null) return element\n\n        val copiedElement = element.copy()\n        val factory = PsiElementFactory.getInstance(element.project)\n\n        PsiTreeUtil.findChildrenOfAnyType(copiedElement, false, KtReferenceExpression::class.java)\n            .filterIsInstance<KtReferenceExpression>()\n            .filter { it.text == oldName }\n            .forEach { reference ->\n                reference.replace(factory.createExpressionFromText(newName, reference.context))\n            }\n\n        return copiedElement\n    }\n\n    /**\n     * Generates a summary for the given Kotlin language PsiElement.\n     *\n     * @param psiElement The PsiElement to generate the summary for.\n     * @return The generated summary as a String, or null if the PsiElement is not applicable.\n     *\n     * If the PsiElement is a KtFile, it creates a copy of it and removes the function implementations.\n     * Then it summarizes each class or object in the file.\n     *\n     * If the PsiElement is a KtClassOrObject, it creates a copy of it and summarizes the class or object.\n     *\n     * The generated summary is returned as a String.\n     * If the PsiElement is not applicable, null is returned.\n     */\n    fun generateSummary(psiElement: PsiElement): String? {\n        if (psiElement.language != KotlinLanguage.INSTANCE || psiElement is KtDecompiledFile) {\n            return null\n        }\n\n        return when (psiElement) {\n            is KtFile -> {\n                val copy = psiElement.copy() as KtFile\n                val project = copy.project\n                val functions = PsiTreeUtil.getChildrenOfType(copy, KtFunction::class.java) ?: emptyArray()\n\n                functions.forEach { function ->\n                    removeFunctionImplementation(project, function)\n                }\n\n                val classesOrObjects = PsiTreeUtil.getChildrenOfType(copy, KtClassOrObject::class.java) ?: emptyArray()\n\n                classesOrObjects.forEach { classOrObject ->\n                    summarizeClassOrObject(project, classOrObject)\n                }\n\n                copy.text\n            }\n            is KtClassOrObject -> {\n                val copy = psiElement.copy() as KtClassOrObject\n                val project = copy.project\n                summarizeClassOrObject(project, copy)\n                copy.text\n            }\n            else -> null\n        }\n    }\n\n    private fun summarizeClassOrObject(project: Project, item: KtClassOrObject) {\n        for (declaration in item.declarations) {\n            when (declaration) {\n                is KtFunction -> {\n                    removeFunctionImplementation(project, declaration)\n                }\n                is KtClassOrObject -> {\n                    summarizeClassOrObject(project, declaration)\n                }\n            }\n        }\n    }\n\n    private const val placeholderMessage = \"/* implementation omitted for shortness */\"\n    /**\n     * Removes the implementation of a function.\n     *\n     * @param project the project context\n     * @param function the function to remove the implementation from\n     *\n     * This method removes the implementation of the given function by deleting the child range of the body expression.\n     * If the length of the placeholder message is greater than or equal to the length of the body expression, no changes are made.\n     * After removing the implementation, a placeholder is added as the first child of the body expression.\n     */\n    private fun removeFunctionImplementation(project: Project, function: KtFunction) {\n        val bodyExpression = function.bodyExpression ?: return\n        if (placeholderMessage.length >= bodyExpression.textLength) return\n\n        bodyExpression.deleteChildRange(bodyExpression.firstChild, bodyExpression.lastChild)\n        bodyExpression.addAfter(makePlaceholder(project), bodyExpression.firstChild)\n    }\n\n    private fun makePlaceholder(project: Project): PsiElement {\n        return KtPsiFactory(project, false).createComment(placeholderMessage)\n    }\n}\n\nfun KtNamedDeclaration.getReturnTypeReferences(): List<KtTypeReference> {\n    return when (this) {\n        is KtCallableDeclaration -> listOfNotNull(typeReference)\n        is KtClassOrObject -> superTypeListEntries.mapNotNull { it.typeReference }\n        is KtScript -> emptyList()\n        else -> throw AssertionError(\"Unexpected declaration kind: $text\")\n    }\n}\n\n\nfun KtTypeReference?.getTypeText(): String? {\n    if (this == null) return null\n\n    val typeElement = this.typeElement\n    if (typeElement is KtUserType) {\n        val typeElementReference = typeElement.referenceExpression?.mainReference?.resolve()\n        if (typeElementReference is KtClass) {\n            return typeElementReference.name\n        }\n    }\n\n    return this.text\n}\n"
  },
  {
    "path": "languages/shire-kotlin/src/main/kotlin/com/phodal/shirelang/kotlin/KotlinPsiUtil.kt",
    "content": "package com.phodal.shirelang.kotlin\n\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.PsiMethod\nimport com.intellij.psi.PsiNameIdentifierOwner\nimport com.intellij.psi.PsiReference\nimport com.intellij.psi.search.GlobalSearchScope\nimport com.intellij.psi.search.SearchScope\nimport com.intellij.psi.search.searches.MethodReferencesSearch\nimport com.intellij.psi.search.searches.ReferencesSearch\nimport org.jetbrains.kotlin.psi.KtClassOrObject\nimport org.jetbrains.kotlin.psi.KtFile\nimport org.jetbrains.kotlin.psi.KtFunction\nimport org.jetbrains.kotlin.psi.KtNamedFunction\n\nobject KotlinPsiUtil {\n    fun getFunctions(kotlinClass: KtClassOrObject): List<KtFunction> {\n        return kotlinClass.getDeclarations().filterIsInstance<KtFunction>()\n    }\n\n    fun getClasses(ktFile: KtFile): List<KtClassOrObject> {\n        return ktFile.declarations.filterIsInstance<KtClassOrObject>()\n    }\n\n    fun signatureString(signatureString: KtNamedFunction): String {\n        val bodyBlockExpression = signatureString.bodyBlockExpression\n        val startOffsetInParent = if (bodyBlockExpression != null) {\n            bodyBlockExpression.startOffsetInParent\n        } else {\n            val bodyExpression = signatureString.bodyExpression\n            bodyExpression?.startOffsetInParent ?: signatureString.textLength\n        }\n\n        val text = signatureString.text\n        val substring = text.substring(0, startOffsetInParent)\n        return substring.replace('\\n', ' ').trim()\n    }\n\n    fun findUsages(nameIdentifierOwner: PsiNameIdentifierOwner): List<PsiReference> {\n        val project = nameIdentifierOwner.project\n        val searchScope = GlobalSearchScope.allScope(project) as SearchScope\n\n        return when (nameIdentifierOwner) {\n            is PsiMethod -> {\n                MethodReferencesSearch.search(nameIdentifierOwner, searchScope, true)\n            }\n\n            else -> {\n                ReferencesSearch.search((nameIdentifierOwner as PsiElement), searchScope, true)\n            }\n        }.findAll().map { it as PsiReference }\n    }\n}"
  },
  {
    "path": "languages/shire-kotlin/src/main/kotlin/com/phodal/shirelang/kotlin/KotlinTypeResolver.kt",
    "content": "package com.phodal.shirelang.kotlin\n\nimport com.intellij.psi.PsiElement\nimport org.jetbrains.kotlin.idea.references.mainReference\nimport org.jetbrains.kotlin.psi.*\nimport org.jetbrains.kotlin.psi.psiUtil.getValueParameters\n\nobject KotlinTypeResolver {\n    fun resolveByElement(element: PsiElement): MutableMap<String, KtClass?> {\n        if (element !is KtNamedDeclaration) return mutableMapOf()\n\n        val resolvedClasses: MutableMap<String, KtClass?> = resolveByMethod(element)\n        when (element) {\n            is KtClassOrObject -> {\n                KotlinPsiUtil.getFunctions(element).forEach {\n                    resolvedClasses.putAll(resolveByMethod(it))\n                }\n\n                resolvedClasses.putAll(resolveByFields(element))\n            }\n\n            is KtFile -> {\n                KotlinPsiUtil.getClasses(element).forEach {\n                    resolvedClasses.putAll(resolveByFields(it))\n                }\n            }\n        }\n        return resolvedClasses\n    }\n\n\n    fun resolveByFields(element: KtClassOrObject): Map<out String, KtClass?> {\n        val resolvedClasses = mutableMapOf<String, KtClass?>()\n        element.primaryConstructorParameters.forEach {\n            val typeReference = it.typeReference\n            val elements = resolveType(typeReference)\n            elements.forEach { element ->\n                if (element is KtClass) {\n                    resolvedClasses[element.name!!] = element\n                }\n            }\n        }\n\n        return resolvedClasses\n    }\n\n    fun resolveByMethod(element: PsiElement): MutableMap<String, KtClass?> {\n        val resolvedClasses = mutableMapOf<String, KtClass?>()\n        when (element) {\n            is KtNamedDeclaration -> {\n                element.getValueParameters().map {\n                    val typeReference = it.typeReference\n                    resolveType(typeReference)\n                }.forEach {\n                    if (it is KtClass) {\n                        resolvedClasses[it.name!!] = it\n                    }\n                }\n\n                // with Generic returnType, like: ResponseEntity<List<Item>>\n                element.getReturnTypeReferences().forEach { returnType ->\n                    resolveType(returnType).filterIsInstance<KtClass>().forEach {\n                        resolvedClasses[it.name!!] = it\n                    }\n                }\n            }\n        }\n\n        return resolvedClasses\n    }\n\n    private fun resolveType(typeReference: KtTypeReference?): List<PsiElement> {\n        if (typeReference == null) return emptyList()\n        val result = mutableListOf<PsiElement>()\n        when (val ktTypeElement = typeReference.typeElement) {\n            is KtUserType -> {\n                ktTypeElement.typeArguments.forEach {\n                    result += resolveType(it.typeReference)\n                }\n\n                val typeElementReference = ktTypeElement.referenceExpression?.mainReference?.resolve()\n                if (typeElementReference is KtClass) {\n                    result += typeElementReference\n                }\n            }\n        }\n\n        return result\n    }\n}"
  },
  {
    "path": "languages/shire-kotlin/src/main/kotlin/com/phodal/shirelang/kotlin/codemodel/KotlinClassStructureProvider.kt",
    "content": "package com.phodal.shirelang.kotlin.codemodel\n\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.PsiNameIdentifierOwner\nimport com.phodal.shirecore.provider.codemodel.ClassStructureProvider\nimport com.phodal.shirecore.provider.codemodel.model.ClassStructure\nimport com.phodal.shirelang.kotlin.KotlinPsiUtil\nimport org.jetbrains.kotlin.psi.KtClassOrObject\nimport org.jetbrains.kotlin.psi.KtParameter\n\nclass KotlinClassStructureProvider : ClassStructureProvider {\n    private fun getPrimaryConstructorFields(kotlinClass: KtClassOrObject): List<KtParameter> {\n        return kotlinClass.getPrimaryConstructorParameters().filter { it.hasValOrVar() }\n    }\n\n    override fun build(psiElement: PsiElement, gatherUsages: Boolean): ClassStructure? {\n        if (psiElement !is KtClassOrObject) return null\n\n        val text = psiElement.text\n        val name = psiElement.name\n        val functions = KotlinPsiUtil.getFunctions(psiElement)\n        val allFields = getPrimaryConstructorFields(psiElement)\n        val usages =\n            if (gatherUsages) KotlinPsiUtil.findUsages(psiElement as PsiNameIdentifierOwner) else emptyList()\n\n        val annotations: List<String> = psiElement.annotationEntries.mapNotNull {\n            it.text\n        }\n\n        val displayName = psiElement.fqName?.asString() ?: psiElement.name ?: \"\"\n        return ClassStructure(\n            psiElement,\n            text,\n            name,\n            displayName,\n            functions,\n            allFields,\n            null,\n            annotations = annotations,\n            usages\n        )\n    }\n}\n\n"
  },
  {
    "path": "languages/shire-kotlin/src/main/kotlin/com/phodal/shirelang/kotlin/codemodel/KotlinFileStructureProvider.kt",
    "content": "package com.phodal.shirelang.kotlin.codemodel\n\nimport com.intellij.psi.PsiFile\nimport com.intellij.psi.util.PsiTreeUtil\nimport com.phodal.shirecore.provider.codemodel.FileStructureProvider\nimport com.phodal.shirecore.provider.codemodel.model.FileStructure\nimport com.phodal.shirecore.relativePath\nimport org.jetbrains.kotlin.psi.KtClassOrObject\nimport org.jetbrains.kotlin.psi.KtImportList\nimport org.jetbrains.kotlin.psi.KtNamedFunction\nimport org.jetbrains.kotlin.psi.KtPackageDirective\n\nclass KotlinFileStructureProvider : FileStructureProvider {\n    override fun build(psiFile: PsiFile): FileStructure? {\n        val name = psiFile.name\n        val path = if (psiFile.virtualFile != null) psiFile.virtualFile!!.relativePath(psiFile.project) else \"\"\n\n        val packageDirective = PsiTreeUtil.getChildrenOfTypeAsList(psiFile, KtPackageDirective::class.java).firstOrNull()\n        val packageName = packageDirective?.text ?: \"\"\n\n        val importList = PsiTreeUtil.getChildrenOfTypeAsList(psiFile, KtImportList::class.java)\n        val imports = importList.flatMap { it.imports }\n\n        val classOrObjects = PsiTreeUtil.getChildrenOfTypeAsList(psiFile, KtClassOrObject::class.java)\n        val namedFunctions = PsiTreeUtil.getChildrenOfTypeAsList(psiFile, KtNamedFunction::class.java)\n\n        return FileStructure(psiFile, name, path, packageName, imports, classOrObjects, namedFunctions)\n    }\n}\n"
  },
  {
    "path": "languages/shire-kotlin/src/main/kotlin/com/phodal/shirelang/kotlin/codemodel/KotlinMethodStructureProvider.kt",
    "content": "package com.phodal.shirelang.kotlin.codemodel\n\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.PsiNameIdentifierOwner\nimport com.phodal.shirecore.provider.codemodel.MethodStructureProvider\nimport com.phodal.shirecore.provider.codemodel.model.MethodStructure\nimport com.phodal.shirelang.kotlin.KotlinPsiUtil\nimport org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.getReturnTypeReference\nimport org.jetbrains.kotlin.psi.KtNamedFunction\nimport org.jetbrains.kotlin.psi.psiUtil.containingClass\n\nclass KotlinMethodStructureProvider : MethodStructureProvider {\n    override fun build(psiElement: PsiElement, includeClassContext: Boolean, gatherUsages: Boolean): MethodStructure? {\n        if (psiElement !is KtNamedFunction) return null\n\n        val returnType = psiElement.getReturnTypeReference()?.text\n        val containingClass = psiElement.containingClass()\n        val signatureString = KotlinPsiUtil.signatureString(psiElement)\n        val displayName = psiElement.language.displayName\n        val valueParameters = psiElement.valueParameters.mapNotNull { it.name }\n        val usages =\n            if (gatherUsages) KotlinPsiUtil.findUsages(psiElement as PsiNameIdentifierOwner) else emptyList()\n\n        return MethodStructure(\n            psiElement,\n            psiElement.text,\n            psiElement.name,\n            signatureString,\n            containingClass,\n            displayName,\n            returnType,\n            valueParameters,\n            includeClassContext,\n            usages\n        )\n    }\n}\n"
  },
  {
    "path": "languages/shire-kotlin/src/main/kotlin/com/phodal/shirelang/kotlin/codemodel/KotlinVariableStructureProvider.kt",
    "content": "package com.phodal.shirelang.kotlin.codemodel\n\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.PsiNameIdentifierOwner\nimport com.intellij.psi.util.PsiTreeUtil\nimport com.phodal.shirecore.provider.codemodel.VariableStructureProvider\nimport com.phodal.shirecore.provider.codemodel.model.VariableStructure\nimport com.phodal.shirelang.kotlin.KotlinPsiUtil\nimport org.jetbrains.kotlin.psi.KtNamedFunction\nimport org.jetbrains.kotlin.psi.KtParameter\nimport org.jetbrains.kotlin.psi.KtVariableDeclaration\nimport org.jetbrains.kotlin.psi.psiUtil.containingClass\n\nclass KotlinVariableStructureProvider : VariableStructureProvider {\n    override fun build(\n        psiElement: PsiElement,\n        withMethodContext: Boolean,\n        withClassContext: Boolean,\n        gatherUsages: Boolean\n    ): VariableStructure? {\n        when (psiElement) {\n            is KtVariableDeclaration -> {\n                val text = psiElement.text\n                val name = psiElement.name\n                val parentOfType = PsiTreeUtil.getParentOfType(psiElement, KtNamedFunction::class.java, true)\n                val containingClass = psiElement.containingClass()\n                val psiNameIdentifierOwner = psiElement as? PsiNameIdentifierOwner\n\n                val usages = if (gatherUsages && psiNameIdentifierOwner != null) {\n                    KotlinPsiUtil.findUsages(psiNameIdentifierOwner)\n                } else {\n                    emptyList()\n                }\n\n                return VariableStructure(psiElement, text, name, parentOfType, containingClass, usages, withMethodContext, withClassContext)\n            }\n\n            is KtParameter -> {\n                val text = psiElement.text\n                val name = psiElement.name\n                val parentOfType = PsiTreeUtil.getParentOfType(psiElement, KtNamedFunction::class.java, true)\n                val containingClass = psiElement.containingClass()\n                val psiNameIdentifierOwner = psiElement as? PsiNameIdentifierOwner\n\n                val usages = if (gatherUsages && psiNameIdentifierOwner != null) {\n                    KotlinPsiUtil.findUsages(psiNameIdentifierOwner)\n                } else {\n                    emptyList()\n                }\n\n                return VariableStructure(psiElement, text, name, parentOfType, containingClass, usages, withMethodContext, withClassContext)\n            }\n\n            else -> {\n                return null\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "languages/shire-kotlin/src/main/kotlin/com/phodal/shirelang/kotlin/complexity/KotlinComplexityProvider.kt",
    "content": "package com.phodal.shirelang.kotlin.complexity\n\nimport com.intellij.psi.PsiElement\nimport com.phodal.shirecore.ast.ComplexitySink\nimport com.phodal.shirecore.ast.ComplexityVisitor\nimport com.phodal.shirecore.provider.complexity.ComplexityProvider\n\nclass KotlinComplexityProvider : ComplexityProvider {\n    override fun process(element: PsiElement): Int {\n        val sink = ComplexitySink()\n        val visitor = visitor(sink)\n        element.accept(visitor)\n        return sink.getComplexity()\n    }\n\n    override fun visitor(sink: ComplexitySink): ComplexityVisitor {\n        return KotlinLanguageVisitor(sink)\n    }\n}\n"
  },
  {
    "path": "languages/shire-kotlin/src/main/kotlin/com/phodal/shirelang/kotlin/complexity/KotlinLanguageVisitor.kt",
    "content": "/**\n * The MIT License (MIT)\n * <p>\n *     https://github.com/nikolaikopernik/code-complexity-plugin\n *  </p>\n */\npackage com.phodal.shirelang.kotlin.complexity\n\nimport com.intellij.psi.NavigatablePsiElement\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.tree.TokenSet\nimport com.phodal.shirecore.ast.ComplexitySink\nimport com.phodal.shirecore.ast.ComplexityVisitor\nimport com.phodal.shirecore.ast.PointType\nimport org.jetbrains.kotlin.idea.caches.resolve.analyze\nimport org.jetbrains.kotlin.lexer.KtToken\nimport org.jetbrains.kotlin.lexer.KtTokens\nimport org.jetbrains.kotlin.name.Name\nimport org.jetbrains.kotlin.psi.*\nimport org.jetbrains.kotlin.psi.psiUtil.parents\nimport org.jetbrains.kotlin.resolve.inline.InlineUtil\nimport org.jetbrains.kotlin.types.expressions.OperatorConventions\nimport org.jetbrains.kotlin.util.OperatorNameConventions\n\nclass KotlinLanguageVisitor(private val sink: ComplexitySink) : ComplexityVisitor() {\n    override fun processElement(element: PsiElement) {\n        when (element) {\n            is KtWhileExpression -> sink.increaseComplexityAndNesting(PointType.LOOP_WHILE)\n            is KtDoWhileExpression -> sink.increaseComplexityAndNesting(PointType.LOOP_WHILE)\n            is KtWhenExpression -> sink.increaseComplexityAndNesting(PointType.SWITCH)\n            is KtIfExpression -> processIfExpression(element)\n            // `else if`\n            is KtContainerNodeForControlStructureBody -> {\n                if ((element.expression is KtIfExpression) && (element.firstChild is KtIfExpression)) {\n                    sink.decreaseNesting()\n                }\n            }\n\n            is KtForExpression -> sink.increaseComplexityAndNesting(PointType.LOOP_FOR)\n            is KtCatchClause -> sink.increaseComplexityAndNesting(PointType.CATCH)\n            is KtBreakExpression -> if (element.labelQualifier != null) sink.increaseComplexity(PointType.BREAK)\n            is KtContinueExpression -> if (element.labelQualifier != null) sink.increaseComplexity(PointType.CONTINUE)\n            is KtLambdaExpression -> sink.increaseNesting()\n            is KtBinaryExpression -> {\n                if (element.parent is KtStatementExpression || element.parent !is KtExpression) {\n                    element.calculateLogicalComplexity()\n                }\n            }\n\n            is KtNameReferenceExpression -> if (isRecursiveCall(element)) sink.increaseComplexity(PointType.RECURSION)\n        }\n    }\n\n    override fun postProcess(element: PsiElement) {\n        if (element is KtWhileExpression ||\n            element is KtWhenExpression ||\n            element is KtDoWhileExpression ||\n            element is KtForExpression ||\n            element is KtCatchClause ||\n            element is KtLambdaExpression ||\n            element is KtIfExpression && element.`else` !is KtIfExpression\n        ) {\n            sink.decreaseNesting()\n        }\n    }\n\n    override fun shouldVisitElement(element: PsiElement): Boolean = true\n\n\n    private fun processIfExpression(element: KtIfExpression) {\n        // if exists `else` that is not `else if`\n        val ktExpression = element.`else`\n        if (ktExpression != null && ktExpression !is KtIfExpression) {\n            sink.increaseComplexity(PointType.ELSE)\n        }\n\n        val parent = element.parent\n        if (parent is KtContainerNodeForControlStructureBody\n            && parent.expression is KtIfExpression\n        ) {\n            sink.increaseNesting()\n            sink.increaseComplexity(PointType.IF)\n        } else {\n            sink.increaseComplexityAndNesting(PointType.IF)\n        }\n    }\n\n    private fun KtExpression.calculateLogicalComplexity(prevToken: KtToken? = null): KtToken? {\n        var prevOperand = prevToken\n        this.children.forEach { element ->\n            when (element) {\n                is KtOperationReferenceExpression -> if (element.operationSignTokenType != null && element.operationSignTokenType in (getLogicalOperationsTokens())) {\n                    if (prevOperand == null || element.operationSignTokenType != prevOperand) {\n                        sink.increaseComplexity(element.operationSignTokenType!!.toPointType())\n                    }\n                    prevOperand = element.operationSignTokenType\n                }\n\n                is KtBinaryExpression -> prevOperand = element.calculateLogicalComplexity(prevOperand)\n                is KtPrefixExpression -> prevOperand = element.calculateLogicalComplexity(prevOperand)\n                is KtParenthesizedExpression -> {\n                    element.calculateLogicalComplexity()\n                    prevOperand = null\n                }\n            }\n        }\n        return prevOperand\n    }\n\n    private fun getLogicalOperationsTokens(): TokenSet {\n        return TokenSet.create(\n            KtTokens.ANDAND,\n            KtTokens.OROR\n        )\n    }\n\n    private fun getNegationOperationToken(): KtToken {\n        return KtTokens.EXCL\n    }\n\n    private fun getTempNegOperationToken(): KtToken {\n        return KtTokens.QUEST\n    }\n\n    /**\n     * Checking if recursion is used.\n     * Have to do it fast and dirty as it should be fast to avoid exception in IDEA.\n     * Basically we check:\n     *  - if the found reference expression name matches the direct parent method name. This code won't detect recursion\n     *    if more than one method involved (method A calling B and then B calling A)\n     *  - if the number of arguments matches the number of parameters\n     *\n     *  Possible improvements:\n     *   - check parameter types as well\n     */\n    private fun isRecursiveCall(element: KtNameReferenceExpression): Boolean {\n        val argumentList = element.nextSibling\n        if (argumentList is KtValueArgumentList) {\n            val parentMethod: KtNamedFunction = element.findCurrentMethod() ?: return false\n            if (element.getReferencedName() != parentMethod.nameIdentifier?.text) return false\n            if (argumentList.arguments.size != parentMethod.valueParameterList?.parameters?.size) return false\n            return true\n        }\n        return false\n    }\n\n    private fun getEnclosingFunction(\n        element: NavigatablePsiElement,\n        stopOnNonInlinedLambdas: Boolean,\n    ): KtNamedFunction? {\n        for (parent in element.parents) {\n            when (parent) {\n                is KtFunctionLiteral -> if (stopOnNonInlinedLambdas && !InlineUtil.isInlinedArgument(\n                        parent,\n                        parent.analyze(),\n                        false\n                    )\n                ) return null\n\n                is KtNamedFunction -> {\n                    when (parent.parent) {\n                        is KtBlockExpression, is KtClassBody, is KtFile, is KtScript -> return parent\n                        else -> if (stopOnNonInlinedLambdas && !InlineUtil.isInlinedArgument(\n                                parent,\n                                parent.analyze(),\n                                false\n                            )\n                        ) return null\n                    }\n                }\n\n                is KtClassOrObject -> return null\n            }\n        }\n        return null\n    }\n\n    private fun getCallNameFromPsi(element: KtElement): Name? {\n        when (element) {\n            is KtSimpleNameExpression -> when (val elementParent = element.getParent()) {\n                is KtCallExpression -> return Name.identifier(element.getText())\n                is KtOperationExpression -> {\n                    val operationReference = elementParent.operationReference\n                    if (element == operationReference) {\n                        val node = operationReference.getReferencedNameElementType()\n                        return if (node is KtToken) {\n                            val conventionName = if (elementParent is KtPrefixExpression)\n                                OperatorConventions.getNameForOperationSymbol(node, true, false)\n                            else\n                                OperatorConventions.getNameForOperationSymbol(node)\n\n                            conventionName ?: Name.identifier(element.getText())\n                        } else {\n                            Name.identifier(element.getText())\n                        }\n                    }\n                }\n            }\n\n            is KtArrayAccessExpression -> return OperatorNameConventions.GET\n            is KtThisExpression -> if (element.getParent() is KtCallExpression) return OperatorNameConventions.INVOKE\n        }\n\n        return null\n    }\n}\n\nprivate fun KtToken.toPointType(): PointType =\n    when (this) {\n        KtTokens.ANDAND -> PointType.LOGICAL_AND\n        KtTokens.OROR -> PointType.LOGICAL_OR\n        else -> PointType.UNKNOWN\n    }\n\nprivate fun PsiElement.findCurrentMethod(): KtNamedFunction? {\n    var element: PsiElement? = this\n    while (element != null && element !is KtNamedFunction) element = element.parent\n    return element?.let { it as KtNamedFunction }\n}\n"
  },
  {
    "path": "languages/shire-kotlin/src/main/kotlin/com/phodal/shirelang/kotlin/impl/KotlinRefactoringTool.kt",
    "content": "package com.phodal.shirelang.kotlin.impl\n\nimport com.intellij.codeInsight.daemon.impl.quickfix.RenameElementFix\nimport com.intellij.codeInsight.daemon.impl.quickfix.SafeDeleteFix\nimport com.intellij.codeInspection.MoveToPackageFix\nimport com.intellij.openapi.diagnostic.logger\nimport com.intellij.openapi.project.ProjectManager\nimport com.intellij.psi.*\nimport com.intellij.psi.search.FileTypeIndex\nimport com.intellij.psi.search.ProjectScope\nimport com.phodal.shirecore.provider.shire.RefactoringTool\nimport com.phodal.shirecore.variable.toolchain.refactoring.RefactorInstElement\nimport org.jetbrains.kotlin.idea.KotlinFileType\nimport org.jetbrains.kotlin.psi.KtFile\nimport org.jetbrains.kotlin.psi.KtNamedFunction\n\nclass KotlinRefactoringTool : RefactoringTool {\n    private val project = ProjectManager.getInstance().openProjects.firstOrNull()\n\n    override fun lookupFile(path: String): PsiFile? {\n        if (project == null) return null\n\n        val elementInfo = getElementInfo(path, null) ?: return null\n        val searchScope = ProjectScope.getProjectScope(project)\n\n        val ktFiles = FileTypeIndex.getFiles(KotlinFileType.INSTANCE, searchScope)\n            .mapNotNull { PsiManager.getInstance(project).findFile(it) as? KtFile }\n\n        val className = elementInfo.className\n        val packageName = elementInfo.pkgName\n\n        val sourceFile = ktFiles.firstOrNull {\n            it.packageFqName.asString() == packageName && it.name == \"$className.kt\"\n        } ?: return null\n\n        return sourceFile\n    }\n\n    override fun rename(sourceName: String, targetName: String, psiFile: PsiFile?): Boolean {\n        if (project == null) return false\n        val retrieveElementInfo = getElementInfo(sourceName, psiFile)\n        val elementInfo = if (retrieveElementInfo != null) {\n            retrieveElementInfo\n        } else {\n            return false\n        }\n\n        val element: PsiNamedElement =\n            if (psiFile != null) {\n                if (psiFile is KtFile) {\n                    val methodName = elementInfo.methodName\n                    val className = elementInfo.className\n                    val pkgName = elementInfo.pkgName\n\n                    if (elementInfo.isMethod) {\n                        val findClassAndMethod: PsiMethod? = psiFile.classes\n                            .firstOrNull {\n                                it.name == className\n                            }\n                            ?.findMethodsByName(methodName, false)?.firstOrNull()\n\n                        // lookup by function only\n                        findClassAndMethod ?: (psiFile.declarations\n                            .filterIsInstance<KtNamedFunction>()\n                            .firstOrNull {\n                                it.name == methodName\n                            } ?: return false)\n                    } else {\n                        psiFile.declarations\n                            .filterIsInstance<KtFile>()\n                            .firstOrNull {\n                                it.name == className && it.packageFqName.asString() == pkgName\n                            } ?: return false\n                    }\n                } else {\n                    return false\n                }\n            } else {\n                return false\n            }\n\n        val rename = RenameElementFix(element, targetName)\n\n        try {\n            rename.invoke(project, psiFile, element, element)\n        } catch (e: Exception) {\n            logger<KotlinRefactoringTool>().error(\"Error in renaming\", e)\n            return false\n        }\n\n        return true\n    }\n\n    private fun getElementInfo(input: String, psiFile: PsiFile?): RefactorInstElement? {\n        if (!input.contains(\"#\") && psiFile != null) {\n            val kotlinFile = psiFile as? KtFile ?: return null\n            val className = kotlinFile.classes.firstOrNull()?.name ?: return null\n            val canonicalName = kotlinFile.packageFqName.asString() + \".\" + className\n            return RefactorInstElement(true, true, input, canonicalName, className, kotlinFile.packageFqName.asString())\n        }\n\n        val isMethod = input.contains(\"#\")\n        val methodName = input.substringAfter(\"#\")\n        val canonicalName = input.substringBefore(\"#\")\n        val maybeClassName = canonicalName.substringAfterLast(\".\")\n        // the clasName should be Uppercase or it will be the package\n        var isClass = false\n        var pkgName = canonicalName.substringBeforeLast(\".\")\n        if (maybeClassName[0].isLowerCase()) {\n            pkgName = \"$pkgName.$maybeClassName\"\n        } else {\n            isClass = true\n        }\n\n        return RefactorInstElement(isClass, isMethod, methodName, canonicalName, maybeClassName, pkgName)\n    }\n\n    override fun safeDelete(element: PsiElement): Boolean {\n        val delete = SafeDeleteFix(element)\n        try {\n            delete.invoke(element.project, element.containingFile, element, element)\n        } catch (e: Exception) {\n            return false\n        }\n\n        return true\n    }\n\n    /**\n     * In Kotlin the canonicalName is the fully qualified name of the target package or class.\n     */\n    override fun move(element: PsiElement, canonicalName: String): Boolean {\n        val file = element.containingFile\n        val fix = MoveToPackageFix(file, canonicalName)\n\n        try {\n            fix.invoke(file.project, file, element, element)\n        } catch (e: Exception) {\n            return false\n        }\n\n        return true\n    }\n}\n"
  },
  {
    "path": "languages/shire-kotlin/src/main/kotlin/com/phodal/shirelang/kotlin/provider/KotlinAutoTestService.kt",
    "content": "package com.phodal.shirelang.kotlin.provider\n\nimport com.intellij.execution.configurations.RunProfile\nimport com.intellij.openapi.application.ReadAction\nimport com.intellij.openapi.application.runReadAction\nimport com.intellij.openapi.command.WriteCommandAction\nimport com.intellij.openapi.fileEditor.FileDocumentManager\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.project.guessProjectDir\nimport com.intellij.openapi.vfs.LocalFileSystem\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.intellij.openapi.vfs.VirtualFileManager\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.PsiFile\nimport com.intellij.psi.PsiManager\nimport com.intellij.psi.util.PsiTreeUtil\nimport com.phodal.shirecore.provider.TestingService\nimport com.phodal.shirecore.provider.codemodel.ClassStructureProvider\nimport com.phodal.shirecore.provider.codemodel.FileStructureProvider\nimport com.phodal.shirecore.provider.codemodel.model.ClassStructure\nimport com.phodal.shirecore.variable.toolchain.unittest.AutoTestingPromptContext\nimport com.phodal.shirelang.kotlin.KotlinPsiUtil\nimport com.phodal.shirelang.kotlin.KotlinTypeResolver\nimport com.phodal.shirelang.kotlin.getReturnTypeReferences\nimport org.jetbrains.kotlin.idea.KotlinLanguage\nimport org.jetbrains.kotlin.idea.references.mainReference\nimport org.jetbrains.kotlin.psi.*\nimport org.jetbrains.kotlin.psi.psiUtil.getValueParameters\nimport org.jetbrains.plugins.gradle.service.execution.GradleRunConfiguration\nimport java.io.File\n\nclass KotlinAutoTestService : TestingService() {\n    override fun isApplicable(element: PsiElement): Boolean = element.language is KotlinLanguage\n    override fun runConfigurationClass(project: Project): Class<out RunProfile> = GradleRunConfiguration::class.java\n    override fun isApplicable(project: Project, file: VirtualFile): Boolean {\n        return (file.extension == \"kt\" || file.extension == \"kts\") && PsiManager.getInstance(project)\n            .findFile(file) is KotlinLanguage\n    }\n\n    override fun findOrCreateTestFile(\n        sourceFile: PsiFile,\n        project: Project,\n        psiElement: PsiElement,\n    ): AutoTestingPromptContext? {\n        val sourceFilePath = sourceFile.virtualFile\n        val parentDir = sourceFilePath.parent\n        val className = sourceFile.name.replace(\".kt\", \"\") + \"Test\"\n\n        val parentDirPath = ReadAction.compute<String, Throwable> {\n            parentDir?.path\n        } ?: return null\n\n        val relatedModels = lookupRelevantClass(project, psiElement).distinctBy { it.name }\n\n        if (!(parentDirPath.contains(\"/main/java\") || parentDirPath.contains(\"/main/kotlin\"))) {\n            log.error(\"SourceFile is not under the main/kotlin or main/java directory: $parentDirPath\")\n            return null\n        }\n\n        var isNewFile = false\n\n        val testDirPath = parentDir.path\n            .replace(\"/main/kotlin/\", \"/test/kotlin/\")\n            .replace(\"/main/java/\", \"/test/java/\")\n\n        var testDir = LocalFileSystem.getInstance().findFileByPath(testDirPath)\n\n        if (testDir == null || !testDir.isDirectory) {\n            isNewFile = true\n            // Create the test directory if it doesn't exist\n            val testDirFile = File(testDirPath)\n            if (!testDirFile.exists()) {\n                testDirFile.mkdirs()\n\n                LocalFileSystem.getInstance().refreshAndFindFileByPath(testDirPath)?.let { refreshedDir ->\n                    testDir = refreshedDir\n                }\n            }\n        }\n\n        val testDirCreated: VirtualFile? =\n            VirtualFileManager.getInstance().refreshAndFindFileByUrl(\"file://$testDirPath\")\n        if (testDirCreated == null) {\n            log.error(\"Failed to create test directory: $testDirPath\")\n            return null\n        }\n\n        // Test directory already exists, find the corresponding test file\n        val testFilePath = testDirPath + \"/\" + sourceFile.name.replace(\".kt\", \"Test.kt\")\n        val testFile = LocalFileSystem.getInstance().findFileByPath(testFilePath)\n\n        project.guessProjectDir()?.refresh(true, true)\n\n        val currentClass: String = ReadAction.compute<String, Throwable> {\n            val classContext = when (psiElement) {\n                is KtFile -> FileStructureProvider.from(psiElement)\n                is KtClassOrObject -> ClassStructureProvider.from(psiElement)\n                is KtNamedFunction -> {\n                    PsiTreeUtil.getParentOfType(psiElement, KtClassOrObject::class.java)?.let {\n                        ClassStructureProvider.from(it)\n                    }\n                }\n\n                else -> null\n            }\n\n            return@compute classContext?.format() ?: \"\"\n        }\n\n        val imports: List<String> = runReadAction {\n            (sourceFile as KtFile).importList?.imports?.map { it.text } ?: emptyList()\n        }\n\n        val relatedCode = relatedModels.map { it.format() }\n\n        return if (testFile != null) {\n            AutoTestingPromptContext(isNewFile, testFile, relatedCode, className, sourceFile.language, currentClass, imports)\n        } else {\n            val targetFile = createTestFile(sourceFile, testDir!!, project)\n            AutoTestingPromptContext(isNewFile = true, targetFile, relatedCode, \"\", sourceFile.language, currentClass, imports)\n        }\n    }\n\n    override fun lookupRelevantClass(project: Project, element: PsiElement): List<ClassStructure> {\n        return ReadAction.compute<List<ClassStructure>, Throwable> {\n            val elements = mutableListOf<ClassStructure>()\n            val projectPath = project.guessProjectDir()?.path\n\n            val resolvedClasses = KotlinTypeResolver.resolveByElement(element)\n\n            // find the class in the same project\n            resolvedClasses.forEach { (_, psiClass) ->\n                val classPath = psiClass?.containingFile?.virtualFile?.path\n                if (classPath?.contains(projectPath!!) == true) {\n                    elements += ClassStructureProvider.from(psiClass) ?: return@forEach\n                }\n            }\n\n            elements\n        }\n    }\n\n    private fun createTestFile(\n        sourceFile: PsiFile,\n        testDir: VirtualFile,\n        project: Project\n    ): VirtualFile {\n        val sourceFileName = sourceFile.name\n        val testFileName = sourceFileName.replace(\".kt\", \"Test.kt\")\n        val testFileContent = \"\"\n\n        return WriteCommandAction.runWriteCommandAction<VirtualFile>(project) {\n            val testFile = testDir.createChildData(this, testFileName)\n\n            val document = FileDocumentManager.getInstance().getDocument(testFile)\n            document?.setText(testFileContent)\n            testFile\n        }\n    }\n}\n"
  },
  {
    "path": "languages/shire-kotlin/src/main/kotlin/com/phodal/shirelang/kotlin/provider/KotlinPsiElementDataBuilder.kt",
    "content": "package com.phodal.shirelang.kotlin.provider\n\nimport com.intellij.openapi.application.runReadAction\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.roots.ProjectFileIndex\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.PsiNameIdentifierOwner\nimport com.intellij.psi.util.PsiTreeUtil\nimport com.intellij.psi.util.PsiUtil\nimport com.phodal.shirecore.provider.psi.PsiElementDataBuilder\nimport com.phodal.shirelang.kotlin.codemodel.KotlinClassStructureProvider\nimport org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.getReturnTypeReference\nimport org.jetbrains.kotlin.idea.references.mainReference\nimport org.jetbrains.kotlin.psi.*\nimport org.jetbrains.kotlin.psi.psiUtil.getContentRange\n\nclass KotlinPsiElementDataBuilder : PsiElementDataBuilder {\n    override fun baseRoute(element: PsiElement): String {\n        if (element !is KtNamedFunction) return \"\"\n\n        val clazz = PsiTreeUtil.getParentOfType(element, PsiNameIdentifierOwner::class.java)\n        if (clazz !is KtClass) return \"\"\n\n        clazz.annotationEntries.forEach {\n            when {\n                it.shortName?.asString() == \"RequestMapping\" -> {\n                    return when (val value = it.valueArguments.firstOrNull()?.getArgumentExpression()) {\n                        is KtStringTemplateExpression -> value.literalContents() ?: value.text\n                        is KtSimpleNameExpression -> value.getReferencedName()\n                        else -> \"\"\n                    }\n                }\n            }\n        }\n\n        return \"\"\n    }\n\n    override fun inboundData(element: PsiElement): Map<String, String> {\n        if (element !is KtNamedFunction) return emptyMap()\n\n        return handleParameters(element.valueParameters)\n    }\n\n    private fun handleParameters(ktParameters: MutableList<KtParameter>): MutableMap<String, String> {\n        val result = mutableMapOf<String, String>()\n        ktParameters.map { parameter ->\n            result += handleFromType(parameter)\n        }\n\n        return result\n    }\n\n    private fun handleFromType(parameter: KtParameter): Map<String, String> {\n        val map = when (val type = parameter.typeReference?.typeElement) {\n            is KtClass -> processingClassType(type)\n            else -> emptyMap()\n        }\n\n        return map\n    }\n\n\n    private fun processingClassType(type: KtClass): Map<String, String> {\n        if (!isProjectContent(type)) return emptyMap()\n\n        val result = mutableMapOf<String, String>()\n        val fqn = type.fqName?.asString() ?: return result\n\n        KotlinClassStructureProvider().build(type, false)?.format()?.let {\n            result += mapOf(fqn to it)\n        }\n\n        return result\n    }\n\n    override fun outboundData(element: PsiElement): Map<String, String> {\n        if (element is KtClass) {\n            return processingMethodsOutbound(element)\n        }\n\n        if (element !is KtNamedFunction) return emptyMap()\n\n        val returnType = element.getReturnTypeReference() ?: return emptyMap()\n\n        return processing(returnType, element)\n    }\n\n    override fun parseComment(project: Project, code: String): String? {\n        return null\n    }\n\n    /**\n     * Processes the outbound methods of a Kotlin class and returns a map of method names and their return types.\n     *\n     * @param element The Kotlin class element to process.\n     * @return A map of method names and their return types.\n     */\n    private fun processingMethodsOutbound(element: KtClass): Map<String, String> {\n        val result = mutableMapOf<String, String>()\n        val methods = element.declarations.filterIsInstance<KtNamedFunction>()\n        for (method in methods) {\n            val parameters = handleParameters(method.valueParameters)\n            result += parameters\n\n            val returnType = method.getReturnTypeReference() ?: continue\n            result += processing(returnType, element)\n        }\n\n        return result\n    }\n\n    private fun processing(returnType: KtTypeReference, element: PsiElement): Map<String, String> {\n        val result = mutableMapOf<String, String>()\n        when (val typeElement = returnType.typeElement) {\n            is KtUserType -> {\n                val referenceExpression = typeElement.referenceExpression?.resolveMainReference()\n                if (referenceExpression is KtClass) {\n                    result += processingClassType(referenceExpression)\n                }\n\n                typeElement.typeArgumentsAsTypes.forEach {\n                    result += processing(it, element)\n                }\n            }\n        }\n\n        return result\n    }\n}\n\ninternal fun KtStringTemplateExpression.literalContents(): String? {\n    val escaper = createLiteralTextEscaper()\n    val ssb = StringBuilder()\n    return when (escaper.decode(getContentRange(), ssb)) {\n        true -> ssb.toString()\n        false -> null\n    }\n}\n\nfun KtReferenceExpression.resolveMainReference(): PsiElement? =\n    try {\n        mainReference.resolve()\n    } catch (e: Exception) {\n        null\n    }\n\nfun isProjectContent(element: PsiElement): Boolean {\n    val virtualFile = PsiUtil.getVirtualFile(element)\n    val project = runReadAction { element.project }\n    return virtualFile == null || ProjectFileIndex.getInstance(project).isInContent(virtualFile)\n}\n"
  },
  {
    "path": "languages/shire-kotlin/src/main/kotlin/com/phodal/shirelang/kotlin/provider/KotlinRelatedClassesProvider.kt",
    "content": "package com.phodal.shirelang.kotlin.provider\n\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.PsiFile\nimport com.phodal.shirecore.provider.psi.RelatedClassesProvider\nimport com.phodal.shirelang.kotlin.KotlinTypeResolver\n\nclass KotlinRelatedClassesProvider : RelatedClassesProvider {\n    override fun lookup(element: PsiElement): List<PsiElement> {\n        return KotlinTypeResolver.resolveByElement(element).values.filterNotNull().toList()\n    }\n\n    override fun lookup(element: PsiFile): List<PsiElement> {\n        return KotlinTypeResolver.resolveByElement(element).values.filterNotNull().toList()\n    }\n}\n"
  },
  {
    "path": "languages/shire-kotlin/src/main/kotlin/com/phodal/shirelang/kotlin/variable/KotlinLanguageToolchainProvider.kt",
    "content": "package com.phodal.shirelang.kotlin.variable\n\nimport com.intellij.openapi.application.runReadAction\nimport com.intellij.openapi.project.Project\nimport com.phodal.shirecore.provider.context.LanguageToolchainProvider\nimport com.phodal.shirecore.provider.context.ToolchainContextItem\nimport com.phodal.shirecore.provider.context.ToolchainPrepareContext\nimport org.jetbrains.kotlin.idea.KotlinLanguage\nimport org.jetbrains.kotlin.idea.base.projectStructure.languageVersionSettings\n\nclass KotlinLanguageToolchainProvider : LanguageToolchainProvider {\n    override fun isApplicable(project: Project, context: ToolchainPrepareContext): Boolean {\n        return context.sourceFile?.language is KotlinLanguage\n    }\n\n    override suspend fun collect(project: Project, context: ToolchainPrepareContext): List<ToolchainContextItem> {\n        val languageVersionSettings = runReadAction {\n            project.languageVersionSettings\n        }\n\n        val languageVersion = languageVersionSettings.languageVersion.versionString\n        return listOf(\n            ToolchainContextItem(\n                KotlinLanguageToolchainProvider::class,\n                \"You are working on a project that uses Kotlin API version: $languageVersion\"\n            )\n        )\n    }\n}\n"
  },
  {
    "path": "languages/shire-kotlin/src/main/kotlin/com/phodal/shirelang/kotlin/variable/KotlinPsiContextVariableProvider.kt",
    "content": "package com.phodal.shirelang.kotlin.variable\n\nimport org.jetbrains.kotlin.idea.KotlinLanguage\nimport com.intellij.openapi.editor.Editor\nimport com.intellij.openapi.project.Project\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.util.PsiTreeUtil\nimport com.intellij.testIntegration.TestFinderHelper\nimport com.phodal.shirecore.provider.variable.PsiContextVariableProvider\nimport com.phodal.shirecore.provider.variable.model.PsiContextVariable\nimport com.phodal.shirecore.psi.CodeSmellCollector\nimport com.phodal.shirelang.kotlin.codemodel.KotlinClassStructureProvider\nimport com.phodal.shirelang.kotlin.provider.KotlinRelatedClassesProvider\nimport org.jetbrains.kotlin.psi.KtClassOrObject\nimport org.jetbrains.kotlin.psi.KtImportList\nimport org.jetbrains.kotlin.psi.KtNamedFunction\n\nclass KotlinPsiContextVariableProvider : PsiContextVariableProvider {\n    override fun resolve(variable: PsiContextVariable, project: Project, editor: Editor, psiElement: PsiElement?): Any {\n        if (psiElement?.language != KotlinLanguage.INSTANCE) return \"\"\n\n        val psiFile = psiElement.containingFile\n        val importList = PsiTreeUtil.getChildrenOfTypeAsList(psiFile, KtImportList::class.java)\n\n        return when (variable) {\n            PsiContextVariable.CURRENT_CLASS_NAME -> {\n                val clazz: KtClassOrObject = PsiTreeUtil.getParentOfType(psiElement, KtClassOrObject::class.java) ?: return \"\"\n                clazz.name ?: \"\"\n            }\n            PsiContextVariable.CURRENT_CLASS_CODE -> {\n                val clazz: KtClassOrObject = PsiTreeUtil.getParentOfType(psiElement, KtClassOrObject::class.java) ?: return \"\"\n                clazz.text\n            }\n            PsiContextVariable.CURRENT_METHOD_NAME -> {\n                when (psiElement) {\n                    is KtClassOrObject -> psiElement.name ?: \"\"\n                    is KtNamedFunction -> psiElement.name ?: \"\"\n                    else -> psiElement.text\n                }\n            }\n            PsiContextVariable.CURRENT_METHOD_CODE -> {\n                when (psiElement) {\n                    is KtClassOrObject -> psiElement.text ?: \"\"\n                    is KtNamedFunction -> psiElement.bodyExpression?.text ?: \"\"\n                    else -> psiElement.text\n                }\n            }\n            PsiContextVariable.RELATED_CLASSES -> {\n                KotlinRelatedClassesProvider().lookup(psiElement.parent).joinToString(\"\\n\") { it.text }\n            }\n            PsiContextVariable.SIMILAR_TEST_CASE -> { \"\" }\n            PsiContextVariable.IMPORTS -> {\n                importList.joinToString(\"\\n\") { it.text }\n            }\n            PsiContextVariable.IS_NEED_CREATE_FILE -> TestFinderHelper.findClassesForTest(psiElement).isEmpty()\n            PsiContextVariable.TARGET_TEST_FILE_NAME -> psiFile.name.replace(\".kt\", \"\") + \"Test.kt\"\n            PsiContextVariable.UNDER_TEST_METHOD_CODE -> { \"\" }\n            PsiContextVariable.FRAMEWORK_CONTEXT -> collectFrameworkContext(psiElement, project)\n            PsiContextVariable.CODE_SMELL -> CodeSmellCollector.collectElementProblemAsSting(psiElement, project, editor)\n            PsiContextVariable.METHOD_CALLER -> TODO()\n            PsiContextVariable.CALLED_METHOD -> TODO()\n            PsiContextVariable.SIMILAR_CODE -> TODO()\n            PsiContextVariable.STRUCTURE -> when (psiElement) {\n                is KtClassOrObject -> KotlinClassStructureProvider().build(psiElement, true)?.toString() ?: \"\"\n                else -> \"\"\n            }\n            PsiContextVariable.CHANGE_COUNT -> calculateChangeCount(psiElement)\n            PsiContextVariable.LINE_COUNT -> calculateLineCount(psiElement)\n            PsiContextVariable.COMPLEXITY_COUNT -> calculateComplexityCount(psiElement)\n        }\n    }\n\n}\n"
  },
  {
    "path": "languages/shire-kotlin/src/main/resources/com.phodal.shirelang.kotlin.xml",
    "content": "<idea-plugin package=\"com.phodal.shirelang.kotlin\">\n    <!--suppress PluginXmlValidity -->\n    <dependencies>\n        <plugin id=\"org.jetbrains.kotlin\"/>\n        <plugin id=\"org.jetbrains.plugins.gradle\"/>\n    </dependencies>\n\n    <extensions defaultExtensionNs=\"com.phodal\">\n        <fileStructureProvider language=\"kotlin\"\n                               implementationClass=\"com.phodal.shirelang.kotlin.codemodel.KotlinFileStructureProvider\"/>\n        <classStructureProvider language=\"kotlin\"\n                                implementationClass=\"com.phodal.shirelang.kotlin.codemodel.KotlinClassStructureProvider\"/>\n        <methodStructureProvider language=\"kotlin\"\n                                 implementationClass=\"com.phodal.shirelang.kotlin.codemodel.KotlinMethodStructureProvider\"/>\n        <variableStructureProvider language=\"kotlin\"\n                                   implementationClass=\"com.phodal.shirelang.kotlin.codemodel.KotlinVariableStructureProvider\"/>\n\n        <shireRefactoringTool\n                language=\"kotlin\"\n                implementationClass=\"com.phodal.shirelang.kotlin.impl.KotlinRefactoringTool\"/>\n\n        <shireLanguageToolchainProvider\n                language=\"kotlin\"\n                implementationClass=\"com.phodal.shirelang.kotlin.variable.KotlinLanguageToolchainProvider\"/>\n\n        <shireComplexityProvider language=\"kotlin\"\n                                 implementationClass=\"com.phodal.shirelang.kotlin.complexity.KotlinComplexityProvider\"/>\n\n        <shireRelatedClass language=\"kotlin\"\n                           implementationClass=\"com.phodal.shirelang.kotlin.provider.KotlinRelatedClassesProvider\"/>\n\n        <shireAutoTesting language=\"kotlin\"\n                          implementationClass=\"com.phodal.shirelang.kotlin.provider.KotlinAutoTestService\"/>\n\n        <shirePsiElementDataBuilder language=\"kotlin\"\n                                    implementationClass=\"com.phodal.shirelang.kotlin.provider.KotlinPsiElementDataBuilder\"/>\n\n        <shirePsiVariableProvider\n                language=\"kotlin\"\n                implementationClass=\"com.phodal.shirelang.kotlin.variable.KotlinPsiContextVariableProvider\"/>\n    </extensions>\n</idea-plugin>\n"
  },
  {
    "path": "languages/shire-markdown/src/main/kotlin/com/phodal/shirelang/markdown/MarkdownNode.kt",
    "content": "package com.phodal.shirelang.markdown\n\nimport org.intellij.markdown.IElementType\nimport org.intellij.markdown.MarkdownElementTypes\nimport org.intellij.markdown.MarkdownTokenTypes\nimport org.intellij.markdown.ast.ASTNode\nimport org.intellij.markdown.flavours.gfm.GFMElementTypes\nimport org.intellij.markdown.flavours.gfm.GFMTokenTypes\n\nclass MarkdownNode(val node: ASTNode, val parent: MarkdownNode?, val rootText: String) {\n    val children: List<MarkdownNode> = node.children.map { MarkdownNode(it, this, rootText) }\n    val endOffset: Int get() = node.endOffset\n    val startOffset: Int get() = node.startOffset\n    val type: IElementType get() = node.type\n    val text: String get() = rootText.substring(startOffset, endOffset)\n\n    fun child(type: IElementType): MarkdownNode? = children.firstOrNull { it.type == type }\n}\n\nprivate fun MarkdownNode.capture(): MutableList<String> {\n    val result = mutableListOf<String>()\n    visit { node, processChildren ->\n        fun wrapChildren(tag: String, newline: Boolean = false) {\n            processChildren()\n        }\n\n        val nodeType = node.type\n        val nodeText = node.text\n        println(\"nodeType: $nodeType, nodeText: $nodeText\")\n\n        when (nodeType) {\n            MarkdownElementTypes.UNORDERED_LIST -> wrapChildren(\"ul\", newline = true)\n            MarkdownElementTypes.ORDERED_LIST -> wrapChildren(\"ol\", newline = true)\n            MarkdownElementTypes.LIST_ITEM -> wrapChildren(\"li\")\n            MarkdownElementTypes.EMPH -> wrapChildren(\"em\")\n            MarkdownElementTypes.STRONG -> wrapChildren(\"strong\")\n            GFMElementTypes.STRIKETHROUGH -> wrapChildren(\"del\")\n            MarkdownElementTypes.ATX_1 -> wrapChildren(\"h1\")\n            MarkdownElementTypes.ATX_2 -> wrapChildren(\"h2\")\n            MarkdownElementTypes.ATX_3 -> wrapChildren(\"h3\")\n            MarkdownElementTypes.ATX_4 -> wrapChildren(\"h4\")\n            MarkdownElementTypes.ATX_5 -> wrapChildren(\"h5\")\n            MarkdownElementTypes.ATX_6 -> wrapChildren(\"h6\")\n            MarkdownElementTypes.BLOCK_QUOTE -> wrapChildren(\"blockquote\")\n            MarkdownElementTypes.PARAGRAPH -> wrapChildren(\"p\", newline = true)\n                MarkdownElementTypes.CODE_SPAN,\n                MarkdownElementTypes.CODE_BLOCK,\n                MarkdownElementTypes.CODE_FENCE,\n                MarkdownTokenTypes.FENCE_LANG,\n                MarkdownTokenTypes.CODE_LINE,\n                MarkdownTokenTypes.CODE_FENCE_CONTENT,\n                        -> {\n                        // skip\n                }\n\n                MarkdownElementTypes.SHORT_REFERENCE_LINK,\n                MarkdownElementTypes.FULL_REFERENCE_LINK,\n                        -> {\n                        val linkLabelNode = node.child(MarkdownElementTypes.LINK_LABEL)\n                        val linkLabelContent = linkLabelNode?.children\n                                ?.dropWhile { it.type == MarkdownTokenTypes.LBRACKET }\n                                ?.dropLastWhile { it.type == MarkdownTokenTypes.RBRACKET }\n\n                        if (linkLabelContent != null) {\n                                val label = linkLabelContent.joinToString(separator = \"\") { it.text }\n                                val linkText = node.child(MarkdownElementTypes.LINK_TEXT)?.text ?: label\n                                result.add(linkText)\n                        }\n                }\n\n            MarkdownElementTypes.INLINE_LINK -> {\n                val destination = node.child(MarkdownElementTypes.LINK_DESTINATION)?.text\n                result.add(destination ?: \"\")\n            }\n\n                MarkdownTokenTypes.TEXT,\n                MarkdownTokenTypes.WHITE_SPACE,\n                MarkdownTokenTypes.COLON,\n                MarkdownTokenTypes.SINGLE_QUOTE,\n                MarkdownTokenTypes.DOUBLE_QUOTE,\n                MarkdownTokenTypes.LPAREN,\n                MarkdownTokenTypes.RPAREN,\n                MarkdownTokenTypes.LBRACKET,\n                MarkdownTokenTypes.RBRACKET,\n                MarkdownTokenTypes.EXCLAMATION_MARK,\n                GFMTokenTypes.CHECK_BOX,\n                GFMTokenTypes.GFM_AUTOLINK,\n                        -> {\n                        result.add(nodeText)\n                }\n\n            MarkdownTokenTypes.EOL -> {}\n            MarkdownTokenTypes.GT -> {}\n            MarkdownTokenTypes.LT -> {}\n            MarkdownElementTypes.LINK_TEXT -> {}\n            MarkdownTokenTypes.EMPH -> {}\n            GFMTokenTypes.TILDE -> {}\n            GFMElementTypes.TABLE -> {}\n\n            // ignore image\n            MarkdownElementTypes.IMAGE -> {}\n\n            else -> {\n//                    println(\"unknown type: $nodeType\")\n                processChildren()\n            }\n        }\n    }\n\n    return result\n}\n\nprivate fun MarkdownNode.visit(action: (MarkdownNode, () -> Unit) -> Unit) {\n    action(this) {\n        for (child in children) {\n            child.visit(action)\n        }\n    }\n}\n"
  },
  {
    "path": "languages/shire-markdown/src/main/kotlin/com/phodal/shirelang/markdown/MarkdownPsiCapture.kt",
    "content": "package com.phodal.shirelang.markdown\n\nimport com.phodal.shirecore.provider.psi.PsiCapture\nimport org.intellij.markdown.IElementType\nimport org.intellij.markdown.MarkdownElementTypes\nimport org.intellij.markdown.ast.ASTNode\nimport org.intellij.markdown.ast.accept\nimport org.intellij.markdown.ast.visitors.RecursiveVisitor\nimport org.intellij.markdown.flavours.gfm.GFMFlavourDescriptor\nimport org.intellij.markdown.flavours.gfm.GFMTokenTypes\nimport org.intellij.markdown.parser.MarkdownParser\n\nclass MarkdownPsiCapture: PsiCapture {\n    private val embeddedHtmlType = IElementType(\"ROOT\")\n\n    /**\n     * Capture markdown text with ast node Node\n     */\n    override fun capture(fileContent: String, type: String): List<String> {\n        val flavour = GFMFlavourDescriptor()\n        val parsedTree: ASTNode = MarkdownParser(flavour).parse(embeddedHtmlType, fileContent)\n\n        val types: List<String> = when (type) {\n            /**\n             *  [GFMTokenTypes.GFM_AUTOLINK] , [MarkdownElementTypes.INLINE_LINK]\n             */\n            \"link\" -> listOf(\"GFM_AUTOLINK\")\n            else -> listOf()\n        }\n\n        // Traverse the AST to find and process nodes of the specified type\n        val result = mutableListOf<String>()\n        parsedTree.accept(object : RecursiveVisitor() {\n            override fun visitNode(node: ASTNode) {\n                when {\n                    // ignore image\n                    node.type == MarkdownElementTypes.IMAGE -> {\n                        return\n                    }\n                    types.contains(node.type.name) -> {\n                        result.add(fileContent.substring(node.startOffset, node.endOffset))\n                    }\n\n                    node.type.name.lowercase() == type -> {\n                        result.add(fileContent.substring(node.startOffset, node.endOffset))\n                    }\n                }\n\n                super.visitNode(node)\n            }\n        })\n\n        return result.map { it.trim() }\n    }\n}"
  },
  {
    "path": "languages/shire-markdown/src/main/resources/com.phodal.shirelang.markdown.xml",
    "content": "<idea-plugin package=\"com.phodal.shirelang.markdown\">\n    <!--suppress PluginXmlValidity -->\n    <dependencies>\n        <plugin id=\"org.intellij.plugins.markdown\"/>\n    </dependencies>\n\n    <extensions defaultExtensionNs=\"com.phodal\">\n        <shirePsiCapture language=\"Markdown\"\n                         implementationClass=\"com.phodal.shirelang.markdown.MarkdownPsiCapture\" />\n    </extensions>\n</idea-plugin>\n"
  },
  {
    "path": "languages/shire-markdown/src/test/kotlin/com/phodal/shirelang/markdown/MarkdownPsiCaptureTest.kt",
    "content": "import com.phodal.shirelang.markdown.MarkdownPsiCapture\nimport junit.framework.TestCase.assertEquals\nimport org.intellij.lang.annotations.Language\nimport org.junit.Test\n\nclass MarkdownPsiCaptureTest {\n\n    @Test\n    fun should_return_url_when_gfm_auto_link() {\n        @Language(\"Markdown\")\n        val markdownText = \"\"\"\n            normal text link: https://shire.phodal.com\n        \"\"\".trimIndent()\n\n        val markdownPsiCapture = MarkdownPsiCapture()\n\n        // when\n        val result = markdownPsiCapture.capture(markdownText, \"link\")\n\n        // then\n        assertEquals(\"https://shire.phodal.com\", result.first())\n    }\n\n    @Test\n    fun should_return_url_when_markdown_label_link() {\n        @Language(\"Markdown\")\n        val markdownText = \"\"\"\n            normal text link: [Shire](https://shire.phodal.com)\n            link 2: https://aise.phodal.com\n        \"\"\".trimIndent()\n        val markdownPsiCapture = MarkdownPsiCapture()\n\n        // when\n        val result = markdownPsiCapture.capture(markdownText, \"link\")\n\n        // then\n        assertEquals(\"https://shire.phodal.com\", result.first())\n        assertEquals(\"https://aise.phodal.com\", result[1])\n    }\n\n    @Test\n    fun should_ignore_image_url() {\n        @Language(\"Markdown\")\n        val markdownText = \"\"\"\n            hello sample \n            \n            ![Shire](https://shire.phodal.com/images/pluginIcon.svg)\n            \"\"\".trimIndent()\n        val markdownPsiCapture = MarkdownPsiCapture()\n\n        // when\n        val result = markdownPsiCapture.capture(markdownText, \"link\")\n\n        // then\n        assertEquals(0, result.size)\n    }\n\n    @Test\n    fun shouldParseLinkInList() {\n        @Language(\"Markdown\")\n        val markdownText = \"\"\"\n1. [aichat](https://github.com/sigoden/aichat) - <small>all-in-one AI powered CLI chat and copilot.</small>\n2. [aider](https://github.com/paul-gauthier/aider) - <small>AI pair programming in your terminal</small>\n3. [elia](https://github.com/darrenburns/elia) - <small>A TUI ChatGPT client built with Textual</small>\n4. [gpterminator](https://github.com/AineeJames/ChatGPTerminator) - <small>A TUI for OpenAI's ChatGPT</small>\n5. [gtt](https://github.com/eeeXun/gtt) - <small>A TUI for Google Translate, ChatGPT, DeepL and other AI services.</small>\n6. [nvitop](https://github.com/XuehaiPan/nvitop) - <small>An interactive NVIDIA-GPU process viewer and beyond.</small>\n7. [nvtop](https://github.com/Syllo/nvtop) - <small>NVIDIA GPUs htop like monitoring tool</small>\n8. [ollama](https://github.com/ollama/ollama) - <small>get up and running with large language models locally.</small>\n9. [oterm](https://github.com/ggozad/oterm) - <small>A text-based terminal client for ollama.</small>\n10. [tgpt](https://github.com/aandrew-me/tgpt) - <small>AI Chatbots in the terminal without needing API keys.</small>\n11. [yai](https://github.com/ekkinox/yai) - <small>Your AI powered terminal assistant</small>\n        \"\"\".trimMargin()\n\n        val markdownPsiCapture = MarkdownPsiCapture()\n\n        val result = markdownPsiCapture.capture(markdownText, \"link\")\n\n        assertEquals(11, result.size)\n    }\n}\n"
  },
  {
    "path": "languages/shire-proto/src/main/kotlin/com/phodal/shirelang/proto/codemodel/ProtoClassStructureProvider.kt",
    "content": "package com.phodal.shirelang.proto.codemodel\n\nimport com.intellij.protobuf.lang.psi.PbDefinition\nimport com.intellij.protobuf.lang.psi.PbMessageDefinition\nimport com.intellij.protobuf.lang.psi.PbServiceDefinition\nimport com.intellij.protobuf.lang.psi.util.PbPsiUtil\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.PsiNameIdentifierOwner\nimport com.intellij.psi.PsiReference\nimport com.intellij.psi.search.GlobalSearchScope\nimport com.intellij.psi.search.searches.ReferencesSearch\nimport com.phodal.shirecore.provider.codemodel.ClassStructureProvider\nimport com.phodal.shirecore.provider.codemodel.model.ClassStructure\n\nclass ProtoClassStructureProvider : ClassStructureProvider {\n    override fun build(psiElement: PsiElement, gatherUsages: Boolean): ClassStructure? {\n        if (psiElement !is PbDefinition) return null\n\n        return when (psiElement) {\n            is PbMessageDefinition -> {\n                val text = psiElement.text\n                val name = psiElement.name\n\n                val usages =\n                    if (gatherUsages) findUsages(psiElement as PsiNameIdentifierOwner) else emptyList()\n\n                val fields = mutableListOf<PsiElement>()\n                psiElement.body?.simpleFieldList?.let { fields += it }\n                psiElement.body?.mapFieldList?.let { fields += it }\n                psiElement.body?.oneofDefinitionList?.let { fields += it }\n\n                ClassStructure(\n                    psiElement,\n                    text,\n                    name,\n                    name,\n                    emptyList(),\n                    fields,\n                    null,\n                    emptyList(),\n                    usages\n                )\n            }\n\n            is PbServiceDefinition -> {\n                val text = psiElement.text\n                val name = psiElement.name\n\n                val methods = psiElement.body?.serviceMethodList ?: emptyList()\n\n                val usages =\n                    if (gatherUsages) findUsages(psiElement as PsiNameIdentifierOwner) else emptyList()\n\n                ClassStructure(\n                    psiElement,\n                    text,\n                    name,\n                    name,\n                    methods,\n                    emptyList(),\n                    null,\n                    emptyList(),\n                    usages\n                )\n            }\n\n            else -> {\n                return null\n            }\n        }\n    }\n\n    companion object {\n        fun findUsages(psiElement: PsiElement): List<PsiReference> {\n            val globalSearchScope = GlobalSearchScope.allScope(psiElement.project)\n\n            return ReferencesSearch.search(psiElement, globalSearchScope, true)\n                .findAll()\n                .toList()\n        }\n    }\n}\n"
  },
  {
    "path": "languages/shire-proto/src/main/kotlin/com/phodal/shirelang/proto/codemodel/ProtoFileStructureProvider.kt",
    "content": "package com.phodal.shirelang.proto.codemodel\n\nimport com.intellij.protobuf.lang.psi.PbImportStatement\nimport com.intellij.protobuf.lang.psi.PbMessageDefinition\nimport com.intellij.protobuf.lang.psi.PbPackageStatement\nimport com.intellij.protobuf.lang.psi.PbServiceDefinition\nimport com.intellij.psi.PsiFile\nimport com.intellij.psi.util.PsiTreeUtil\nimport com.phodal.shirecore.provider.codemodel.FileStructureProvider\nimport com.phodal.shirecore.provider.codemodel.model.FileStructure\nimport com.phodal.shirecore.relativePath\n\nclass ProtoFileStructureProvider : FileStructureProvider {\n    override fun build(psiFile: PsiFile): FileStructure? {\n        val name = psiFile.name\n        val path = if (psiFile.virtualFile != null) psiFile.virtualFile!!.relativePath(psiFile.project) else \"\"\n\n        val packageName =\n            PsiTreeUtil.getChildrenOfTypeAsList(psiFile, PbPackageStatement::class.java).firstOrNull()?.text ?: \"\"\n        val imports = PsiTreeUtil.getChildrenOfTypeAsList(psiFile, PbImportStatement::class.java)\n\n        val messages: List<PbMessageDefinition> =\n            PsiTreeUtil.getChildrenOfTypeAsList(psiFile, PbMessageDefinition::class.java)\n        val services = PsiTreeUtil.getChildrenOfTypeAsList(psiFile, PbServiceDefinition::class.java)\n        val enum = PsiTreeUtil.getChildrenOfTypeAsList(psiFile, PbMessageDefinition::class.java)\n\n        val classes = messages + services + enum\n\n        return FileStructure(psiFile, name, path, packageName, imports, classes, emptyList())\n    }\n}\n\n"
  },
  {
    "path": "languages/shire-proto/src/main/kotlin/com/phodal/shirelang/proto/provider/ShireProtoPsiVariableProvider.kt",
    "content": "package com.phodal.shirelang.proto.provider\n\nimport com.intellij.openapi.editor.Editor\nimport com.intellij.openapi.project.Project\nimport com.intellij.protobuf.lang.PbLanguage\nimport com.intellij.protobuf.lang.psi.*\nimport com.intellij.psi.PsiElement\nimport com.phodal.shirecore.provider.variable.PsiContextVariableProvider\nimport com.phodal.shirecore.psi.CodeSmellCollector\nimport com.phodal.shirecore.provider.variable.model.PsiContextVariable\nimport com.phodal.shirelang.proto.codemodel.ProtoClassStructureProvider\nimport com.phodal.shirelang.proto.codemodel.ProtoFileStructureProvider\n\nclass ShireProtoPsiVariableProvider : PsiContextVariableProvider {\n    override fun resolve(variable: PsiContextVariable, project: Project, editor: Editor, psiElement: PsiElement?): Any {\n        if (psiElement?.language !is PbLanguage) return \"\"\n\n        val containingFile: PbFile = psiElement.containingFile as? PbFile ?: return \"\"\n\n        return when (variable) {\n            PsiContextVariable.CURRENT_CLASS_NAME -> {\n                return when (psiElement) {\n                    is PbFile -> psiElement.name ?: \"\"\n                    is PbMessageDefinition -> psiElement.name ?: \"\"\n                    is PbEnumDefinition -> psiElement.name ?: \"\"\n                    is PbServiceDefinition -> psiElement.name ?: \"\"\n                    else -> \"\"\n                }\n            }\n\n            PsiContextVariable.CURRENT_CLASS_CODE -> return psiElement.text\n            PsiContextVariable.CURRENT_METHOD_NAME -> {\n                return when (psiElement) {\n                    is PbServiceMethod -> psiElement.nameIdentifier ?: \"\"\n                    else -> \"\"\n                }\n            }\n\n            PsiContextVariable.CURRENT_METHOD_CODE -> return psiElement.text\n            PsiContextVariable.RELATED_CLASSES -> {\n                if (psiElement !is PbServiceDefinition) return \"\"\n                ShireProtoUtils.lookupDependenceMessages(psiElement, project).map {\n                    it.text ?: \"\"\n                }\n            }\n            PsiContextVariable.IMPORTS -> {\n                return containingFile.importStatements.joinToString(\"\\n\") { it.text }\n            }\n            PsiContextVariable.FRAMEWORK_CONTEXT -> return collectFrameworkContext(psiElement, project)\n            PsiContextVariable.CODE_SMELL -> return CodeSmellCollector.collectElementProblemAsSting(\n                psiElement,\n                project,\n                editor\n            )\n            PsiContextVariable.METHOD_CALLER -> ShireProtoUtils.lookupUsage(psiElement, project).joinToString(\"\\n\")\n            PsiContextVariable.CALLED_METHOD -> {\n                if (psiElement !is PbServiceDefinition) return \"\"\n                ShireProtoUtils.lookupDependenceMessages(psiElement, project).map {\n                    it.text ?: \"\"\n                }\n            }\n            PsiContextVariable.STRUCTURE -> when (psiElement) {\n                is PbFile -> ProtoFileStructureProvider().build(psiElement)?.toString() ?: \"\"\n                is PbMessageDefinition,\n                is PbServiceDefinition,\n                    -> ProtoClassStructureProvider().build(psiElement, true)?.toString() ?: \"\"\n\n                else -> null\n            } ?: \"\"\n\n            PsiContextVariable.SIMILAR_CODE -> \"\"\n            PsiContextVariable.SIMILAR_TEST_CASE -> \"\"\n            PsiContextVariable.IS_NEED_CREATE_FILE -> \"\"\n            PsiContextVariable.TARGET_TEST_FILE_NAME -> \"\"\n            PsiContextVariable.UNDER_TEST_METHOD_CODE -> \"\"\n            PsiContextVariable.CHANGE_COUNT -> calculateChangeCount(psiElement)\n            PsiContextVariable.LINE_COUNT -> calculateLineCount(psiElement)\n            PsiContextVariable.COMPLEXITY_COUNT -> calculateComplexityCount(psiElement)\n        }\n    }\n}\n"
  },
  {
    "path": "languages/shire-proto/src/main/kotlin/com/phodal/shirelang/proto/provider/ShireProtoUtils.kt",
    "content": "package com.phodal.shirelang.proto.provider\n\nimport com.intellij.openapi.project.Project\nimport com.intellij.protobuf.ide.gutter.findImplementations\nimport com.intellij.protobuf.lang.findusages.PbFindUsagesProvider\nimport com.intellij.protobuf.lang.psi.PbMessageDefinition\nimport com.intellij.protobuf.lang.psi.PbMessageTypeName\nimport com.intellij.protobuf.lang.psi.PbNamedElement\nimport com.intellij.protobuf.lang.psi.PbServiceDefinition\nimport com.intellij.protobuf.lang.resolve.ProtoSymbolPathReference\nimport com.intellij.protobuf.lang.stub.index.QualifiedNameIndex\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.PsiFile\nimport com.intellij.psi.search.GlobalSearchScope\nimport com.intellij.psi.stubs.StubIndex\nimport com.phodal.shirecore.provider.codemodel.ClassStructureProvider\nimport com.phodal.shirecore.provider.codemodel.FileStructureProvider\nimport com.phodal.shirecore.provider.codemodel.MethodStructureProvider\n\nobject ShireProtoUtils {\n    fun lookupDependenceMessages(psiElement: PbServiceDefinition, project: Project): List<PbMessageDefinition> {\n        val methods = psiElement.body?.serviceMethodList ?: return listOf()\n\n        val type = methods.map { method ->\n            method.serviceMethodTypeList.mapNotNull { type ->\n                val messageTypeName: PbMessageTypeName = type.messageTypeName\n                val protoSymbolPathReference = messageTypeName.symbolPath.reference as ProtoSymbolPathReference\n                protoSymbolPathReference.multiResolve(true).mapNotNull {\n                    it.element as? PbMessageDefinition\n                }\n            }.flatten()\n        }.flatten()\n\n        val messages = type.distinct()\n\n        val secondLevels = messages.mapNotNull {\n            it.body?.simpleFieldList?.mapNotNull { field ->\n                field.typeName.symbolPath.reference?.resolve() as? PbMessageDefinition\n            }\n        }.flatten().distinct()\n\n        return messages + secondLevels\n    }\n\n    /**\n     * Maybe can use for GoTo Languages?\n     */\n    private fun getItemsByName(\n        project: Project,\n        name: String,\n    ): List<PbNamedElement> {\n        val projectScope = GlobalSearchScope.projectScope(project)\n        val results: MutableCollection<PbNamedElement> = StubIndex.getElements(\n            QualifiedNameIndex.KEY, name, project, projectScope,\n            PbNamedElement::class.java\n        )\n\n        return results.toList()\n    }\n\n    fun lookupUsage(psiElement: PsiElement, project: Project): List<String> {\n        if (!PbFindUsagesProvider().canFindUsagesFor(psiElement)) {\n            return listOf()\n        }\n\n        return when (psiElement) {\n            is PbMessageDefinition -> {\n                return findImplClassCode(psiElement)\n            }\n\n            is PbServiceDefinition -> {\n                findImplClassCode(psiElement)\n            }\n\n            else -> listOf()\n        }\n    }\n\n    private fun findImplClassCode(psiElement: PsiElement): List<String> {\n        return findImplementations(psiElement).map {\n            formatElement(it) ?: it.text\n        }.toList()\n    }\n\n    private fun formatElement(psiElement: PsiElement): String? {\n        return when {\n            psiElement is PsiFile -> {\n                FileStructureProvider.from(psiElement)?.format()\n            }\n            else -> ClassStructureProvider.from(psiElement, false)?.format()\n                ?: MethodStructureProvider.from(psiElement, false)?.format()\n        }\n    }\n}\n"
  },
  {
    "path": "languages/shire-proto/src/main/kotlin/com/phodal/shirelang/proto/variable/ProtobufToolchainProvider.kt",
    "content": "package com.phodal.shirelang.proto.variable\n\nimport com.intellij.openapi.project.Project\nimport com.intellij.protobuf.lang.PbLanguage\nimport com.intellij.protobuf.lang.psi.PbFile\nimport com.phodal.shirecore.provider.context.LanguageToolchainProvider\nimport com.phodal.shirecore.provider.context.ToolchainContextItem\nimport com.phodal.shirecore.provider.context.ToolchainPrepareContext\n\nclass ProtobufToolchainProvider : LanguageToolchainProvider {\n    override fun isApplicable(project: Project, context: ToolchainPrepareContext): Boolean {\n        return context.sourceFile?.language is PbLanguage\n    }\n\n    override suspend fun collect(project: Project, context: ToolchainPrepareContext): List<ToolchainContextItem> {\n        val file = context.sourceFile as? PbFile ?: return emptyList()\n        val protoVersion = file.syntaxLevel\n\n        return listOf(\n            ToolchainContextItem(\n                ProtobufToolchainProvider::class,\n                \"- Protobuf version: $protoVersion\"\n            )\n        )\n    }\n}\n"
  },
  {
    "path": "languages/shire-proto/src/main/resources/com.phodal.shirelang.proto.xml",
    "content": "<idea-plugin package=\"com.phodal.shirelang.proto\">\n    <!--suppress PluginXmlValidity -->\n    <dependencies>\n        <plugin id=\"idea.plugin.protoeditor\"/>\n    </dependencies>\n\n    <extensions defaultExtensionNs=\"com.phodal\">\n        <fileStructureProvider language=\"protobuf\"\n                               implementationClass=\"com.phodal.shirelang.proto.codemodel.ProtoFileStructureProvider\"/>\n\n        <classStructureProvider language=\"protobuf\"\n                                implementationClass=\"com.phodal.shirelang.proto.codemodel.ProtoClassStructureProvider\"/>\n\n        <shirePsiVariableProvider language=\"protobuf\"\n                                  implementationClass=\"com.phodal.shirelang.proto.provider.ShireProtoPsiVariableProvider\"/>\n\n        <shireLanguageToolchainProvider\n                language=\"protobuf\"\n                implementationClass=\"com.phodal.shirelang.proto.variable.ProtobufToolchainProvider\"/>\n    </extensions>\n</idea-plugin>\n"
  },
  {
    "path": "languages/shire-python/src/main/kotlin/com/phodal/shirelang/python/provider/ShirePythonAutoTesting.kt",
    "content": "package com.phodal.shirelang.python.provider\n\nimport com.intellij.execution.RunManager\nimport com.intellij.execution.actions.ConfigurationContext\nimport com.intellij.execution.actions.RunConfigurationProducer\nimport com.intellij.execution.configurations.RunConfiguration\nimport com.intellij.execution.configurations.RunProfile\nimport com.intellij.openapi.application.WriteAction\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.PsiFile\nimport com.intellij.psi.PsiManager\nimport com.jetbrains.python.PythonLanguage\nimport com.jetbrains.python.psi.PyFile\nimport com.jetbrains.python.run.PythonRunConfiguration\nimport com.jetbrains.python.run.PythonRunConfigurationProducer\nimport com.phodal.shirecore.provider.codemodel.model.ClassStructure\nimport com.phodal.shirecore.provider.TestingService\nimport com.phodal.shirecore.variable.toolchain.unittest.AutoTestingPromptContext\nimport com.phodal.shirelang.python.util.PyTestUtil\n\nclass ShirePythonAutoTesting : TestingService() {\n    override fun isApplicable(element: PsiElement): Boolean = element.language.displayName == \"Python\"\n    override fun isApplicable(project: Project, file: VirtualFile): Boolean = file.extension == \"py\"\n\n    override fun runConfigurationClass(project: Project): Class<out RunProfile> = PythonRunConfiguration::class.java\n\n    override fun createConfiguration(project: Project, virtualFile: VirtualFile): RunConfiguration? {\n        val psiFile: PyFile = PsiManager.getInstance(project).findFile(virtualFile) as? PyFile ?: return null\n        val runManager = RunManager.getInstance(project)\n\n        val context = ConfigurationContext(psiFile)\n        val configProducer = RunConfigurationProducer.getInstance(\n            PythonRunConfigurationProducer::class.java\n        )\n        var settings = configProducer.findExistingConfiguration(context)\n\n        if (settings == null) {\n            val fromContext = configProducer.createConfigurationFromContext(context) ?: return null\n            settings = fromContext.configurationSettings\n            runManager.setTemporaryConfiguration(settings)\n        }\n        val configuration = settings.configuration as PythonRunConfiguration\n        return configuration\n    }\n\n    override fun findOrCreateTestFile(sourceFile: PsiFile, project: Project, psiElement: PsiElement): AutoTestingPromptContext? {\n        val testFileName = PyTestUtil.getTestNameExample(sourceFile.virtualFile)\n        val testDir = PyTestUtil.getTestsDirectory(sourceFile.virtualFile, project)\n        val testFile = WriteAction.computeAndWait<VirtualFile?, Throwable> {\n            testDir.findOrCreateChildData(this, PyTestUtil.toTestFileName(testFileName, sourceFile.name))\n        }\n\n        return AutoTestingPromptContext(true, testFile, listOf(), \"\", PythonLanguage.INSTANCE)\n    }\n\n    override fun lookupRelevantClass(project: Project, element: PsiElement): List<ClassStructure> {\n        return listOf()\n    }\n\n}\n"
  },
  {
    "path": "languages/shire-python/src/main/kotlin/com/phodal/shirelang/python/provider/ShirePythonPsiVariableProvider.kt",
    "content": "package com.phodal.shirelang.python.provider\n\nimport com.intellij.openapi.application.WriteAction\nimport com.intellij.openapi.editor.Editor\nimport com.intellij.openapi.progress.ProgressManager\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.intellij.openapi.vfs.findFile\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.search.GlobalSearchScope\nimport com.intellij.psi.search.searches.ReferencesSearch\nimport com.jetbrains.python.PythonLanguage\nimport com.jetbrains.python.psi.PyClass\nimport com.jetbrains.python.psi.PyFile\nimport com.jetbrains.python.psi.PyFunction\nimport com.phodal.shirecore.provider.variable.PsiContextVariableProvider\nimport com.phodal.shirecore.psi.CodeSmellCollector\nimport com.phodal.shirecore.provider.variable.model.PsiContextVariable\nimport com.phodal.shirelang.python.util.PyTestUtil\nimport com.phodal.shirelang.python.util.PythonPsiUtil\n\nclass ShirePythonPsiVariableProvider : PsiContextVariableProvider {\n    override fun resolve(\n        variable: PsiContextVariable,\n        project: Project,\n        editor: Editor,\n        psiElement: PsiElement?,\n    ): Any {\n        if (psiElement?.language !is PythonLanguage) return \"\"\n\n        val underTestElement = PyTestUtil.getElementForTests(project, editor)\n        val sourceFile = underTestElement?.containingFile as? PyFile ?: return \"\"\n\n        return when (variable) {\n            PsiContextVariable.CURRENT_CLASS_NAME -> {\n                when (underTestElement) {\n                    is PyClass -> underTestElement.name ?: \"\"\n                    is PyFunction -> underTestElement.name ?: \"\"\n                    else -> \"\"\n                }\n            }\n\n            PsiContextVariable.CURRENT_CLASS_CODE -> {\n                when (underTestElement) {\n                    is PyClass -> underTestElement.text\n                    is PyFunction -> underTestElement.text\n                    else -> \"\"\n                }\n            }\n\n            PsiContextVariable.CURRENT_METHOD_NAME -> {\n                when (underTestElement) {\n                    is PyFunction -> underTestElement.name ?: \"\"\n                    else -> \"\"\n                }\n            }\n\n            PsiContextVariable.CURRENT_METHOD_CODE -> {\n                when (underTestElement) {\n                    is PyFunction -> underTestElement.text\n                    else -> \"\"\n                }\n            }\n\n            PsiContextVariable.RELATED_CLASSES -> {\n                when (underTestElement) {\n                    is PyFunction -> {\n                        PythonPsiUtil.findRelatedTypes(underTestElement).mapNotNull { it?.name ?: \"\" }\n                    }\n\n                    else -> listOf()\n                }.joinToString(\"\\n\")\n            }\n\n            PsiContextVariable.SIMILAR_TEST_CASE -> TODO()\n            PsiContextVariable.IMPORTS -> PythonPsiUtil.getImportsInFile(sourceFile)\n            PsiContextVariable.IS_NEED_CREATE_FILE -> {\n                val testFileName = PyTestUtil.getTestNameExample(sourceFile.virtualFile)\n                val testDir = PyTestUtil.getTestsDirectory(sourceFile.virtualFile, project)\n                val testFile = WriteAction.computeAndWait<VirtualFile?, Throwable> {\n                    testDir.findFile(PyTestUtil.toTestFileName(testFileName, sourceFile.name))\n                }\n\n                testFile != null\n            }\n\n            PsiContextVariable.TARGET_TEST_FILE_NAME -> {\n                PyTestUtil.getTestNameExample(sourceFile.virtualFile)\n            }\n\n            PsiContextVariable.UNDER_TEST_METHOD_CODE -> TODO()\n            PsiContextVariable.FRAMEWORK_CONTEXT -> return collectFrameworkContext(psiElement, project)\n            PsiContextVariable.CODE_SMELL -> CodeSmellCollector.collectElementProblemAsSting(\n                underTestElement,\n                project,\n                editor\n            )\n\n            PsiContextVariable.METHOD_CALLER -> {\n                val psiReferences = ReferencesSearch.search(underTestElement, GlobalSearchScope.projectScope(project))\n                ProgressManager.checkCanceled()\n                psiReferences.mapNotNull { it.element?.text }.toList()\n            }\n\n            PsiContextVariable.CALLED_METHOD -> {\n                PythonPsiUtil.collectAndResolveReferences(underTestElement)\n            }\n\n            PsiContextVariable.SIMILAR_CODE -> TODO()\n            PsiContextVariable.STRUCTURE -> TODO()\n            PsiContextVariable.CHANGE_COUNT -> calculateChangeCount(psiElement)\n            PsiContextVariable.LINE_COUNT -> calculateLineCount(psiElement)\n            PsiContextVariable.COMPLEXITY_COUNT -> calculateComplexityCount(psiElement)\n        }\n    }\n}\n"
  },
  {
    "path": "languages/shire-python/src/main/kotlin/com/phodal/shirelang/python/provider/ShirePythonRunService.kt",
    "content": "package com.phodal.shirelang.python.provider\n\nimport com.intellij.execution.RunManager\nimport com.intellij.execution.actions.ConfigurationContext\nimport com.intellij.execution.actions.RunConfigurationProducer\nimport com.intellij.execution.configurations.RunConfiguration\nimport com.intellij.execution.configurations.RunProfile\nimport com.intellij.openapi.application.runReadAction\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.intellij.psi.PsiManager\nimport com.jetbrains.python.psi.PyFile\nimport com.jetbrains.python.run.PythonRunConfiguration\nimport com.jetbrains.python.run.PythonRunConfigurationProducer\nimport com.phodal.shirecore.provider.shire.FileRunService\n\nclass ShirePythonRunService : FileRunService {\n    override fun isApplicable(project: Project, file: VirtualFile): Boolean {\n        return file.extension == \"py\"\n    }\n\n    override fun runConfigurationClass(project: Project): Class<out RunProfile> = PythonRunConfiguration::class.java\n\n    override fun createConfiguration(project: Project, virtualFile: VirtualFile): RunConfiguration? {\n        val psiFile: PyFile = runReadAction {\n            PsiManager.getInstance(project).findFile(virtualFile) as? PyFile\n        } ?: return null\n        val runManager = RunManager.getInstance(project)\n\n        val context = runReadAction {\n            ConfigurationContext(psiFile)\n        }\n\n        val configProducer = RunConfigurationProducer.getInstance(\n            PythonRunConfigurationProducer::class.java\n        )\n        var settings = configProducer.findExistingConfiguration(context)\n\n        if (settings == null) {\n            val fromContext = configProducer.createConfigurationFromContext(context)\n                ?: throw IllegalStateException(\"Failed to create configuration from context\")\n\n            settings = fromContext.configurationSettings\n            runManager.setTemporaryConfiguration(settings)\n        }\n\n        return settings.configuration as PythonRunConfiguration\n    }\n}\n"
  },
  {
    "path": "languages/shire-python/src/main/kotlin/com/phodal/shirelang/python/util/PyTestUtil.kt",
    "content": "package com.phodal.shirelang.python.util\n\nimport com.intellij.lang.injection.InjectedLanguageManager\nimport com.intellij.openapi.editor.Editor\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.roots.ProjectFileIndex\nimport com.intellij.openapi.vfs.VfsUtil\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.PsiFile\nimport com.intellij.psi.util.PsiTreeUtil\nimport com.intellij.psi.util.PsiUtilBase\nimport com.jetbrains.python.psi.PyClass\nimport com.jetbrains.python.psi.PyFunction\n\nobject PyTestUtil {\n    fun getElementForTests(project: Project, editor: Editor): PsiElement? {\n        val element = PsiUtilBase.getElementAtCaret(editor) ?: return null\n        val containingFile: PsiFile = element.containingFile ?: return null\n\n        if (InjectedLanguageManager.getInstance(project).isInjectedFragment(containingFile)) {\n            return containingFile\n        }\n\n        return PsiTreeUtil.getParentOfType(element, PyFunction::class.java, false)\n            ?: PsiTreeUtil.getParentOfType(element, PyClass::class.java, false)\n            ?: containingFile\n    }\n\n    fun getTestNameExample(file: VirtualFile): String {\n        val children = file.children\n        for (child in children) {\n            val fileName = (child ?: continue).name\n            if (fileName.endsWith(\".py\") && !fileName.startsWith(\"_\")) {\n                return fileName\n            }\n        }\n\n        return \"test_example.py\"\n    }\n\n    fun getTestsDirectory(file: VirtualFile, project: Project): VirtualFile {\n        val baseDirectory: VirtualFile? = ProjectFileIndex.getInstance(project).getContentRootForFile(file)\n        if (baseDirectory == null) {\n            val parent = file.parent\n            return parent\n        }\n\n        val testDir = VfsUtil.createDirectoryIfMissing(\"tests\") ?: baseDirectory\n        return testDir\n    }\n\n    fun toTestFileName(testFileName: String, exampleName: String): String {\n        if (exampleName.startsWith(\"test_\")) return \"test_$testFileName.py\"\n        return \"${testFileName}_test.py\"\n    }\n}\n"
  },
  {
    "path": "languages/shire-python/src/main/kotlin/com/phodal/shirelang/python/util/PythonPsiUtil.kt",
    "content": "package com.phodal.shirelang.python.util\n\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.roots.ProjectFileIndex\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.PsiFile\nimport com.intellij.psi.util.PsiTreeUtil\nimport com.intellij.util.concurrency.annotations.RequiresReadLock\nimport com.jetbrains.python.psi.*\nimport com.jetbrains.python.psi.types.PyType\nimport com.jetbrains.python.psi.types.TypeEvalContext\n\nobject PythonPsiUtil {\n    fun getImportsInFile(file: PsiFile): String {\n        if (file !is PyFile) return \"\"\n\n        val fromImports = file.fromImports\n            .map { it.text }.distinct()\n            .joinToString(\"\\n\")\n\n        val imports = file.importTargets\n            .asSequence()\n            .map { it.parent.text }\n            .distinct()\n            .joinToString(\"\\n\")\n\n        return (imports + \"\\n\" + fromImports).trimIndent()\n    }\n\n    fun getClassWithoutMethod(clazz: PyClass, function: PyFunction): PyClass {\n        val classCopy = clazz.copy() as PyClass\n        val methods = classCopy.methods\n\n        val methodsToDelete = methods.filter { it.name == function.name }\n        methodsToDelete.forEach {\n            it.delete()\n        }\n\n        return classCopy\n    }\n\n    private fun getFunctionSignature(function: PyFunction, inplace: Boolean): PyFunction {\n        val functionCopy = if (inplace) {\n            function\n        } else {\n            function.copy() as PyFunction\n        }\n\n        functionCopy.statementList.statements.forEach { it.delete() }\n        return functionCopy\n    }\n\n    fun clearClass(classCopy: PyClass) {\n        classCopy.instanceAttributes.forEach {\n            it.findAssignedValue()?.replace(makeEllipsisExpression(classCopy.project))\n        }\n\n        classCopy.classAttributes.forEach {\n            it.findAssignedValue()?.replace(makeEllipsisExpression(classCopy.project))\n        }\n\n        classCopy.methods.forEach { method ->\n            method.statementList.statements.forEach { statement ->\n                statement.delete()\n            }\n        }\n\n        classCopy.nestedClasses.forEach { nestedClass ->\n            clearClass(nestedClass)\n        }\n    }\n\n    private fun makeEllipsisExpression(project: Project): PyExpression {\n        return PyElementGenerator.getInstance(project).createEllipsis()\n    }\n\n    @RequiresReadLock\n    fun findRelatedTypes(function: PyFunction): List<PyType?> {\n        val context = TypeEvalContext.codeCompletion(function.project, function.containingFile)\n\n        val resultType = function.getReturnStatementType(context)\n\n        val parameters = (function as? PyCallable)?.parameterList?.parameters?.toList() ?: emptyList()\n\n        val parameterTypes = parameters\n            .filterIsInstance<PyTypedElement>()\n            .map { context.getType(it) }\n            .toMutableList()\n\n        return parameterTypes + resultType\n    }\n\n    fun collectAndResolveReferences(psiElement: PsiElement): String {\n        val list = mutableListOf<String>()\n        psiElement.accept(object : PyRecursiveElementVisitor() {\n            override fun visitPyCallExpression(expression: PyCallExpression) {\n                super.visitPyCallExpression(expression)\n                val callee = expression.callee\n                val resolved = callee?.reference?.resolve()\n                addResolvedElement(expression.text, resolved)\n            }\n\n            override fun visitPyReferenceExpression(expression: PyReferenceExpression) {\n                super.visitPyReferenceExpression(expression)\n                val resolved = expression.reference.multiResolve(false).firstOrNull()?.element\n                addResolvedElement(expression.text, resolved)\n            }\n\n            private fun addResolvedElement(declarationName: String, element: PsiElement?) {\n                if (element == null) return\n                if (ProjectFileIndex.getInstance(element.project).isInLibrary(element.containingFile.virtualFile)) return\n\n                val resolvedElement = if (PyUtil.isInitOrNewMethod(element)) {\n                    PsiTreeUtil.getParentOfType(element, PyClass::class.java, false)\n                } else {\n                    element\n                }\n\n                when (resolvedElement) {\n                    is PyFunction -> addFunction(declarationName, resolvedElement)\n                    is PyClass -> addClass(declarationName, resolvedElement)\n                }\n            }\n\n            private fun addClass(declarationName: String, clazz: PyClass) {\n                list.add(clazz.text)\n            }\n\n            private fun addFunction(declarationName: String, function: PyFunction) {\n                list.add(function.text)\n            }\n        })\n\n        return list.joinToString(\"\\n\")\n    }\n}"
  },
  {
    "path": "languages/shire-python/src/main/resources/com.phodal.shirelang.python.xml",
    "content": "<idea-plugin package=\"com.phodal.shirelang.python\">\n    <!--suppress PluginXmlValidity -->\n    <dependencies>\n        <plugin id=\"com.intellij.modules.python\"/>\n    </dependencies>\n\n    <extensions defaultExtensionNs=\"com.phodal\">\n        <shireFileRunService implementation=\"com.phodal.shirelang.python.provider.ShirePythonRunService\"/>\n\n        <shireAutoTesting language=\"Python\"\n                          implementationClass=\"com.phodal.shirelang.python.provider.ShirePythonAutoTesting\"/>\n\n        <shirePsiVariableProvider language=\"Python\"\n                                  implementationClass=\"com.phodal.shirelang.python.provider.ShirePythonPsiVariableProvider\"/>\n    </extensions>\n</idea-plugin>\n"
  },
  {
    "path": "languages/shire-python/src/test/kotlin/com/phodal/shirelang/python/util/PyTestUtilTest.kt",
    "content": "package com.phodal.shirelang.python.util\n\nimport com.intellij.testFramework.fixtures.BasePlatformTestCase\nimport org.intellij.lang.annotations.Language\n\nclass PyTestUtilTest : BasePlatformTestCase() {\n    fun testShouldReturnCorrectPythonTestName() {\n        @Language(\"Python\")\n        val code = \"\"\"\n            def hello():\n                pass\n        \"\"\".trimIndent()\n\n        val psiFile = myFixture.addFileToProject(\"Hello.py\", code)\n\n        val testName = PyTestUtil.getTestNameExample(psiFile.virtualFile)\n        assertEquals(\"test_example.py\", testName)\n    }\n}\n"
  },
  {
    "path": "languages/shire-python/src/test/kotlin/com/phodal/shirelang/python/util/PythonPsiUtilTest.kt",
    "content": "package com.phodal.shirelang.python.util\n\nimport com.intellij.testFramework.fixtures.BasePlatformTestCase\nimport org.intellij.lang.annotations.Language\n\nclass PythonPsiUtilTest : BasePlatformTestCase() {\n    fun testShouldGetClassWithoutMethod() {\n        @Language(\"Python\")\n        val code = \"\"\"\n            class MathHelper:\n                def addition_with_positive_numbers(self):\n                    pass\n                def addition_with_negative_numbers(self):\n                    pass\n        \"\"\".trimIndent()\n\n        val file = myFixture.addFileToProject(\"Hello.py\", code)\n\n//        val psiFile = PsiManager.getInstance(project).findFile(file.virtualFile) as PyFile\n//\n//        val firstClass = psiFile.children[0] as PyClass\n//        val firstMethod = firstClass.children[0] as PyFunction\n//\n//        val testName = PythonPsiUtil.getClassWithoutMethod(firstClass, firstMethod)\n//        assertEquals(\"test_example.py\", testName)\n    }\n}\n"
  },
  {
    "path": "languages/shire-python/src/test/resources/META-INF/plugin.xml",
    "content": "<idea-plugin package=\"com.phodal.python-test\" xmlns:xi=\"http://www.w3.org/2001/XInclude\">\n    <id>com.phodal.shire</id>\n    <depends>com.intellij.modules.python</depends>\n\n    <xi:include href=\"/META-INF/shire-main.xml\" xpointer=\"xpointer(/idea-plugin/*)\"/>\n    <xi:include href=\"/com.phodal.shirecore.xml\" xpointer=\"xpointer(/idea-plugin/*)\"/>\n\n    <content>\n        <module name=\"com.phodal.shirelang\"/>\n        <module name=\"com.phodal.shirelang.python\"/>\n    </content>\n</idea-plugin>\n"
  },
  {
    "path": "qodana.yml",
    "content": "# Qodana configuration:\n# https://www.jetbrains.com/help/qodana/qodana-yaml.html\n\nversion: 1.0\nlinter: jetbrains/qodana-jvm-community:latest\nprojectJDK: \"17\"\nprofile:\n  name: qodana.recommended\nexclude:\n  - name: All\n    paths:\n      - .qodana\n"
  },
  {
    "path": "settings.gradle.kts",
    "content": "rootProject.name = \"intellij-shire\"\n\nenableFeaturePreview(\"TYPESAFE_PROJECT_ACCESSORS\")\n\ninclude(\n    \"core\",\n    \"shirelang\",\n)\n\ninclude(\n    \"languages:shire-java\",\n    \"languages:shire-javascript\",\n    \"languages:shire-python\",\n    \"languages:shire-kotlin\",\n    \"languages:shire-go\",\n    \"languages:shire-markdown\",\n    \"languages:shire-json\",\n    \"languages:shire-proto\",\n\n    \"toolsets:git\",\n    \"toolsets:httpclient\",\n    \"toolsets:terminal\",\n    \"toolsets:sonarqube\",\n    \"toolsets:plantuml\",\n    \"toolsets:database\",\n    \"toolsets:mock\",\n    \"toolsets:openrewrite\",\n    \"toolsets:mermaid\",\n    \"toolsets:docker\",\n//    \"toolsets:uitest\",\n)\n"
  },
  {
    "path": "shirelang/.gitignore",
    "content": "src/gen"
  },
  {
    "path": "shirelang/README.md",
    "content": "# The Shire Language and Compiler\n\n\n## Http Request\n\n    ```http request\n    ### GET request to example server\n    GET https://examples.http-client.intellij.net/get$END$\n    ?generated-in=IntelliJ IDEA\n    ```\n\n## OpenWrite\n\nDocs: [https://docs.openrewrite.org/](https://docs.openrewrite.org/)\n\n[Open Rewrite](https://github.com/openrewrite/rewrite) Fast, repeatable refactoring for developers\n\n```yml\n---\ntype: specs.openrewrite.org/v1beta/recipe\nname: com.yourorg.FindAndReplaceJDK17\ndisplayName: Find and replace JDK 17 example\nrecipeList:\n  - org.openrewrite.text.FindAndReplace:\n      find: eclipse-temurin:17-jdk-jammy\n      replace: eclipse-temurin:21.0.2_13-jdk-jammy\n      filePattern: 'Dockerfile'\n```\n"
  },
  {
    "path": "shirelang/editor/ShireVariablePanel.kt",
    "content": ""
  },
  {
    "path": "shirelang/src/main/grammar/ShireLexer.flex",
    "content": "package com.phodal.shirelang.lexer;\n\nimport com.intellij.lexer.FlexLexer;\nimport com.intellij.psi.tree.IElementType;\nimport static com.phodal.shirelang.psi.ShireTypes.*;\nimport com.intellij.psi.TokenType;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\n%%\n\n%{\n  public _ShireLexer() {\n    this((java.io.Reader)null);\n  }\n%}\n\n%class ShireLexer\n%class _ShireLexer\n%implements FlexLexer\n%unicode\n%function advance\n%type IElementType\n%eof{  return;\n%eof}\n\n%s YYUSED\n%s AGENT_BLOCK\n%s VARIABLE_BLOCK\n\n%s COMMAND_BLOCK\n%s SINGLE_COMMNET_BLOCK\n%s COMMAND_VALUE_BLOCK\n\n%s EXPR_BLOCK\n\n%s CODE_BLOCK\n%s CONTENT_COMMENT_BLOCK\n%s LINE_BLOCK\n%s FRONT_MATTER_BLOCK\n%s FRONT_MATTER_VALUE_BLOCK\n%s FRONT_MATTER_VAL_OBJECT\n%s PATTERN_ACTION_BLOCK\n%s CONDITION_EXPR_BLOCK\n%s FUNCTION_DECL_BLOCK\n%s EXT_FUNCTION_BLOCK\n\n%s LANG_ID\n\nSPACE                    = [ \\t\\n\\x0B\\f\\r]+\nIDENTIFIER               = [a-zA-Z0-9][_\\-a-zA-Z0-9]*\nFRONTMATTER_KEY          = [a-zA-Z0-9][_\\-a-zA-Z0-9]*\nDATE                     = [0-9]{4}-[0-9]{2}-[0-9]{2}\nSTRING                   = [a-zA-Z0-9][_\\-a-zA-Z0-9]*\n// in Intellij Platform, the language id enable whitespace\nLANGUAGE_IDENTIFIER      = [a-zA-Z][_\\-a-zA-Z0-9 .]*\n\nEOL=\\R\nINDENT                   = [ \\t][ \\t]+\nWHITE_SPACE              = [ \\t]+\nCOMMENTS                 = \"//\"[^\\r\\n]*\nCONTENT_COMMENTS         = \\[ ([^\\]]+)? \\] [^\\t\\r\\n]*\nBLOCK_COMMENT            = [/][*][^*]*[*]+([^/*][^*]*[*]+)*[/]\nEscapedChar              = \"\\\\\" [^\\n]\nRegexWord                = [^\\r\\n\\\\\\\"' \\t$`()] | {EscapedChar}\nREGEX                    = \\/{RegexWord}+\\/\nPATTERN_EXPR             = \\/{RegexWord}+\\/\n\nLPAREN                   = \\(\nRPAREN                   = \\)\nPIPE                     = \\|\n\nNUMBER                   = [0-9]+\nBOOLEAN                  = true|false|TRUE|FALSE|\"true\"|\"false\"\n\nTEXT_SEGMENT             = [^$/@#\\n]+\nDOUBLE_QUOTED_STRING     = \\\"([^\\\\\\\"\\r\\n]|\\\\[^\\r\\n])*\\\"?\nSINGLE_QUOTED_STRING     = '([^\\\\'\\r\\n]|\\\\[^\\r\\n])*'?\nQUOTE_STRING             = {DOUBLE_QUOTED_STRING}|{SINGLE_QUOTED_STRING}\n\n// READ LINE FORMAT: L2C2-L0C100 or L1-L1\nLINE_INFO                = L[0-9]+(C[0-9]+)?(-L[0-9]+(C[0-9]+)?)?\nCOMMAND_PROP             = [^\\ \\t\\r\\n]*\nCODE_CONTENT             = [^\\n]+\nNEWLINE                  = \\n | \\r | \\r\\n\n\nCOLON                    =:\nSHARP                    =#\nLBRACKET                 =\\[\nRBRACKET                 =\\]\nCOMMA                    =,\nACCESS                   =::\nPROCESS                  =->\n\nDEFAULT                  =default\nCASE                     =case\nARROW                    ==>\nWHEN                     =when\nFUNCTIONS                =functions\nCONDITION                =condition\nIF                       =if\nELSE                     =else\nELSEIF                   =elseif\nENDIF                    =endif\nEND                      =end\nAND                      =and\n\nON_STREAMING             =onStreaming\nBEFORE_STREAMING         =beforeStreaming\nON_STREAMING_END         =onStreamingEnd\nAFTER_STREAMING          =afterStreaming\n\n%{\n    private boolean isCodeStart = false;\n    private boolean isInsideShireTemplate = false;\n    private boolean isInsideFunctionBlock = false;\n    private boolean isInsideFrontMatter = false;\n    private boolean hasFrontMatter = false;\n    private boolean patternActionBraceStart = false;\n    private int patternActionBraceLevel = 0;\n%}\n\n%{\n    private IElementType codeContent() {\n        yybegin(YYINITIAL);\n\n        // handle for end which is \\n```\n        String text = yytext().toString().trim();\n        if ((text.equals(\"\\n```\") || text.equals(\"```\")) && isCodeStart == true ) {\n            isCodeStart = false;\n            return CODE_BLOCK_END;\n        }\n\n        // new line\n        if (text.equals(\"\\n\")) {\n            return NEWLINE;\n        }\n\n        if (isCodeStart == false) {\n            return TEXT_SEGMENT;\n        }\n\n        return CODE_CONTENT;\n    }\n\n    private IElementType content() {\n        String text = yytext().toString().trim();\n        if (isCodeStart == true && text.equals(\"```\")) {\n            return codeContent();\n        }\n\n        if (isCodeStart == false && text.startsWith(\"```\")) {\n            isCodeStart = true;\n            yypushback(yylength() - 3);\n            yybegin(LANG_ID);\n\n            return CODE_BLOCK_START;\n        }\n\n        if (isCodeStart) {\n            return CODE_CONTENT;\n        } else {\n            if (text.startsWith(\"[\")) {\n                yybegin(CONTENT_COMMENT_BLOCK);\n                return comment();\n            }\n\n            yypushback(yylength());\n            yybegin(YYUSED);\n\n            return TEXT_SEGMENT;\n        }\n    }\n\n    private IElementType comment() {\n        String text = yytext().toString();\n        if (text.contains(\"[\") && text.contains(\"]\")) {\n            return CONTENT_COMMENTS;\n        } else {\n            return TEXT_SEGMENT;\n        }\n    }\n\n    private IElementType command_value() {\n        String text = yytext().toString().trim();\n        String [] split = text.split(\"#\");\n\n        if (split.length == 1) {\n            return COMMAND_PROP;\n        }\n\n        // split by # if it is a line info\n        String last = split[split.length - 1];\n        Pattern compile = Pattern.compile(\"L\\\\d+(C\\\\d+)?(-L\\\\d+(C\\\\d+)?)?\");\n        Matcher matcher = compile.matcher(last);\n        if (matcher.matches()) {\n            // before # is command prop, after # is line info\n            int number = last.length() + \"#\".length();\n            if (number > 0) {\n                yypushback(number);\n                yybegin(LINE_BLOCK);\n                return COMMAND_PROP;\n            } else {\n                return COMMAND_PROP;\n            }\n        }\n\n        return COMMAND_PROP;\n    }\n\n\n    /** @param offset offset from currently matched token start (could be negative) */\n    private char getCharAtOffset(final int offset) {\n      final int loc = getTokenStart() + offset;\n      return 0 <= loc && loc < zzBuffer.length() ? zzBuffer.charAt(loc) : (char) -1;\n    }\n\n    private boolean isAfterEol() {\n      final char prev = getCharAtOffset(-1);\n      return prev == (char)-1 || prev == '\\n';\n    }\n%}\n\n%%\n<YYINITIAL> {\n  \"---\"  {\n          if (isCodeStart) {\n              return CODE_CONTENT;\n          } else {\n              isInsideFrontMatter = true;\n              yybegin(FRONT_MATTER_BLOCK);\n              return FRONTMATTER_START;\n          }\n      }\n\n  {CODE_CONTENT}          { return content(); }\n  {NEWLINE}               { return NEWLINE;  }\n\n  {BLOCK_COMMENT}         { return BLOCK_COMMENT; }\n  {COMMENTS}              { return COMMENTS; }\n}\n\n<FRONT_MATTER_BLOCK> {\n  {WHEN}                  { return WHEN; }\n  {ON_STREAMING}          { return ON_STREAMING; }\n  {BEFORE_STREAMING}      { return BEFORE_STREAMING; }\n  {ON_STREAMING_END}      { return ON_STREAMING_END; }\n  {AFTER_STREAMING}       { return AFTER_STREAMING; }\n  {FUNCTIONS}             { return FUNCTIONS; }\n\n  {IDENTIFIER}            { return IDENTIFIER; }\n  {PATTERN_EXPR}          { return PATTERN_EXPR; }\n  \":\"                     { yybegin(FRONT_MATTER_VALUE_BLOCK); return COLON; }\n  \"{\"                     { patternActionBraceLevel++; yybegin(FUNCTION_DECL_BLOCK); return OPEN_BRACE; }\n\n  {INDENT}                { yybegin(FRONT_MATTER_VAL_OBJECT); return INDENT; }\n  {NEWLINE}               { return NEWLINE; }\n\n  \"---\"                   { isInsideFrontMatter = false; hasFrontMatter = true; yybegin(YYINITIAL); return FRONTMATTER_END; }\n  [^]                     { yypushback(yylength()); yybegin(YYINITIAL); }\n}\n\n<FRONT_MATTER_VAL_OBJECT> {\n  {COMMENTS}              { return COMMENTS; }\n  {NEWLINE}               { return NEWLINE; }\n  {QUOTE_STRING}          { return QUOTE_STRING; }\n\n  \":\"                     { yybegin(FRONT_MATTER_VALUE_BLOCK); return COLON; }\n  [^]                     { yypushback(yylength()); yybegin(FRONT_MATTER_BLOCK); }\n}\n\n<FRONT_MATTER_VALUE_BLOCK>  {\n  {NUMBER}                { return NUMBER; }\n  {DATE}                  { return DATE; }\n  {BOOLEAN}               { return BOOLEAN; }\n  {IDENTIFIER}            { return IDENTIFIER; }\n  {QUOTE_STRING}          { return QUOTE_STRING; }\n  {PATTERN_EXPR}          { yybegin(PATTERN_ACTION_BLOCK); return PATTERN_EXPR; }\n\n  {ACCESS}                { return ACCESS; }\n  {PROCESS}               { return PROCESS; }\n\n  \"[\"                     { return LBRACKET; }\n  \"]\"                     { return RBRACKET; }\n  \",\"                     { return COMMA; }\n  \"!\"                     { return NOT; }\n  \"&&\"                    { return ANDAND; }\n  \"||\"                    { return OROR; }\n  \".\"                     { return DOT; }\n  \"==\"                    { return EQEQ; }\n  \"!=\"                    { return NEQ; }\n  \"<\"                     { return LT; }\n  \"<=\"                    { return LTE; }\n  \">\"                     { return GT; }\n  \">=\"                    { return GTE; }\n  \"$\"                     { return VARIABLE_START; }\n  \"(\"                     { return LPAREN; }\n  \")\"                     { return RPAREN; }\n\n  {COMMENTS}              { return COMMENTS; }\n  {WHITE_SPACE}           { return TokenType.WHITE_SPACE; }\n  [^]                     { yypushback(yylength()); yybegin(FRONT_MATTER_BLOCK); }\n}\n\n<PATTERN_ACTION_BLOCK> {\n  \"{\"                    { patternActionBraceStart = true; patternActionBraceLevel++; return OPEN_BRACE; }\n  \"}\"                    { patternActionBraceLevel--; return CLOSE_BRACE; }\n  \"|\"                    { return PIPE; }\n  \",\"                    { return COMMA; }\n  \"(\"                    { return LPAREN; }\n  \")\"                    { return RPAREN; }\n  {INDENT}               {\n          if (patternActionBraceStart && patternActionBraceLevel == 0) {\n              patternActionBraceStart = false;\n              yybegin(FRONT_MATTER_VAL_OBJECT);\n              return INDENT;\n          } else {\n              return TokenType.WHITE_SPACE;\n          }\n      }\n  {WHITE_SPACE}          { return TokenType.WHITE_SPACE; }\n  {NEWLINE}              { return NEWLINE; }\n\n  // keywords\n  \"case\"                 { return CASE; }\n  \"default\"              { return DEFAULT; }\n\n  {IDENTIFIER}            {\n          if (isInsideFunctionBlock) {\n              yypushback(yylength()); yybegin(EXPR_BLOCK);\n          } else {\n              switch (yytext().toString().trim()) {\n                  case \"when\": return WHEN;\n                  case \"onStreaming\": return ON_STREAMING;\n                  case \"beforeStreaming\": return BEFORE_STREAMING;\n                  case \"onStreamingEnd\": return ON_STREAMING_END;\n                  case \"afterStreaming\": return AFTER_STREAMING;\n                  default: return IDENTIFIER;\n              }\n          }\n      }\n\n  {QUOTE_STRING}         { return QUOTE_STRING; }\n  {PATTERN_EXPR}         { return PATTERN_EXPR; }\n  \"$\"                    { return VARIABLE_START; }\n  \"=>\"                   { return ARROW; }\n  [^]                    { patternActionBraceStart = false; yypushback(yylength()); yybegin(FRONT_MATTER_VALUE_BLOCK); }\n}\n\n<CONTENT_COMMENT_BLOCK> {\n  {CONTENT_COMMENTS}      { return comment(); }\n  [^]                     { yypushback(yylength()); yybegin(YYINITIAL); return TEXT_SEGMENT; }\n}\n\n<FUNCTION_DECL_BLOCK> {\n  \"from\"                  { return FROM; }\n  \"where\"                 { return WHERE; }\n  \"select\"                { return SELECT; }\n  \"condition\"             { return CONDITION; }\n  \"case\"                  { return CASE; }\n  \"default\"               { return DEFAULT; }\n\n  \"{\"                     { patternActionBraceLevel++; isInsideFunctionBlock = true; yybegin(EXPR_BLOCK); return OPEN_BRACE; }\n  \"}\"                     { patternActionBraceLevel--; if (patternActionBraceLevel == 0) { isInsideFunctionBlock = false; } return CLOSE_BRACE; }\n\n  {NUMBER}                { return NUMBER; }\n  {IDENTIFIER}            {\n          if (isInsideFunctionBlock) {\n              yypushback(yylength()); yybegin(EXPR_BLOCK);\n          } else {\n              switch (yytext().toString().trim()) {\n                  case \"when\": return WHEN;\n                  case \"onStreaming\": return ON_STREAMING;\n                  case \"beforeStreaming\": return BEFORE_STREAMING;\n                  case \"onStreamingEnd\": return ON_STREAMING_END;\n                  case \"afterStreaming\": return AFTER_STREAMING;\n                  default: return IDENTIFIER;\n              }\n          }\n      }\n\n  {DATE}                  { return DATE; }\n  {BOOLEAN}               { return BOOLEAN; }\n  {QUOTE_STRING}          { return QUOTE_STRING; }\n  {PATTERN_EXPR}          { yybegin(PATTERN_ACTION_BLOCK); return PATTERN_EXPR; }\n\n  \"[\"                     { return LBRACKET; }\n  \"]\"                     { return RBRACKET; }\n  \",\"                     { return COMMA; }\n  \"!\"                     { return NOT; }\n  \"&&\"                    { return ANDAND; }\n  \"||\"                    { return OROR; }\n  \".\"                     { return DOT; }\n  \"==\"                    { return EQEQ; }\n  \"!=\"                    { return NEQ; }\n  \"<\"                     { return LT; }\n  \"<=\"                    { return LTE; }\n  \">\"                     { return GT; }\n  \">=\"                    { return GTE; }\n  \"$\"                     { return VARIABLE_START; }\n  \"(\"                     { return LPAREN; }\n  \")\"                     { return RPAREN; }\n  \"|\"                     { return PIPE; }\n\n  {COMMENTS}              { return COMMENTS; }\n  {BLOCK_COMMENT}         { return BLOCK_COMMENT; }\n  {COMMA}                 { return COMMA; }\n\n  {NEWLINE}               { return NEWLINE; }\n  {WHITE_SPACE}           {\n          if (isInsideFunctionBlock && patternActionBraceLevel == 0) {\n              isInsideFunctionBlock = false;\n              System.out.println(\"PatternActionBraceLevel: \" + patternActionBraceLevel);\n              yybegin(FRONT_MATTER_VAL_OBJECT);\n              return INDENT;\n          } else {\n              return TokenType.WHITE_SPACE;\n          }\n      }\n\n  [^]                     {\n          isInsideFunctionBlock = false;\n          yypushback(yylength());\n          yybegin(FRONT_MATTER_BLOCK);\n      }\n}\n\n<YYUSED> {\n  \"@\"                     { yybegin(AGENT_BLOCK);    return AGENT_START; }\n  \"//\"  {TEXT_SEGMENT}    { yybegin(SINGLE_COMMNET_BLOCK); return COMMENTS; }\n  \"/\"                     { yybegin(COMMAND_BLOCK);  return COMMAND_START; }\n  \"$\"                     { yybegin(VARIABLE_BLOCK); return VARIABLE_START; }\n\n  \"```\" {IDENTIFIER}?     {\n          yybegin(LANG_ID);\n          if (isCodeStart == true) {\n              isCodeStart = false;\n              return CODE_BLOCK_END;\n          } else {\n              isCodeStart = true;\n          };\n          yypushback(yylength());\n      }\n\n  {NEWLINE}               { return NEWLINE; }\n  {TEXT_SEGMENT}          { return TEXT_SEGMENT; }\n  {SHARP}                 { yybegin(EXPR_BLOCK); return SHARP; }\n\n  [^]                     { return TokenType.BAD_CHARACTER; }\n}\n\n<COMMAND_BLOCK> {\n  {IDENTIFIER}            { return IDENTIFIER; }\n  {COLON}                 { yybegin(COMMAND_VALUE_BLOCK); return COLON; }\n  [^]                     { yypushback(1); yybegin(YYINITIAL); }\n}\n\n<SINGLE_COMMNET_BLOCK> {\n  {NEWLINE}               { return NEWLINE; }\n  [^]                     {\n          yypushback(yylength());\n          if (isInsideFrontMatter) {\n              yybegin(FRONT_MATTER_BLOCK);\n          } else {\n              yybegin(YYINITIAL);\n          }\n      }\n}\n\n<COMMAND_VALUE_BLOCK> {\n  {COMMAND_PROP}          { return command_value();  }\n  [^]                     { yypushback(yylength()); yybegin(YYINITIAL); }\n}\n\n<LINE_BLOCK> {\n  {SHARP}                 { return SHARP; }\n  {LINE_INFO}             { return LINE_INFO; }\n  [^]                     { yypushback(yylength()); yybegin(COMMAND_VALUE_BLOCK); }\n}\n\n<AGENT_BLOCK> {\n  {IDENTIFIER}           { yybegin(YYINITIAL); return IDENTIFIER; }\n  {QUOTE_STRING}         { yybegin(YYINITIAL); return QUOTE_STRING; }\n  [^]                    { yypushback(yylength()); yybegin(YYINITIAL); }\n}\n\n<VARIABLE_BLOCK> {\n  {IDENTIFIER}         { return IDENTIFIER; }\n  \"{\"                  { return OPEN_BRACE; }\n  \"}\"                  { return CLOSE_BRACE; }\n  \".\"                  { return DOT; }\n  \"(\"                  { return LPAREN; }\n  \")\"                  { return RPAREN; }\n\n  {COMMENTS}           { return COMMENTS; }\n  [^]                  { yypushback(yylength()); yybegin(YYINITIAL); }\n}\n\n<EXPR_BLOCK> {\n  {IF}                 { return IF; }\n  {ELSE}               { return ELSE; }\n  {ELSEIF}             { return ELSEIF; }\n  {ENDIF}              { return ENDIF; }\n  {END}                { return END; }\n  {AND}                { return AND; }\n  \"(\"                  { return LPAREN; }\n  \")\"                  { return RPAREN; }\n  \"<\"                  { return LT; }\n  \"[\"                  { return LBRACKET; }\n  \"]\"                  { return RBRACKET; }\n  \",\"                  { return COMMA; }\n  \"!\"                  { return NOT; }\n  \"&&\"                 { return ANDAND; }\n  \"||\"                 { return OROR; }\n  \".\"                  { return DOT; }\n  \"==\"                 { return EQEQ; }\n  \"!=\"                 { return NEQ; }\n  \"<\"                  { return LT; }\n  \"<=\"                 { return LTE; }\n  \">\"                  { return GT; }\n  \">=\"                 { return GTE; }\n  \"$\"                  { return VARIABLE_START; }\n  \"{\"                  { return OPEN_BRACE; }\n  \"}\"                  { return CLOSE_BRACE; }\n\n  {COMMA}              { return COMMA; }\n  {NUMBER}             { return NUMBER; }\n  {IDENTIFIER}         { return IDENTIFIER; }\n  {QUOTE_STRING}       { return QUOTE_STRING; }\n  {WHITE_SPACE}        { return TokenType.WHITE_SPACE; }\n\n  // FOR Markdown Header\n  \"#\"                  { yybegin(YYUSED); return SHARP; }\n  [^]                  { yypushback(yylength()); if (isInsideShireTemplate) { yybegin(CODE_BLOCK); }  else if (isInsideFunctionBlock) { yybegin(FUNCTION_DECL_BLOCK);} else { yybegin(YYINITIAL); } }\n}\n\n<CODE_BLOCK> {\n  {CODE_CONTENT}       { if(isCodeStart) { return codeContent(); } else { yybegin(YYINITIAL); yypushback(yylength()); } }\n  {NEWLINE}            { return NEWLINE; }\n  <<EOF>>              { isCodeStart = false; isInsideShireTemplate = false; yybegin(YYINITIAL); yypushback(yylength()); }\n}\n\n<LANG_ID> {\n   \"```\"             { return CODE_BLOCK_START; }\n   {LANGUAGE_IDENTIFIER}     { return LANGUAGE_IDENTIFIER;  }\n   \"$\"               { isInsideShireTemplate = true; yybegin(EXPR_BLOCK); return VARIABLE_START; }\n   [^]               { yypushback(yylength()); yybegin(CODE_BLOCK); }\n}\n"
  },
  {
    "path": "shirelang/src/main/grammar/ShireParser.bnf",
    "content": "{\n  parserClass=\"com.phodal.shirelang.parser.ShireParser\"\n\n  extends=\"com.intellij.extapi.psi.ASTWrapperPsiElement\"\n\n  psiClassPrefix=\"Shire\"\n  psiImplClassSuffix=\"Impl\"\n  psiPackage=\"com.phodal.shirelang.psi\"\n  psiImplPackage=\"com.phodal.shirelang.psi.impl\"\n\n  elementTypeHolderClass=\"com.phodal.shirelang.psi.ShireTypes\"\n  elementTypeClass=\"com.phodal.shirelang.psi.ShireElementType\"\n  tokenTypeClass=\"com.phodal.shirelang.lexer.ShireTokenType\"\n\n  extends(\".*Expr\") = expr\n\n  tokens=[\n    COMMENTS             = 'regexp://[^\\r\\n]*'\n    BLOCK_COMMENT        = 'regexp:/[*][^*]*[*]+([^/*][^*]*[*]+)*/'\n    CONTENT_COMMENTS     = 'regexp:X?\\[([^\\]]+)?\\][^\\t\\r\\n]*'\n    CODE_BLOCK_START     = \"regexp:X?```[a-zA-Z]*\"\n    CODE_BLOCK_END       = \"regexp:X?```\"\n    SINGLE_QUOTED_STRING = \"regexp:X?'(''|[^'])*'\"\n    DOUBLE_QUOTED_STRING = \"regexp:X?\\\"(\\\"\\\"|[^\\\"])*\\\"\"\n    QUOTE_STRING         = \"regexp:X?'(''|[^'])*' | X?\\\"(\\\"\\\"|[^\\\"])*\\\"\"\n    CODE_BLOCK           = \"CODE_BLOCK\"\n    CODE_CONTENT         = \"CODE_CONTENT\"\n    IDENTIFIER           = 'regexp:[_a-zA-Z0-9]\\w*'\n    COLON                = \"regexp:X?:\"\n    COMMAND_PROP         = \"regexp:X?[^\\\\ \\\\t\\\\r\\\\n]*\"\n    SHARP                = \"#\"\n    LINE_INFO            = \"regexp:X?L[0-9]+(C[0-9]+)?(-L[0-9]+(C[0-9]+)?)?\"\n    FRONTMATTER_START    = \"FRONTMATTER_START\"\n    FRONTMATTER_END      = \"FRONTMATTER_END\"\n    IDENTIFIER           = 'regexp:[_a-zA-Z]\\w*'\n    LBRACKET             = \"[\"\n    RBRACKET             = \"]\"\n    INDENT               = \"INDENT\"\n    ARROW                = \"regexp:X?=>\"\n    OPEN_BRACE           = \"{\"\n    CLOSE_BRACE          = \"}\"\n    LPAREN               = \"(\"\n    RPAREN               = \")\"\n    NEWLINE              = \"regexp:\\n\"\n    CASE                 = \"case\"\n    DEFAULT              = \"default\"\n    IF                   = 'if'\n    ELSE                 = 'else'\n    ELSEIF               = 'elseif'\n    END                  = 'end'\n    ENDIF                = 'endif'\n    FROM                 = 'from'\n    WHERE                = 'where'\n    SELECT               = 'select'\n    CONDITION            = 'condition'\n\n    WHEN                 = \"when\"\n    ON_STREAMING         = \"onStreaming\"\n    BEFORE_STREAMING     = \"beforeStreaming\"\n    ON_STREAMING_END     = \"onStreamingEnd\"\n    AFTER_STREAMING      = \"afterStreaming\"\n    FUNCTIONS            = \"functions\"\n\n    // operators\n    DASH                 = \"-\"\n    EQEQ                 = '=='\n    NEQ                  = '!='\n    LT                   = '<'\n    GT                   = '>'\n    LTE                  = '<='\n    GTE                  = '>='\n    ANDAND               = '&&'\n    AND                  = 'and'\n    OROR                 = '||'\n    DOT                  = '.'\n    NOT                  = '!'\n    COMMA                = ','\n    PIPE                 = '|'\n\n    ACCESS               = '::'\n    PROCESS              = \"regexp:X?->\"\n  ]\n}\n\nShireFile ::= frontMatterHeader? (used | code | velocityExpr | markdownHeader | TEXT_SEGMENT | NEWLINE | CONTENT_COMMENTS)*\n\nfrontMatterHeader ::= FRONTMATTER_START NEWLINE frontMatterEntries FRONTMATTER_END\n\nfrontMatterEntries ::= frontMatterEntry*\nfrontMatterEntry ::=\n    // life_cycle\n    lifecycleId COLON (functionStatement | conditionExpr) COMMENTS? NEWLINE?\n    // normal declaration\n    | frontMatterKey COLON (foreignFunction | frontMatterValue | patternAction | functionStatement) COMMENTS? NEWLINE?\n    // handle for comments in code\n    | COMMENTS NEWLINE\n\nlifecycleId       ::= \"when\" | \"onStreaming\" | \"beforeStreaming\" | \"onStreamingEnd\" | \"afterStreaming\"\n\nfrontMatterKey    ::= frontMatterId | QUOTE_STRING | pattern | FUNCTIONS\nfrontMatterValue  ::= (NEWLINE objectKeyValue) | frontMatterArray | IDENTIFIER | QUOTE_STRING | NUMBER | DATE | BOOLEAN\nfrontMatterArray  ::= \"[\" (frontMatterValue (COMMA frontMatterValue)*) \"]\"\n\nfrontMatterId     ::= IDENTIFIER\n\nobjectKeyValue    ::= (INDENT keyValue)*\nkeyValue          ::= frontMatterEntry\n\n/// changed pipelineArgs to types\nforeignFunction   ::= foreignPath (ACCESS foreignFuncName)? \"(\" (foreignType (COMMA foreignType)*)? \")\" (PROCESS foreignOutput)?\nforeignOutput     ::= outputVar (COMMA outputVar)*\noutputVar         ::= IDENTIFIER\n\nforeignPath       ::= QUOTE_STRING\nforeignType       ::= IDENTIFIER\nforeignFuncName   ::= IDENTIFIER\n\npatternAction     ::= pattern actionBlock\nactionBlock       ::=  blockStart (actionBody) blockEnd\n\nactionBody        ::= (actionExpr PIPE)* actionExpr\nactionExpr        ::= funcCall | caseBody\n\nfuncCall          ::= funcName (\"(\" pipelineArgs? \")\")?\nfuncName          ::= IDENTIFIER\npipelineArgs      ::= (pipelineArg (COMMA pipelineArg)*)?\npipelineArg       ::= NUMBER | IDENTIFIER | QUOTE_STRING | variableStart variableId\n\ncaseBody          ::= conditionFlag? CASE NEWLINE* ('condition' | QUOTE_STRING) blockStart casePatternAction* blockEnd\ncasePatternAction ::= caseCondition blockStart actionBody blockEnd\ncaseCondition ::= DEFAULT | pattern | QUOTE_STRING\n\nconditionFlag ::= 'condition' blockStart conditionStatement* blockEnd\nconditionStatement ::= caseCondition blockStart conditionExpr blockEnd NEWLINE?\n\npattern ::= PATTERN_EXPR\n\nconditionExpr ::= expr\nexpr ::=\n    logicalOrExpr\n    | logicalAndExpr\n    | eqComparisonExpr\n    | ineqComparisonExpr\n    | callExpr\n    | qualRefExpr\n    | simpleRefExpr\n    | literalExpr\n    | parenExpr\n    | variableExpr\n\n// See also:\n// b/37137454: Modify databinding expression grammar to allow calls to unqualified methods\n// https://github.com/JetBrains/Grammar-Kit/blob/master/HOWTO.md#24-compact-expression-parsing-with-priorities\n// https://github.com/JetBrains/Grammar-Kit/blob/master/testData/generator/ExprParser.bnf\nfake refExpr ::= expr? '.' IDENTIFIER\nsimpleRefExpr ::= IDENTIFIER {extends=refExpr elementType=refExpr}\nqualRefExpr ::= expr '.' IDENTIFIER {extends=refExpr elementType=refExpr}\n\nlogicalOrExpr ::= expr '||' expr\nlogicalAndExpr ::= expr ('&&' | 'and') expr\neqComparisonExpr ::= expr eqComparisonOp expr\nineqComparisonExpr ::= expr ineqComparisonOp expr\ncallExpr ::= refExpr '(' expressionList? ')'\nexpressionList ::= expr (',' expr)*\n/// variable block\nvariableExpr ::= \"{\" expr \"}\"\n\nliteralExpr ::= literal\nparenExpr ::= '(' expr ')'\n// when\nprivate eqComparisonOp ::= '==' | 'and' | 'AND'\nineqComparisonOp ::= '<=' | '>=' | '<' | '>' | '!='\n\nprivate literal ::= NUMBER\n  | TRUE | FALSE\n  | QUOTE_STRING\n  | IDENTIFIER\n  | \"$\" IDENTIFIER\n\nused ::= (\n    agentStart agentId\n    | commandStart commandId (COLON COMMAND_PROP (SHARP LINE_INFO)?)?\n    | variableStart (variableId | varAccess)\n    | '#' variableStart expr\n)\n\nagentStart ::= '@'\ncommandStart ::= '/'\nvariableStart ::= '$'\n\nagentId ::= IDENTIFIER | QUOTE_STRING\ncommandId ::= IDENTIFIER\nvariableId ::= IDENTIFIER\nlanguageId ::= LANGUAGE_IDENTIFIER\n\n// just make template pass success not fail\nvarAccess ::= OPEN_BRACE variableId (DOT variableId)* CLOSE_BRACE\n\ncode ::= CODE_BLOCK_START (languageId | variableStart expr)? NEWLINE? code_contents? CODE_BLOCK_END?\n\ncode_contents ::= (NEWLINE | CODE_CONTENT)*\n\nvelocityExpr ::=\n    ifExpr NEWLINE*\n\nvelocityBlock ::= (used | code | velocityExpr | markdownHeader | TEXT_SEGMENT | NEWLINE | CONTENT_COMMENTS)*\n\nifExpr ::= ifClause elseifClause* elseClause? '#' 'end'\n\nifClause ::= '#' 'if' '(' expr ')' velocityBlock\n\nelseifClause ::= '#' 'elseif' '(' expr ')' velocityBlock\n\nelseClause ::= '#' 'else' velocityBlock\n\n    // for example, the commit id will be #$storyId\n\nmarkdownHeader ::= SHARP SHARP* TEXT_SEGMENT\n\nfunctionStatement ::= blockStart functionBody? blockEnd\n\nfunctionBody ::=\n    queryStatement\n    | actionBody\n    | conditionExpr\n\nqueryStatement ::= from_clause where_clause select_clause\n\nfrom_clause ::= FROM blockStart psi_element_decl blockEnd\npsi_element_decl ::= psi_var_decl (\",\" psi_var_decl)* (NEWLINE)*\n\nwhere_clause ::= WHERE blockStart expr blockEnd\n\nselect_clause ::= SELECT blockStart expr (',' expr)* blockEnd\n\nprivate blockStart ::= NEWLINE* \"{\" NEWLINE*\nprivate blockEnd ::= NEWLINE* \"}\" NEWLINE*\n\npsi_var_decl ::= psi_type IDENTIFIER\npsi_type ::= IDENTIFIER\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/ShireActionStartupActivity.kt",
    "content": "package com.phodal.shirelang\n\nimport com.intellij.openapi.actionSystem.ActionManager\nimport com.intellij.openapi.actionSystem.Constraints\nimport com.intellij.openapi.actionSystem.DefaultActionGroup\nimport com.intellij.openapi.application.ApplicationManager\nimport com.intellij.openapi.application.smartReadAction\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.startup.ProjectActivity\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.intellij.openapi.wm.ex.ToolWindowManagerListener\nimport com.intellij.psi.PsiManager\nimport com.intellij.psi.search.FileTypeIndex\nimport com.intellij.psi.search.ProjectScope\nimport com.phodal.shirecore.config.InteractionType\nimport com.phodal.shirecore.config.ShireActionLocation\nimport com.phodal.shirecore.provider.ide.InlineChatProvider\nimport com.phodal.shirelang.actions.GlobalShireFileChangesProvider\nimport com.phodal.shirelang.actions.ShireFileChangesProvider\nimport com.phodal.shirelang.actions.base.DynamicShireActionService\nimport com.phodal.shirelang.actions.copyPaste.PasteManagerService\nimport com.phodal.shirelang.compiler.ast.hobbit.HobbitHole\nimport com.phodal.shirelang.thirdparty.ShireSonarLintToolWindowListener\nimport com.phodal.shirelang.psi.ShireFile\n\n\nclass ShireActionStartupActivity : ProjectActivity {\n    override suspend fun execute(project: Project) {\n        bindingShireActions(project)\n    }\n\n    private suspend fun bindingShireActions(project: Project) {\n        GlobalShireFileChangesProvider.getInstance().startup(::attachCopyPasteAction)\n        val changesProvider = ShireFileChangesProvider.getInstance(project)\n        smartReadAction(project) {\n            changesProvider.startup { shireConfig, shireFile ->\n                attachCopyPasteAction(shireConfig, shireFile)\n                attachInlineChat(project)\n            }\n\n            obtainShireFiles(project).forEach {\n                changesProvider.onUpdated(it)\n            }\n\n            attachTerminalAction()\n            attachDatabaseAction()\n            attachVcsLogAction()\n\n            // attache extension actions, like SonarLint\n            attachExtensionActions(project)\n        }\n    }\n\n    private fun attachCopyPasteAction(shireConfig: HobbitHole, shireFile: ShireFile) {\n        if (shireConfig.interaction == InteractionType.OnPaste) {\n            PasteManagerService.getInstance()\n                .registerPasteProcessor(shireConfig, shireFile)\n        }\n    }\n\n    /**\n     * We make terminal plugin optional, so can't add to `TerminalToolwindowActionGroup` the plugin.xml.\n     * So we add it manually here, if terminal plugin is not enabled, this action will not be shown.\n     */\n    private fun attachTerminalAction() {\n        val actionManager = ActionManager.getInstance()\n        val toolsMenu = actionManager.getAction(\"TerminalToolwindowActionGroup\") as? DefaultActionGroup ?: return\n\n        val action = actionManager.getAction(\"ShireTerminalAction\")\n        if (!toolsMenu.containsAction(action)) {\n            toolsMenu.add(action)\n        }\n    }\n\n    private fun attachDatabaseAction() {\n        val actionManager = ActionManager.getInstance()\n        val toolsMenu = actionManager.getAction(\"DatabaseViewPopupMenu\") as? DefaultActionGroup ?: return\n\n        val action = actionManager.getAction(\"ShireDatabaseAction\")\n        if (!toolsMenu.containsAction(action)) {\n            toolsMenu.add(action, Constraints.LAST)\n        }\n    }\n\n    private fun attachVcsLogAction() {\n        val actionManager = ActionManager.getInstance()\n        val toolsMenu = actionManager.getAction(\"Vcs.Log.ContextMenu\") as? DefaultActionGroup ?: return\n\n        val action = actionManager.getAction(\"ShireVcsLogAction\")\n        if (!toolsMenu.containsAction(action)) {\n            toolsMenu.add(action, Constraints.FIRST)\n        }\n    }\n\n    private fun attachInlineChat(project: Project) {\n        if (DynamicShireActionService.getInstance(project).getActions(ShireActionLocation.INLINE_CHAT).isNotEmpty()) {\n            InlineChatProvider.provide()?.addListener(project)\n        }else{\n            InlineChatProvider.provide()?.removeListener(project)\n        }\n    }\n\n    private fun attachExtensionActions(project: Project) {\n        project.messageBus.connect().subscribe(ToolWindowManagerListener.TOPIC, ShireSonarLintToolWindowListener());\n    }\n\n    companion object {\n        private fun obtainShireFiles(project: Project): List<ShireFile> {\n            ApplicationManager.getApplication().assertReadAccessAllowed()\n            val projectShire = obtainProjectShires(project).map {\n                PsiManager.getInstance(project).findFile(it) as ShireFile\n            }\n\n            return projectShire\n        }\n\n        private fun obtainProjectShires(project: Project): List<VirtualFile> {\n            val scope = ProjectScope.getContentScope(project)\n            val projectShire = FileTypeIndex.getFiles(ShireFileType.INSTANCE, scope).mapNotNull {\n                it\n            }\n\n            return projectShire\n        }\n\n        fun findShireFile(project: Project, filename: String): ShireFile? {\n            return DynamicShireActionService.getInstance(project).getAllActions().map {\n                it.shireFile\n            }.firstOrNull {\n                it.name == filename\n            }\n        }\n    }\n}\n\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/ShireAstFactory.kt",
    "content": "package com.phodal.shirelang\n\nimport com.intellij.lang.ASTFactory\n\nclass ShireAstFactory : ASTFactory()\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/ShireBundle.kt",
    "content": "package com.phodal.shirelang\n\nimport com.intellij.DynamicBundle\nimport org.jetbrains.annotations.NonNls\nimport org.jetbrains.annotations.PropertyKey\n\n\n@NonNls\nprivate const val ShireBUNDLE: String = \"messages.ShireBundle\"\n\nobject ShireBundle : DynamicBundle(ShireBUNDLE) {\n    @Suppress(\"SpreadOperator\")\n    @JvmStatic\n    fun message(@PropertyKey(resourceBundle = ShireBUNDLE) key: String, vararg params: Any) = getMessage(key, *params)\n\n    @Suppress(\"SpreadOperator\", \"unused\")\n    @JvmStatic\n    fun messagePointer(@PropertyKey(resourceBundle = ShireBUNDLE) key: String, vararg params: Any) =\n        getLazyMessage(key, *params)\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/ShireFileType.kt",
    "content": "package com.phodal.shirelang\n\nimport com.intellij.openapi.fileTypes.LanguageFileType\nimport com.intellij.openapi.vfs.VirtualFile\nimport javax.swing.Icon\n\nclass ShireFileType private constructor() : LanguageFileType(ShireLanguage.INSTANCE) {\n    companion object {\n        @JvmStatic\n        val INSTANCE = ShireFileType()\n    }\n\n    override fun getName(): String = \"ShireFile\"\n\n    override fun getIcon(): Icon = ShireIcons.DEFAULT\n\n    override fun getDefaultExtension(): String = \"shire\"\n\n    override fun getCharset(file: VirtualFile, content: ByteArray): String = \"UTF-8\"\n\n    override fun getDescription(): String = \"Shire file\"\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/ShireIcons.kt",
    "content": "package com.phodal.shirelang\n\nimport com.intellij.openapi.util.IconLoader\nimport javax.swing.Icon\n\nobject ShireIcons {\n    @JvmField\n    val DEFAULT: Icon = IconLoader.getIcon(\"/icons/shire.svg\", ShireIcons::class.java)\n    @JvmField\n    val COMMAND: Icon = IconLoader.getIcon(\"/icons/shire.svg\", ShireIcons::class.java)\n    @JvmField\n    val Terminal: Icon = IconLoader.getIcon(\"/icons/terminal.svg\", ShireIcons::class.java)\n    @JvmField\n    val Idea: Icon = IconLoader.getIcon(\"/icons/idea.svg\", ShireIcons::class.java)\n    @JvmField\n    val PsiExpr: Icon = IconLoader.getIcon(\"/icons/shire-psi-expr.svg\", ShireIcons::class.java)\n    @JvmField\n    val Variable: Icon = IconLoader.getIcon(\"/icons/shire-variable.svg\", ShireIcons::class.java)\n    @JvmField\n    val Pipeline: Icon = IconLoader.getIcon(\"/icons/shire-pipeline.svg\", ShireIcons::class.java)\n    @JvmField\n    val Case: Icon = IconLoader.getIcon(\"/icons/shire-case.svg\", ShireIcons::class.java)\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/ShireInCommentInjector.kt",
    "content": "package com.phodal.shirelang\n\nimport com.intellij.lang.injection.MultiHostInjector\nimport com.intellij.lang.injection.MultiHostRegistrar\nimport com.intellij.psi.PsiComment\nimport com.intellij.psi.PsiDocCommentBase\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.PsiLanguageInjectionHost\nimport org.jetbrains.annotations.NotNull\nimport java.util.regex.Matcher\nimport java.util.regex.Pattern\n\n/**\n * Comment injector for Shire language\n */\nclass ShireInCommentInjector : MultiHostInjector {\n    private val SHIRE_CODE_PATTERN: Pattern = Pattern.compile(\"```shire\\\\s*(.*?)\\\\s*```\", Pattern.DOTALL)\n\n    override fun getLanguagesToInject(@NotNull registrar: MultiHostRegistrar, @NotNull host: PsiElement) {\n        if (host !is PsiDocCommentBase) {\n            return\n        }\n\n        val commentText = host.getText()\n        val matcher: Matcher = SHIRE_CODE_PATTERN.matcher(commentText)\n\n        if (host as? PsiLanguageInjectionHost == null) {\n            return\n        }\n\n        /// refs to : https://github.com/intellij-rust/intellij-rust/blob/c6657c02bb62075bf7b7ceb84d000f93dda34dc1/src/main/kotlin/org/rust/ide/injected/RsDoctestLanguageInjector.kt\n//        while (matcher.find()) {\n//            val start: Int = matcher.start(1)\n//            val end: Int = matcher.end(1)\n//\n//            registrar.startInjecting(ShireLanguage.INSTANCE)\n//                .addPlace(null, null, host as PsiLanguageInjectionHost, TextRange.create(start, end))\n//                .doneInjecting()\n//        }\n    }\n\n    @NotNull\n    override fun elementsToInjectIn(): List<Class<out PsiElement>?> {\n        return listOf(PsiComment::class.java)\n    }\n\n    companion object {\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/ShireLanguage.kt",
    "content": "package com.phodal.shirelang\n\nimport com.intellij.lang.Language\n\nclass ShireLanguage : Language(\"Shire\", \"text/shire\", \"text/x-shire\", \"application/x-shire\") {\n    companion object {\n        @JvmStatic\n        val INSTANCE = ShireLanguage()\n    }\n\n    override fun isCaseSensitive() = true\n    override fun getDisplayName() = \"Shire\"\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/ShireLanguageInjector.kt",
    "content": "// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage com.phodal.shirelang\n\nimport com.intellij.lang.Language\nimport com.intellij.openapi.diagnostic.logger\nimport com.intellij.openapi.util.TextRange\nimport com.intellij.psi.InjectedLanguagePlaces\nimport com.intellij.psi.LanguageInjector\nimport com.intellij.psi.PsiLanguageInjectionHost\nimport com.intellij.psi.util.elementType\nimport com.phodal.shirecore.utils.markdown.CodeFenceLanguage\nimport com.phodal.shirelang.parser.CodeBlockElement\nimport com.phodal.shirelang.parser.PatternElement\nimport com.phodal.shirelang.psi.ShireTypes\nimport com.phodal.shirelang.psi.impl.ShireFuncCallImpl\n\nclass ShireLanguageInjector : LanguageInjector {\n    override fun getLanguagesToInject(host: PsiLanguageInjectionHost, registrar: InjectedLanguagePlaces) {\n        injectRegexLanguage(host, registrar)\n        injectCodeBlockLanguage(host, registrar)\n        injectRegexFunction(host, registrar)\n    }\n\n    private fun injectRegexFunction(host: PsiLanguageInjectionHost, registrar: InjectedLanguagePlaces) {\n        if (host !is ShireFuncCallImpl || !host.isValidHost()) return\n\n        val args = host.pipelineArgs?.children ?: return\n\n        val language = CodeFenceLanguage.findLanguage(\"RegExp\")\n        val funcLength = host.funcName.text.length\n\n        args.firstOrNull()?.let { element ->\n            when (element.node.firstChildNode.elementType) {\n                ShireTypes.QUOTE_STRING -> {\n                    val startOffset = element.startOffsetInParent + funcLength + 2\n                    val endOffset = element.startOffsetInParent + funcLength + element.textLength\n                    registrar.addPlace(language, TextRange(startOffset, endOffset), null, null)\n                }\n            }\n        }\n    }\n\n    private fun injectRegexLanguage(host: PsiLanguageInjectionHost, registrar: InjectedLanguagePlaces) {\n        if (host !is PatternElement || !host.isValidHost()) return\n\n        val text = host.text\n        val language = CodeFenceLanguage.findLanguage(\"RegExp\")\n\n        val range = TextRange(0, text.length)\n        registrar.addPlace(language, range, null, null)\n    }\n\n    private fun injectCodeBlockLanguage(\n        host: PsiLanguageInjectionHost,\n        registrar: InjectedLanguagePlaces,\n    ) {\n        if (host !is CodeBlockElement || !host.isValidHost()) return\n\n        val hasCodeContents = host.children.any { it.elementType == ShireTypes.CODE_CONTENTS }\n        if (!hasCodeContents) return\n\n        val text: String = if (host.isShireTemplateCodeBlock()) {\n            ShireLanguage.INSTANCE.id\n        } else {\n            host.getLanguageId()?.text\n        } ?: return\n\n        val contentList = CodeBlockElement.obtainFenceContent(host) ?: return\n        if (contentList.isEmpty()) return\n\n        val language = CodeFenceLanguage.findLanguage(text)\n        injectAsOnePlace(host, language, registrar)\n    }\n\n    private fun injectAsOnePlace(host: CodeBlockElement, language: Language, registrar: InjectedLanguagePlaces) {\n        val contentList = CodeBlockElement.obtainFenceContent(host) ?: return\n\n        val first = contentList.first()\n        val last = contentList.last()\n\n        try {\n            val textRange = TextRange.create(first.startOffsetInParent, last.startOffsetInParent + last.textLength)\n            registrar.addPlace(language, textRange, null, null)\n        } catch (e: Exception) {\n            logger<ShireLanguageInjector>().warn(\"Failed to inject language\", e)\n        }\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/ShireTypedHandler.kt",
    "content": "// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage com.phodal.shirelang\n\nimport com.intellij.codeInsight.AutoPopupController\nimport com.intellij.codeInsight.CodeInsightSettings\nimport com.intellij.codeInsight.editorActions.TypedHandlerDelegate\nimport com.intellij.openapi.editor.Editor\nimport com.intellij.openapi.fileTypes.FileType\nimport com.intellij.openapi.project.Project\nimport com.intellij.psi.PsiDocumentManager\nimport com.intellij.psi.PsiFile\nimport com.intellij.psi.impl.source.tree.LeafPsiElement\nimport com.intellij.psi.util.elementType\nimport com.phodal.shirelang.psi.ShireFile\nimport com.phodal.shirelang.psi.ShireTypes\nimport org.jetbrains.kotlin.utils.addToStdlib.UnsafeCastFunction\nimport org.jetbrains.kotlin.utils.addToStdlib.safeAs\n\nclass ShireTypedHandler : TypedHandlerDelegate() {\n    override fun checkAutoPopup(charTyped: Char, project: Project, editor: Editor, file: PsiFile): Result {\n        if (file !is ShireFile) return Result.CONTINUE\n\n        return when (charTyped) {\n            '`' -> {\n                val offset = editor.caretModel.primaryCaret.offset\n                if (offset == 0) return Result.CONTINUE\n\n                val element = file.findElementAt(offset - 1)\n                if (element?.elementType == ShireTypes.CODE_CONTENT || element?.elementType == ShireTypes.CODE_BLOCK_END) {\n                    return Result.CONTINUE\n                }\n\n                PsiDocumentManager.getInstance(project).commitDocument(editor.document)\n                AutoPopupController.getInstance(project).autoPopupMemberLookup(editor, null)\n                return Result.STOP\n            }\n\n            '@', '/', '$', ':' -> {\n                PsiDocumentManager.getInstance(project).commitDocument(editor.document)\n                AutoPopupController.getInstance(project).autoPopupMemberLookup(editor, null)\n                Result.STOP\n            }\n\n            else -> {\n                Result.CONTINUE\n            }\n        }\n    }\n\n\n    override fun beforeCharTyped(c: Char, project: Project, editor: Editor, file: PsiFile, fileType: FileType): Result {\n        if (file.fileType != ShireFileType.INSTANCE) return Result.CONTINUE\n\n        return Result.CONTINUE\n    }\n\n\n    @OptIn(UnsafeCastFunction::class)\n    override fun charTyped(c: Char, project: Project, editor: Editor, file: PsiFile): Result {\n        if (file.fileType != ShireFileType.INSTANCE) return Result.CONTINUE\n\n        when {\n            c == '{' && CodeInsightSettings.getInstance().AUTOINSERT_PAIR_BRACKET -> {\n                PsiDocumentManager.getInstance(project).commitDocument(editor.document)\n                val offset = editor.caretModel.offset\n                val previousElement = file.findElementAt(offset - 1)\n\n                if (previousElement is LeafPsiElement) {\n                    val identifier = file.findElementAt(offset)\n                        ?.safeAs<LeafPsiElement>()\n                        ?.takeIf { it.elementType == ShireTypes.IDENTIFIER || it.elementType == ShireTypes.QUOTE_STRING }\n                        ?: kotlin.run {\n                            editor.document.insertString(offset, \"}\")\n                            return Result.STOP\n                        }\n\n                    // todo: add more logic here\n                }\n            }\n        }\n\n        return Result.CONTINUE\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/actions/GlobalShireFileChangesProvider.kt",
    "content": "package com.phodal.shirelang.actions\n\nimport com.intellij.concurrency.ConcurrentCollectionFactory\nimport com.intellij.openapi.application.ApplicationManager\nimport com.intellij.openapi.application.PathManager\nimport com.intellij.openapi.application.ReadAction\nimport com.intellij.openapi.components.Service\nimport com.intellij.openapi.diagnostic.logger\nimport com.intellij.openapi.project.ProjectManager\nimport com.intellij.openapi.vfs.LocalFileSystem\nimport com.intellij.openapi.vfs.LocalFileSystem.WatchRequest\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.intellij.openapi.vfs.VirtualFileEvent\nimport com.intellij.openapi.vfs.VirtualFileListener\nimport com.intellij.openapi.vfs.newvfs.RefreshQueue\nimport com.intellij.openapi.vfs.newvfs.impl.VfsData\nimport com.intellij.psi.PsiManager\nimport com.phodal.shirecore.workerThread\nimport com.phodal.shirelang.actions.base.GlobalShireActionService\nimport com.phodal.shirelang.compiler.ast.hobbit.HobbitHole\nimport com.phodal.shirelang.psi.ShireFile\nimport kotlinx.coroutines.CoroutineScope\nimport java.nio.file.Paths\nimport java.util.*\nimport kotlin.io.path.pathString\n\n/**\n * Load all shire files in the specified path and its sub paths,and watch their changes.\n *\n * The root path may be outside the project path,It is necessary to load the shire files\n * and directories into vfs to ensure that [allChildrenLoaded][VfsData.DirectoryData.myAllChildrenLoaded] is true,\n * otherwise related events cannot be generated in processEvents of [RefreshQueue].\n *\n * @author lk\n */\n@Service(Service.Level.APP)\nclass GlobalShireFileChangesProvider {\n\n    // The root path can be configured in the future\n    private val homeShirePath: String? =\n        runCatching { Paths.get(System.getProperty(\"user.home\"), \".shire\").pathString }.getOrNull()\n\n    private val watchRoots = ConcurrentCollectionFactory.createConcurrentMap<String, WatchRequest?>()\n\n    private val localFileSystem: LocalFileSystem = LocalFileSystem.getInstance()\n\n    private val listener: VirtualFileListener = object : VirtualFileListener {\n        override fun fileCreated(event: VirtualFileEvent) {\n            if (event.file.isDirectory) {\n                loadShireAction(PathManager.getAbsolutePath(event.file.path))\n            }\n        }\n\n        override fun beforeFileDeletion(event: VirtualFileEvent) {\n            if (event.file.isDirectory) {\n                removeShireAction(PathManager.getAbsolutePath(event.file.path))\n            }\n\n        }\n    }\n\n    @Volatile\n    var shireFileModifier: ShireFileModifier? = null\n\n    private fun addRootToWatch(path: String) {\n        if (watchRoots.isEmpty()) localFileSystem.addVirtualFileListener(listener)\n        watchRoots.computeIfAbsent(path) {\n            localFileSystem.addRootToWatch(it, false)\n        }\n    }\n\n    private fun removeRootToWatch(path: String) {\n        // Do not delete the root path\n        if (homeShirePath == path) return\n        watchRoots.remove(path)\n        if (watchRoots.isEmpty()) localFileSystem.removeVirtualFileListener(listener)\n    }\n\n    private fun loadShireAction(path: String) {\n        if (homeShirePath == null || !path.startsWith(homeShirePath)) return\n        refreshShireAction(path, ::addRootToWatch)\n    }\n\n    private fun removeShireAction(path: String) {\n        val removedRoots = mutableSetOf<String>()\n        refreshShireAction(path, removedRoots::add)\n        removedRoots.forEach(::removeRootToWatch)\n    }\n\n    private fun refreshShireAction(path: String, handle: (String) -> Unit) {\n        val queue = LinkedList<VirtualFile>()\n        val file = localFileSystem.findFileByPath(path) ?: return\n        queue.push(file)\n\n        while (!queue.isEmpty()) {\n            val virtualFile = queue.pop()\n            if (virtualFile.isDirectory) {\n                handle(PathManager.getAbsolutePath(virtualFile.path))\n                virtualFile.children.forEach {\n                    queue.push(it)\n                }\n            } else {\n                ShireUpdater.publisher.onUpdated(virtualFile)\n            }\n        }\n    }\n\n    fun startup(afterUpdater: (HobbitHole, ShireFile) -> Unit) {\n        if (homeShirePath == null) {\n            logger.warn(\"Unable to access the root directory of the global shire file configuration\")\n            return\n        }\n        var initial = false\n        (shireFileModifier ?: synchronized(this) {\n            shireFileModifier ?: ShireFileModifier(ShireFileModificationContext(\n                GlobalShireActionService.getInstance(),\n                afterUpdater,\n                CoroutineScope(workerThread)\n            ) {\n                ReadAction.compute<ShireFile?, Throwable> {\n                    it.run {\n                        ProjectManager.getInstance().openProjects.firstNotNullOfOrNull {\n                            PsiManager.getInstance(it).findFile(this) as? ShireFile\n                        }\n                    }\n                }\n            }).also {\n                initial = true\n                shireFileModifier = it\n            }\n        }).startup {\n            watchRoots.keys.contains(PathManager.getAbsolutePath(it.parent.path))\n        }\n\n        if (initial) loadShireAction(homeShirePath)\n\n    }\n\n    companion object {\n\n        private val logger = logger<GlobalShireFileChangesProvider>()\n\n        fun getInstance(): GlobalShireFileChangesProvider =\n            ApplicationManager.getApplication().getService(GlobalShireFileChangesProvider::class.java)\n    }\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/actions/ShireFileChangesProvider.kt",
    "content": "package com.phodal.shirelang.actions\n\nimport com.intellij.openapi.Disposable\nimport com.intellij.openapi.application.ReadAction\nimport com.intellij.openapi.components.Service\nimport com.intellij.openapi.editor.Document\nimport com.intellij.openapi.editor.event.DocumentEvent\nimport com.intellij.openapi.editor.event.DocumentListener\nimport com.intellij.openapi.fileEditor.FileDocumentManager\nimport com.intellij.openapi.fileEditor.FileDocumentManagerListener\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.roots.ProjectFileIndex\nimport com.intellij.openapi.vfs.AsyncFileListener\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.intellij.openapi.vfs.newvfs.events.VFileCopyEvent\nimport com.intellij.openapi.vfs.newvfs.events.VFileDeleteEvent\nimport com.intellij.openapi.vfs.newvfs.events.VFileEvent\nimport com.intellij.psi.PsiManager\nimport com.intellij.testFramework.LightVirtualFile\nimport com.phodal.shirecore.ShireCoroutineScope\nimport com.phodal.shirelang.ShireFileType\nimport com.phodal.shirelang.actions.base.DynamicShireActionService\nimport com.phodal.shirelang.compiler.ast.hobbit.HobbitHole\nimport com.phodal.shirelang.psi.ShireFile\n\n/**\n * It is used after the project is started\n * and can use [shireFileModifier] to handle shire file change events.\n *\n * @author lk\n */\n@Service(Service.Level.PROJECT)\nclass ShireFileChangesProvider(val project: Project) : Disposable {\n\n    @Volatile\n    var shireFileModifier: ShireFileModifier? = null\n\n    fun startup(afterUpdater: (HobbitHole, ShireFile) -> Unit) {\n        (shireFileModifier ?: synchronized(this) {\n            shireFileModifier ?: ShireFileModifier(\n                ShireFileModificationContext(\n                    DynamicShireActionService.getInstance(project),\n                    afterUpdater,\n                    ShireCoroutineScope.scope(project)\n                ) {\n                    ReadAction.compute<ShireFile?, Throwable> {\n                        PsiManager.getInstance(project).findFile(it) as? ShireFile\n                    }\n                }).also { shireFileModifier = it }\n        }).startup {\n            ReadAction.compute<Boolean, Throwable> { ProjectFileIndex.getInstance(project).isInProject(it) }\n        }\n\n    }\n\n    fun onUpdated(file: ShireFile) {\n        ShireUpdater.publisher.onUpdated(file.virtualFile)\n    }\n\n    companion object {\n        fun getInstance(project: Project): ShireFileChangesProvider {\n            return project.getService(ShireFileChangesProvider::class.java)\n        }\n\n    }\n\n    override fun dispose() {\n        shireFileModifier?.dispose()\n    }\n}\n\ninternal class ShireFileModificationListener : FileDocumentManagerListener, DocumentListener, ShireFileListener {\n    fun onUpdated(document: Document) {\n        FileDocumentManager.getInstance().getFile(document).let { onUpdated(it) }\n    }\n\n    override fun documentChanged(event: DocumentEvent) {\n        onUpdated(event.document)\n    }\n\n    override fun bulkUpdateFinished(document: Document) {\n        onUpdated(document)\n    }\n\n    override fun unsavedDocumentDropped(document: Document) {\n        onUpdated(document)\n    }\n\n}\n\ninternal class AsyncShireFileListener : AsyncFileListener, ShireFileListener {\n    override fun prepareChange(events: MutableList<out VFileEvent>): AsyncFileListener.ChangeApplier {\n        val beforeChangedEvents = mutableListOf<VFileEvent>()\n        val afterChangedEvents = mutableListOf<VFileEvent>()\n        for (event in events) {\n            when (event) {\n                is VFileDeleteEvent -> {\n                    beforeChangedEvents.add(event)\n                }\n\n                else -> {\n                    afterChangedEvents.add(event) // Maybe the file type has been changed\n                }\n            }\n        }\n\n        return object : AsyncFileListener.ChangeApplier {\n            override fun beforeVfsChange() {\n                beforeChangedEvents.forEach { onUpdated(it.file) }\n            }\n\n            override fun afterVfsChange() {\n                afterChangedEvents.forEach {\n                    when (it) {\n                        is VFileCopyEvent -> {\n                            onUpdated(it.findCreatedFile())\n                        }\n\n                        else -> {\n                            onUpdated(it.file)\n                        }\n                    }\n                }\n            }\n        }\n\n    }\n\n\n}\n\n/**\n * Only handle events related to shire file\n */\ninterface ShireFileListener {\n    fun onUpdated(file: VirtualFile?) {\n        try {\n            if (file == null || !file.isValid || file.isDirectory) return\n            if (file.fileType !is ShireFileType) return\n            if (file is LightVirtualFile) return\n            ShireUpdater.publisher.onUpdated(file)\n        } catch (e: Exception) {\n            e.printStackTrace()\n        }\n    }\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/actions/ShireFileModifier.kt",
    "content": "package com.phodal.shirelang.actions\n\nimport com.intellij.openapi.application.ApplicationManager\nimport com.intellij.openapi.application.runReadAction\nimport com.intellij.openapi.diagnostic.logger\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.intellij.util.messages.MessageBusConnection\nimport com.intellij.util.messages.Topic\nimport com.phodal.shirelang.actions.base.DynamicActionService\nimport com.phodal.shirelang.actions.base.DynamicShireActionConfig\nimport com.phodal.shirelang.compiler.ast.hobbit.HobbitHole\nimport com.phodal.shirelang.compiler.parser.HobbitHoleParser\nimport com.phodal.shirelang.psi.ShireFile\nimport kotlinx.coroutines.*\nimport java.util.concurrent.TimeUnit\n\n/**\n * This class is provided to ShireFileChangesProvider to dynamically adjust\n * the shire action config when the content of the shire file changes.\n *\n * It supports delayed processing([delayTime]) to avoid duplicate updates as much as possible.\n *\n * @author lk\n */\nclass ShireFileModifier(val context: ShireFileModificationContext) {\n\n    private val dynamicActionService: DynamicActionService\n\n    private val scope: CoroutineScope\n\n    init {\n        dynamicActionService = context.dynamicActionService\n        scope = context.scope\n    }\n\n    private val queue: MutableSet<ShireFile> = mutableSetOf()\n\n    private val waitingUpdateQueue: ArrayDeque<ShireFile> = ArrayDeque()\n\n    private val delayTime: Long = TimeUnit.SECONDS.toMillis(3)\n\n    @OptIn(ExperimentalCoroutinesApi::class)\n    private val dispatcher = Dispatchers.IO.limitedParallelism(1)\n\n    @Volatile\n    var connect: MessageBusConnection? = null\n\n    private fun modify(afterUpdater: ((HobbitHole, ShireFile) -> Unit)?) {\n        scope.launch(dispatcher) {\n            delay(delayTime)\n            synchronized(queue) {\n                waitingUpdateQueue.addAll(queue)\n                queue.clear()\n            }\n            runBlocking {\n                runReadAction {\n                    waitingUpdateQueue.forEach { file ->\n                        if (!file.isValid) {\n                            dynamicActionService.removeAction(file)\n                            logger.debug(\"Shire file[${file.name}] is deleted\")\n                            file.virtualFile.takeIf { it.isValid }?.run { context.convertor.invoke(this)?.let { println(\"reload.\")\n                                loadShireAction(it, afterUpdater) } }\n                            return@forEach\n                        }\n                        if (!file.isPhysical) return@forEach\n                        loadShireAction(file, afterUpdater)\n                    }\n                }\n                waitingUpdateQueue.clear()\n            }\n        }\n    }\n\n    private fun loadShireAction(file: ShireFile, afterUpdater: ((HobbitHole, ShireFile) -> Unit)?) {\n        try {\n            HobbitHoleParser.parse(file).let {\n                dynamicActionService.putAction(file, DynamicShireActionConfig(it?.name ?: file.name, it, file))\n                if (it != null) afterUpdater?.invoke(it, file)\n                logger.debug(\"Shire file[${file.virtualFile.path}] is loaded\")\n            }\n        } catch (e: Exception) {\n            logger.error(\"An error occurred while parsing shire file: ${file.virtualFile.path}\", e)\n        }\n    }\n\n    fun startup(predicate: (VirtualFile) -> Boolean) {\n        connect ?: synchronized(this) {\n            connect ?: ShireUpdater.register { it.takeIf(predicate)?.let(context.convertor)?.let { add(it) } }.also { connect = it }\n        }\n\n    }\n\n    private fun add(file: ShireFile) {\n        synchronized(queue) {\n            queue.add(file)\n        }\n        modify(context.afterUpdater)\n    }\n\n    fun dispose() {\n        connect?.dispose()\n    }\n\n    companion object {\n\n        private val logger = logger<ShireFileModifier>()\n\n    }\n}\n\n\nfun interface ShireUpdater {\n\n    fun onUpdated(file: VirtualFile)\n\n    companion object {\n\n        @Topic.ProjectLevel\n        val TOPIC: Topic<ShireUpdater> = Topic.create(\"shire file updated\", ShireUpdater::class.java)\n\n        val publisher: ShireUpdater\n            get() = ApplicationManager.getApplication().messageBus.syncPublisher(TOPIC)\n\n        fun register(subscriber: ShireUpdater): MessageBusConnection {\n            val connection = ApplicationManager.getApplication().messageBus.connect()\n            connection.subscribe(TOPIC, subscriber)\n            return connection\n        }\n    }\n}\n\ndata class ShireFileModificationContext(\n    val dynamicActionService: DynamicActionService,\n    val afterUpdater: ((HobbitHole, ShireFile) -> Unit)?,\n    val scope: CoroutineScope,\n    val convertor: (VirtualFile) -> ShireFile?\n)\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/actions/ShireRunFileAction.kt",
    "content": "package com.phodal.shirelang.actions\n\nimport com.intellij.execution.ExecutionManager\nimport com.intellij.execution.RunManager\nimport com.intellij.execution.RunnerAndConfigurationSettings\nimport com.intellij.execution.actions.ConfigurationContext\nimport com.intellij.execution.actions.RunConfigurationProducer\nimport com.intellij.execution.executors.DefaultRunExecutor\nimport com.intellij.execution.process.ProcessEvent\nimport com.intellij.execution.runners.ExecutionEnvironmentBuilder\nimport com.intellij.openapi.actionSystem.ActionUpdateThread\nimport com.intellij.openapi.actionSystem.AnActionEvent\nimport com.intellij.openapi.actionSystem.CommonDataKeys\nimport com.intellij.openapi.application.ApplicationManager\nimport com.intellij.openapi.diagnostic.logger\nimport com.intellij.openapi.project.DumbAwareAction\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.util.Disposer\nimport com.phodal.shirecore.middleware.post.PostProcessorContext\nimport com.phodal.shirelang.actions.base.DynamicShireActionConfig\nimport com.phodal.shirelang.psi.ShireFile\nimport com.phodal.shirelang.run.*\nimport org.jetbrains.annotations.NonNls\nimport java.util.concurrent.CompletableFuture\n\nclass ShireRunFileAction : DumbAwareAction() {\n    override fun getActionUpdateThread(): ActionUpdateThread = ActionUpdateThread.BGT\n\n    override fun update(e: AnActionEvent) {\n        val file = e.getData(CommonDataKeys.PSI_FILE) ?: return\n        e.presentation.isEnabledAndVisible = file is ShireFile\n\n        if (e.presentation.text.isNullOrBlank()) {\n            e.presentation.text = \"Run Shire file: ${file.name}\"\n        }\n    }\n\n    override fun actionPerformed(e: AnActionEvent) {\n        val file = e.getData(CommonDataKeys.PSI_FILE) as? ShireFile ?: return\n        val project = file.project\n        val config = DynamicShireActionConfig.from(file)\n\n        val existingConfiguration = createRunConfig(e)\n        executeFile(project, config, existingConfiguration)\n    }\n\n    companion object {\n        const val ID: @NonNls String = \"runShireFileAction\"\n\n        fun createRunConfig(e: AnActionEvent): RunnerAndConfigurationSettings? {\n            val context = ConfigurationContext.getFromContext(e.dataContext, e.place)\n            return RunConfigurationProducer.getInstance(ShireRunConfigurationProducer::class.java)\n                .findExistingConfiguration(context)\n        }\n\n        /**\n         * Executes a Shire file within the specified project context.\n         *\n         * ```kotlin\n         * val project = ... // IntelliJ IDEA project\n         * val config = ... // DynamicShireActionConfig object\n         * val runSettings = ... // Optional RunnerAndConfigurationSettings\n         * val variables = mapOf(\"key1\" to \"value1\", \"key2\" to \"value2\")\n         *\n         * executeFile(project, config, runSettings, variables)\n         * ```\n         *\n         * @param project The IntelliJ IDEA project in which the Shire file is to be executed.\n         * @param config The configuration object containing details about the Shire file to be executed.\n         * @param runSettings Optional runner and configuration settings to use for execution. If null, a new configuration will be created.\n         * @param variables A map of variables to be passed to the Shire file during execution. Defaults to an empty map.\n         *\n         * @throws Exception If there is an error creating the run configuration or execution environment.\n         */\n        fun executeFile(\n            project: Project,\n            config: DynamicShireActionConfig,\n            runSettings: RunnerAndConfigurationSettings?,\n            variables: Map<String, String> = mapOf(),\n        ) {\n            val settings = try {\n                runSettings ?: RunManager.getInstance(project)\n                    .createConfiguration(config.name, ShireConfigurationType::class.java)\n            } catch (e: Exception) {\n                logger<ShireRunFileAction>().error(\"Failed to create configuration\", e)\n                return\n            }\n\n            val runConfiguration = settings.configuration as ShireConfiguration\n            runConfiguration.setScriptPath(config.shireFile.virtualFile.path)\n            if (variables.isNotEmpty()) {\n                runConfiguration.setVariables(variables)\n                PostProcessorContext.updateRunConfigVariables(variables)\n            }\n\n            val executorInstance = DefaultRunExecutor.getRunExecutorInstance()\n            val executionEnvironment = ExecutionEnvironmentBuilder\n                .createOrNull(executorInstance, runConfiguration)\n                ?.build()\n\n            if (executionEnvironment == null) {\n                logger<ShireRunFileAction>().error(\"Failed to create execution environment\")\n                return\n            }\n\n            ExecutionManager.getInstance(project).restartRunProfile(executionEnvironment)\n        }\n\n        fun suspendExecuteFile(\n            project: Project,\n            file: ShireFile,\n            variableNames: Array<String> = arrayOf(),\n            variableTable: MutableMap<String, Any?> = mutableMapOf(),\n        ): String? {\n            val variables: MutableMap<String, String> = mutableMapOf()\n            for (i in variableNames.indices) {\n                val varName = variableNames[i]\n                val varValue = variableTable[varName].toString()\n                variables[varName] = varValue\n            }\n\n            val config = DynamicShireActionConfig.from(file)\n\n            val settings = try {\n                RunManager.getInstance(project)\n                    .createConfiguration(config.name, ShireConfigurationType::class.java)\n            } catch (e: Exception) {\n                logger<ShireRunFileAction>().error(\"Failed to create configuration\", e)\n                return null\n            }\n\n            val runConfiguration = settings.configuration as ShireConfiguration\n            runConfiguration.setScriptPath(config.shireFile.virtualFile.path)\n            if (variables.isNotEmpty()) {\n                runConfiguration.setVariables(variables)\n                PostProcessorContext.updateRunConfigVariables(variables)\n            }\n\n            val executorInstance = DefaultRunExecutor.getRunExecutorInstance()\n            val executionEnvironment = ExecutionEnvironmentBuilder\n                .createOrNull(executorInstance, runConfiguration)\n                ?.build()\n\n            if (executionEnvironment == null) {\n                logger<ShireRunFileAction>().error(\"Failed to create execution environment\")\n                return null\n            }\n\n            val future = CompletableFuture<String>()\n\n            val hintDisposable = Disposer.newDisposable()\n            val connection = ApplicationManager.getApplication().messageBus.connect(hintDisposable)\n            connection.subscribe(ShireRunListener.TOPIC, object : ShireRunListener {\n                override fun runFinish(\n                    allOutput: String,\n                    llmOutput: String,\n                    event: ProcessEvent,\n                    scriptPath: String,\n                    consoleView: ShireConsoleView?,\n                ) {\n                    future.complete(llmOutput)\n                    connection.disconnect()\n                    Disposer.dispose(hintDisposable)\n                }\n            })\n\n            ExecutionManager.getInstance(project).restartRunProfile(\n                project,\n                executorInstance,\n                executionEnvironment.executionTarget,\n                settings,\n                null\n            )\n\n            return future.get()\n        }\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/actions/base/DynamicShireActionConfig.kt",
    "content": "package com.phodal.shirelang.actions.base\n\nimport com.intellij.openapi.editor.Editor\nimport com.phodal.shirelang.compiler.ast.hobbit.HobbitHole\nimport com.phodal.shirelang.compiler.parser.HobbitHoleParser\nimport com.phodal.shirelang.psi.ShireFile\n\ndata class DynamicShireActionConfig(\n    val name: String,\n    val hole: HobbitHole? = null,\n    val shireFile: ShireFile,\n    val editor: Editor? = null,\n) {\n    companion object {\n        fun from(file: ShireFile): DynamicShireActionConfig {\n            val hole = HobbitHoleParser.parse(file)\n            return DynamicShireActionConfig(file.name, hole, file)\n        }\n    }\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/actions/base/DynamicShireActionService.kt",
    "content": "package com.phodal.shirelang.actions.base\n\nimport com.intellij.openapi.actionSystem.ActionManager\nimport com.intellij.openapi.actionSystem.AnAction\nimport com.intellij.openapi.actionSystem.KeyboardShortcut\nimport com.intellij.openapi.application.ApplicationManager\nimport com.intellij.openapi.components.Service\nimport com.intellij.openapi.keymap.KeymapManager\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.phodal.shirecore.config.ShireActionLocation\nimport com.phodal.shirelang.psi.ShireFile\nimport java.util.*\n\n@Service(Service.Level.PROJECT)\nclass DynamicShireActionService: DynamicActionService {\n    private val dynamicActionService = GlobalShireActionService.getInstance()\n\n    private val actionCache = WeakHashMap<ShireFile, DynamicShireActionConfig>()\n\n    override fun putAction(key: ShireFile, action: DynamicShireActionConfig) {\n        actionCache[key] = action\n    }\n\n    override fun removeAction(key: ShireFile) = actionCache.keys.removeIf {\n        it == key\n    }\n\n    override fun getAllActions(): List<DynamicShireActionConfig> {\n        return (actionCache.values.toList() + dynamicActionService.getAllActions())\n            .distinctBy { it.shireFile.virtualFile }\n    }\n\n    fun getActions(location: ShireActionLocation): List<DynamicShireActionConfig> {\n        return getAllActions().filter {\n            it.hole?.actionLocation == location && it.hole.enabled\n        }\n    }\n\n    /**\n     * Sets a keymap shortcut for a specified action ID.\n     *\n     * This method takes in the action ID of the desired action and a keyboard string representing the shortcut keys to be set.\n     * It retrieves the action manager and keymap manager instances, then adds the specified keyboard shortcut to the active keymap.\n     *\n     * @param action The ID of the action for which the shortcut is being set.\n     * @param keyboardShortcut A string representing the keyboard shortcut keys (e.g. \"ctrl shift A\").\n     */\n    fun bindShortcutToAction(action: AnAction, keyboardShortcut: KeyboardShortcut) {\n        val actionId = ActionManager.getInstance().getId(action) ?: return\n\n        val activeKeymap = KeymapManager.getInstance().activeKeymap\n\n        activeKeymap.removeAllActionShortcuts(actionId)\n        activeKeymap.addShortcut(actionId, keyboardShortcut)\n    }\n\n    companion object {\n        fun getInstance(project: Project): DynamicShireActionService =\n            project.getService(DynamicShireActionService::class.java)\n    }\n}\n\n@Service(Service.Level.APP)\nclass GlobalShireActionService: DynamicActionService {\n\n    private val globalActionCache = WeakHashMap<VirtualFile, DynamicShireActionConfig>()\n\n    override fun putAction(key: ShireFile, action: DynamicShireActionConfig) {\n        globalActionCache[key.virtualFile] = action\n    }\n\n    override fun removeAction(key: ShireFile) = globalActionCache.keys.removeIf{ key.virtualFile == it }\n\n    override fun getAllActions(): List<DynamicShireActionConfig> = globalActionCache.values.toList()\n\n    companion object {\n        fun getInstance(): GlobalShireActionService =\n            ApplicationManager.getApplication().getService(GlobalShireActionService::class.java)\n    }\n\n}\n\ninterface DynamicActionService {\n\n    fun putAction(key: ShireFile, action: DynamicShireActionConfig)\n\n    fun removeAction(key: ShireFile): Boolean\n\n    fun getAllActions(): List<DynamicShireActionConfig>\n\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/actions/base/validator/WhenConditionValidator.kt",
    "content": "package com.phodal.shirelang.actions.base.validator\n\nimport com.intellij.psi.PsiFile\nimport com.phodal.shirecore.provider.variable.model.ConditionPsiVariable\nimport com.phodal.shirelang.compiler.ast.FrontMatterType\nimport com.phodal.shirelang.compiler.ast.Statement\n\nobject WhenConditionValidator {\n    private fun buildPsiVariable(file: PsiFile): Map<String, String> {\n        return ConditionPsiVariable.values().associate {\n            when (it) {\n                ConditionPsiVariable.FILE_PATH -> it.variableName to file.virtualFile.path\n                ConditionPsiVariable.FILE_NAME -> it.variableName to file.name\n                ConditionPsiVariable.FILE_EXTENSION -> it.variableName to (file.virtualFile.extension ?: \"\")\n                ConditionPsiVariable.FILE_CONTENT -> it.variableName to file.text\n            }\n        }\n    }\n\n    fun isAvailable(conditions: FrontMatterType.EXPRESSION, file: PsiFile): Boolean {\n        return (conditions.value as? Statement)?.evaluate(buildPsiVariable(file)) == true\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/actions/console/ShireConsoleAction.kt",
    "content": "package com.phodal.shirelang.actions.console\n\nimport com.intellij.openapi.actionSystem.ActionUpdateThread\nimport com.intellij.openapi.actionSystem.AnAction\nimport com.intellij.openapi.actionSystem.AnActionEvent\nimport com.intellij.openapi.project.Project\nimport com.phodal.shirecore.config.ShireActionLocation\nimport com.phodal.shirecore.variable.template.VariableActionEventDataHolder\nimport com.phodal.shirelang.actions.ShireRunFileAction\nimport com.phodal.shirelang.actions.base.DynamicShireActionService\n\nclass ShireConsoleAction : AnAction() {\n    override fun getActionUpdateThread() = ActionUpdateThread.EDT\n\n    private fun shireActionConfigs(project: Project) =\n        DynamicShireActionService.getInstance(project).getActions(ShireActionLocation.CONSOLE_MENU)\n\n    override fun update(e: AnActionEvent) {\n        val project = e.project ?: return\n        val isOnlyOneConfig = shireActionConfigs(project).size == 1\n\n        val hobbitHole = shireActionConfigs(project).firstOrNull()?.hole\n        e.presentation.isVisible = isOnlyOneConfig\n        e.presentation.isEnabled = hobbitHole != null && hobbitHole.enabled\n        if (hobbitHole != null) {\n            e.presentation.text = hobbitHole.name ?: \"<Placeholder>\"\n        }\n    }\n\n    override fun actionPerformed(e: AnActionEvent) {\n        val project = e.project ?: return\n\n        VariableActionEventDataHolder.putData(VariableActionEventDataHolder(e.dataContext))\n\n        val config = shireActionConfigs(project).firstOrNull() ?: return\n        ShireRunFileAction.executeFile(project, config, null)\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/actions/context/ShireContextMenuAction.kt",
    "content": "package com.phodal.shirelang.actions.context\n\nimport com.intellij.openapi.actionSystem.ActionUpdateThread\nimport com.intellij.openapi.actionSystem.AnActionEvent\nimport com.intellij.openapi.actionSystem.CommonDataKeys\nimport com.intellij.openapi.diagnostic.logger\nimport com.intellij.openapi.project.DumbAwareAction\nimport com.phodal.shirelang.ShireIcons\nimport com.phodal.shirelang.actions.ShireRunFileAction\nimport com.phodal.shirelang.actions.base.DynamicShireActionConfig\nimport com.phodal.shirelang.actions.base.validator.WhenConditionValidator\n\nclass ShireContextMenuAction(private val config: DynamicShireActionConfig) :\n    DumbAwareAction(config.name, config.hole?.description, ShireIcons.DEFAULT) {\n\n    init {\n        templatePresentation.text = config.name.ifBlank { \"Unknown\" }\n    }\n\n    override fun getActionUpdateThread(): ActionUpdateThread = ActionUpdateThread.BGT\n\n\n    override fun update(e: AnActionEvent) {\n        //2024-07-13 10:32:57,277 [51307999] SEVERE - #c.i.o.a.i.Utils - Empty menu item text for ShireContextMenuAction@EditorPopup (com.phodal.shirelang.actions.context.ShireContextMenuAction). The default action text must be specified in plugin.xml or its class constructor [Plugin: com.phodal.shire]\n        // com.intellij.diagnostic.PluginException: Empty menu item text for ShireContextMenuAction@EditorPopup (com.phodal.shirelang.actions.context.ShireContextMenuAction). The default action text must be specified in plugin.xml or its class constructor [Plugin: com.phodal.shire]\n        try {\n            val conditions = config.hole?.when_ ?: return\n            val psiFile = e.getData(CommonDataKeys.PSI_FILE) ?: return\n\n            WhenConditionValidator.isAvailable(conditions, psiFile).let {\n                e.presentation.isEnabled = it\n                e.presentation.isVisible = it\n\n                e.presentation.text = config.hole.name\n            }\n        } catch (e: Exception) {\n            logger<ShireContextMenuAction>().error(\"Error in ShireContextMenuAction\", e)\n        }\n    }\n\n    override fun actionPerformed(e: AnActionEvent) {\n        val project = e.project ?: return\n        ShireRunFileAction.executeFile(\n            project,\n            config,\n            ShireRunFileAction.createRunConfig(e)\n        )\n    }\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/actions/context/ShireContextMenuActionGroup.kt",
    "content": "package com.phodal.shirelang.actions.context\n\nimport com.intellij.openapi.actionSystem.*\nimport com.phodal.shirecore.config.ShireActionLocation\nimport com.phodal.shirelang.actions.base.DynamicShireActionService\nimport com.phodal.shirelang.actions.base.validator.WhenConditionValidator\n\nclass ShireContextMenuActionGroup : ActionGroup() {\n    override fun getActionUpdateThread(): ActionUpdateThread {\n        return ActionUpdateThread.BGT\n    }\n\n    override fun update(e: AnActionEvent) {\n        val project = e.project ?: return\n        e.presentation.isPopupGroup = DynamicShireActionService.getInstance(project).getAllActions().size > 1\n    }\n\n    override fun getChildren(e: AnActionEvent?): Array<AnAction> {\n        val project = e?.project ?: return emptyArray()\n        val actionService = DynamicShireActionService.getInstance(project)\n\n        return actionService.getActions(ShireActionLocation.CONTEXT_MENU).mapNotNull { actionConfig ->\n            if (actionConfig.hole == null) return@mapNotNull null\n            if (!actionConfig.hole.enabled) return@mapNotNull null\n\n            actionConfig.hole.when_?.let {\n                val psiFile = e.getData(CommonDataKeys.PSI_FILE) ?: return@mapNotNull null\n                if (!WhenConditionValidator.isAvailable(it, psiFile)) return@mapNotNull null\n            }\n\n            val menuAction = ShireContextMenuAction(actionConfig)\n            if (actionConfig.hole.shortcut != null) {\n                actionService.bindShortcutToAction(menuAction, actionConfig.hole.shortcut)\n            }\n\n            menuAction\n        }.distinctBy { it.templatePresentation.text }.toTypedArray()\n    }\n}\n\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/actions/copyPaste/ShireCopyPastePreProcessor.kt",
    "content": "package com.phodal.shirelang.actions.copyPaste\n\nimport com.intellij.codeInsight.editorActions.CopyPastePreProcessor\nimport com.intellij.openapi.application.ApplicationManager\nimport com.intellij.openapi.components.Service\nimport com.intellij.openapi.diagnostic.logger\nimport com.intellij.openapi.editor.Editor\nimport com.intellij.openapi.editor.RawText\nimport com.intellij.openapi.project.Project\nimport com.intellij.psi.PsiFile\nimport com.phodal.shirecore.ShireCoroutineScope\nimport com.phodal.shirecore.llm.LlmProvider\nimport com.phodal.shirecore.utils.markdown.CodeFence\nimport com.phodal.shirecore.middleware.post.PostProcessorContext\nimport com.phodal.shirelang.compiler.template.ShireVariableTemplateCompiler\nimport com.phodal.shirelang.compiler.ast.hobbit.HobbitHole\nimport com.phodal.shirelang.psi.ShireFile\nimport com.phodal.shirelang.run.runner.ShireRunner\nimport kotlinx.coroutines.flow.Flow\nimport kotlinx.coroutines.flow.cancellable\nimport kotlinx.coroutines.launch\nimport kotlinx.coroutines.runBlocking\nimport java.util.concurrent.CompletableFuture\n\n@Service(Service.Level.APP)\nclass PasteManagerService {\n    private val pasteProcessorMap = mutableMapOf<HobbitHole, ShireFile>()\n\n    fun registerPasteProcessor(key: HobbitHole, file: ShireFile) {\n        pasteProcessorMap[key] = file\n    }\n\n    fun firstProcessor(): HobbitHole? {\n        return pasteProcessorMap.keys.firstOrNull()\n    }\n\n    fun executeProcessor(\n        project: Project,\n        hobbitHole: HobbitHole,\n        text: String,\n        file: PsiFile,\n        editor: Editor\n    ): String {\n        val future = CompletableFuture<String>()\n        val shireFile = pasteProcessorMap[hobbitHole] ?: return text\n\n        val compileResult = ShireRunner.preAnalysisAndLocalExecute(shireFile, project)\n        val variableTable = compileResult.variableTable\n\n        val templateCompiler =\n            ShireVariableTemplateCompiler(project, hobbitHole, variableTable, compileResult.shireOutput, editor)\n        templateCompiler.putCustomVariable(\"text\", text)\n\n        val promptText = runBlocking {\n            templateCompiler.compile().trim()\n        }\n\n        PostProcessorContext.getData()?.lastTaskOutput?.let {\n            templateCompiler.putCustomVariable(\"output\", it)\n        }\n\n        val flow: Flow<String>? = LlmProvider.provider(project)?.stream(promptText, \"\", false)\n        ShireCoroutineScope.scope(project).launch {\n            val suggestion = StringBuilder()\n\n            flow?.cancellable()?.collect { char ->\n                suggestion.append(char)\n            }\n\n            val code = CodeFence.parse(suggestion.toString())\n            future.complete(code.text)\n\n            logger<ShireCopyPastePreProcessor>().info(\"paste code: $code\")\n        }\n\n        return future.get()\n    }\n\n    companion object {\n        fun getInstance(): PasteManagerService =\n            ApplicationManager.getApplication().getService(PasteManagerService::class.java)\n    }\n}\n\nclass ShireCopyPastePreProcessor : CopyPastePreProcessor {\n    override fun preprocessOnCopy(file: PsiFile, startOffsets: IntArray, endOffsets: IntArray, text: String): String? {\n        return text\n    }\n\n    override fun preprocessOnPaste(\n        project: Project,\n        file: PsiFile,\n        editor: Editor,\n        text: String,\n        rawText: RawText?,\n    ): String {\n        val instance = PasteManagerService.getInstance()\n        val hobbitHole = instance.firstProcessor() ?: return text\n        if (!hobbitHole.enabled) return text\n\n        /// only for test java and kotlin\n        val language = file.language.displayName.lowercase()\n        if (!(language == \"java\" || language == \"kotlin\")) {\n            return text\n        }\n\n        /// should be more than 7 lines\n        if (text.lines().size < 5) {\n            return text\n        }\n\n        return instance.executeProcessor(project, hobbitHole, text, file, editor)\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/actions/database/ShireDatabaseAction.kt",
    "content": "package com.phodal.shirelang.actions.database\n\nimport com.intellij.openapi.actionSystem.ActionUpdateThread\nimport com.intellij.openapi.actionSystem.AnAction\nimport com.intellij.openapi.actionSystem.AnActionEvent\nimport com.intellij.openapi.project.Project\nimport com.phodal.shirecore.config.ShireActionLocation\nimport com.phodal.shirecore.variable.template.VariableActionEventDataHolder\nimport com.phodal.shirelang.actions.base.DynamicShireActionService\nimport com.phodal.shirelang.actions.ShireRunFileAction\n\nclass ShireDatabaseAction : AnAction() {\n    override fun getActionUpdateThread() = ActionUpdateThread.EDT\n\n    private fun shireActionConfigs(project: Project) =\n        DynamicShireActionService.getInstance(project).getActions(ShireActionLocation.DATABASE_MENU)\n\n    override fun update(e: AnActionEvent) {\n        val project = e.project ?: return\n        val isOnlyOneConfig = shireActionConfigs(project).size == 1\n\n        val hobbitHole = shireActionConfigs(project).firstOrNull()?.hole\n        e.presentation.isVisible = isOnlyOneConfig\n        e.presentation.isEnabled = hobbitHole != null && hobbitHole.enabled\n        if (hobbitHole != null) {\n            e.presentation.text = hobbitHole.name ?: \"<Placeholder>\"\n        }\n    }\n\n    override fun actionPerformed(e: AnActionEvent) {\n        val project = e.project ?: return\n\n        VariableActionEventDataHolder.putData(VariableActionEventDataHolder(e.dataContext))\n\n        val config = shireActionConfigs(project).firstOrNull() ?: return\n        ShireRunFileAction.executeFile(project, config, null)\n    }\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/actions/external/ShireSonarLintAction.kt",
    "content": "package com.phodal.shirelang.actions.external\n\nimport com.intellij.openapi.actionSystem.ActionUpdateThread\nimport com.intellij.openapi.actionSystem.AnAction\nimport com.intellij.openapi.actionSystem.AnActionEvent\nimport com.intellij.openapi.project.Project\nimport com.phodal.shirecore.config.ShireActionLocation\nimport com.phodal.shirecore.variable.template.VariableActionEventDataHolder\nimport com.phodal.shirelang.actions.ShireRunFileAction\nimport com.phodal.shirelang.actions.base.DynamicShireActionService\n\nclass ShireSonarLintAction : AnAction() {\n    override fun getActionUpdateThread() = ActionUpdateThread.EDT\n\n    private fun shireActionConfigs(project: Project) =\n        DynamicShireActionService.getInstance(project).getActions(ShireActionLocation.EXT_SONARQUBE_MENU)\n\n    override fun update(e: AnActionEvent) {\n        val project = e.project ?: return\n        val isOnlyOneConfig = shireActionConfigs(project).size == 1\n\n        val hobbitHole = shireActionConfigs(project).firstOrNull()?.hole\n        e.presentation.isVisible = isOnlyOneConfig\n        e.presentation.isEnabled = hobbitHole != null && hobbitHole.enabled\n        if (hobbitHole != null) {\n            e.presentation.text = hobbitHole.name ?: \"<Placeholder>\"\n        }\n    }\n\n    override fun actionPerformed(e: AnActionEvent) {\n        val project = e.project ?: return\n\n        VariableActionEventDataHolder.putData(VariableActionEventDataHolder(e.dataContext))\n\n        val config = shireActionConfigs(project).firstOrNull() ?: return\n        ShireRunFileAction.executeFile(project, config, null)\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/actions/input/ShireInputBoxAction.kt",
    "content": "package com.phodal.shirelang.actions.input\n\nimport com.intellij.openapi.actionSystem.ActionUpdateThread\nimport com.intellij.openapi.actionSystem.AnActionEvent\nimport com.intellij.openapi.actionSystem.CommonDataKeys\nimport com.intellij.openapi.editor.ex.EditorEx\nimport com.intellij.openapi.editor.ex.util.EditorUtil\nimport com.intellij.openapi.project.DumbAwareAction\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.util.Disposer\nimport com.intellij.openapi.wm.IdeFocusManager\nimport com.phodal.shirecore.config.ShireActionLocation\nimport com.phodal.shirelang.actions.ShireRunFileAction.Companion.executeFile\nimport com.phodal.shirelang.actions.base.DynamicShireActionConfig\nimport com.phodal.shirelang.actions.base.DynamicShireActionService\nimport com.phodal.shirelang.actions.input.inlay.CustomInputBox\nimport com.phodal.shirelang.actions.input.inlay.CustomInputBox.Companion.CUSTOM_INPUT_CANCEL_ACTION\nimport com.phodal.shirelang.actions.input.inlay.CustomInputBox.Companion.CUSTOM_INPUT_SUBMIT_ACTION\nimport com.phodal.shirelang.actions.input.inlay.InlayPanel\nimport java.awt.event.ActionEvent\nimport javax.swing.AbstractAction\n\nclass ShireInputBoxAction : DumbAwareAction() {\n    override fun getActionUpdateThread() = ActionUpdateThread.EDT\n\n    private fun shireActionConfigs(project: Project) =\n        DynamicShireActionService.getInstance(project).getActions(ShireActionLocation.INPUT_BOX)\n\n    override fun update(e: AnActionEvent) {\n        val project = e.project ?: return\n        val hobbitHole = shireActionConfigs(project).firstOrNull()?.hole ?: return\n\n        e.presentation.isEnabled = hobbitHole.enabled\n        e.presentation.text = hobbitHole?.description ?: \"\"\n    }\n\n    override fun actionPerformed(e: AnActionEvent) {\n        val dataContext = e.dataContext\n        val editor = dataContext.getData(CommonDataKeys.EDITOR) ?: return\n        val offset = editor.caretModel.offset\n        val project = dataContext.getData(CommonDataKeys.PROJECT) ?: return\n\n        val instance = DynamicShireActionService.getInstance(project)\n        val config = shireActionConfigs(project).firstOrNull() ?: return\n\n        if (config.hole?.shortcut != null) {\n            instance.bindShortcutToAction(this, config.hole.shortcut)\n        }\n\n        InlayPanel.add(editor as EditorEx, offset, CustomInputBox())?.let {\n            doExecute(it, project, editor, config)\n        }\n    }\n\n    private fun doExecute(\n        inlay: InlayPanel<CustomInputBox>,\n        project: Project,\n        editor: EditorEx,\n        config: DynamicShireActionConfig,\n    ) {\n        val component = inlay.component\n        component.actionMap.put(CUSTOM_INPUT_SUBMIT_ACTION, object : AbstractAction() {\n            override fun actionPerformed(e: ActionEvent?) {\n                val inputText = component.getText()\n                executeFile(project, config, null, variables = mutableMapOf(\"input\" to inputText))\n                Disposer.dispose(inlay.inlay!!)\n            }\n        })\n\n        component.actionMap.put(CUSTOM_INPUT_CANCEL_ACTION, object : AbstractAction() {\n            override fun actionPerformed(e: ActionEvent?) {\n                Disposer.dispose(inlay.inlay!!)\n            }\n        })\n\n        IdeFocusManager.getInstance(project).requestFocus(component, false)\n        EditorUtil.disposeWithEditor(editor, inlay.inlay!!)\n    }\n\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/actions/input/inlay/ComponentInlaysContainer.kt",
    "content": "package com.phodal.shirelang.actions.input.inlay\n\nimport com.intellij.openapi.Disposable\nimport com.intellij.openapi.application.ReadAction\nimport com.intellij.openapi.editor.Editor\nimport com.intellij.openapi.editor.Inlay\nimport com.intellij.openapi.util.Disposer\nimport com.intellij.openapi.util.Key\nimport java.awt.Rectangle\nimport java.awt.event.ComponentAdapter\nimport java.awt.event.ComponentEvent\nimport javax.swing.JComponent\nimport javax.swing.RepaintManager\nimport javax.swing.SwingUtilities\n\ninternal class ComponentInlaysContainer(val editor: Editor) : JComponent() {\n    private val inlays: MutableList<Inlay<InlayRenderer>> = mutableListOf()\n\n    val editorResizeListener: ComponentAdapter = object : ComponentAdapter() {\n        override fun componentResized(e: ComponentEvent?) {\n            revalidate()\n            repaint()\n        }\n    }\n\n    fun getInlays(): MutableList<Inlay<InlayRenderer>> = inlays\n\n    override fun invalidate() {\n        super.invalidate()\n        RepaintManager.currentManager(this).addInvalidComponent(this)\n    }\n\n    override fun doLayout() {\n        val inlays: List<Inlay<InlayRenderer>> = inlays\n        if (inlays.isEmpty()) return\n\n        val content: JComponent = editor.contentComponent\n        inlays.forEach {\n            it.renderer.inlaySize = it.renderer.component.preferredSize\n        }\n\n        ReadAction.run<RuntimeException> {\n            editor.inlayModel.execute(true) {\n                inlays.forEach {\n                    if (it.renderer.inlaySize.width != it.widthInPixels || it.renderer.inlaySize.height != it.heightInPixels) {\n                        it.update()\n                    }\n                }\n            }\n        }\n\n        if (content.width < content.width) {\n            content.parent.doLayout()\n        }\n\n        bounds = SwingUtilities.calculateInnerArea(content, null as Rectangle?)\n        inlays.forEach {\n            it.renderer.component.size = it.renderer.inlaySize\n        }\n    }\n\n    companion object {\n        private val INLAYS_CONTAINER = Key<ComponentInlaysContainer>(\"INLAYS_CONTAINER\")\n        fun addInlay(inlay: Inlay<InlayRenderer>) {\n            val editor: Editor = inlay.editor\n            var component = editor.getUserData(INLAYS_CONTAINER)\n            if (component == null) {\n                val newContainer = ComponentInlaysContainer(editor)\n                editor.putUserData(INLAYS_CONTAINER, newContainer)\n\n                editor.contentComponent.add(newContainer)\n                editor.contentComponent.addComponentListener(newContainer.editorResizeListener)\n\n                component = newContainer\n            }\n\n            component.getInlays().add(inlay)\n            component.add(inlay.renderer.component)\n\n\n            inlay.whenDisposed {\n                if (!component.getInlays().remove(inlay)) {\n                    return@whenDisposed\n                }\n                component.remove(inlay.renderer.component)\n\n                if (component.getInlays().isEmpty()) {\n                    editor.contentComponent.removeComponentListener(component.editorResizeListener)\n                    editor.contentComponent.remove(inlay.renderer.component)\n                }\n            }\n        }\n    }\n}\n\nfun Disposable.whenDisposed(listener: () -> Unit) {\n    Disposer.register(this) { listener() }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/actions/input/inlay/CustomInputBox.kt",
    "content": "package com.phodal.shirelang.actions.input.inlay\n\nimport com.intellij.ide.KeyboardAwareFocusOwner\nimport com.intellij.ui.scale.JBUIScale\nimport java.awt.Dimension\nimport java.awt.event.KeyEvent\nimport javax.swing.JComponent\nimport javax.swing.JTextField\nimport javax.swing.KeyStroke\nimport kotlin.properties.ReadWriteProperty\nimport kotlin.reflect.KMutableProperty1\nimport kotlin.reflect.KProperty\n\nclass CustomInputBox : JTextField(), KeyboardAwareFocusOwner {\n    override fun skipKeyEventDispatcher(event: KeyEvent): Boolean = true\n\n    init {\n        this.minimumWidth = JBUIScale.scale(480)\n        this.preferredSize = this.minimumSize\n\n        inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), CUSTOM_INPUT_CANCEL_ACTION)\n        inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), CUSTOM_INPUT_SUBMIT_ACTION)\n    }\n\n    companion object {\n        const val CUSTOM_INPUT_CANCEL_ACTION = \"custom.input.cancel\"\n        const val CUSTOM_INPUT_SUBMIT_ACTION = \"custom.input.submit\"\n    }\n}\n\nvar JComponent.minimumWidth: Int by dimensionProperty(JComponent::getMinimumSize, JComponent::setMinimumSize, Dimension::width)\n\nprivate fun <Receiver> dimensionProperty(\n    getSize: Receiver.() -> Dimension,\n    setSize: Receiver.(Dimension) -> Unit,\n    dimensionProperty: KMutableProperty1<Dimension, Int>\n): ReadWriteProperty<Receiver, Int> {\n    return object : ReadWriteProperty<Receiver, Int> {\n\n        override fun getValue(thisRef: Receiver, property: KProperty<*>): Int {\n            return dimensionProperty.get(getSize(thisRef))\n        }\n\n        override fun setValue(thisRef: Receiver, property: KProperty<*>, value: Int) {\n            val size = Dimension(getSize(thisRef))\n            dimensionProperty.set(size, value)\n            setSize(thisRef, size)\n        }\n    }\n}\n\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/actions/input/inlay/InlayPanel.kt",
    "content": "package com.phodal.shirelang.actions.input.inlay\n\nimport com.intellij.openapi.Disposable\nimport com.intellij.openapi.application.ReadAction\nimport com.intellij.openapi.editor.Inlay\nimport com.intellij.openapi.editor.InlayProperties\nimport com.intellij.openapi.editor.event.VisibleAreaListener\nimport com.intellij.openapi.editor.ex.EditorEx\nimport com.intellij.openapi.rd.paint2DLine\nimport com.intellij.ui.JBColor\nimport com.intellij.ui.paint.LinePainter2D\nimport com.intellij.util.DocumentUtil\nimport com.intellij.util.text.CharArrayUtil\nimport com.intellij.util.ui.JBPoint\nimport com.intellij.util.ui.JBUI\nimport java.awt.*\nimport javax.swing.JComponent\nimport javax.swing.JPanel\n\nopen class InlayPanel<T : JComponent?>(var component: T) : JPanel() {\n    val panel: JPanel = object : JPanel() {\n        init {\n            setOpaque(false)\n        }\n\n        override fun paintComponent(g: Graphics) {\n            super.paintComponent(g)\n            val create: Graphics2D? = g.create() as Graphics2D?\n            try {\n                create!!.paint2DLine(JBPoint(0, 0), JBPoint(0, height),\n                    LinePainter2D.StrokeType.INSIDE, 3.0,\n                    JBColor(Color(0, 100, 89, 100), Color(0, 0, 0, 90))\n                )\n\n                create.dispose()\n            } catch (th: Throwable) {\n                create!!.dispose()\n                throw th\n            }\n        }\n    }\n\n    var inlay: Inlay<*>? = null\n    private val visibleAreaListener: VisibleAreaListener = VisibleAreaListener { invalidate() }\n\n    protected open fun setupPane(inlay: Inlay<*>) {\n        this.inlay = inlay\n\n        add(panel)\n        add(component)\n        setOpaque(true)\n\n        inlay.editor.scrollingModel.addVisibleAreaListener(visibleAreaListener, (inlay as Disposable))\n\n        setLayout(object : LayoutManager {\n            override fun addLayoutComponent(name: String, comp: Component?) {}\n            override fun removeLayoutComponent(comp: Component?) {}\n            override fun preferredLayoutSize(parent: Container?): Dimension {\n                if (!inlay.isValid || parent == null) return Dimension(0, 0)\n\n                val dimension: Dimension = component!!.preferredSize\n                val xOffsetPosition = dimension.width + getXOffsetPosition(inlay)\n                val insets = component!!.getInsets()\n\n                return Dimension(xOffsetPosition, dimension.height + insets.height)\n            }\n\n            override fun minimumLayoutSize(parent: Container?): Dimension {\n                if (!inlay.isValid || parent == null) return Dimension(0, 0)\n\n                val size: Dimension = component!!.getMinimumSize()\n                val xOffsetPosition = size.width + getXOffsetPosition(inlay)\n                val insets = component!!.getInsets()\n\n                return Dimension(xOffsetPosition, size.height + insets.height)\n            }\n\n            override fun layoutContainer(parent: Container?) {\n                if (!inlay.isValid) return\n\n                val size = parent?.size ?: Dimension(0, 0)\n\n                val x = getXOffsetPosition(inlay)\n                component!!.setBounds(x, 0, size.width - x, size.height)\n\n                val scrollPane = (inlay.editor as EditorEx).scrollPane\n                panel.setBounds(scrollPane.viewport.viewRect.x - 1, 0, 5, size.height)\n            }\n        })\n    }\n\n    companion object {\n        fun add(editor: EditorEx, offset: Int, component: CustomInputBox): InlayPanel<CustomInputBox>? {\n            val properties = InlayProperties().showAbove(false).showWhenFolded(true);\n\n            val inlayPanel = InlayPanel(component)\n            val inlayRenderer = InlayRenderer(inlayPanel)\n\n            val inlayElement =\n                editor.inlayModel.addBlockElement(offset, properties, inlayRenderer) ?: return null\n\n            ComponentInlaysContainer.addInlay(inlayElement)\n\n            inlayPanel.setupPane(inlayElement)\n\n            return inlayPanel\n        }\n    }\n}\n\nval Insets.width: Int get() = left + right\nval Insets.height: Int get() = top + bottom\n\nfun getXOffsetPosition(inlay: Inlay<*>): Int {\n    if (!inlay.isValid) return 0\n\n    val editor = inlay.editor\n    val compute = ReadAction.compute<Int, RuntimeException> {\n        val lineStartOffset = DocumentUtil.getLineStartOffset(inlay.offset, editor.document)\n        val shiftForward =\n            CharArrayUtil.shiftForward(editor.document.immutableCharSequence, lineStartOffset, \" \\t\")\n        editor.offsetToXY(shiftForward).x\n    }\n\n    return compute\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/actions/input/inlay/InlayRenderer.kt",
    "content": "package com.phodal.shirelang.actions.input.inlay\n\nimport com.intellij.openapi.editor.EditorCustomElementRenderer\nimport com.intellij.openapi.editor.Inlay\nimport com.intellij.openapi.editor.markup.TextAttributes\nimport java.awt.*\n\nclass InlayRenderer(var component: Component) : EditorCustomElementRenderer {\n    var inlaySize: Dimension = Dimension(0, 0)\n\n    override fun calcWidthInPixels(inlay: Inlay<*>): Int = inlaySize.width\n\n    override fun calcHeightInPixels(inlay: Inlay<*>): Int = inlaySize.height\n\n    override fun paint(inlay: Inlay<*>, g: Graphics, targetRegion: Rectangle, textAttributes: TextAttributes) {\n        val bounds = inlay.bounds ?: return\n        val inlayLocation: Point = bounds.location ?: return\n\n        if (component.location != inlayLocation) {\n            component.location = inlayLocation\n        }\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/actions/intention/ShireIntentionAction.kt",
    "content": "package com.phodal.shirelang.actions.intention\n\nimport com.intellij.codeInsight.intention.IntentionAction\nimport com.intellij.openapi.actionSystem.AnActionEvent\nimport com.intellij.openapi.editor.Editor\nimport com.intellij.openapi.project.Project\nimport com.intellij.psi.PsiFile\nimport com.phodal.shirecore.config.ShireActionLocation\nimport com.phodal.shirelang.ShireBundle\nimport com.phodal.shirelang.actions.ShireRunFileAction\nimport com.phodal.shirelang.actions.base.DynamicShireActionService\nimport com.phodal.shirelang.actions.base.validator.WhenConditionValidator\nimport com.phodal.shirelang.compiler.ast.hobbit.HobbitHole\nimport kotlin.collections.firstOrNull\n\nclass ShireIntentionAction(private val hobbitHole: HobbitHole?, val file: PsiFile, private val event: AnActionEvent?) :\n    IntentionAction {\n    override fun startInWriteAction(): Boolean = true\n    override fun getFamilyName(): String = ShireBundle.message(\"shire.intention\")\n    override fun getText(): String = hobbitHole?.name ?: ShireBundle.message(\"shire.intention\")\n\n    override fun isAvailable(project: Project, editor: Editor, file: PsiFile): Boolean {\n        val conditions = hobbitHole?.when_ ?: return true\n        return WhenConditionValidator.isAvailable(conditions, file)\n    }\n\n    override fun invoke(project: Project, editor: Editor?, file: PsiFile?) {\n        val config = DynamicShireActionService.getInstance(project)\n            .getActions(ShireActionLocation.INTENTION_MENU)\n            .firstOrNull { it.hole == hobbitHole } ?: return\n\n        ShireRunFileAction.executeFile(project, config, null)\n    }\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/actions/intention/ShireIntentionHelper.kt",
    "content": "package com.phodal.shirelang.actions.intention\n\nimport com.intellij.codeInsight.intention.IntentionAction\nimport com.intellij.lang.injection.InjectedLanguageManager\nimport com.intellij.openapi.actionSystem.AnActionEvent\nimport com.intellij.openapi.diagnostic.logger\nimport com.intellij.openapi.editor.Editor\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.ui.popup.JBPopupFactory\nimport com.intellij.openapi.util.Iconable\nimport com.intellij.psi.PsiFile\nimport com.phodal.shirecore.ShireCoreBundle\nimport javax.swing.Icon\nimport com.phodal.shirecore.config.ShireActionLocation\nimport com.phodal.shirelang.ShireIcons\nimport com.phodal.shirelang.actions.base.DynamicShireActionService\nimport com.phodal.shirelang.actions.intention.ui.CustomPopupStep\n\nclass ShireIntentionHelper : IntentionAction, Iconable {\n    override fun startInWriteAction(): Boolean = true\n    override fun getText(): String = ShireCoreBundle.message(\"intentions.assistant.name\")\n    override fun getFamilyName(): String = ShireCoreBundle.message(\"intentions.assistant.name\")\n    override fun getIcon(flags: Int): Icon = ShireIcons.Idea\n\n    override fun isAvailable(project: Project, editor: Editor?, file: PsiFile?): Boolean {\n        if (file == null) return false\n\n        val instance = InjectedLanguageManager.getInstance(project)\n        if (instance.getTopLevelFile(file)?.virtualFile == null) return false\n\n        return getAiAssistantIntentions(file, null).isNotEmpty()\n    }\n\n    override fun invoke(project: Project, editor: Editor, file: PsiFile) {\n        val intentions = getAiAssistantIntentions(file, null)\n        if (intentions.isEmpty()) return\n\n        val title = ShireCoreBundle.message(\"intentions.assistant.popup.title\")\n        val popupStep = CustomPopupStep(intentions, project, editor, file, title)\n        try {\n            val popup = JBPopupFactory.getInstance().createListPopup(popupStep)\n            popup.showInBestPositionFor(editor)\n        } catch (e: Exception) {\n            logger<ShireIntentionHelper>().warn(\"Failed to show popup\", e)\n        }\n    }\n\n    companion object {\n        fun getAiAssistantIntentions(file: PsiFile, event: AnActionEvent?): List<IntentionAction> {\n            val project = event?.project ?: return emptyList()\n            val shireActionConfigs = DynamicShireActionService.getInstance(project).getActions(ShireActionLocation.INTENTION_MENU)\n\n            return shireActionConfigs.map { actionConfig ->\n                ShireIntentionAction(actionConfig.hole, file, event)\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/actions/intention/ShireIntentionsActionGroup.kt",
    "content": "package com.phodal.shirelang.actions.intention\n\nimport com.intellij.codeInsight.intention.IntentionAction\nimport com.intellij.openapi.actionSystem.ActionGroup\nimport com.intellij.openapi.actionSystem.AnAction\nimport com.intellij.openapi.actionSystem.AnActionEvent\nimport com.intellij.openapi.actionSystem.CommonDataKeys\nimport com.intellij.openapi.editor.Editor\nimport com.intellij.openapi.project.DumbAware\nimport com.intellij.openapi.project.DumbAwareAction\nimport com.intellij.openapi.project.Project\nimport com.intellij.psi.PsiFile\n\nclass ShireIntentionsActionGroup : ActionGroup(\"Shire Intention\", true), DumbAware {\n    override fun getChildren(e: AnActionEvent?): Array<AnAction> {\n        val project: Project = e?.project ?: return emptyArray()\n        val editor: Editor = e.getData(CommonDataKeys.EDITOR) ?: return emptyArray()\n        val file: PsiFile = e.getData(CommonDataKeys.PSI_FILE) ?: return emptyArray()\n\n        val intentions: List<IntentionAction> = ShireIntentionHelper.getAiAssistantIntentions(file, e)\n\n        return intentions.map { action ->\n            DumbAwareAction.create(action.text) {\n                action.invoke(project, editor, file)\n            }\n        }.toTypedArray()\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/actions/intention/ui/CustomPopupStep.kt",
    "content": "package com.phodal.shirelang.actions.intention.ui\n\nimport com.intellij.codeInsight.intention.IntentionAction\nimport com.intellij.openapi.editor.Editor\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.ui.popup.ListSeparator\nimport com.intellij.openapi.ui.popup.PopupStep\nimport com.intellij.openapi.ui.popup.util.BaseListPopupStep\nimport com.intellij.psi.PsiFile\n\nclass CustomPopupStep(\n    private val intentionAction: List<IntentionAction>,\n    private val project: Project,\n    private val editor: Editor,\n    private val psiFile: PsiFile,\n    private val popupTitle: String,\n) : BaseListPopupStep<IntentionAction>(popupTitle, intentionAction) {\n    override fun getTextFor(value: IntentionAction): String = value.text\n\n    override fun getSeparatorAbove(value: IntentionAction?): ListSeparator? =\n        if (value != null && value == intentionAction) ListSeparator() else null\n\n    override fun onChosen(selectedValue: IntentionAction?, finalChoice: Boolean): PopupStep<*>? {\n        return doFinalStep {\n            selectedValue?.invoke(project, editor, psiFile)\n        }\n    }\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/actions/template/NewShireFileAction.kt",
    "content": "package com.phodal.shirelang.actions.template\n\nimport com.intellij.ide.actions.CreateFileFromTemplateAction\nimport com.intellij.ide.actions.CreateFileFromTemplateDialog\nimport com.intellij.ide.fileTemplates.FileTemplateManager\nimport com.intellij.openapi.project.DumbAware\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.ui.NonEmptyInputValidator\nimport com.intellij.psi.PsiDirectory\nimport com.intellij.psi.PsiFile\nimport com.phodal.shirelang.ShireBundle\nimport com.phodal.shirelang.ShireIcons\n\nclass NewShireFileAction : CreateFileFromTemplateAction(\n    ShireBundle.message(\"shire.newFile\"), \"Creates New Shire Action\", ShireIcons.DEFAULT\n), DumbAware {\n    override fun getDefaultTemplateProperty(): String = \"DefaultShireTemplate\"\n\n    override fun getActionName(psi: PsiDirectory?, p1: String, p2: String?): String =\n        ShireBundle.message(\"shire.newFile\")\n\n    override fun buildDialog(project: Project, psiDir: PsiDirectory, builder: CreateFileFromTemplateDialog.Builder) {\n        builder\n            .setTitle(ShireBundle.message(\"shire.newFile\"))\n            .addKind(ShireBundle.message(\"shire.file\"), ShireIcons.DEFAULT, \"Shire Action\")\n            .setValidator(NonEmptyInputValidator())\n    }\n\n    override fun createFile(name: String?, templateName: String?, dir: PsiDirectory?): PsiFile? {\n        val template = FileTemplateManager.getInstance(dir!!.project).getInternalTemplate(templateName!!)\n        val newName = name!!.lowercase().replace(\" \", \"_\")\n\n        template.text = template.text.replace(\"{{name}}\", name)\n        return createFileFromTemplate(newName, template, dir)\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/actions/terminal/ShireTerminalAction.kt",
    "content": "package com.phodal.shirelang.actions.terminal\n\nimport com.intellij.openapi.actionSystem.ActionUpdateThread\nimport com.intellij.openapi.actionSystem.AnActionEvent\nimport com.intellij.openapi.actionSystem.AnActionHolder\nimport com.intellij.openapi.project.DumbAwareAction\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.ui.popup.Balloon\nimport com.intellij.openapi.ui.popup.JBPopupFactory\nimport com.intellij.openapi.ui.popup.JBPopupListener\nimport com.intellij.openapi.ui.popup.LightweightWindowEvent\nimport com.intellij.openapi.wm.IdeFocusManager\nimport com.intellij.ui.DocumentAdapter\nimport com.intellij.ui.awt.RelativePoint\nimport com.intellij.ui.components.JBLabel\nimport com.intellij.util.ui.JBUI\nimport com.intellij.util.ui.SwingHelper\nimport com.intellij.util.ui.UIUtil\nimport com.phodal.shirecore.ShireCoreBundle\nimport com.phodal.shirecore.config.ShireActionLocation\nimport com.phodal.shirecore.provider.action.TerminalLocationExecutor\nimport com.phodal.shirelang.actions.ShireRunFileAction\nimport com.phodal.shirelang.actions.base.DynamicShireActionService\nimport java.awt.Component\nimport java.awt.Font\nimport java.awt.Point\nimport java.awt.event.*\nimport javax.swing.Box\nimport javax.swing.JTextField\nimport javax.swing.event.DocumentEvent\n\nclass ShireTerminalAction : DumbAwareAction() {\n    override fun getActionUpdateThread() = ActionUpdateThread.EDT\n    private val OUTLINE_PROPERTY = \"JComponent.outline\"\n    private val ERROR_VALUE = \"error\"\n\n    private fun shireActionConfigs(project: Project) =\n        DynamicShireActionService.getInstance(project).getActions(ShireActionLocation.TERMINAL_MENU)\n\n    override fun update(e: AnActionEvent) {\n        val shireActionConfigs = e.project?.let { shireActionConfigs(it) }\n        val firstHole = shireActionConfigs?.firstOrNull()?.hole\n\n        e.presentation.isVisible = shireActionConfigs?.size == 1 && firstHole?.enabled == true\n        e.presentation.isEnabled = shireActionConfigs?.size == 1 && firstHole?.enabled == true\n\n        e.presentation.text = firstHole?.description ?: \"AutoDev Placeholder (Bug)\"\n    }\n\n    override fun actionPerformed(e: AnActionEvent) {\n        val project = e.project ?: return\n\n        val config = shireActionConfigs(project).firstOrNull() ?: return\n\n        TerminalLocationExecutor.provide(project)?.getComponent(e)?.let { component ->\n            showInputBoxPopup(component, getPreferredPopupPoint(e)) { userInput ->\n                ShireRunFileAction.executeFile(project, config, null,\n                    variables = mapOf(\"input\" to userInput)\n                )\n            }\n        }\n    }\n\n    private fun getPreferredPopupPoint(e: AnActionEvent): RelativePoint? {\n        if (e.inputEvent is MouseEvent) {\n            val comp = e.inputEvent?.component\n            if (comp is AnActionHolder) {\n                return RelativePoint(comp.parent, Point(comp.x + JBUI.scale(3), comp.y + comp.height + JBUI.scale(3)))\n            }\n        }\n\n        return null\n    }\n\n    private fun showInputBoxPopup(component: Component, popupPoint: RelativePoint?, callback: (String) -> Unit) {\n        val textField = JTextField().also {\n            it.text = ShireCoreBundle.message(\"shell.command.suggestion.action.default.text\")\n            it.selectAll()\n        }\n\n        val label = JBLabel().also {\n            it.font = UIUtil.getLabelFont().deriveFont(Font.BOLD)\n        }\n\n        val panel = SwingHelper.newLeftAlignedVerticalPanel(label, Box.createVerticalStrut(JBUI.scale(2)), textField)\n        panel.addFocusListener(object : FocusAdapter() {\n            override fun focusGained(e: FocusEvent?) {\n                IdeFocusManager.findInstance().requestFocus(textField, false)\n            }\n        })\n\n        val balloon = JBPopupFactory.getInstance().createDialogBalloonBuilder(panel, null)\n            .setShowCallout(true)\n            .setCloseButtonEnabled(false)\n            .setAnimationCycle(0)\n            .setHideOnKeyOutside(true)\n            .setHideOnClickOutside(true)\n            .setRequestFocus(true)\n            .setBlockClicksThroughBalloon(true)\n            .createBalloon()\n\n        textField.addKeyListener(object : KeyAdapter() {\n            override fun keyPressed(e: KeyEvent?) {\n                if (e != null && e.keyCode == KeyEvent.VK_ENTER) {\n                    if (textField.text.isEmpty()) {\n                        textField.putClientProperty(OUTLINE_PROPERTY, ERROR_VALUE)\n                        textField.repaint()\n                        return\n                    }\n\n                    callback(textField.text)\n                    balloon.hide()\n                }\n            }\n        })\n\n        textField.document.addDocumentListener(object : DocumentAdapter() {\n            override fun textChanged(e: DocumentEvent) {\n                val outlineValue = textField.getClientProperty(OUTLINE_PROPERTY)\n                if (outlineValue == ERROR_VALUE) {\n                    textField.putClientProperty(OUTLINE_PROPERTY, null)\n                    textField.repaint()\n                }\n            }\n        })\n\n        balloon.show(popupPoint, Balloon.Position.above)\n        balloon.addListener(object : JBPopupListener {\n            override fun onClosed(event: LightweightWindowEvent) {\n                IdeFocusManager.findInstance().requestFocus(component, false)\n            }\n        })\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/actions/vcs/ShireVcsActionGroup.kt",
    "content": "package com.phodal.shirelang.actions.vcs\n\nimport com.intellij.openapi.actionSystem.ActionGroup\nimport com.intellij.openapi.actionSystem.ActionUpdateThread\nimport com.intellij.openapi.actionSystem.AnAction\nimport com.intellij.openapi.actionSystem.AnActionEvent\nimport com.intellij.openapi.project.DumbAwareAction\nimport com.intellij.openapi.project.Project\nimport com.phodal.shirecore.config.ShireActionLocation\nimport com.phodal.shirecore.variable.template.VariableActionEventDataHolder\nimport com.phodal.shirelang.ShireIcons\nimport com.phodal.shirelang.actions.ShireRunFileAction\nimport com.phodal.shirelang.actions.base.DynamicShireActionConfig\nimport com.phodal.shirelang.actions.base.DynamicShireActionService\n\nclass ShireVcsActionGroup : ActionGroup() {\n    override fun getActionUpdateThread() = ActionUpdateThread.BGT\n\n    override fun update(e: AnActionEvent) {\n        val project = e.project ?: return\n        val isMultipleActions = shireActionConfigs(project).size > 1\n        e.presentation.isVisible = isMultipleActions\n        e.presentation.isEnabled = shireActionConfigs(project).any { it.hole?.enabled == true }\n        e.presentation.isPopupGroup = true\n    }\n\n    override fun getChildren(e: AnActionEvent?): Array<AnAction> {\n        val project = e?.project ?: return emptyArray()\n        return shireActionConfigs(project).map(::ShireVcsAction).toTypedArray()\n    }\n\n    private fun shireActionConfigs(project: Project) =\n        DynamicShireActionService.getInstance(project).getActions(ShireActionLocation.COMMIT_MENU)\n}\n\nclass ShireVcsAction(val config: DynamicShireActionConfig) :\n    DumbAwareAction(config.name, config.hole?.description, ShireIcons.DEFAULT) {\n    override fun actionPerformed(e: AnActionEvent) {\n        val project = e.project ?: return\n\n        VariableActionEventDataHolder.putData(VariableActionEventDataHolder(e.dataContext))\n\n        ShireRunFileAction.executeFile(project, config, null)\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/actions/vcs/ShireVcsLogAction.kt",
    "content": "package com.phodal.shirelang.actions.vcs\n\nimport com.intellij.openapi.actionSystem.ActionUpdateThread\nimport com.intellij.openapi.actionSystem.AnAction\nimport com.intellij.openapi.actionSystem.AnActionEvent\nimport com.intellij.openapi.project.Project\nimport com.phodal.shirecore.config.ShireActionLocation\nimport com.phodal.shirecore.variable.template.VariableActionEventDataHolder\nimport com.phodal.shirelang.actions.ShireRunFileAction\nimport com.phodal.shirelang.actions.base.DynamicShireActionService\n\nclass ShireVcsLogAction : AnAction() {\n    override fun getActionUpdateThread() = ActionUpdateThread.EDT\n\n    private fun shireActionConfigs(project: Project) =\n        DynamicShireActionService.getInstance(project).getActions(ShireActionLocation.VCS_LOG_MENU)\n\n    override fun update(e: AnActionEvent) {\n        val project = e.project ?: return\n        val isOnlyOneConfig = shireActionConfigs(project).size == 1\n\n        val hobbitHole = shireActionConfigs(project).firstOrNull()?.hole\n        e.presentation.isVisible = isOnlyOneConfig\n        e.presentation.isEnabled = hobbitHole != null && hobbitHole.enabled\n        if (hobbitHole != null) {\n            e.presentation.text = hobbitHole.name ?: \"<Placeholder>\"\n        }\n    }\n\n    override fun actionPerformed(e: AnActionEvent) {\n        val project = e.project ?: return\n\n        VariableActionEventDataHolder.putData(VariableActionEventDataHolder(e.dataContext))\n\n        val config = shireActionConfigs(project).firstOrNull() ?: return\n        ShireRunFileAction.executeFile(project, config, null)\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/actions/vcs/ShireVcsSingleAction.kt",
    "content": "package com.phodal.shirelang.actions.vcs\n\nimport com.intellij.openapi.actionSystem.ActionUpdateThread\nimport com.intellij.openapi.actionSystem.AnActionEvent\nimport com.intellij.openapi.project.DumbAwareAction\nimport com.intellij.openapi.project.Project\nimport com.phodal.shirecore.config.ShireActionLocation\nimport com.phodal.shirecore.variable.template.VariableActionEventDataHolder\nimport com.phodal.shirelang.actions.ShireRunFileAction\nimport com.phodal.shirelang.actions.base.DynamicShireActionService\n\nclass ShireVcsSingleAction : DumbAwareAction() {\n    override fun getActionUpdateThread() = ActionUpdateThread.EDT\n\n    private fun shireActionConfigs(project: Project) =\n        DynamicShireActionService.getInstance(project).getActions(ShireActionLocation.COMMIT_MENU)\n\n    override fun update(e: AnActionEvent) {\n        val project = e.project ?: return\n        val isOnlyOneConfig = shireActionConfigs(project).size == 1\n\n        val hobbitHole = shireActionConfigs(project).firstOrNull()?.hole\n        e.presentation.isVisible = isOnlyOneConfig\n        e.presentation.isEnabled = hobbitHole != null && hobbitHole.enabled\n        if (hobbitHole != null) {\n            e.presentation.text = hobbitHole.name ?: \"<Placeholder>\"\n        }\n    }\n\n    override fun actionPerformed(e: AnActionEvent) {\n        val project = e.project ?: return\n\n        VariableActionEventDataHolder.putData(VariableActionEventDataHolder(e.dataContext))\n\n        val config = shireActionConfigs(project).firstOrNull() ?: return\n        ShireRunFileAction.executeFile(project, config, null)\n    }\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/comment/ShireCommenter.kt",
    "content": "package com.phodal.shirelang.comment\n\nimport com.intellij.application.options.CodeStyle\nimport com.intellij.codeInsight.generation.CommenterDataHolder\nimport com.intellij.codeInsight.generation.SelfManagingCommenter\nimport com.intellij.codeInsight.generation.SelfManagingCommenterUtil\nimport com.intellij.lang.Commenter\nimport com.intellij.openapi.editor.Document\nimport com.intellij.openapi.util.TextRange\nimport com.intellij.psi.PsiFile\nimport com.intellij.util.text.CharArrayUtil\nimport com.phodal.shirelang.ShireLanguage\n\ndata class CommentHolder(val file: PsiFile) : CommenterDataHolder() {\n    fun useSpaceAfterLineComment(): Boolean = CodeStyle.getLanguageSettings(file, ShireLanguage.INSTANCE).LINE_COMMENT_ADD_SPACE\n}\n\nclass ShireCommenter : Commenter, SelfManagingCommenter<CommentHolder> {\n    override fun getLineCommentPrefix(): String = \"//\"\n\n    override fun getBlockCommentPrefix(): String = \"/*\"\n    override fun getBlockCommentSuffix(): String = \"*/\"\n\n    override fun getCommentedBlockCommentPrefix(): String = \"*//*\"\n    override fun getCommentedBlockCommentSuffix(): String = \"*//*\"\n\n    private val LINE_PREFIXES: List<String> = listOf(\"//\")\n\n    override fun getBlockCommentPrefix(\n        selectionStart: Int,\n        document: Document,\n        data: CommentHolder\n    ): String = blockCommentPrefix\n\n    override fun getBlockCommentSuffix(\n        selectionEnd: Int,\n        document: Document,\n        data: CommentHolder\n    ): String = blockCommentSuffix\n\n    override fun getBlockCommentRange(\n        selectionStart: Int,\n        selectionEnd: Int,\n        document: Document,\n        data: CommentHolder\n    ): TextRange? = SelfManagingCommenterUtil.getBlockCommentRange(\n        selectionStart,\n        selectionEnd,\n        document,\n        blockCommentPrefix,\n        blockCommentSuffix\n    )\n\n    override fun insertBlockComment(\n        startOffset: Int,\n        endOffset: Int,\n        document: Document,\n        data: CommentHolder?\n    ): TextRange = SelfManagingCommenterUtil.insertBlockComment(\n        startOffset,\n        endOffset,\n        document,\n        blockCommentPrefix,\n        blockCommentSuffix\n    )\n\n    override fun uncommentBlockComment(\n        startOffset: Int,\n        endOffset: Int,\n        document: Document,\n        data: CommentHolder?\n    ) = SelfManagingCommenterUtil.uncommentBlockComment(\n        startOffset,\n        endOffset,\n        document,\n        blockCommentPrefix,\n        blockCommentSuffix\n    )\n\n    override fun commentLine(line: Int, offset: Int, document: Document, data: CommentHolder) {\n        val addSpace = data.useSpaceAfterLineComment()\n        document.insertString(offset, \"//\" + if (addSpace) \" \" else \"\")\n    }\n\n    override fun getCommentPrefix(line: Int, document: Document, data: CommentHolder): String = lineCommentPrefix\n\n    override fun createBlockCommentingState(\n        selectionStart: Int,\n        selectionEnd: Int,\n        document: Document,\n        file: PsiFile\n    ): CommentHolder = CommentHolder(file)\n\n    override fun isLineCommented(line: Int, offset: Int, document: Document, data: CommentHolder): Boolean {\n        return LINE_PREFIXES.any { CharArrayUtil.regionMatches(document.charsSequence, offset, it) }\n    }\n\n    override fun uncommentLine(line: Int, offset: Int, document: Document, data: CommentHolder) {\n        val addSpace = data.useSpaceAfterLineComment()\n        document.insertString(offset, \"//\" + if (addSpace) \" \" else \"\")\n    }\n\n    override fun createLineCommentingState(\n        startLine: Int,\n        endLine: Int,\n        document: Document,\n        file: PsiFile\n    ): CommentHolder = CommentHolder(file)\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/ast/ExpressionBuiltInMethod.kt",
    "content": "package com.phodal.shirelang.compiler.ast\n\n/**\n * This enum class `ExpressionBuiltInMethod` provides a set of built-in methods for string manipulation in Kotlin.\n * Each enum constant represents a specific built-in method, and holds information about the method's name, description,\n * the string to be inserted after the method call, and the position to move the caret to after insertion.\n *\n */\nenum class ExpressionBuiltInMethod(\n    val methodName: String,\n    val description: String,\n    val postInsertString: String = \"()\",\n    val moveCaret: Int = 2,\n) {\n    LENGTH(\"length\", \"The length of the string\"),\n    TRIM(\"trim\", \"The trimmed string\"),\n    CONTAINS(\"contains\", \"Check if the string contains a substring\", \"(\\\"\\\")\", 2),\n    STARTS_WITH(\"startsWith\", \"Check if the string starts with a substring\", \"(\\\"\\\")\", 2),\n    ENDS_WITH(\"endsWith\", \"Check if the string ends with a substring\", \"(\\\"\\\")\", 2),\n    LOWERCASE(\"lowercase\", \"The lowercase version of the string\"),\n    UPPERCASE(\"uppercase\", \"The uppercase version of the string\"),\n    IS_EMPTY(\"isEmpty\", \"Check if the string is empty\"),\n    IS_NOT_EMPTY(\"isNotEmpty\", \"Check if the string is not empty\"),\n    FIRST(\"first\", \"The first character of the string\"),\n    LAST(\"last\", \"The last character of the string\"),\n    MATCHES(\"matches\", \"Check if the string matches a regex pattern\", \"(\\\"//\\\")\", 3);\n\n    companion object {\n        fun fromString(methodName: String): ExpressionBuiltInMethod? {\n            return values().find { it.methodName == methodName }\n        }\n\n        fun completionProvider(): Array<ExpressionBuiltInMethod> {\n            return values()\n        }\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/ast/ForeignFunction.kt",
    "content": "package com.phodal.shirelang.compiler.ast\n\ndata class ForeignFunction(\n    val funcName: String,\n    val funcPath: String,\n    val accessFuncName: String,\n    val inputTypes: List<String>,\n    val returnVars: Map<String, Any>,\n) {\n    companion object {\n        fun from(map: Map<String, FrontMatterType>): List<ForeignFunction> {\n            return map\n                .filter { (_, value) ->\n                    value is FrontMatterType.EXPRESSION && value.value is ForeignFunctionStmt\n                }\n                .map { (key, value) ->\n                    val stmt = value.value as ForeignFunctionStmt\n                    ForeignFunction(\n                        key,\n                        stmt.funcPath,\n                        stmt.accessFuncName,\n                        stmt.inputTypes,\n                        stmt.returnVars\n                    )\n                }\n        }\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/ast/FrontMatterType.kt",
    "content": "package com.phodal.shirelang.compiler.ast\n\nimport com.phodal.shirelang.compiler.ast.action.RuleBasedPatternAction\n\n\n/**\n * The `FrontMatterType` is a sealed class in Kotlin that represents different types of front matter data.\n * It has several subclasses to represent different types of data.\n * Each subclass overrides the `display()` function to return a string representation of the data.\n *\n * @property value The value of the front matter data.\n *\n * @constructor Creates an instance of `FrontMatterType`.\n *\n * @see STRING A subclass of `FrontMatterType` that represents a string.\n * @see NUMBER A subclass of `FrontMatterType` that represents a number.\n * @see DATE A subclass of `FrontMatterType` that represents a date.\n * @see BOOLEAN A subclass of `FrontMatterType` that represents a boolean.\n * @see ARRAY A subclass of `FrontMatterType` that represents a JSON array.\n * @see OBJECT A subclass of `FrontMatterType` that represents a JSON object.\n * @see PATTERN A subclass of `FrontMatterType` that represents a pattern action.\n * @see CASE_MATCH A subclass of `FrontMatterType` that represents a case match.\n * @see VARIABLE A subclass of `FrontMatterType` that represents a variable.\n * @see EXPRESSION A subclass of `FrontMatterType` that represents an expression.\n * @see IDENTIFIER A subclass of `FrontMatterType` that represents an identifier.\n * @see QUERY_STATEMENT A subclass of `FrontMatterType` that represents a query statement.\n */\nsealed class FrontMatterType(val value: Any) {\n    open fun display(): String = value.toString()\n    open fun toValue(): Any = value\n\n    class STRING(value: String) : FrontMatterType(value) {\n        override fun display(): String {\n            return \"\\\"$value\\\"\"\n        }\n    }\n\n    class NUMBER(value: Int) : FrontMatterType(value) {\n        override fun display(): String {\n            return value.toString()\n        }\n    }\n\n    /**\n     * The `DATE` class is a subclass of `FrontMatterType` that represents a date.\n     */\n    class DATE(value: String) : FrontMatterType(value) {\n        override fun display(): String {\n            return value.toString()\n        }\n    }\n\n    class BOOLEAN(value: Boolean) : FrontMatterType(value) {\n        override fun display(): String {\n            return value.toString()\n        }\n    }\n\n    class ERROR(value: String) : FrontMatterType(value) {\n        override fun display(): String {\n            return value.toString()\n        }\n    }\n\n    class EMPTY() : FrontMatterType(\"\") {\n        override fun display(): String {\n            return \"\"\n        }\n    }\n\n    /**\n     * The `ARRAY` class is a subclass of `FrontMatterType` that represents a JSON array.\n     *\n     * ```shire\n     * ---\n     * variables: [\"var1\", \"var2\"]\n     * ---\n     */\n    class ARRAY(value: List<FrontMatterType>) : FrontMatterType(value) {\n        override fun display(): String {\n            return (value as List<FrontMatterType>).joinToString(\", \", \"[\", \"]\") { it.display() }\n        }\n\n        override fun toValue(): List<FrontMatterType> {\n            return (value as List<FrontMatterType>)\n        }\n    }\n\n    /**\n     * The `OBJECT` class is a subclass of `FrontMatterType` that represents a JSON object.\n     * It takes a `Map` of `String` to `FrontMatterType` as its constructor parameter.\n     *\n     * ```shire\n     * ---\n     * variables:\n     *   \"var1\": \"value1\"\n     * ---\n     * ```\n     */\n    class OBJECT(value: Map<String, FrontMatterType>) : FrontMatterType(value) {\n        override fun display(): String {\n            return (value as Map<String, FrontMatterType>).entries.joinToString(\n                \", \",\n                \"{\",\n                \"}\"\n            ) { \"\\\"${it.key}\\\": ${it.value.display()}\" }\n        }\n\n        override fun toValue(): Map<String, FrontMatterType> {\n            return value as Map<String, FrontMatterType>\n        }\n    }\n\n    /**\n     * The pattern action handles for processing\n     *\n     * ```shire\n     * ---\n     * variables:\n     *   \"var2\": /.*.java/ { grep(\"error.log\") | sort | xargs(\"rm\")}\n     * ---\n     * ````\n     */\n    class PATTERN(value: RuleBasedPatternAction) : FrontMatterType(value) {\n        override fun display(): String {\n            return (value as RuleBasedPatternAction).pattern + \" -> \" + (value.processors.joinToString(\", \") { it.funcName })\n        }\n    }\n\n    /**\n     * The case match for the front matter.\n     *\n     * ```shire\n     * ---\n     * case \"$0\" {\n     *      \"error\" { grep(\"ERROR\") | sort | xargs(\"notify_admin\") }\n     *      \"warn\" { grep(\"WARN\") | sort | xargs(\"notify_admin\") }\n     *      \"info\" { grep(\"INFO\") | sort | xargs(\"notify_user\") }\n     *      default  { grep(\"ERROR\") | sort | xargs(\"notify_admin\") }\n     * }\n     * ---\n     */\n    class CASE_MATCH(value: Map<String, PATTERN>) : FrontMatterType(value) {\n        /**\n         * output sample:\n         * ```shire\n         * case \"$0\" {\n         *       \"error\" { grep(\"ERROR\") | sort | xargs(\"notify_admin\") }\n         *       \"warn\" { grep(\"WARN\") | sort | xargs(\"notify_admin\") }\n         *       \"info\" { grep(\"INFO\") | sort | xargs(\"notify_user\") }\n         *       default  { grep(\"ERROR\") | sort | xargs(\"notify_admin\") }\n         * }\n         * ```s\n         */\n        override fun display(): String {\n            return (value as Map<String, PATTERN>).entries.joinToString(\n                \"\\n\",\n                \"case \\\"\\$0\\\" {\\n\",\n                \"\\n}\"\n            ) { (key, value: PATTERN) ->\n                val pattern = (value as RuleBasedPatternAction).pattern\n                val processors = value.processors.joinToString(\" | \") { it.funcName }\n                \"    \\\"$key\\\" { $processors }\"\n            }\n        }\n    }\n\n    /**\n     * Variable same start with $, other will same to String or IDENTIFIER\n     */\n    class VARIABLE(value: String) : FrontMatterType(value) {\n        override fun display(): String {\n            return \"\\$$value\"\n        }\n    }\n\n    /**\n     * The simple expression for the [HobbitHole.WHEN] condition.\n     *\n     * ```shire\n     * ---\n     * when: $selection.length >= 1 && $selection.first() == 'p'\n     * ---\n     * ```\n     */\n    class EXPRESSION(value: Statement) : FrontMatterType(value) {\n        override fun display(): String {\n            return (value as Statement).display()\n        }\n    }\n\n    /**\n     * Identifier for the front matter config expression and template, like [EXPRESSION] or [MethodCall]\n     *\n     * ```shire\n     * ---\n     * when: $selection.length >= 1 && $selection.first() == 'p'\n     * ---\n     * ```\n     */\n    class IDENTIFIER(value: String) : FrontMatterType(value) {\n        override fun display(): String {\n            return value.toString()\n        }\n    }\n\n    /**\n     * The [QUERY_STATEMENT] class is a subclass of [FrontMatterType] that represents a query statement.\n     * for example:\n     * ```shire\n     * ---\n     * variables:\n     *   \"var1\": {\n     *       from {\n     *           PsiClass clazz\n     *       }\n     *       where {\n     *         clazz.extends(\"org.springframework.web.bind.annotation.RestController\") and clazz.getAnAnnotation() == \"org.springframework.web.bind.annotation.RequestMapping\"\n     *       }\n     *       select {\n     *         clazz.id, clazz.name, \"code\"\n     *       }\n     *   }\n     * ---\n     * ```\n     */\n    class QUERY_STATEMENT(value: ShirePsiQueryStatement) : FrontMatterType(value) {\n        override fun display(): String {\n            return value.toString()\n        }\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/ast/LineInfo.kt",
    "content": "package com.phodal.shirelang.compiler.ast\n\ndata class LineInfo(\n    val startLine: Int,\n    val endLine: Int,\n    val startColumn: Int = 0,\n    val endColumn: Int = 0\n) {\n    fun splitContent(content: String): String {\n        val fileContent = run {\n            val subContent = try {\n                content.split(\"\\n\").slice(startLine - 1 until endLine)\n                    .joinToString(\"\\n\")\n            } catch (e: StringIndexOutOfBoundsException) {\n                content\n            }\n\n            subContent\n        }\n        return fileContent\n    }\n\n    companion object {\n        private val regex = Regex(\"\"\"L(\\d+)(?:C(\\d+))?(?:-L(\\d+)(?:C(\\d+))?)?\"\"\")\n\n        /**\n         * Convert a string to a `TextRange`, if possible. The string should be in the format: \"filepath#L1-L12\",\n         * where \"filepath\" is the path to the file, \"#\" is a hash symbol, \"L1-L12\" is a range of lines from line 1 to line 12.\n         */\n        fun fromString(input: String): LineInfo? {\n            val matchResult = regex.find(input) ?: return null\n\n            val startLine = matchResult.groupValues[1].toIntOrNull() ?: 0\n            val startColumn = matchResult.groupValues[2].toIntOrNull() ?: 0\n            val endLine = matchResult.groupValues[3].toIntOrNull() ?: startLine // set end line to start line if not found\n            val endColumn = matchResult.groupValues[4].toIntOrNull() ?: 0\n\n            return LineInfo(startLine, endLine, startColumn, endColumn)\n        }\n    }\n\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/ast/ShireExpression.kt",
    "content": "package com.phodal.shirelang.compiler.ast\n\nimport com.intellij.openapi.diagnostic.logger\nimport com.phodal.shirelang.compiler.ast.patternaction.PatternActionFunc\nimport java.util.regex.Pattern\n\n/**\n * Represents the base class for all statements.\n */\nabstract class Statement {\n    abstract fun evaluate(variables: Map<String, String>): Any\n\n    fun display(): String {\n        return when (this) {\n            is Operator -> this.type.display\n            is StringOperatorStatement -> this.type.display\n            is Comparison -> \"${this.left.display()} ${this.operator.display()} ${this.right.display()}\"\n            is StringComparison -> \"${this.variable} ${this.operator.display()} ${this.value}\"\n            is LogicalExpression -> \"${this.left.display()} ${this.operator.display} ${this.right.display()}\"\n            is NotExpression -> \"!${this.operand.display()}\"\n            is MethodCall -> {\n                val parameters = this.arguments?.joinToString(\", \") {\n                    when (it) {\n                        is FrontMatterType -> it.display()\n                        else -> it.toString()\n                    }\n                }\n                val formattedParameters = if (parameters == null) \"\" else \"($parameters)\"\n\n                val dotWithTarget = if (this.methodName is FrontMatterType.EMPTY) {\n                    \"\"\n                } else if (this.methodName is FrontMatterType.IDENTIFIER) {\n                    if (this.methodName.value == \"\") {\n                        \"\"\n                    } else {\n                        \".${this.methodName.display()}\"\n                    }\n                } else {\n                    \".${this.methodName.display()}\"\n                }\n\n                \"${this.objectName.display()}${dotWithTarget}$formattedParameters\"\n            }\n\n            is Value -> this.value.display()\n            is Processor -> this.processors.joinToString(\" | \") { it.toString() }\n            else -> throw IllegalArgumentException(\"Unsupported statement type: $this\")\n        }\n    }\n}\n\n/**\n * This function `evaluate` is used to evaluate the `value` based on its type.\n *\n * @param variables A map of variables where the key is a `String` and the value is also a `String`.\n *\n * @return Returns the value of the `FrontMatterType` if it is one of the supported types (STRING, NUMBER, DATE, BOOLEAN).\n *\n * @throws IllegalArgumentException If the `FrontMatterType` is not one of the supported types,\n * an `IllegalArgumentException` is thrown with a message indicating the unsupported value type.\n *\n * @since 1.0\n */\ndata class Value(val value: FrontMatterType) : Statement() {\n    override fun evaluate(variables: Map<String, String>): Any {\n        return when (value) {\n            is FrontMatterType.STRING -> value.value\n            is FrontMatterType.NUMBER -> value.value\n            is FrontMatterType.DATE -> value.value\n            is FrontMatterType.BOOLEAN -> value.value\n            else -> throw IllegalArgumentException(\"Unsupported value type: $value\")\n        }\n    }\n}\n\n/**\n * Enumeration of operator types used in logical and comparison expressions.\n *\n * @property display The string representation of the operator.\n */\nsealed class OperatorType(val display: String) {\n    /** Logical OR operator (||). */\n    object Or : OperatorType(\"||\")\n\n    /** Logical AND operator (&&). */\n    object And : OperatorType(\"&&\")\n\n    /** Logical NOT operator (!). */\n    object Not : OperatorType(\"!\")\n\n    /** Equality operator (==). */\n    object Equal : OperatorType(\"==\")\n\n    /** Inequality operator (!=). */\n    object NotEqual : OperatorType(\"!=\")\n\n    /** Less than operator (<). */\n    object LessThan : OperatorType(\"<\")\n\n    /** Greater than operator (>). */\n    object GreaterThan : OperatorType(\">\")\n\n    /** Less than or equal operator (<=). */\n    object LessEqual : OperatorType(\"<=\")\n\n    /** Greater than or equal operator (>=). */\n    object GreaterEqual : OperatorType(\">=\")\n\n    companion object {\n        fun fromString(operator: String): OperatorType {\n            return when (operator) {\n                \"||\" -> Or\n                \"&&\" -> And\n                \"!\" -> Not\n                \"==\" -> Equal\n                \"!=\" -> NotEqual\n                \"<\" -> LessThan\n                \">\" -> GreaterThan\n                \"<=\" -> LessEqual\n                \">=\" -> GreaterEqual\n                else -> throw IllegalArgumentException(\"Invalid operator: $operator\")\n            }\n        }\n    }\n}\n\n/**\n * Enumeration of string operator types used in string comparison expressions.\n *\n * @property display The string representation of the string operator.\n */\nsealed class StringOperator(val display: String) {\n    /** Contains operator (contains). */\n    object Contains : StringOperator(\"contains\")\n\n    /** Starts with operator (startsWith). */\n    object StartsWith : StringOperator(\"startsWith\")\n\n    /** Ends with operator (endsWith). */\n    object EndsWith : StringOperator(\"endsWith\")\n\n    /** Matches regex operator (matches). */\n    object Matches : StringOperator(\"matches\")\n}\n\n/**\n * Represents an operator used in a comparison expression.\n *\n * @property type The type of operator.\n */\ndata class Operator(val type: OperatorType) : Statement() {\n    override fun evaluate(variables: Map<String, String>) = type.display\n}\n\n/**\n * Represents a string operator used in a string comparison expression.\n *\n * @property type The type of string operator.\n */\ndata class StringOperatorStatement(val type: StringOperator) : Statement() {\n    override fun evaluate(variables: Map<String, String>) = type.display\n}\n\n/**\n * Represents a comparison expression, including a variable, an operator, and a value.\n *\n * @property left The name of the variable being compared.\n * @property operator The operator used for comparison.\n * @property right The value being compared against.\n */\ndata class Comparison(\n    val left: FrontMatterType,\n    val operator: Operator,\n    val right: FrontMatterType,\n) : Statement() {\n    override fun evaluate(variables: Map<String, String>): Boolean {\n        val variableValue = when (left.value) {\n            is MethodCall -> left.value.evaluate(variables)\n            is FrontMatterType.STRING -> left.value.value\n            else -> {\n                logger<Comparison>().error(\"Variable not found: ${left.value}, will use: ${variables[left.value]}\")\n                variables[left.value]\n            }\n        }\n\n        val value = right.value\n\n        return when (operator.type) {\n            OperatorType.Equal -> variableValue == value\n            OperatorType.NotEqual -> variableValue != value\n            OperatorType.LessThan -> (variableValue as Comparable<Any>) < value\n            OperatorType.GreaterThan -> (variableValue as Comparable<Any>) > value\n            OperatorType.LessEqual -> (variableValue as Comparable<Any>) <= value\n            OperatorType.GreaterEqual -> (variableValue as Comparable<Any>) >= value\n            else -> throw IllegalArgumentException(\"Invalid comparison operator: ${operator.type}\")\n        }\n    }\n}\n\n/**\n * Represents a string comparison expression, including a variable, a string operator, and a value.\n *\n * @property variable The name of the variable being compared.\n * @property operator The string operator used for comparison.\n * @property value The string value being compared against.\n */\ndata class StringComparison(\n    val variable: String,\n    val operator: StringOperatorStatement,\n    val value: String,\n) : Statement() {\n    override fun evaluate(variables: Map<String, String>): Boolean {\n        return when (operator.type) {\n            StringOperator.Contains -> variable.contains(value)\n            StringOperator.StartsWith -> variable.startsWith(value)\n            StringOperator.EndsWith -> variable.endsWith(value)\n            StringOperator.Matches -> variable.matches(Pattern.compile(value).toRegex())\n        }\n    }\n}\n\n/**\n * Represents a logical expression, including left and right operands and an operator.\n *\n * @property left The left operand of the logical expression.\n * @property operator The logical operator used in the expression.\n * @property right The right operand of the logical expression.\n */\ndata class LogicalExpression(\n    val left: Statement,\n    val operator: OperatorType,\n    val right: Statement,\n) : Statement() {\n    override fun evaluate(variables: Map<String, String>): Boolean {\n        val leftValue = left.evaluate(variables) as Boolean\n        val rightValue = right.evaluate(variables) as Boolean\n\n        return when (operator) {\n            OperatorType.And -> leftValue && rightValue\n            OperatorType.Or -> leftValue || rightValue\n            else -> throw IllegalArgumentException(\"Invalid logical operator: $operator\")\n        }\n    }\n}\n\n/**\n * Represents a negation expression, including an operand.\n *\n * @property operand The operand to be negated.\n */\ndata class NotExpression(val operand: Statement) : Statement() {\n    override fun evaluate(variables: Map<String, String>): Boolean {\n        return !(operand.evaluate(variables) as Boolean)\n    }\n}\n\n/**\n * Represents a method call expression, including the object and method being called.\n *\n * @property objectName The name of the object on which the method is called.\n * @property methodName The name of the method being called.\n * @property arguments The arguments passed to the method.\n */\ndata class MethodCall(\n    val objectName: FrontMatterType,\n    val methodName: FrontMatterType,\n    val arguments: List<Any>?,\n) : Statement() {\n    override fun evaluate(variables: Map<String, String>): Any {\n        val value = when (objectName) {\n            is FrontMatterType.STRING -> variables[objectName.value]\n            is FrontMatterType.VARIABLE -> variables[objectName.value]\n            else -> null\n        } ?: throw IllegalArgumentException(\"Variable not found: ${objectName.value}\")\n\n        val parameters: List<Any>? = parameters()\n\n        return evaluateExpression(methodName, parameters, value)\n    }\n\n    fun parameters() = this.arguments?.map {\n        when (it) {\n            is FrontMatterType.STRING -> it.display().removeSurrounding(\"\\\"\")\n            is FrontMatterType.NUMBER -> it.value\n            is FrontMatterType.DATE -> it.value\n            is FrontMatterType -> it.display()\n            else -> it.toString()\n        }\n    }\n\n    fun evaluateExpression(methodNode: FrontMatterType, parameters: List<Any>?, value: String): Comparable<*> {\n        val method = ExpressionBuiltInMethod.fromString(methodNode.value.toString())\n        return when (method) {\n            ExpressionBuiltInMethod.LENGTH -> value.length\n            ExpressionBuiltInMethod.TRIM -> value.trim()\n            ExpressionBuiltInMethod.CONTAINS -> {\n                if (parameters != null) {\n                    value.contains(parameters[0] as String)\n                } else {\n                    throw IllegalArgumentException(\"Missing parameter for method: $methodNode\")\n                }\n            }\n\n            ExpressionBuiltInMethod.STARTS_WITH -> {\n                if (parameters != null) {\n                    value.startsWith(parameters[0] as String)\n                } else {\n                    throw IllegalArgumentException(\"Missing parameter for method: $methodNode\")\n                }\n            }\n\n            ExpressionBuiltInMethod.ENDS_WITH -> {\n                if (parameters != null) {\n                    value.endsWith(parameters[0] as String)\n                } else {\n                    throw IllegalArgumentException(\"Missing parameter for method: $methodNode\")\n                }\n            }\n\n            ExpressionBuiltInMethod.LOWERCASE -> value.lowercase()\n            ExpressionBuiltInMethod.UPPERCASE -> value.uppercase()\n            ExpressionBuiltInMethod.IS_EMPTY -> value.isEmpty()\n            ExpressionBuiltInMethod.IS_NOT_EMPTY -> value.isNotEmpty()\n            ExpressionBuiltInMethod.FIRST -> value.first().toString()\n            ExpressionBuiltInMethod.LAST -> value.last().toString()\n            ExpressionBuiltInMethod.MATCHES -> {\n                if (parameters != null) {\n                    value.matches((parameters[0] as String).toRegex())\n                } else {\n                    throw IllegalArgumentException(\"Missing parameter for method: $methodNode\")\n                }\n            }\n\n            else -> throw IllegalArgumentException(\"Unsupported method: $methodNode\")\n        }\n    }\n}\n\ndata class Processor(\n    val processors: List<PatternActionFunc>,\n) : Statement() {\n    override fun evaluate(variables: Map<String, String>): List<PatternActionFunc> {\n        return processors\n    }\n}\n\ndata class CaseKeyValue(\n    val key: FrontMatterType,\n    val value: FrontMatterType.EXPRESSION,\n) : Statement() {\n    override fun evaluate(variables: Map<String, String>): Any {\n        return key.display() to value\n    }\n}\n\ndata class ForeignFunctionStmt(\n    val funcName: String,\n    val funcPath: String,\n    val accessFuncName: String,\n    val inputTypes: List<String>,\n    val returnVars: Map<String, Any>,\n) : Statement() {\n    override fun evaluate(variables: Map<String, String>): ForeignFunction {\n        return ForeignFunction(funcName, funcPath, accessFuncName, inputTypes, returnVars)\n    }\n}\n\n/**\n * Switch case\n */\ndata class ConditionCase(\n    val conditions: List<FrontMatterType>,\n    val cases: List<FrontMatterType>,\n) : Statement() {\n    override fun evaluate(variables: Map<String, String>): Any {\n        val condition = conditions.map { it.display() }\n        val case = cases.map { it.display() }\n\n        return condition to case\n    }\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/ast/ShirePsiQueryStatement.kt",
    "content": "package com.phodal.shirelang.compiler.ast\n\nimport com.phodal.shirelang.compiler.ast.patternaction.PatternActionFunc\n\nclass ShirePsiQueryStatement(\n    val from: List<VariableElement>,\n    val where: Statement,\n    val select: List<Statement>,\n) {\n    override fun toString(): String {\n        return \"\"\"\n            from {\n                ${from.joinToString(\", \")} \n            }\n            where {\n                $where\n            } \n            select ${select.joinToString(\", \")}\"\"\"\n            .trimIndent()\n    }\n\n    fun toPatternActionFunc(): List<PatternActionFunc> {\n        val selectFunc = PatternActionFunc.From(from)\n        val whereFunc = PatternActionFunc.Where(where)\n        val selectFuncs = PatternActionFunc.Select(select)\n        return listOf(selectFunc, whereFunc, selectFuncs)\n    }\n}\n\nclass VariableElement(\n    val variableType: String,\n    val value: String,\n) {\n    override fun toString(): String {\n        return \"$variableType $value\"\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/ast/TaskRoutes.kt",
    "content": "package com.phodal.shirelang.compiler.ast\n\nimport com.intellij.openapi.diagnostic.logger\nimport com.intellij.openapi.project.Project\nimport com.phodal.shirecore.middleware.post.PostProcessorContext\nimport com.phodal.shirelang.compiler.ast.hobbit.HobbitHole\nimport com.phodal.shirelang.compiler.execute.FunctionStatementProcessor\n\ndata class Condition(\n    val conditionKey: String,\n    val valueExpression: FrontMatterType.EXPRESSION,\n)\n\nsealed class Task(open val expression: FrontMatterType.EXPRESSION?) {\n    class CustomTask(override val expression: FrontMatterType.EXPRESSION?) : Task(expression)\n    class Default(override val expression: FrontMatterType.EXPRESSION?) : Task(expression)\n}\n\ndata class Case(\n    val caseKey: String,\n    val valueExpression: Task,\n)\n\ndata class TaskRoutes(\n    val conditions: List<Condition>,\n    val cases: List<Case>,\n    /**\n     * A placeholder for the default task\n     */\n    val defaultTask: Task? = null,\n) {\n    fun execute(myProject: Project, context: PostProcessorContext, hobbitHole: HobbitHole): Any? {\n        val conditionResult = mutableMapOf<String, Any?>()\n        /// todo: get processor variable table\n        val variableTable = context.compiledVariables.toMutableMap()\n        variableTable[\"output\"] = context.genText\n\n        val processor = FunctionStatementProcessor(myProject, hobbitHole)\n        conditions.forEach {\n            val statement = it.valueExpression.value as Statement\n            val result = processor.execute(statement, variableTable)\n            conditionResult[it.conditionKey] = result\n        }\n\n        val matchedCase = cases.filter {\n            val caseKey = it.caseKey\n\n            when (val condValue = conditionResult[caseKey]) {\n                is Boolean -> {\n                    condValue == true || condValue == \"true\"\n                }\n\n                is String -> {\n                    condValue.isNotEmpty()\n                }\n\n                else -> {\n                    false\n                }\n            }\n        }\n\n        var result: Any? = null\n        if (matchedCase.isEmpty()) {\n            val result = ((defaultTask as? Task.Default)?.expression?.value as? Statement)?.let {\n                processor.execute(it, variableTable)\n            }\n\n            logger<TaskRoutes>().info(\"no matched case, execute default task: $result\")\n            return result\n        }\n\n        matchedCase.forEach {\n            val statement = (it.valueExpression as Task.CustomTask).expression?.value as Statement\n            result = processor.execute(statement, variableTable)\n        }\n\n        return result\n    }\n\n    companion object {\n        fun from(expression: FrontMatterType.ARRAY): TaskRoutes? {\n            val arrays = expression.value as List<FrontMatterType>\n            val taskRoutes = arrays.filterIsInstance<FrontMatterType.EXPRESSION>()\n                .mapNotNull { caseExpr ->\n                    when (val value = caseExpr.value) {\n                        is ConditionCase -> {\n                            transformConditionCasesToRoutes(value)\n                        }\n                        else -> {\n                            null\n                        }\n                    }\n                }\n\n            return taskRoutes.firstOrNull()\n        }\n\n        /**\n         * Transforms a given [ConditionCase] into a [TaskRoutes] object which contains a structured set of conditions and corresponding tasks.\n         *\n         * @param conditionCase The [ConditionCase] object to transform. This object contains conditions and cases that determine routing logic.\n         * @return A [TaskRoutes] object that encapsulates the transformed conditions and cases, along with a default task if specified.\n         */\n        private fun transformConditionCasesToRoutes(conditionCase: ConditionCase): TaskRoutes {\n            val conditions: List<Condition> = conditionCase.conditions.map {\n                val caseKeyValue = it.value as CaseKeyValue\n                Condition(caseKeyValue.key.display(), caseKeyValue.value)\n            }\n\n            var defaultTask: Task? = null\n\n            val cases: List<Case> = conditionCase.cases.map {\n                val caseKeyValue = it.value as CaseKeyValue\n                val caseKey = caseKeyValue.key.display()\n                if (caseKey == \"default\") {\n                    defaultTask = Task.Default(caseKeyValue.value)\n                }\n\n                Case(caseKey, Task.CustomTask(caseKeyValue.value))\n            }\n\n            return TaskRoutes(\n                conditions = conditions,\n                cases = cases,\n                defaultTask = defaultTask\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/ast/action/DirectAction.kt",
    "content": "package com.phodal.shirelang.compiler.ast.action\n\nimport com.phodal.shirelang.compiler.ast.FrontMatterType\nimport com.phodal.shirelang.compiler.ast.MethodCall\nimport com.phodal.shirelang.compiler.ast.patternaction.PatternActionFunc\n\nopen class DirectAction(open val processors: List<PatternActionFunc>) {\n    companion object {\n        fun from(fmt: FrontMatterType): DirectAction? {\n            return when (fmt) {\n                is FrontMatterType.ARRAY -> {\n                    val list = fmt.value as List<FrontMatterType>\n                    val actions = list.mapNotNull {\n                        when (it) {\n                            is FrontMatterType.EXPRESSION -> {\n                                val methodCall = it.value as? MethodCall ?: return@mapNotNull null\n                                val methodName = methodCall.objectName.display()\n\n                                val methodArgs: List<String> = methodCall.arguments?.map { arg ->\n                                    arg.toString()\n                                } ?: emptyList()\n\n                                PatternActionFunc.from(methodName, methodArgs)\n                            }\n\n                            else -> {\n                                null\n                            }\n                        }\n                    }\n\n                    return DirectAction(actions)\n                }\n\n                else -> {\n                    null\n                }\n            }\n        }\n    }\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/ast/action/PatternAction.kt",
    "content": "package com.phodal.shirelang.compiler.ast.action\n\nimport com.intellij.openapi.diagnostic.logger\nimport com.phodal.shirelang.compiler.ast.FrontMatterType\nimport com.phodal.shirelang.compiler.ast.ShirePsiQueryStatement\nimport com.phodal.shirelang.compiler.ast.patternaction.PatternActionFunc\n\n/**\n * PatternFun is a sealed class in Kotlin representing different pattern processing functions.\n * It has several subclasses: Prompt, Grep, Sed, Sort, Uniq, Head, Tail, Xargs, and Print,\n * each representing a specific pattern processing function.\n *\n * @property funcName The name of the pattern processing function.\n */\ndata class PatternAction(\n    val pattern: String,\n    val patternFuncs: List<PatternActionFunc>,\n    val isQueryStatement: Boolean = false\n) : DirectAction(patternFuncs) {\n    companion object {\n        /**\n         * Creates a list of PatternFun instances from a FrontMatterType object.\n         *\n         * @param value The FrontMatterType object.\n         * @return A list of corresponding PatternFun instances.\n         */\n        fun from(value: FrontMatterType): PatternAction? {\n            return when (value) {\n                is FrontMatterType.STRING -> {\n                    PatternAction(\"\", listOf(PatternActionFunc.Print(value.value as? String ?: \"\")))\n                }\n\n                is FrontMatterType.PATTERN -> {\n                    val action = value.value as? RuleBasedPatternAction ?: return null\n                    PatternAction(action.pattern, action.processors)\n                }\n\n                is FrontMatterType.QUERY_STATEMENT -> {\n                    val action = value.value as? ShirePsiQueryStatement ?: return null\n                    PatternAction(\"\", action.toPatternActionFunc(), true)\n                }\n\n                else -> {\n                    logger<PatternAction>().warn(\"Unknown pattern processor type: ${value.display()}\")\n                    null\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/ast/action/RuleBasedPatternAction.kt",
    "content": "package com.phodal.shirelang.compiler.ast.action\n\nimport com.phodal.shirelang.compiler.ast.patternaction.PatternActionFunc\n\nclass RuleBasedPatternAction(val pattern: String, override val processors: List<PatternActionFunc>) :\n    DirectAction(processors)\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/ast/hobbit/HobbitHole.kt",
    "content": "package com.phodal.shirelang.compiler.ast.hobbit\n\nimport com.intellij.execution.ui.ConsoleView\nimport com.intellij.execution.ui.ConsoleViewContentType\nimport com.intellij.openapi.actionSystem.KeyboardShortcut\nimport com.intellij.openapi.application.runReadAction\nimport com.intellij.openapi.diagnostic.logger\nimport com.intellij.openapi.editor.Editor\nimport com.intellij.openapi.project.Project\nimport com.phodal.shirecore.config.InteractionType\nimport com.phodal.shirecore.config.ShireActionLocation\nimport com.phodal.shirecore.runner.console.isCanceled\nimport com.phodal.shirecore.middleware.post.PostProcessor\nimport com.phodal.shirecore.middleware.post.PostProcessorContext\nimport com.phodal.shirecore.middleware.post.LifecycleProcessorSignature\nimport com.phodal.shirecore.middleware.select.SelectElementStrategy\nimport com.phodal.shirecore.middleware.select.SelectedEntry\nimport com.phodal.shirecore.workerThread\nimport com.phodal.shirelang.compiler.ast.ForeignFunction\nimport com.phodal.shirelang.compiler.ast.FrontMatterType\nimport com.phodal.shirelang.compiler.ast.MethodCall\nimport com.phodal.shirelang.compiler.ast.TaskRoutes\nimport com.phodal.shirelang.compiler.ast.action.DirectAction\nimport com.phodal.shirelang.compiler.ast.action.PatternAction\nimport com.phodal.shirelang.compiler.execute.FunctionStatementProcessor\nimport com.phodal.shirelang.compiler.ast.hobbit.base.Smials\nimport com.phodal.shirelang.compiler.parser.HobbitHoleParser\nimport com.phodal.shirelang.compiler.ast.patternaction.VariableTransform\nimport com.phodal.shirelang.psi.ShireFile\nimport kotlinx.coroutines.CoroutineScope\nimport kotlinx.coroutines.launch\n\n/**\n * Hobbit Hole 用于定义 IDE 交互逻辑与用户数据的流处理。\n *\n * 示例\n * ```shire\n * ---\n * name: \"Summary\"\n * description: \"Generate Summary\"\n * interaction: AppendCursor\n * actionLocation: ContextMenu\n * ---\n * ```\n *\n */\nopen class HobbitHole(\n    /**\n     * Display name of the Shire command, will show in the IDE's UI base on [HobbitHole.interaction].\n     *\n     * For example: [ShireActionLocation.CONTEXT_MENU], will show in the context menu.\n     *\n     * ```shire\n     * ---\n     * name: \"AutoTest\"\n     * ---\n     * ```\n     */\n    val name: String,\n    /**\n     * Tooltips for the action, will show in Hover tips on the UI.\n     *\n     * ```shire\n     * ---\n     * description: \"Generate Test\"\n     * ---\n     * ```\n     */\n    val description: String? = null,\n    /**\n     * The output of the action can be in editor with streaming text when use use [InteractionType.AppendCursorStream\n     *\n     * ```shire\n     * ---\n     * interaction: AppendCursor\n     * ---\n     * ```\n     */\n    val interaction: InteractionType = InteractionType.RunPanel,\n    /**\n     * The location of the action, should be one of [ShireActionLocation], the default is [ShireActionLocation.RUN_PANEL].\n     *\n     * ```shire\n     * ---\n     * actionLocation: ContextMenu\n     * ---\n     * ```\n     */\n    val actionLocation: ShireActionLocation = ShireActionLocation.RUN_PANEL,\n    /**\n     * The strategy to select the element to apply the action.\n     * If not selected text, will according the element position to select the element block.\n     * For example, if cursor in a function, select the function block.\n     *\n     * ```shire\n     * ---\n     * selectionStrategy: \"Block\"\n     * ---\n     */\n    val selectionStrategy: SelectElementStrategy? = null,\n\n    /**\n     * The list of variables with PatternAction for build the variable.\n     *\n     * ```shire\n     * ---\n     * variables:\n     *   \"name\": \"/[a-zA-Z]+/\"\n     *   \"var2\": /.*.java/ { grep(\"error.log\") | sort | print }\n     *   \"testTemplate\": /\\(.*\\).java/ {\n     *     case \"$1\" {\n     *       \"Controller\" { cat(\".shire/templates/ControllerTest.java\") }\n     *       \"Service\" { cat(\".shire/templates/ServiceTest.java\") }\n     *       default  { cat(\".shire/templates/DefaultTest.java\") }\n     *     }\n     *   }\n     *\n     * ---\n     */\n    val variables: MutableMap<String, VariableTransform> = mutableMapOf(),\n\n    /**\n     * This code snippet declares a variable 'when_' of type List<VariableCondition> and initializes it with an empty list.\n     * 'when_' is a list that stores VariableCondition objects.\n     *\n     * Which is used for: [com.intellij.codeInsight.intention.IntentionAction.isAvailable], [com.intellij.openapi.project.DumbAwareAction.DumbAwareAction.update] to check is show menu.\n     *\n     * ```shire\n     * ---\n     * when: { $filePath.contains(\"src/main/java\") && $fileName.contains(\".java\") }\n     * ---\n     * ```\n     */\n    val when_: FrontMatterType.EXPRESSION? = null,\n\n    /**\n     * This property represents a list of post-middleware actions to be executed after the streaming process ends.\n     * It allows for the definition of various operations such as logging, metrics collection, code verification,\n     * execution of code, or parsing code, among others.\n     *\n     * ```shire\n     * ---\n     * onStreamingEnd: { parseCode | saveFile(\"docs/shire/shire-context-variable.md\")  }\n     * ---\n     * ```\n     */\n    val onStreamingEnd: List<LifecycleProcessorSignature> = emptyList(),\n\n    /**\n     * This property represents a list of middleware actions to be executed\n     *\n     * ```shire\n     * ---\n     * onStreaming: { logging() | redacting() }\n     * ---\n     */\n    val onStreaming: List<LifecycleProcessorSignature> = emptyList(),\n\n    /**\n     * ```shire\n     * ---\n     * beforeStreaming: { parseCode}\n     * ---\n     * ```\n     */\n    val beforeStreaming: DirectAction? = null,\n\n    /**\n     * The list of actions that this action depends on.\n     *\n     * ```shire\n     * ---\n     * afterStreaming: {\n     *     condition {\n     *       \"variable-success\" { $selection.length > 1 }\n     *       \"jsonpath-success\" { jsonpath(\"/bookstore/book[price>35]\") }\n     *     }\n     *     case condition {\n     *       \"variable-sucesss\" { done }\n     *       \"jsonpath-success\" { TODO }\n     *       default { TODO }\n     *     }\n     *   }\n     * ---\n     * ```\n     */\n    val afterStreaming: TaskRoutes? = null,\n\n    /**\n     * The IDE shortcut for the action, which use the IntelliJ IDEA's shortcut format.\n     *\n     * ```shire\n     * ---\n     * shortcut: \"meta pressed V\"\n     * ---\n     * ```\n     */\n    val shortcut: KeyboardShortcut? = null,\n\n    /**\n     * the status of the action, default is true.\n     *\n     * ```shire\n     * ---\n     * enabled: false\n     * ---\n     */\n    val enabled: Boolean = true,\n\n    /**\n     * the LLM model for action, default is null which will use the default model.\n     *\n     * ```shire\n     * ---\n     * model: \"default\"\n     * ---\n     *\n     */\n    val model: String? = null,\n\n    /**\n     * Custom Functions for the action.\n     */\n    val foreignFunctions: MutableMap<String, ForeignFunction> = mutableMapOf(),\n\n    /**\n     * The rest of the data.\n     */\n    val userData: Map<String, FrontMatterType> = mutableMapOf(),\n) : Smials {\n    fun pickupElement(project: Project, editor: Editor?): SelectedEntry? {\n        return runReadAction {\n            this.selectionStrategy?.select(project, editor)\n            return@runReadAction selectionStrategy?.getSelectedElement(project, editor)\n        }\n    }\n\n    fun setupStreamingEndProcessor(project: Project, context: PostProcessorContext) {\n        onStreamingEnd.forEach { funcNode ->\n            PostProcessor.handler(funcNode.funcName)?.setup(context)\n        }\n    }\n\n    fun executeStreamingEndProcessor(\n        project: Project,\n        console: ConsoleView?,\n        context: PostProcessorContext,\n        compiledVariables: Map<String, Any>,\n    ): String? {\n        console?.print(\"\\n\", ConsoleViewContentType.SYSTEM_OUTPUT)\n        onStreamingEnd.forEach { funcNode ->\n            if (console?.isCanceled() == true) return@forEach\n            console?.print(\"execute streamingEnd: ${funcNode.funcName}\\n\", ConsoleViewContentType.SYSTEM_OUTPUT)\n            val postProcessor = PostProcessor.handler(funcNode.funcName)\n            if (postProcessor == null) {\n                // TODO: change execute\n                console?.print(\"Not found function: ${funcNode.funcName}\\n\", ConsoleViewContentType.SYSTEM_OUTPUT)\n                return@forEach\n            }\n\n            val args: List<Any> = funcNode.args.map { arg ->\n                when (arg) {\n                    is String -> {\n                        if (arg.startsWith(\"$\")) {\n                            if (arg == \"\\$output\" && context.lastTaskOutput != null) {\n                                context.lastTaskOutput ?: \"\\$output\"\n                            } else {\n                                compiledVariables[arg.substring(1)] ?: \"\"\n                            }\n                        } else {\n                            arg\n                        }\n                    }\n\n                    else -> arg\n                }\n            }\n\n            val lastResult = postProcessor.execute(project, context, console, args)\n            context.lastTaskOutput = lastResult as? String\n        }\n\n        return context.lastTaskOutput\n    }\n\n    fun executeBeforeStreamingProcessor(\n        myProject: Project,\n        context: PostProcessorContext,\n        console: ConsoleView?,\n        compiledVariables: MutableMap<String, Any?>,\n    ): Any? {\n        if (console?.isCanceled() == true) return null\n        if (beforeStreaming == null) return null\n        if (beforeStreaming.processors.isEmpty()) return null\n\n        CoroutineScope(workerThread).launch {\n            FunctionStatementProcessor(myProject, this@HobbitHole).execute(\n                beforeStreaming.processors,\n                compiledVariables\n            )\n        }\n\n        return context.lastTaskOutput\n    }\n\n    fun executeAfterStreamingProcessor(\n        myProject: Project,\n        console: ConsoleView?,\n        context: PostProcessorContext,\n    ): Any? {\n        if (console?.isCanceled() == true) return null\n        val result = afterStreaming?.execute(myProject, context, this)\n        context.lastTaskOutput = result as? String\n        return result\n    }\n\n    companion object {\n        const val NAME = \"name\"\n        const val ACTION_LOCATION = \"actionLocation\"\n        const val INTERACTION = \"interaction\"\n        const val STRATEGY_SELECTION = \"selectionStrategy\"\n\n        const val ON_STREAMING_END = \"onStreamingEnd\"\n        const val BEFORE_STREAMING = \"beforeStreaming\"\n        const val AFTER_STREAMING = \"afterStreaming\"\n        const val ON_STREAMING = \"onStreaming\"\n\n        const val ENABLED = \"enabled\"\n        const val MODEL = \"model\"\n\n        private const val DESCRIPTION = \"description\"\n        private const val VARIABLES = \"variables\"\n        private const val FUNCTIONS = \"functions\"\n\n        private const val WHEN = \"when\"\n        private const val SHORTCUT = \"shortcut\"\n\n        fun from(file: ShireFile): HobbitHole? {\n            return HobbitHoleParser.parse(file)\n        }\n\n        fun create(name: String, description: String, interactionType: InteractionType, chatBox: ShireActionLocation): HobbitHole {\n            return HobbitHole(name, description, interactionType, actionLocation = chatBox)\n        }\n\n        /**\n         * For Code completion ,\n         * todo: modify to map with description\n         */\n        fun keys(): Map<String, String> {\n            return mapOf(\n                NAME to \"The display name of the action\",\n                DESCRIPTION to \"The tips for the action\",\n                WHEN to \"The condition to run the action\",\n\n                INTERACTION to \"The output of the action can be a file, a string, etc.\",\n                ACTION_LOCATION to \"The location of the action, can [ShireActionLocation]\",\n                SHORTCUT to \"The shortcut for the action\",\n\n                STRATEGY_SELECTION to \"The strategy to select the element to apply the action\",\n                VARIABLES to \"The list of variables to apply for the action\",\n                FUNCTIONS to \"The list of custom functions for the action\",\n\n                ON_STREAMING to \"Some actions when receive the streaming text\",\n                ON_STREAMING_END to \"After Streaming end middleware actions, like Logging, Metrics, CodeVerify, RunCode, ParseCode etc.\",\n                BEFORE_STREAMING to \"The task/patternAction before streaming\",\n                AFTER_STREAMING to \"Decision to run the task after streaming, routing to different tasks\",\n            )\n        }\n\n        fun from(frontMatterMap: MutableMap<String, FrontMatterType>): HobbitHole {\n            val name = frontMatterMap[NAME]?.value as? String ?: \"\"\n            val description = frontMatterMap[DESCRIPTION]?.value as? String ?: \"\"\n            val interaction = frontMatterMap[INTERACTION]?.value as? String ?: \"\"\n            val actionLocation = frontMatterMap[ACTION_LOCATION]?.value as? String ?: ShireActionLocation.default()\n            val enabled = frontMatterMap[ENABLED]?.value as? Boolean ?: true\n            val model = frontMatterMap[MODEL]?.value as? String\n\n            val shortcut = (frontMatterMap[SHORTCUT]?.value as? String)?.let {\n                KeyboardShortcut.fromString(it)\n            }\n\n            val data = mutableMapOf<String, FrontMatterType>()\n            frontMatterMap.forEach { (key, value) ->\n                if (key !in listOf(NAME, DESCRIPTION, INTERACTION, ACTION_LOCATION)) {\n                    data[key] = value\n                }\n            }\n\n            val selectionStrategy = SelectElementStrategy.fromString(\n                frontMatterMap[STRATEGY_SELECTION]?.value as? String ?: \"\"\n            )\n\n            val endProcessors = frontMatterMap[ON_STREAMING_END]?.let {\n                buildLifecycleProcessors(it)\n            } ?: mutableListOf()\n\n            val onStreamingProcessors = frontMatterMap[ON_STREAMING]?.let {\n                buildLifecycleProcessors(it)\n            } ?: mutableListOf()\n\n            val variables = (frontMatterMap[VARIABLES] as? FrontMatterType.OBJECT)?.let {\n                buildVariableTransformations(it.toValue())\n            } ?: mutableMapOf()\n\n            val foreignFunctions: MutableMap<String, ForeignFunction> =\n                (frontMatterMap[FUNCTIONS] as? FrontMatterType.OBJECT)?.let {\n                    ForeignFunction.from(it.toValue())\n                }.orEmpty().associateBy { it.funcName }.toMutableMap()\n\n            val beforeStreaming: DirectAction? = if (frontMatterMap[BEFORE_STREAMING] != null) {\n                DirectAction.from(frontMatterMap[BEFORE_STREAMING]!!)\n            } else {\n                null\n            }\n\n            val afterStreaming: TaskRoutes? = (frontMatterMap[AFTER_STREAMING] as? FrontMatterType.ARRAY)?.let {\n                try {\n                    TaskRoutes.from(it)\n                } catch (e: Exception) {\n                    logger<HobbitHole>().warn(\"Error to parse after streaming: $e\")\n                    null\n                }\n            }\n\n            val whenCondition = frontMatterMap[WHEN] as? FrontMatterType.EXPRESSION\n\n            return HobbitHole(\n                name = name,\n                description = description,\n                interaction = InteractionType.from(interaction),\n                actionLocation = ShireActionLocation.from(actionLocation),\n                selectionStrategy = selectionStrategy,\n                variables = variables,\n                foreignFunctions = foreignFunctions,\n                userData = data,\n                when_ = whenCondition,\n                beforeStreaming = beforeStreaming,\n                onStreamingEnd = endProcessors,\n                onStreaming = onStreamingProcessors,\n                afterStreaming = afterStreaming,\n                shortcut = shortcut,\n                enabled = enabled,\n                model = model\n            )\n        }\n\n        private fun buildVariableTransformations(variableObject: Map<String, FrontMatterType>): MutableMap<String, VariableTransform> {\n            return variableObject.mapNotNull { (key, value) ->\n                val variable = key.removeSurrounding(\"\\\"\")\n                PatternAction.from(value)?.let {\n                    val pattern = it.pattern.removeSurrounding(\"/\")\n                    VariableTransform(variable, pattern, it.patternFuncs, it.isQueryStatement)\n                }\n            }.associateBy { it.variable }.toMutableMap()\n        }\n\n        private fun buildLifecycleProcessors(item: FrontMatterType): List<LifecycleProcessorSignature> {\n            val endProcessors: MutableList<LifecycleProcessorSignature> = mutableListOf()\n            when (item) {\n                is FrontMatterType.ARRAY -> {\n                    item.toValue().forEach { matterType ->\n                        when (matterType) {\n                            is FrontMatterType.EXPRESSION -> {\n                                val processorNode = toPostProcessorNode(matterType)\n                                endProcessors.add(processorNode)\n                            }\n\n                            else -> {}\n                        }\n                    }\n                }\n\n                is FrontMatterType.STRING -> {\n                    val handleName = item.value as String\n                    endProcessors.add(LifecycleProcessorSignature(handleName, emptyList()))\n                }\n\n                else -> {}\n            }\n\n            return endProcessors\n        }\n\n        private fun toPostProcessorNode(expression: FrontMatterType.EXPRESSION): LifecycleProcessorSignature {\n            return when (val child = expression.value) {\n                is MethodCall -> {\n                    val handleName = child.objectName.display()\n                    val args: List<String> = child.arguments?.map { it.toString() } ?: emptyList()\n                    return LifecycleProcessorSignature(handleName, args)\n                }\n\n                else -> {\n                    val handleName = expression.display()\n                    LifecycleProcessorSignature(handleName, emptyList())\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/ast/hobbit/base/Smials.kt",
    "content": "package com.phodal.shirelang.compiler.ast.hobbit.base\n\n/**\n * Smials is a type of Hobbit Hole\n */\ninterface Smials {\n\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/ast/patternaction/PatternActionFunc.kt",
    "content": "package com.phodal.shirelang.compiler.ast.patternaction\n\nimport com.intellij.openapi.diagnostic.logger\nimport com.phodal.shirelang.compiler.ast.CaseKeyValue\nimport com.phodal.shirelang.compiler.ast.Statement\nimport com.phodal.shirelang.compiler.ast.VariableElement\n\n/**\n * The `PatternActionFunc` is a sealed class in Kotlin that represents a variety of pattern action functions.\n * Each subclass represents a different function, and each has a unique set of properties relevant to its function.\n *\n * @property funcName The name of the function.\n */\nsealed class PatternActionFunc(val type: PatternActionFuncDef) {\n    open val funcName: String = type.funcName\n\n    /**\n     * Grep subclass for searching with one or more patterns.\n     *\n     * @property patterns The patterns to search for.\n     */\n    class Grep(vararg val patterns: String) : PatternActionFunc(PatternActionFuncDef.GREP)\n\n    /**\n     * Find subclass for searching with text\n     * @property text The text to search for.\n     */\n    class Find(val text: String) : PatternActionFunc(PatternActionFuncDef.FIND)\n\n    /**\n     * Sed subclass for find and replace operations.\n     *\n     * @property pattern The pattern to search for.\n     * @property replacements The string to replace matches with.\n     *\n     * For example, `sed(\"foo\", \"bar\")` would replace all instances of \"foo\" with \"bar\".\n     */\n    class Sed(val pattern: String, val replacements: String, val isRegex: Boolean = true) :\n        PatternActionFunc(PatternActionFuncDef.SED)\n\n    /**\n     * Sort subclass for sorting with one or more arguments.\n     *\n     * @property arguments The arguments to use for sorting.\n     */\n    class Sort(vararg val arguments: String) : PatternActionFunc(PatternActionFuncDef.SORT)\n\n    /**\n     * Uniq subclass for removing duplicates based on one or more arguments.\n     *\n     * @property texts The texts to process for uniqueness.\n     */\n    class Uniq(vararg val texts: String) : PatternActionFunc(PatternActionFuncDef.UNIQ)\n\n    /**\n     * Head subclass for retrieving the first few lines.\n     *\n     * @property number The number of lines to retrieve from the start.\n     */\n    class Head(val number: Number) : PatternActionFunc(PatternActionFuncDef.HEAD)\n\n    /**\n     * Tail subclass for retrieving the last few lines.\n     *\n     * @property number The number of lines to retrieve from the end.\n     */\n    class Tail(val number: Number) : PatternActionFunc(PatternActionFuncDef.TAIL)\n\n    /**\n     * Xargs subclass for processing one or more variables.\n     *\n     * @property variables The variables to process.\n     */\n    class Xargs(vararg val variables: String) : PatternActionFunc(PatternActionFuncDef.XARGS)\n\n    /**\n     * Print subclass for printing one or more texts.\n     *\n     * @property texts The texts to be printed.\n     */\n    class Print(vararg val texts: String) : PatternActionFunc(PatternActionFuncDef.PRINT)\n\n    /**\n     * Cat subclass for concatenating one or more files.\n     * Paths can be absolute or relative to the current working directory.\n     */\n    class Cat(vararg val paths: String) : PatternActionFunc(PatternActionFuncDef.CAT)\n\n    /**\n     * Select subclass for selecting one or more elements.\n     */\n    class From(val variables: List<VariableElement>) : PatternActionFunc(PatternActionFuncDef.FROM)\n\n    /**\n     * Where subclass for filtering elements.\n     */\n    class Where(val statement: Statement) : PatternActionFunc(PatternActionFuncDef.WHERE)\n\n    /**\n     * OrderBy subclass for ordering elements.\n     */\n    class Select(val statements: List<Statement>) : PatternActionFunc(PatternActionFuncDef.SELECT)\n\n    /**\n     * Execute a shire script\n     */\n    class Execute(val filename: String, val variableNames: Array<String>) :\n        PatternActionFunc(PatternActionFuncDef.EXECUTE)\n\n    /**\n     * Approval Execution\n     */\n    class ApprovalExecute(val filename: String, val variableNames: Array<String>) :\n        PatternActionFunc(PatternActionFuncDef.APPROVAL_EXECUTE)\n\n    /**\n     * Use IDE Notify\n     */\n    class Notify(val message: String) : PatternActionFunc(PatternActionFuncDef.NOTIFY)\n\n    /**\n     * Case Match\n     */\n    class CaseMatch(val keyValue: List<CaseKeyValue>) : PatternActionFunc(PatternActionFuncDef.CASE_MATCH)\n\n    /**\n     * Splitting\n     */\n    class Splitting(val paths: Array<String>) : PatternActionFunc(PatternActionFuncDef.SPLITTING)\n\n    /**\n     * Embedding text\n     */\n    class Embedding(val entries: Array<String>) : PatternActionFunc(PatternActionFuncDef.EMBEDDING)\n\n    /**\n     * Searching text\n     */\n    class Searching(val text: String, val threshold: Double = 0.5) : PatternActionFunc(PatternActionFuncDef.SEARCHING)\n\n    /**\n     * Caching semantic\n     */\n    class Caching(val text: String) : PatternActionFunc(PatternActionFuncDef.CACHING)\n\n    /**\n     * Reranking the result\n     */\n    class Reranking(val strategy: String) : PatternActionFunc(PatternActionFuncDef.RERANKING)\n\n    /**\n     * The Redact class is designed for handling sensitive data by applying a specified redaction strategy.\n     *\n     * @param strategy The redaction strategy to be used. This string defines how the sensitive data will be handled or obscured.\n     */\n    class Redact(val strategy: String) : PatternActionFunc(PatternActionFuncDef.REDACT)\n\n    /**\n     * The Crawl function is used to crawl a list of urls, get markdown from html and save it to a file.\n     *\n     * @param urls The urls to crawl.\n     */\n    class Crawl(vararg val urls: String) : PatternActionFunc(PatternActionFuncDef.CRAWL)\n\n    /**\n     * The capture function used to capture file by NodeType\n     *\n     * @param fileName The file name to save the capture to.\n     * @param nodeType The node type to capture.\n     */\n    class Capture(val fileName: String, val nodeType: String) : PatternActionFunc(PatternActionFuncDef.CAPTURE)\n\n    /**\n     * The thread function will run the function in a new thread\n     *\n     * @param fileName The file name to run\n     */\n    class Thread(val fileName: String, val variableNames: Array<String>) :\n        PatternActionFunc(PatternActionFuncDef.THREAD)\n\n    /**\n     * The jsonpath function will parse the json and get the value by jsonpath\n     */\n    class JsonPath(val obj: String?, val path: String, val sseMode: Boolean = false) :\n        PatternActionFunc(PatternActionFuncDef.JSONPATH)\n\n    class Destroy : PatternActionFunc(PatternActionFuncDef.DESTROY)\n\n    class Batch(val fileName: String, val inputs: List<String>, val batchSize: Int = 1) :\n        PatternActionFunc(PatternActionFuncDef.BATCH)\n\n    class Tokenizer(var text: String, val tokType: String) : PatternActionFunc(PatternActionFuncDef.TOKENIZER)\n\n    /**\n     * Line Number\n     */\n    class LineNo(var text: String) : PatternActionFunc(PatternActionFuncDef.LINE_NO)\n\n    /**\n     * User Custom Functions\n     */\n    class ToolchainFunction(override val funcName: String, val args: List<String>) :\n        PatternActionFunc(PatternActionFuncDef.TOOLCHAIN_FUNCTION) {\n        override fun toString(): String {\n            return \"$funcName(${args.joinToString(\", \")})\"\n        }\n    }\n\n    companion object {\n        private val logger = logger<PatternActionFunc>()\n\n        fun findDocByName(funcName: String?): String? {\n            val actionFuncType = PatternActionFuncDef.entries.find { it.funcName == funcName } ?: return null\n            return \"\"\"\n                | ${actionFuncType.description}\n                | \n                | Example:\n                | ${actionFuncType.example}\n            \"\"\".trimMargin()\n        }\n\n        fun all(): List<PatternActionFuncDef> {\n            return PatternActionFuncDef.entries\n        }\n\n        fun from(funcName: String, args: List<String>): PatternActionFunc? {\n            return when (PatternActionFuncDef.entries.find { it.funcName == funcName }) {\n                PatternActionFuncDef.GREP -> {\n                    if (args.isEmpty()) {\n                        logger.error(\"PatternActionFun,`grep` func requires at least 1 argument\")\n                        return null\n                    }\n                    Grep(*args.toTypedArray())\n                }\n\n                PatternActionFuncDef.SORT -> Sort(*args.toTypedArray())\n\n                PatternActionFuncDef.FIND -> {\n                    if (args.isEmpty()) {\n                        logger.error(\"PatternActionFun,`find` func requires at least 1 argument\")\n                        return null\n                    }\n                    Find(args[0])\n                }\n\n                PatternActionFuncDef.SED -> {\n                    if (args.size < 2) {\n                        logger.error(\"PatternActionFun,`sed` func requires at least 2 arguments\")\n                        return null\n                    }\n                    if (args[0].startsWith(\"/\") && args[0].endsWith(\"/\")) {\n                        Sed(args[0], args[1], true)\n                    } else {\n                        Sed(args[0], args[1])\n                    }\n                }\n\n                PatternActionFuncDef.XARGS -> Xargs(*args.toTypedArray())\n\n                PatternActionFuncDef.UNIQ -> Uniq(*args.toTypedArray())\n\n                PatternActionFuncDef.HEAD -> {\n                    if (args.isEmpty()) {\n                        Head(10)\n                    } else {\n                        Head(args[0].toInt())\n                    }\n                }\n\n                PatternActionFuncDef.TAIL -> {\n                    if (args.isEmpty()) {\n                        Tail(10)\n                    } else {\n                        Tail(args[0].toInt())\n                    }\n                }\n\n                PatternActionFuncDef.PRINT -> Print(*args.toTypedArray())\n\n                PatternActionFuncDef.CAT -> Cat(*args.toTypedArray())\n\n                PatternActionFuncDef.EXECUTE -> {\n                    val first = args.firstOrNull() ?: \"\"\n                    val rest = args.drop(1).toTypedArray()\n                    Execute(first, rest)\n                }\n\n                PatternActionFuncDef.APPROVAL_EXECUTE -> {\n                    val first = args.firstOrNull() ?: \"\"\n                    val rest = args.drop(1).toTypedArray()\n                    ApprovalExecute(first, rest)\n                }\n\n                PatternActionFuncDef.NOTIFY -> {\n                    val first = args.firstOrNull() ?: \"\"\n                    Notify(first)\n                }\n\n                PatternActionFuncDef.EMBEDDING -> Embedding(args.toTypedArray())\n\n                PatternActionFuncDef.SPLITTING -> Splitting(args.toTypedArray())\n\n                PatternActionFuncDef.SEARCHING -> Searching(\n                    args[0],\n                    args.getOrNull(1)?.toDouble() ?: 0.5\n                )\n\n                PatternActionFuncDef.RERANKING -> {\n                    val first = args.firstOrNull() ?: \"default\"\n                    Reranking(first)\n                }\n\n                PatternActionFuncDef.CACHING -> Caching(args[0])\n\n                PatternActionFuncDef.REDACT -> {\n                    val first = args.firstOrNull() ?: \"default\"\n                    Redact(first)\n                }\n\n                PatternActionFuncDef.CRAWL -> {\n                    val urls: List<String> = args.filter { it.trim().isNotEmpty() }\n                    Crawl(*urls.toTypedArray())\n                }\n\n                PatternActionFuncDef.CAPTURE -> {\n                    if (args.size < 2) {\n                        logger.error(\"PatternActionFun,`capture` func requires at least 2 arguments\")\n                        return null\n                    }\n                    Capture(args[0], args[1])\n                }\n\n                PatternActionFuncDef.THREAD -> {\n                    if (args.isEmpty()) {\n                        logger.error(\"PatternActionFun,`thread` func requires at least 1 argument\")\n                        return null\n                    }\n                    val rest = args.drop(1).toTypedArray()\n                    Thread(args.first(), rest)\n                }\n\n                PatternActionFuncDef.JSONPATH -> {\n                    if (args.isEmpty()) {\n                        logger.error(\"PatternActionFun,`jsonpath` func requires at least 1 argument\")\n                        return null\n                    }\n                    if (args.size < 2) {\n                        JsonPath(null, args[0], false)\n                    } else {\n                        when (args[1]) {\n                            \"true\" -> JsonPath(null, args[0], true)\n                            else -> JsonPath(args[0], args[1])\n                        }\n                    }\n                }\n\n                PatternActionFuncDef.FROM,\n                PatternActionFuncDef.WHERE,\n                PatternActionFuncDef.SELECT,\n                PatternActionFuncDef.CASE_MATCH,\n                    -> {\n                    ToolchainFunction(funcName, args)\n                }\n\n                PatternActionFuncDef.BATCH -> {\n                    Batch(args[0], args.drop(1))\n                }\n\n                PatternActionFuncDef.DESTROY -> {\n                    Destroy()\n                }\n\n                PatternActionFuncDef.TOOLCHAIN_FUNCTION -> ToolchainFunction(funcName, args)\n                PatternActionFuncDef.TOKENIZER -> {\n                    if (args.isEmpty()) {\n                        logger.error(\"PatternActionFun,`tokenizer` func requires at least 1 argument\")\n                        return null\n                    }\n                    Tokenizer(args[0], args.getOrNull(1) ?: \"word\")\n                }\n                PatternActionFuncDef.LINE_NO -> {\n                    if (args.isEmpty()) {\n                        logger.error(\"PatternActionFun,`lineNo` func requires at least 1 argument\")\n                        return null\n                    }\n                    LineNo(args[0])\n                }\n\n                else -> {\n                    ToolchainFunction(funcName, args)\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/ast/patternaction/PatternActionFuncDef.kt",
    "content": "package com.phodal.shirelang.compiler.ast.patternaction\n\n/**\n * `PatternActionFuncType` was for show documentation when user hovers on the function.\n */\nenum class PatternActionFuncDef(val funcName: String, val description: String, val example: String) {\n    GREP(\n        \"grep\", \"`grep` function searches any given input files, selecting lines that match one or more patterns.\",\n        \"\"\"\n        | ```shire\n        | ---\n        | variables:\n        |   \"controllers\": /.*.java/ { cat | grep(\"class\\s+([a-zA-Z]*Controller)\")  }\n        | ---\n        | ```\n        \"\"\".trimMargin()\n    ),\n    FIND(\n        \"find\", \"`find` will find the first occurrence of a pattern in a file.\",\n        \"\"\"\n        | ```shire\n        | ---\n        | variables:\n        |   \"story\": /any/ { find(\"epic\") }\n        | ---\n        | ```\n        \"\"\".trimMargin()\n    ),\n    SED(\n        \"sed\", \"`sed` will replace text in a file.\",\n        \"\"\" \n        | ```shire\n        | ---\n        | variables:\n        |   \"var2\": /.*ple.shire/ { cat | find(\"openai\") | sed(\"(?i)\\b(sk-[a-zA-Z0-9]{20}T3BlbkFJ[a-zA-Z0-9]{20})(?:['|\\\"|\\n|\\r|\\s|\\x60|;]|${'$'})\", \"sk-***\") }\n        | ---\n        | ```\n    \"\"\".trimMargin()\n    ),\n    PRINT(\n        \"print\", \"`print` will print text or last output.\",\n        \"\"\" \n        | ```shire\n        | ---\n        | variables:\n        |   \"story\": /BlogController\\.java/ { print }\n        | ---\n        | ```\n        | \n        | Text content Example:\n        | \n        | ```shire\n        | ---\n        | variables:\n        |   \"story\": /any/ { print(\"hello world\") }\n        | ---  \n        | ```\n    \"\"\".trimMargin()\n    ),\n    CAT(\n        \"cat\", \"`cat` will concatenate one or more files.\",\n        \"\"\"\n        | ```shire\n        | ---\n        | variables:\n        |   \"story\": /BlogController\\.java/ { cat } // Paths can be absolute or relative to the current working directory.\n        | ---\n        | ```\n        | \n        | File path Example:\n        | \n        | ```shire\n        | ---\n        | variables:\n        |   \"story\": /any/ { cat(\"file.txt\") }\n        | ---  \n        | ```\n    \"\"\".trimMargin()\n    ),\n    EXECUTE(\n        \"execute\", \"`execute` will execute a new script，like `shell`, `bash`, `python`, `ruby` and `javascript`.\",\n        \"\"\"\n        | ```shire\n        | ---\n        | name: \"Search\"\n        | afterStreaming: { execute(\"search.shire\") }\n        | ---\n        | ```\n        | \n    \"\"\".trimMargin()\n    ),\n    APPROVAL_EXECUTE(\n        \"approvalExecute\", \"`approvalExecute` will show a dialog to confirm is execute to next job.\",\n        \"\"\"\n        | ```shire\n        | ---\n        | name: \"Search\"\n        | afterStreaming: { approvalExecute(\"search.shire\") }\n        | ---\n        | ```\n        | \n    \"\"\".trimMargin()\n    ),\n    NOTIFY(\n        \"notify\", \"`notify` will use IDEA's notification system to display a message.\",\n        \"\"\"\n        | ```shire\n        | ---\n        | name: \"Search\"\n        | afterStreaming: { notify(\"Failed to Generate JSON\") }\n        | ---\n        | ```\n        | \n    \"\"\".trimMargin()\n    ),\n    CASE_MATCH(\"switch\", \"TODO, not implemented yet.\", \"\"),\n    SPLITTING(\n        \"splitting\", \"`splitting` (RAG function) will split the file into chunks.\",\n        \"\"\"\n        | ```shire\n        | ---\n        | variables:\n        |   \"testTemplate\": /.*.kt/ { caching(\"disk\") | splitting | embedding }\n        | ---\n        | ```\n        | \n        | Support format: code, txt, pdf, html, doc, xls, ppt, md.\n        \"\"\".trimMargin()\n    ),\n    EMBEDDING(\n        \"embedding\", \"`embedding` (RAG function) will embedding text.\",\n        \"\"\"\n        | ```shire\n        | ---\n        | variables:\n        |  \"testTemplate\": /.*.kt/ { caching(\"disk\") | splitting | embedding }\n        | ---\n        | ```\n    \"\"\".trimMargin()\n    ),\n    SEARCHING(\n        \"searching\", \" `searching` (RAG function) will search embedding text.\",\n        \"\"\"\n        | ```shire\n        | ---\n        | variables:\n        |   \"story\": /any/ { caching(\"disk\") | splitting | embedding | searching(\"epic\") }\n        | ---\n        | ```\n        | with threshold:\n        |\n        | ```shire\n        | ---\n        | variables:\n        |  \"story\": /any/ { caching(\"disk\") | splitting | embedding | searching(\"epic\", 0.5) }\n        | ---\n        | ```\n        | \n        \"\"\".trimMargin()\n    ),\n    CACHING(\n        \"caching\",\n        \"\"\"`caching` (RAG function) will cache the semantic. support \"disk\" and \"memory\", default is \"memory\".\"\"\",\n        \"\"\"\n        | ```shire\n        | ---\n        | variables:\n        |  \"testTemplate\": /.*.kt/ { caching(\"disk\") }\n        |  \"story\": /any/ { caching(\"memory\") }\n        | ---\n        | ```\n        |  \n    \"\"\".trimMargin()\n    ),\n    RERANKING(\n        \"reranking\",\n        \"`reranking` (RAG function) will rerank the result. current only support \\\"Lost In Middle\\\" pattern\",\n        \"\"\"\n        | ```shire\n        | ---\n        | variables:\n        |    \"story\": /any/ {  caching(\"disk\") | splitting | embedding | reranking }\n        | ---\n        | ```\n        | \n    \"\"\".trimMargin()\n    ),\n    REDACT(\n        \"redact\", \"`redact` will handling sensitive data by applying a specified redaction strategy.\",\n        \"\"\"\n        | ```shire\n        | ---\n        | variables:\n        |   \"phoneNumber\": \"086-1234567890\"\n        |   \"var2\": /.*ple.shire/ { cat | redact }\n        | ---\n        | ```    \n    \"\"\".trimMargin()\n    ),\n    CRAWL(\n        \"crawl\", \"`crawl` will crawl a list of urls, get markdown from html and save it to a file.\",\n        \"\"\"\n        | ```shire\n        | ---\n        | variables:\n        |   \"websites\": /*\\.md/ { capture(\"docs/crawlSample.md\", \"link\") | crawl() | thread(\"summary.shire\") }\n        |   \"confluence\": { thread(\"confluence.bash\", param1, param2) }\n        |   \"pythonNode.js\": { thread(\"python.py\", param1, param2) }  \n        | ---\n        | ```\n        | \n    \"\"\".trimMargin()\n    ),\n    CAPTURE(\n        \"capture\", \"`capture` function used to capture url link by NodeType, support Markdown only for now.\",\n        \"\"\"\n        | ```shire\n        | ---\n        | variables:\n        |   \"websites\": /*\\.md/ { capture(\"docs/crawlSample.md\", \"link\") | crawl() | thread(\"summary.shire\") }\n        |   \"confluence\": { thread(\"confluence.bash\", param1, param2) }\n        |   \"pythonNode.js\": { thread(\"python.py\", param1, param2) }  \n        | ---\n        | ```\n        | \n    \"\"\".trimMargin()\n    ),\n    THREAD(\n        \"thread\", \"`thread` will run the function in a new thread\",\n        \"\"\" \n        | ```shire\n        | ---\n        | variables:\n        |   \"story\": /any/ { thread(\".shire/shell/dify-epic-story.curl.sh\") | jsonpath(\"${'$'}.answer\", true) }\n        | ---\n        | ```\n        | \n        | With shire file Example:\n        | \n        | ```shire\n        | ---\n        | variables:\n        |  \"story\": /any/ { thread(\"dify-epic-story.shire\") }\n        | ---\n        | ```  \n        \"\"\".trimMargin()\n    ),\n    JSONPATH(\n        \"jsonpath\", \"`jsonpath` function will parse the json and get the value by jsonpath.\",\n        \"\"\"\n        | ```shire\n        | ---\n        | variables:\n        |   \"api\": /sampl.sh/ { thread(\".shire/toolchain/bigmodel.curl.sh\") | jsonpath(\"${'$'}.choices[0].message.content\") }\n        | ---\n        | ```\n        | \n        | With SSE Example:\n        | ```shire\n        | ---\n        | variables:\n        |   \"story\": /any/ { thread(\".shire/shell/dify-epic-story.curl.sh\") | jsonpath(\"${'$'}.answer\", true) }\n        | ---\n        | ```\n        | \n        | \"\"\".trimMargin()\n    ),\n    SORT(\"sort\", \"`sort` will sorting with one or more arguments.\", \"\"),\n    UNIQ(\"uniq\", \"`uniq` will removing duplicates based on one or more arguments.\", \"\"),\n    HEAD(\n        \"head\", \"`head` will retrieving the first few lines.\",\n        \"\"\"\n        | ```shire\n        | ---\n        | variables:\n        |   \"controllers\": /.*.java/ { find(\"Controller\") | grep(\"src/main/java/.*\") | head(1)  | cat }\n        | ---\n        | ```\n        \"\"\".trimMargin()\n    ),\n    TAIL(\"tail\", \"`tail` will retrieving the last few lines.\", \"\"),\n    XARGS(\"xargs\", \"`xargs` will processing one or more variables.\", \"\"),\n    FROM(\"from\", \"`select` (ShireQL) will selecting one or more elements.\", \"\"),\n    WHERE(\"where\", \"`where` (ShireQL) will filtering elements.\", \"\"),\n    SELECT(\"select\", \"`select` (ShireQL) will select element.\", \"\"),\n    BATCH(\n        \"batch\", \"`batch` will execute a batch Shire script.\",\n        \"\"\"\n        | ```shire\n        | ---\n        | name: \"Generate Swagger Doc\"\n        | variables:\n        |   \"controllers\": /BlogController.java/ { cat }\n        |   \"gen-swagger\": /any/ { batch(\"controller-with-swagger.shire\", ${\"$\"}controllers, 2) }\n        | ---\n        | \n        | ```\n        \"\"\".trimMargin()\n    ),\n\n    DESTROY(\"destroy\", \"Destroy the current task.\", \"\"),\n\n    TOOLCHAIN_FUNCTION(\n        \"toolchain\",\n        \"Toolchain functions are defined by the different IDEA plugins. Supports JetBrains' plugin: Database, Shell Script plugin etc, Community plugin: SonarLint etc.\",\n        \"\"\"\n        | \n        | \n        | Database Plugin Example:\n        | \n        | ```shire\n        | ---\n        | variables:\n        |   \"relatedTableInfo\": /./ { column(\"user\", \"post\", \"tag\") }\n        | ---\n        | ```\n        | \n        | For more goto: [https://shire.phodal.com/shire/shire-toolchain-function](https://shire.phodal.com/shire/shire-toolchain-function)\n        \"\"\".trimMargin()\n    ),\n\n    TOKENIZER(\n        \"tokenizer\",\n        \"`tokenizer` tokenizes text using traditional NLP methods. Support type for : \" + \"`word`, `naming`, `stopwords`，`jieba`.\",\n        \"\"\"\n        | ```shire\n        | ---\n        | variables:\n        |   \"controllers\": /.*.java/ { cat }\n        |   \"tokens\": /any/ { tokenizer(${'$'}controllers, \"word\") }\n        | ---\n        | ```\n        | \n        | Output Example: package, com, phodal, shirelang, controller, import, org, springframework, web, bind, ...\n        | \n        \"\"\".trimMargin()\n    ),\n\n    LINE_NO(\n        \"lineNo\",\n        \"`lineNo` will add line number to the text.\",\n        \"\"\"\n        | ```shire\n        | ---\n        | variables:\n        |   \"controllers\": /.*.java/ { cat | lineNo }\n        | ---\n        | ```\n        | \n        | Output Example: 1: package com.phodal.shirelang.controller; 2: import org.springframework.web.bind.annotation.GetMapping; 3: import org.springframework.web.bind.annotation.RestController; 4: import org.springframework.web.bind.annotation.RequestMapping; 5: import org.springframework.web.bind.annotation.RequestParam;\n        | \n        \"\"\".trimMargin()\n    )\n    ;\n\n    override fun toString(): String = description\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/ast/patternaction/PatternProcessor.kt",
    "content": "package com.phodal.shirelang.compiler.ast.patternaction\n\ninterface PatternProcessor {\n    val type: PatternActionFuncDef\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/ast/patternaction/VariableTransform.kt",
    "content": "package com.phodal.shirelang.compiler.ast.patternaction\n\n/**\n * The `PatternActionTransform` class is a utility class in Kotlin that is used to perform various actions based on a provided pattern and action.\n * The class takes a pattern of type `String` and an action of type `PatternAction` as parameters.\n *\n * @property variable This property represents the pattern to be used for the transformation.\n * @property patternActionFuncs This property represents the action to be performed on the input.\n *\n * @constructor This constructor creates a new instance of `PatternActionTransform` with the specified pattern and action.\n *\n * The `execute` function is a member function of this class which is used to perform the action on the input and return the result as a `String`.\n *\n * @param variable This parameter represents the input on which the action is to be performed.\n * @return The result of the action performed on the input as a `String`.\n *\n * The `execute` function iterates over each action in `patternActionFuncs` and performs the corresponding action on the input.\n * The result of each action is stored in the `result` variable which is initially set to the input.\n * The type of action to be performed is determined using a `when` statement that checks the type of each action.\n * The result of the `execute` function is the final value of the `result` variable converted to a `String`.\n *\n * @see PatternAction\n */\nclass VariableTransform(\n    val variable: String,\n    val pattern: String,\n    val patternActionFuncs: List<PatternActionFunc>,\n    val isQueryStatement: Boolean = false\n) {\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/execute/FunctionStatementProcessor.kt",
    "content": "package com.phodal.shirelang.compiler.execute\n\nimport com.intellij.openapi.application.runReadAction\nimport com.intellij.openapi.diagnostic.logger\nimport com.intellij.openapi.project.Project\nimport com.intellij.psi.PsiElement\nimport com.nfeld.jsonpathkt.JsonPath\nimport com.nfeld.jsonpathkt.extension.read\nimport com.phodal.shirecore.variable.vcs.ShireGitCommit\nimport com.phodal.shirelang.compiler.ast.*\nimport com.phodal.shirelang.compiler.ast.hobbit.HobbitHole\nimport com.phodal.shirelang.compiler.execute.shireql.ShireDateSchema\nimport com.phodal.shirelang.compiler.execute.shireql.ShireQLSchema\nimport com.phodal.shirelang.compiler.execute.variable.ShireQLVariableBuilder\nimport com.phodal.shirelang.compiler.execute.variable.VariableContainerManager\nimport com.phodal.shirelang.compiler.ast.patternaction.PatternActionFunc\nimport com.phodal.shirelang.compiler.ast.patternaction.VariableTransform\nimport kotlinx.coroutines.runBlocking\n\n/**\n * The `FunctionStatementProcessor` class is responsible for processing function statements within a project context.\n * It extends the `PatternFuncProcessor` class, which is part of a larger framework likely dealing with pattern matching and processing within a domain-specific language or a scripting environment.\n *\n * This class operates on statements that can be comparisons, processor invocations, or method calls, and it manages a variable table to keep track of variable values during execution.\n *\n * @property myProject The project in which the processing occurs, extending the functionality of the base class `PatternFuncProcessor`.\n * @property hole The hobbit hole, which seems to be a metaphorical representation of a context or a scope, also extending the base class functionality.\n *\n * ### Methods:\n *\n * This class uses the Kotlin `runBlocking` coroutine scope to handle asynchronous operations and may throw an `IllegalArgumentException` for unknown types during evaluation.\n */\nopen class FunctionStatementProcessor(override val myProject: Project, override val hole: HobbitHole) :\n    PatternFuncProcessor(myProject, hole) {\n    open fun execute(transform: VariableTransform): String {\n        val fromStmt = transform.patternActionFuncs.find { it is PatternActionFunc.From } as PatternActionFunc.From\n        val selectStmt =\n            transform.patternActionFuncs.find { it is PatternActionFunc.Select } as PatternActionFunc.Select\n        val whereStmt = transform.patternActionFuncs.find { it is PatternActionFunc.Where } as PatternActionFunc.Where\n\n        val variableElementsMap: Map<String, List<Any>> = runReadAction {\n            ShireQLVariableBuilder(myProject, hole).buildVariables(fromStmt)\n        }\n\n        val valuedType = evalValueByElementStmt(variableElementsMap, whereStmt.statement)\n\n        val handledElements = processStatement(whereStmt.statement, variableElementsMap, valuedType)\n        val selectElements = processSelect(selectStmt, handledElements)\n\n        return selectElements.joinToString(\"\\n\")\n    }\n\n    fun processSelect(selectStmt: PatternActionFunc.Select, handledElements: List<Any>): List<String> {\n        return selectStmt.statements.flatMap {\n            processSelectStatement(it, handledElements)\n        }\n    }\n\n    private fun processSelectStatement(statement: Statement, handledElements: List<Any>): List<String> {\n        val result = mutableListOf<String>()\n        handledElements.forEach { element ->\n            when (element) {\n                is PsiElement -> {\n                    when (statement) {\n                        is Value -> {\n                            result.add(statement.display())\n                        }\n\n                        is MethodCall -> {\n                            invokeMethodOrField(statement, element)?.let {\n                                result.add(it.toString())\n                            }\n                        }\n                    }\n                }\n\n                is ShireGitCommit -> {\n                    when (statement) {\n                        is Value -> {\n                            result.add(statement.display())\n                        }\n\n                        is MethodCall -> {\n                            invokeMethodOrField(statement, element)?.let {\n                                result.add(it.toString())\n                            }\n                        }\n                    }\n                }\n\n                is ShireDateSchema -> {\n                    when (statement) {\n                        is Value -> {\n                            invokeMethod(element, null)?.let {\n                                result.add(it.toString())\n                            }\n                        }\n\n                        is MethodCall -> {\n                            val methodName = statement.methodName.display()\n                            val args = statement.arguments\n                            invokeMethod(element, methodName, args)?.let {\n                                result.add(it.toString())\n                            }\n                        }\n                    }\n                }\n\n                else -> {\n                    logger<FunctionStatementProcessor>().error(\"unknown element: $element\")\n                }\n            }\n        }\n\n        return result\n    }\n\n    fun execute(statement: Statement, variableTable: MutableMap<String, Any?>): Any? = runBlocking {\n        return@runBlocking when (statement) {\n            is Comparison -> {\n                executeComparison(statement, variableTable)\n            }\n\n            is Processor -> {\n                execute(statement.processors, variableTable)\n            }\n\n            is MethodCall -> {\n                invokeLocalMethodCall(statement, variableTable)\n            }\n\n            is Value -> {\n                statement.value\n            }\n\n            else -> {\n                logger<FunctionStatementProcessor>().error(\"unknown stmt: $statement expr: ${statement.display()}\")\n                null\n            }\n        }\n    }\n\n    private fun invokeLocalMethodCall(statement: MethodCall, variableTable: MutableMap<String, Any?>): Any? {\n        val objName = statement.objectName.display()\n        val methodName = statement.methodName.display()\n        val methodArgs = statement.arguments\n\n        if (methodName == \"\") {\n            val firstArg = methodArgs?.get(0)\n            when (objName) {\n                \"jsonpath\" -> {\n                    val output = (variableTable[\"output\"] ?: \"\").toString()\n                    val arg: String = when (firstArg) {\n                        is FrontMatterType.STRING -> (methodArgs[0] as FrontMatterType.STRING).value.toString()\n                        else -> firstArg.toString()\n                    }\n                    val string: String = try {\n                        JsonPath.parse(output)?.read<Any>(arg).toString()\n                    } catch (e: Exception) {\n                        logger<FunctionStatementProcessor>().warn(\"jsonpath error: $e\")\n                        return null\n                    }\n\n                    return string\n                }\n\n                \"print\" -> {\n                    println(firstArg)\n                }\n\n                else -> {\n                    logger<FunctionStatementProcessor>().warn(\"unknown method: $objName\")\n                }\n            }\n        }\n\n        return null\n    }\n\n    suspend fun execute(processors: List<PatternActionFunc>, variableTable: MutableMap<String, Any?>): Any? {\n        val input: Any = variableTable[\"output\"] ?: \"\"\n        var result: Any = variableTable[\"output\"] ?: \"\"\n\n        var lastOutput: Any? = result\n\n        processors.forEach { action ->\n            result = patternFunctionExecute(action, result, input, variableTable)\n\n            if (action.funcName == \"execute\") {\n                if (lastOutput != null) {\n                    result = lastOutput as Any\n                }\n            }\n\n            lastOutput = result\n            variableTable[\"output\"] = result\n        }\n\n        return result.toString()\n    }\n\n    private fun FunctionStatementProcessor.executeComparison(\n        statement: Comparison,\n        value: Any,\n    ): Boolean {\n        val operator = statement.operator\n        val left = evaluate(statement.left, value)\n        val right = evaluate(statement.right, value)\n\n        return when (operator.type) {\n            OperatorType.Equal -> left == right\n            OperatorType.And -> left == right\n            OperatorType.NotEqual -> left != right\n            OperatorType.Or -> left == true || right == true\n\n            OperatorType.GreaterEqual -> {\n                if (left == null || right == null) {\n                    false\n                } else {\n                    left as Comparable<Any> >= right as Comparable<Any>\n                }\n            }\n\n            OperatorType.GreaterThan -> {\n                if (left == null || right == null) {\n                    false\n                } else {\n                    left as Comparable<Any> > right as Comparable<Any>\n                }\n            }\n\n            OperatorType.LessEqual -> {\n                if (left == null || right == null) {\n                    false\n                } else {\n                    left as Comparable<Any> <= right as Comparable<Any>\n                }\n            }\n\n            OperatorType.LessThan -> {\n                if (left == null || right == null) {\n                    false\n                } else {\n                    left as Comparable<Any> < right as Comparable<Any>\n                }\n            }\n\n            else -> {\n                logger<FunctionStatementProcessor>().warn(\"unknown operator: $operator\")\n                false\n            }\n        }\n    }\n\n    /// todo: fix this for multiple variables\n    private fun processStatement(\n        statement: Statement,\n        variableElementsMap: Map<String, List<Any>>,\n        valuedType: VariableContainerManager,\n    ): List<Any> {\n        val result = mutableListOf<Any>()\n        variableElementsMap.forEach { (variableName, elements) ->\n            elements.forEach { element ->\n                when (statement) {\n                    is Comparison -> {\n                        val operator = statement.operator\n                        /// for commit.authorDate <= date.now()\n                        /// if we use element (Date) for commit.authorDate, will be null, should use element (ShireGitCommit)\n                        val left = valuedType.getValue(element, statement.left) ?: evaluate(statement.right, element)\n                        val right = valuedType.getValue(element, statement.right) ?: evaluate(statement.right, element)\n\n                        if (left == null) {\n                            logger<FunctionStatementProcessor>().warn(\"left is null: ${statement.left.display()}\")\n                            return@forEach\n                        }\n\n                        if (right == null) {\n                            logger<FunctionStatementProcessor>().warn(\"right is null: ${statement.right.display()}\")\n                            return@forEach\n                        }\n\n                        when (operator.type) {\n                            OperatorType.Equal -> if (left == right) result.add(element)\n                            OperatorType.And -> if (left == right) result.add(element)\n                            OperatorType.NotEqual -> if (left != right) result.add(element)\n                            OperatorType.Or -> if (left == true || right == true) result.add(element)\n                            OperatorType.GreaterEqual -> {\n                                if (left as Comparable<Any> >= right as Comparable<Any>) {\n                                    result.add(element)\n                                }\n                            }\n\n                            OperatorType.GreaterThan -> {\n                                if (left as Comparable<Any> > right as Comparable<Any>) {\n                                    result.add(element)\n                                }\n                            }\n\n                            OperatorType.LessEqual -> {\n                                if (left as Comparable<Any> <= right as Comparable<Any>) {\n                                    result.add(element)\n                                }\n                            }\n\n                            OperatorType.LessThan -> {\n                                if (left as Comparable<Any> < right as Comparable<Any>) {\n                                    result.add(element)\n                                }\n                            }\n\n                            else -> {\n                                logger<FunctionStatementProcessor>().warn(\"unknown operator: $operator\")\n                            }\n                        }\n                    }\n\n                    is MethodCall -> {\n                        when (val output = invokeMethodOrField(statement, element)) {\n                            is Collection<*> -> {\n                                output.forEach {\n                                    if (it is Any) {\n                                        result.add(it)\n                                    }\n                                }\n                            }\n\n                            is Any -> {\n                                result.add(output)\n                            }\n                        }\n                    }\n\n                    is LogicalExpression -> {\n                        val left = processStatement(statement.left, variableElementsMap, valuedType)\n                        val right = processStatement(statement.right, variableElementsMap, valuedType)\n\n                        when (statement.operator) {\n                            OperatorType.And -> {\n                                if (left.isNotEmpty() && right.isNotEmpty()) {\n                                    result.add(element)\n                                }\n                            }\n\n                            OperatorType.Or -> {\n                                if (left.isNotEmpty() || right.isNotEmpty()) {\n                                    result.add(element)\n                                }\n                            }\n\n                            else -> {\n                                logger<FunctionStatementProcessor>().warn(\"unknown operator: ${statement.operator}\")\n                            }\n                        }\n                    }\n\n                    else -> {\n                        logger<FunctionStatementProcessor>().warn(\"unknown statement: ${statement.display()}\")\n                    }\n                }\n            }\n        }\n\n        return result\n    }\n\n    /// a dirty implementation for multiple variables\n    private fun evalValueByElementStmt(\n        variableElementsMap: Map<String, List<Any>>,\n        statement: Statement,\n    ): VariableContainerManager {\n        val typeValued = VariableContainerManager()\n        variableElementsMap.forEach { (variableName, elements) ->\n            elements.forEach { element ->\n                when (statement) {\n                    is Comparison -> {\n                        evaluateComparison(element, typeValued, statement.left, statement.right)\n                    }\n\n                    is LogicalExpression -> {\n                        val left = evalValueByElementStmt(variableElementsMap, statement.left)\n                        val right = evalValueByElementStmt(variableElementsMap, statement.right)\n\n                        when (statement.operator) {\n                            OperatorType.And -> {\n                                if (left.isNotEmpty() && right.isNotEmpty()) {\n                                    left.variables.forEach { evaluatorEntry ->\n                                        evaluatorEntry.value.valued.forEach { (key, value) ->\n                                            if (value != null) {\n                                                typeValued.putValue(element, key, value)\n                                            }\n                                        }\n                                    }\n                                    right.variables.forEach { evaluatorEntry ->\n                                        evaluatorEntry.value.valued.forEach { (key, value) ->\n                                            if (value != null) {\n                                                typeValued.putValue(element, key, value)\n                                            }\n                                        }\n                                    }\n                                }\n                            }\n\n                            OperatorType.Or -> {\n                                if (left.isNotEmpty() || right.isNotEmpty()) {\n                                    left.variables.forEach { evaluatorEntry ->\n                                        evaluatorEntry.value.valued.forEach { (key, value) ->\n                                            if (value != null) {\n                                                typeValued.putValue(element, key, value)\n                                            }\n                                        }\n                                    }\n                                    right.variables.forEach { evaluatorEntry ->\n                                        evaluatorEntry.value.valued.forEach { (key, value) ->\n                                            if (value != null) {\n                                                typeValued.putValue(element, key, value)\n                                            }\n                                        }\n                                    }\n                                }\n                            }\n\n                            else -> {\n                                logger<FunctionStatementProcessor>().warn(\"unknown operator: ${statement.operator}\")\n                            }\n                        }\n                    }\n\n                    else -> {\n                        logger<FunctionStatementProcessor>().warn(\"unknown statement: ${statement.display()}\")\n                    }\n                }\n            }\n        }\n\n        return typeValued\n    }\n\n    private fun evaluateComparison(\n        element: Any,\n        typeValued: VariableContainerManager,\n        leftStmt: FrontMatterType,\n        rightStmt: FrontMatterType,\n    ) {\n        if (element is ShireQLSchema) {\n            val left = evaluate(leftStmt, element)\n            if (left != null) {\n                typeValued.putValue(element, leftStmt, left)\n            } else {\n                val right = evaluate(rightStmt, element)\n                if (right != null) {\n                    typeValued.putValue(element, rightStmt, right)\n                }\n            }\n\n            return\n        }\n\n        val left = evaluate(leftStmt, element)\n        if (left != null) {\n            typeValued.putValue(element, leftStmt, left)\n        } else {\n            val right = evaluate(rightStmt, element)\n            if (right != null) {\n                typeValued.putValue(element, rightStmt, right)\n            }\n        }\n    }\n\n\n    fun <T : Any> evaluate(type: FrontMatterType, element: T): Any? {\n        return when (type) {\n            is FrontMatterType.ARRAY -> {\n                (type.value as List<FrontMatterType>).map {\n                    evaluate(it, element)\n                }\n            }\n\n            is FrontMatterType.EXPRESSION -> {\n                evalExpression(type, element)\n            }\n\n            is FrontMatterType.BOOLEAN,\n            is FrontMatterType.DATE,\n            is FrontMatterType.IDENTIFIER,\n            is FrontMatterType.STRING,\n            -> {\n                type.value\n            }\n\n            is FrontMatterType.NUMBER -> {\n                (type.value as Int).toLong()\n            }\n\n            else -> {\n                throw IllegalArgumentException(\"unknown type: $type\")\n            }\n        }\n    }\n\n    open fun <T : Any> evalExpression(type: FrontMatterType, element: T): Any? {\n        when (type.value) {\n            is MethodCall -> {\n                return invokeMethodOrField(type.value, element)\n            }\n\n            else -> {\n                throw IllegalArgumentException(\"unknown type: $type\")\n            }\n        }\n    }\n\n    open fun <T : Any> invokeMethodOrField(methodCall: MethodCall, element: T): Any? {\n        val objName = methodCall.objectName.display()\n        when (element) {\n            is Map<*, *> -> {\n                val variable = element[objName] as? String ?: return null\n                return methodCall.evaluateExpression(methodCall.methodName, listOf(), variable)\n            }\n\n            else -> return null\n        }\n    }\n\n    open fun invokeMethod(element: ShireDateSchema, methodName: String?, args: List<Any>? = null): Any? {\n        val allMethods = element.javaClass.methods\n        val method = allMethods.find {\n            it.name == methodName\n        }\n        if (method != null) {\n            return method.invoke(element)\n        }\n\n        return element.toString()\n    }\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/execute/PatternActionProcessor.kt",
    "content": "package com.phodal.shirelang.compiler.execute\n\nimport com.intellij.openapi.project.Project\nimport com.phodal.shirecore.middleware.post.PostProcessorContext\nimport com.phodal.shirelang.compiler.ast.hobbit.HobbitHole\nimport com.phodal.shirelang.compiler.ast.patternaction.VariableTransform\nimport com.phodal.shirelang.compiler.execute.shireql.ShireQLProcessor\nimport com.phodal.shirelang.debugger.snapshot.VariableSnapshotRecorder\n\npublic class PatternActionProcessor(\n    override val myProject: Project,\n    override val hole: HobbitHole,\n    private val variableMap: MutableMap<String, Any?>\n) :\n    PatternFuncProcessor(myProject, hole) {\n\n    private val record: VariableSnapshotRecorder = VariableSnapshotRecorder.getInstance(myProject)\n\n\n    /**\n     * We should execute the variable function with the given key and pipeline functions.\n     *\n     * Each function output will be the input of the next function.\n     */\n    suspend fun execute(actionTransform: VariableTransform): String {\n        if (actionTransform.patternActionFuncs.isEmpty()) {\n            return \"\"\n        }\n\n        if (actionTransform.isQueryStatement) {\n            return ShireQLProcessor(myProject, hole).execute(actionTransform)\n        }\n\n        var input: Any = \"\"\n        if (actionTransform.pattern.isNotBlank() && actionTransform.pattern != \"any\" && actionTransform.pattern != \"null\") {\n            input = com.phodal.shirelang.compiler.execute.searcher.PatternSearcher.findFilesByRegex(myProject, actionTransform.pattern)\n                .map { it.path }\n                .toTypedArray()\n        }\n\n        return execute(actionTransform, input)\n    }\n\n    /**\n     * This method is used to execute a series of transformations on the input based on the provided PatternActionTransform.\n     * The transformations are applied in the order they are defined in the PatternActionTransform.\n     * The input can be of any type, but the transformations are applied as if the input is a String.\n     * If the input is not a String, it will be converted to a String before applying the transformations.\n     * The result of each transformation is used as the input for the next transformation.\n     * If the transformation is a Cat, the executeCatFunc method is called with the action and the original input.\n     * The result of the last transformation is returned as a String.\n     *\n     * @param transform The PatternActionTransform that defines the transformations to be applied.\n     * @param input The input on which the transformations are to be applied.\n     * @return The result of applying the transformations to the input as a String.\n     */\n    suspend fun execute(transform: VariableTransform, input: Any): String {\n        record.addSnapshot(transform.variable, input)\n\n        var result = input\n        val data = PostProcessorContext.getData()\n        if (data?.lastTaskOutput != null && data.lastTaskOutput != \"null\") {\n            if (variableMap[\"output\"] == null) {\n                variableMap[\"output\"] = data.lastTaskOutput\n            }\n        }\n\n        transform.patternActionFuncs.forEach { action ->\n            result = patternFunctionExecute(action, result, input, variableMap)\n            record.addSnapshot(transform.variable, result, action.funcName, result)\n        }\n\n        variableMap[transform.variable] = result\n        return result.toString()\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/execute/PatternFuncProcessor.kt",
    "content": "package com.phodal.shirelang.compiler.execute\n\nimport com.intellij.openapi.diagnostic.logger\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.project.guessProjectDir\nimport com.intellij.openapi.vfs.LocalFileSystem\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.intellij.openapi.vfs.findFile\nimport com.intellij.openapi.vfs.readText\nimport com.phodal.shirecore.ShirelangNotifications\nimport com.phodal.shirelang.compiler.execute.processor.RedactProcessor\nimport com.phodal.shirecore.provider.function.ToolchainFunctionProvider\nimport com.phodal.shirecore.search.function.ScoredText\nimport com.phodal.shirecore.search.function.SemanticService\nimport com.phodal.shirecore.workerThread\nimport com.phodal.shirelang.ShireBundle\nimport com.phodal.shirelang.compiler.ast.hobbit.HobbitHole\nimport com.phodal.shirelang.compiler.ast.FrontMatterType\nimport com.phodal.shirelang.compiler.ast.Statement\nimport com.phodal.shirelang.compiler.execute.processor.JsonPathProcessor\nimport com.phodal.shirelang.compiler.execute.processor.*\nimport com.phodal.shirelang.compiler.ast.patternaction.PatternActionFunc\nimport kotlinx.coroutines.CoroutineScope\nimport kotlinx.coroutines.launch\nimport java.io.File\n\nopen class PatternFuncProcessor(open val myProject: Project, open val hole: HobbitHole) {\n    private val logger = logger<PatternActionProcessor>()\n\n    /**\n     * This function `patternFunctionExecute` is used to execute a specific action based on the type of `PatternActionFunc` provided.\n     * It takes three parameters: `action`, `input`, and `lastResult`.\n     *\n     * @param action This is an instance of `PatternActionFunc` which is a sealed class. The function behavior changes based on the type of `PatternActionFunc`.\n     * @param input This is a generic parameter which can be of any type. It is used in the `PatternActionFunc.Cat` case.\n     * @param lastResult This is a generic parameter which can be of any type. It is used in all cases except `PatternActionFunc.Prompt`, `PatternActionFunc.Cat`, `PatternActionFunc.`Print`` and `PatternActionFunc.Xargs`.\n     *\n     * @return The return type is `Any`. The actual return type depends on the type of `PatternActionFunc`. For example, if `PatternActionFunc` is `Prompt`, it returns a `String`. If `PatternActionFunc` is `Grep`, it returns a `String` joined by \"\\n\" from an `Array` or `String` that contains the specified patterns. If `PatternActionFunc` is `Sed`, it returns a `String` joined by \"\\n\" from an `Array` or `String` where the specified pattern has been replaced. If `PatternActionFunc` is `Sort`, it returns a sorted `String` joined by \"\\n\" from an `Array` or `String`. If `PatternActionFunc` is `Uniq`, it returns a `String` joined by \"\\n\" from an `Array` or `String` with distinct elements. If `PatternActionFunc` is `Head`, it returns a `String` joined by \"\\n\" from the first 'n' elements of an `Array` or `String`. If `PatternActionFunc` is `Tail`, it returns a `String` joined by \"\\n\" from the last 'n' elements of an `Array` or `String`. If `PatternActionFunc` is `Cat`, it executes the `executeCatFunc` function. If `PatternActionFunc` is `Print`, it returns a `String` joined by \"\\n\" from the `texts` property of `Print`. If `PatternActionFunc` is `Xargs`, it returns the `variables` property of `Xargs`. If `PatternActionFunc` is `UserCustom`, it logs an error message. If `PatternActionFunc` is of an unknown type, it logs an error message and returns an empty `String`.\n     */\n    open suspend fun patternFunctionExecute(\n        action: PatternActionFunc,\n        lastResult: Any,\n        input: Any,\n        variableTable: MutableMap<String, Any?> = mutableMapOf(),\n    ): Any {\n        val semanticService = myProject.getService(SemanticService::class.java)\n\n        return when (action) {\n            is PatternActionFunc.Find -> {\n                when (lastResult) {\n                    is Array<*> -> {\n                        (lastResult as Array<String>)\n                            .filter { line -> line.contains(action.text) }\n                            .toTypedArray()\n                    }\n\n                    else -> {\n                        (lastResult as String).split(\"\\n\")\n                            .filter { line -> line.contains(action.text) }\n                            .joinToString(\"\\n\")\n                    }\n                }\n            }\n\n            is PatternActionFunc.Grep -> {\n                val regexs = action.patterns.map { it.toRegex() }\n                when (lastResult) {\n                    is Array<*> -> {\n                        val inputArray = (lastResult as Array<String>)\n                        val result = regexs.map { regex ->\n                            inputArray.map { line ->\n                                regex.findAll(line)\n                                    .map {\n                                        if (it.groupValues.size > 1) {\n                                            it.groupValues[1]\n                                        } else {\n                                            it.groupValues[0]\n                                        }\n                                    }.toList()\n                            }.flatten()\n                        }.flatten()\n\n                        result.toTypedArray()\n                    }\n\n                    is String -> {\n                        val result = regexs.map { regex ->\n                            regex.findAll(lastResult)\n                                .map {\n                                    if (it.groupValues.size > 1) {\n                                        it.groupValues[1]\n                                    } else {\n                                        it.groupValues[0]\n                                    }\n                                }.toList()\n                        }.flatten().joinToString(\"\\n\")\n\n                        result\n                    }\n\n                    else -> {\n                        logger.error(\"Unknown pattern input for ${action.funcName}, lastResult: $lastResult\")\n                        \"\"\n                    }\n                }\n            }\n\n            is PatternActionFunc.Sed -> {\n                when (lastResult) {\n                    is Array<*> -> {\n                        (lastResult as Array<String>).joinToString(\"\\n\") { line ->\n                            line.replace(\n                                action.pattern.toRegex(),\n                                action.replacements\n                            )\n                        }\n                    }\n\n                    else -> {\n                        (lastResult as String).split(\"\\n\").joinToString(\"\\n\") { line ->\n                            line.replace(\n                                action.pattern.toRegex(),\n                                action.replacements\n                            )\n                        }\n                    }\n                }\n            }\n\n            is PatternActionFunc.Sort -> {\n                when (lastResult) {\n                    is Array<*> -> {\n                        (lastResult as Array<String>).sorted()\n                    }\n\n                    else -> {\n                        (lastResult as String).split(\"\\n\").sorted().joinToString(\"\\n\")\n                    }\n                }\n            }\n\n            is PatternActionFunc.Uniq -> {\n                when (lastResult) {\n                    is Array<*> -> {\n                        (lastResult as Array<String>).distinct()\n                    }\n\n                    else -> {\n                        (lastResult as String).split(\"\\n\").distinct().joinToString(\"\\n\")\n                    }\n                }\n            }\n\n            is PatternActionFunc.Head -> {\n                when (lastResult) {\n                    is Array<*> -> {\n                        (lastResult as Array<String>).take(action.number.toInt())\n                    }\n\n                    else -> {\n                        (lastResult as String).split(\"\\n\").take(action.number.toInt()).joinToString(\"\\n\")\n                    }\n                }\n            }\n\n            is PatternActionFunc.Tail -> {\n                when (lastResult) {\n                    is Array<*> -> {\n                        (lastResult as Array<String>).takeLast(action.number.toInt())\n                    }\n\n                    else -> {\n                        (lastResult as String).split(\"\\n\").takeLast(action.number.toInt()).joinToString(\"\\n\")\n                    }\n                }\n            }\n\n            is PatternActionFunc.Cat -> {\n                val path: Array<String> = action.paths.map { it.fillVariable(variableTable) }.toTypedArray()\n                cat(path, lastResult)\n            }\n\n            is PatternActionFunc.Print -> {\n                if (action.texts.isEmpty()) {\n                    return when (lastResult) {\n                        is Array<*> -> {\n                            (lastResult as Array<String>).joinToString(\"\\n\")\n                        }\n\n                        is List<*> -> {\n                            (lastResult as List<String>).joinToString(\"\\n\")\n                        }\n\n                        else -> {\n                            lastResult.toString()\n                        }\n                    }\n                }\n\n                action.texts.joinToString(\"\\n\") { it.fillVariable(variableTable) }\n            }\n\n            is PatternActionFunc.Xargs -> {\n                action.variables\n            }\n\n            is PatternActionFunc.ToolchainFunction -> {\n                /// maybe User custom functions\n                val args: MutableList<Any> = action.args.toMutableList()\n                /// add lastResult at args first\n                when (lastResult) {\n                    is String -> {\n                        args.add(lastResult.fillVariable(variableTable))\n                    }\n\n                    is List<*> -> {\n                        if (lastResult.isNotEmpty()) {\n                            args.add(lastResult)\n                        }\n                    }\n\n                    is Array<*> -> {\n                        if (lastResult.isNotEmpty()) {\n                            args.add(lastResult)\n                        }\n                    }\n\n                    else -> {\n                        args.add(lastResult)\n                    }\n                }\n\n                val result = args.map {\n                    when (it) {\n                        is String -> it.fillVariable(variableTable)\n                        else -> it\n                    }\n                }\n\n                if (hole.foreignFunctions.containsKey(action.funcName)) {\n                    val func = hole.foreignFunctions[action.funcName]!!\n                    return ForeignFunctionProcessor.execute(myProject, action.funcName, result, variableTable, func)\n                }\n\n                ToolchainFunctionProvider.provide(myProject, action.funcName)\n                    ?.execute(myProject, action.funcName, result, variableTable)\n                    ?: logger.error(ShireBundle.message(\"shire.toolchain.function.not.found\", action.funcName))\n            }\n\n            is PatternActionFunc.Notify -> {\n                // action.message is empty get lastResult\n                val message = action.message.ifEmpty {\n                    lastResult.toString()\n                }\n\n                ShirelangNotifications.info(myProject, message)\n                // return last result for next step\n                lastResult\n            }\n\n            is PatternActionFunc.From,\n            is PatternActionFunc.Select,\n            is PatternActionFunc.Where,\n                -> {\n                logger.error(\"Unknown pattern processor type: ${action.funcName}\")\n            }\n\n            is PatternActionFunc.CaseMatch -> {\n                val actions = evaluateCase(action, input) ?: return \"\"\n                FunctionStatementProcessor(myProject, hole)\n                    .execute(actions.value as Statement, mutableMapOf(\"output\" to parseInput(input)))\n                    .toString()\n            }\n\n            is PatternActionFunc.Embedding -> {\n                var result: List<ScoredText> = mutableListOf()\n                if (lastResult is List<*>) {\n                    result = if (lastResult.isNotEmpty() && lastResult.first() is ScoredText) {\n                        semanticService.embedding(lastResult as List<ScoredText>)\n                    } else {\n                        semanticService.embedList(action.entries)\n                    }\n                }\n\n\n                result\n            }\n\n            is PatternActionFunc.Splitting -> {\n                semanticService.splitting(resolvePaths(action.paths, input))\n            }\n\n            is PatternActionFunc.Searching -> {\n                semanticService.searching(action.text, action.threshold)\n            }\n\n            is PatternActionFunc.Caching -> {\n                semanticService.configCache(action.text)\n            }\n\n            is PatternActionFunc.Reranking -> {\n                semanticService.reranking(action.strategy)\n            }\n\n            is PatternActionFunc.Redact -> {\n                RedactProcessor.execute(myProject, lastResult)\n            }\n\n            is PatternActionFunc.Crawl -> {\n                val urls: MutableList<String> = mutableListOf()\n                if (action.urls.isEmpty()) {\n                    when (lastResult) {\n                        is ArrayList<*> -> {\n                            (lastResult as ArrayList<String>).forEach {\n                                urls.add(it)\n                            }\n                        }\n\n                        is String -> {\n                            lastResult.split(\"\\n\").forEach {\n                                urls.add(it)\n                            }\n                        }\n\n                        else -> {\n                            logger.warn(\"crawl error: $lastResult\")\n                        }\n                    }\n                } else {\n                    urls.addAll(action.urls)\n                }\n\n                val finalUrls = urls.map { it.trim() }.filter { it.isNotEmpty() }\n                CrawlProcessor.execute(finalUrls.toTypedArray())\n            }\n\n            is PatternActionFunc.Capture -> {\n                CaptureProcessor.execute(myProject, action.fileName, action.nodeType)\n            }\n\n            is PatternActionFunc.Execute -> {\n                /// don't need to fill variable for filename\n                val variableNames: Array<String> = action.variableNames.map {\n                    if (it.startsWith(\"\\$\")) {\n                        it.substring(1)\n                    } else {\n                        it\n                    }\n                }.toTypedArray()\n\n                ExecuteProcessor.execute(myProject, action.filename, variableNames, variableTable)\n            }\n\n            is PatternActionFunc.ApprovalExecute -> {\n                val variableNames: Array<String> = action.variableNames.map {\n                    if (it.startsWith(\"\\$\")) {\n                        it.substring(1)\n                    } else {\n                        it\n                    }\n                }.toTypedArray()\n\n                ApprovalExecuteProcessor.execute(myProject, action.filename, variableNames, variableTable,\n                    approve = {\n                        CoroutineScope(workerThread).launch {\n                            ExecuteProcessor.execute(myProject, action.filename, variableNames, variableTable)\n                        }\n                    })\n            }\n\n            is PatternActionFunc.Batch -> {\n                val inputs: List<String> = action.inputs.map { input ->\n                    if (input.startsWith(\"\\$\")) {\n                        when (val variable = variableTable[input.substring(1)]) {\n                            is String -> listOf(variable.toString())\n                            is List<*> -> variable.map(Any?::toString)\n                            is Array<*> -> variable.map(Any?::toString)\n                            else -> listOf()\n                        }\n                    } else {\n                        listOf(input)\n                    }\n                }.flatten()\n\n                BatchProcessor.execute(myProject, action.fileName, inputs, action.batchSize, variableTable)\n            }\n\n            is PatternActionFunc.Thread -> {\n                val varNames = action.variableNames.toMutableList().apply {\n                    if (!contains(\"output\")) {\n                        add(\"output\")\n                    }\n                }.map {\n                    it.fillVariable(variableTable)\n                }.toTypedArray()\n\n                if (!variableTable.containsKey(\"output\")) {\n                    variableTable[\"output\"] = lastResult\n                }\n\n                ThreadProcessor.execute(myProject, action.fileName, varNames, variableTable)\n            }\n\n            is PatternActionFunc.JsonPath -> {\n                var jsonStr = action.obj ?: lastResult as String\n                jsonStr = jsonStr.fillVariable(variableTable)\n\n                JsonPathProcessor.execute(myProject, jsonStr, action) ?: jsonStr\n            }\n\n            is PatternActionFunc.Destroy -> {\n                TODO()\n            }\n\n            is PatternActionFunc.Tokenizer -> {\n                if (action.text.startsWith(\"\\$\")) {\n                    action.text = variableTable[action.text.substring(1)]?.toString() ?: action.text\n                }\n\n                TokenizerProcessor.execute(myProject, action)\n            }\n            is PatternActionFunc.LineNo -> {\n                when (lastResult) {\n                    is Array<*> -> {\n                        (lastResult as Array<String>).mapIndexed { index, line ->\n                            \"${index + 1}: $line\"\n                        }.toTypedArray()\n                    }\n\n                    else -> {\n                        (lastResult as String).split(\"\\n\").mapIndexed { index, line ->\n                            \"${index + 1}: $line\"\n                        }.joinToString(\"\\n\")\n                    }\n                }\n            }\n        }\n    }\n\n    private fun evaluateCase(action: PatternActionFunc.CaseMatch, input: Any): FrontMatterType.EXPRESSION? {\n        var fitCondition = action.keyValue.firstOrNull { it.key.toValue() == parseInput(input) }\n        if (fitCondition == null) {\n            fitCondition = action.keyValue.firstOrNull { it.key.toValue() == \"default\" }\n        }\n\n        return fitCondition?.value\n    }\n\n    private fun parseInput(input: Any): String {\n        return when (input) {\n            is String -> {\n                input\n            }\n\n            is Array<*> -> {\n                input.firstOrNull().toString()\n            }\n\n            else -> {\n                input.toString()\n            }\n        }\n    }\n\n    fun cat(paths: Array<String>, input: Any): String {\n        val absolutePaths: List<VirtualFile> = resolvePaths(paths, input)\n        return absolutePaths.joinToString(\"\\n\") { it.readText() }\n    }\n\n    /**\n     * @param userPaths The paths provided by the user in the script: `cat(\"file1.txt\", \"file2.txt\")`.\n     * @param patterMatchPaths The paths provided by the pattern match: `/.*.txt/ { cat } `.\n     */\n    private fun resolvePaths(userPaths: Array<out String>, patterMatchPaths: Any): List<VirtualFile> {\n        val baseDir = myProject.guessProjectDir()!!\n        var paths = userPaths\n        if (userPaths.isEmpty()) {\n            when (patterMatchPaths) {\n                is List<*> -> {\n                    paths = (patterMatchPaths as List<String>).toTypedArray()\n                }\n\n                is Array<*> -> {\n                    paths = patterMatchPaths as Array<String>\n                }\n\n                is String -> {\n                    paths = arrayOf(patterMatchPaths)\n                }\n\n                else -> {\n                    logger.warn(\"resolvePaths error: $patterMatchPaths\")\n                }\n            }\n        }\n\n        val absolutePaths: List<VirtualFile> = paths.mapNotNull {\n            baseDir.findFile(it) ?: try {\n                LocalFileSystem.getInstance().findFileByIoFile(File(it))\n            } catch (e: Exception) {\n                null\n            }\n        }\n\n        return absolutePaths\n    }\n}\n\nfun String.fillVariable(\n    variableTable: MutableMap<String, Any?>,\n): String {\n    return if (this.startsWith(\"\\$\")) {\n        variableTable[this.substring(1)]?.toString() ?: this\n    } else {\n        this\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/execute/command/BrowseShireCommand.kt",
    "content": "package com.phodal.shirelang.compiler.execute.command\n\nimport com.phodal.shirecore.agent.agenttool.browse.BrowseTool\nimport com.intellij.openapi.application.runInEdt\nimport com.intellij.openapi.project.Project\nimport com.phodal.shirelang.completion.dataprovider.BuiltinCommand\n\nclass BrowseShireCommand(val myProject: Project, private val prop: String,\n) : ShireCommand {\n    override val commandName = BuiltinCommand.BROWSE\n\n    override suspend fun doExecute(): String? {\n        var body: String? = null\n        runInEdt {\n            val parse = BrowseTool.parse(prop)\n            body = parse.body\n        }\n\n        return body\n    }\n}\n\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/execute/command/CommitShireCommand.kt",
    "content": "package com.phodal.shirelang.compiler.execute.command\n\nimport com.intellij.openapi.project.Project\nimport com.phodal.shirecore.provider.shire.RevisionProvider\nimport com.phodal.shirecore.utils.markdown.CodeFence\nimport com.phodal.shirelang.completion.dataprovider.BuiltinCommand\n\nclass CommitShireCommand(val myProject: Project, val commitMsg: String) : ShireCommand {\n    override val commandName = BuiltinCommand.COMMIT\n\n    override suspend fun doExecute(): String {\n        RevisionProvider.provide()?.let {\n            return it.commitCode(myProject, commitMsg)\n        } ?: return \"No revision provider found\"\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/execute/command/DatabaseShireCommand.kt",
    "content": "package com.phodal.shirelang.compiler.execute.command\n\nimport com.intellij.openapi.project.Project\nimport com.phodal.shirecore.provider.function.ToolchainFunctionProvider\nimport com.phodal.shirecore.utils.markdown.CodeFence\nimport com.phodal.shirelang.completion.dataprovider.BuiltinCommand\n\nclass DatabaseShireCommand(val myProject: Project, private val prop: String, private val codeContent: String?) :\n    ShireCommand {\n    override val commandName = BuiltinCommand.DATABASE\n\n    override suspend fun doExecute(): String {\n        val args = if (codeContent != null) {\n            listOf(codeContent)\n        } else {\n            listOf()\n        }\n\n        val result = ToolchainFunctionProvider.lookup(\"DatabaseFunctionProvider\")\n            ?.execute(myProject, prop, args, emptyMap())\n\n        return result?.toString() ?: \"No database provider found\"\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/execute/command/DirShireCommand.kt",
    "content": "package com.phodal.shirelang.compiler.execute.command\n\nimport com.intellij.openapi.application.runReadAction\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.util.text.StringUtilRt\nimport com.intellij.openapi.vcs.FileStatus\nimport com.intellij.openapi.vcs.FileStatusManager\nimport com.intellij.psi.PsiDirectory\nimport com.intellij.psi.PsiManager\nimport com.phodal.shirecore.lookupFile\nimport com.phodal.shirelang.completion.dataprovider.BuiltinCommand\n\n/**\n * The `DirShireCommand` class is responsible for listing files and directories in a tree-like structure for a given directory path within a project.\n * It implements the `ShireCommand` interface and provides an `execute` method to perform the directory listing operation asynchronously.\n *\n * The tree structure is visually represented using indentation and symbols (`├──`, `└──`) to denote files and subdirectories. Files are listed\n * first, followed by subdirectories, which are recursively processed to display their contents.\n *\n * Example output:\n * ```\n * myDirectory/\n *   ├── file1.txt\n *   ├── file2.txt\n *   └── subDirectory/\n *       ├── file3.txt\n *       └── subSubDirectory/\n *           └── file4.txt\n * ```\n *\n * @param myProject The project instance in which the directory resides.\n * @param dir The path of the directory to list.\n */\nclass DirShireCommand(private val myProject: Project, private val dir: String) : ShireCommand {\n    override val commandName = BuiltinCommand.DIR\n\n    private val defaultMaxDepth = 2\n\n    private sealed class TreeNode {\n        abstract val name: String\n        data class FileNode(override val name: String, val size: String?) : TreeNode()\n        data class DirectoryNode(\n            override val name: String, val children: MutableList<TreeNode> = mutableListOf()\n        ) : TreeNode() {\n            fun addChild(child: TreeNode) {\n                children.add(child)\n            }\n        }\n        data class CompressedNode(override val name: String, val subdirNames: List<String>) : TreeNode()\n        data class ParallelDirsNode(\n            override val name: String,\n            val dirNames: List<String>,\n            val commonChildName: String\n        ) : TreeNode()\n    }\n\n    override suspend fun doExecute(): String? {\n        val virtualFile = myProject.lookupFile(dir) ?: return \"File not found: $dir\"\n        val psiDirectory = PsiManager.getInstance(myProject).findDirectory(virtualFile) ?: return null\n\n        val rootNode = runReadAction { buildDirectoryTree(myProject, psiDirectory, 1) } ?: return null\n\n        val output = StringBuilder().apply {\n            appendLine(\"$dir/\")\n            renderTree(rootNode, 1, this)\n        }\n\n        return output.toString()\n    }\n\n\n    private fun buildDirectoryTree(project: Project, directory: PsiDirectory, depth: Int): TreeNode.DirectoryNode? {\n        if (isExcluded(project, directory)) return null\n\n        val dirNode = TreeNode.DirectoryNode(directory.name)\n\n        if (depth <= defaultMaxDepth) {\n            directory.files.forEach { file ->\n                val fileSize = StringUtilRt.formatFileSize(file.virtualFile.length)\n                dirNode.addChild(TreeNode.FileNode(file.name, fileSize))\n            }\n        }\n\n        val subdirectories = directory.subdirectories.filter { !isExcluded(project, it) }\n        val parallelDirsNode = detectParallelSimpleDirs(project, subdirectories)\n        if (parallelDirsNode != null) {\n            dirNode.addChild(parallelDirsNode)\n            processRemainingDirs(project, subdirectories, parallelDirsNode.dirNames, dirNode, depth)\n            return dirNode\n        }\n\n        if (shouldCompressSubdirectories(project, directory, subdirectories, depth)) {\n            val compressableSubdirs = getCompressableSubdirectories(subdirectories)\n            if (compressableSubdirs.isNotEmpty()) {\n                dirNode.addChild(TreeNode.CompressedNode(\"compressed\", compressableSubdirs.map { it.name }))\n            }\n        } else {\n            subdirectories.forEach { subdir ->\n                buildDirectoryTree(project, subdir, depth + 1)?.let { subdirNode ->\n                    dirNode.addChild(subdirNode)\n                }\n            }\n        }\n\n        return dirNode\n    }\n\n    /**\n     * 处理剩余的不符合并列目录模式的子目录\n     */\n    private fun processRemainingDirs(\n        project: Project,\n        allDirs: List<PsiDirectory>,\n        parallelDirNames: List<String>,\n        parentNode: TreeNode.DirectoryNode,\n        depth: Int\n    ) {\n        val remainingDirs = allDirs.filter { dir -> dir.name !in parallelDirNames }\n        remainingDirs.forEach { dir ->\n            buildDirectoryTree(project, dir, depth + 1)?.let { subdirNode ->\n                parentNode.addChild(subdirNode)\n            }\n        }\n    }\n\n    /**\n     * 检测并列的简单目录模式，如多个组件目录下都只有一个相同名称的子目录\n     */\n    private fun detectParallelSimpleDirs(project: Project, subdirs: List<PsiDirectory>): TreeNode.ParallelDirsNode? {\n        if (subdirs.size < 2) return null\n\n        // 收集有相同子目录结构的目录组\n        val dirGroups = mutableMapOf<String, MutableList<PsiDirectory>>()\n\n        // 对每个目录，检查它是否有单一子目录，如果有，记录子目录名\n        subdirs.forEach { dir ->\n            val nonExcludedChildren = dir.subdirectories.filter { !isExcluded(project, it) }\n            if (nonExcludedChildren.size == 1) {\n                val childName = nonExcludedChildren.first().name\n                dirGroups.getOrPut(childName) { mutableListOf() }.add(dir)\n            }\n        }\n\n        // 找出最大的组（具有相同子目录名的父目录组）\n        val largestGroup = dirGroups.maxByOrNull { it.value.size }\n\n        // 如果最大组至少有2个目录且子目录名不为空，则创建并列目录节点\n        if (largestGroup != null && largestGroup.value.size >= 2 && largestGroup.key.isNotEmpty()) {\n            val commonChildName = largestGroup.key\n            val parentDirNames = largestGroup.value.map { it.name }\n\n            return TreeNode.ParallelDirsNode(\"parallelDirs\", parentDirNames, commonChildName)\n        }\n\n        return null\n    }\n\n    /**\n     * 判断是否应该压缩显示子目录\n     */\n    private fun shouldCompressSubdirectories(\n        project: Project, directory: PsiDirectory, subdirectories: List<PsiDirectory>, depth: Int): Boolean {\n        // 深度超过阈值且有多个子目录时考虑压缩\n        return depth > defaultMaxDepth + 1 && subdirectories.size > 1 &&\n                // 确保这些子目录大多是叶子节点或近似叶子节点\n                subdirectories.all { subdir ->\n                    val childDirs = subdir.subdirectories.filter { !isExcluded(project, it) }\n                    childDirs.isEmpty() || childDirs.all { it.subdirectories.isEmpty() }\n                }\n    }\n\n    /**\n     * 获取可以压缩显示的子目录\n     */\n    private fun getCompressableSubdirectories(subdirectories: List<PsiDirectory>): List<PsiDirectory> {\n        // 这里可以添加更复杂的逻辑来决定哪些目录可以压缩\n        return subdirectories\n    }\n\n    /**\n     * 将目录树渲染为文本输出\n     */\n    private fun renderTree(node: TreeNode, depth: Int, output: StringBuilder) {\n        val indent = \" \".repeat(depth)\n\n        when (node) {\n            is TreeNode.DirectoryNode -> {\n                // 目录节点的子节点渲染\n                node.children.forEachIndexed { index, child ->\n                    val isLast = index == node.children.lastIndex\n                    val prefix = if (isLast) \"└\" else \"├\"\n\n                    when (child) {\n                        is TreeNode.FileNode -> {\n                            val sizeInfo = child.size?.let { \" ($it)\" } ?: \"\"\n                            output.appendLine(\"$indent$prefix── ${child.name}$sizeInfo\")\n                        }\n\n                        is TreeNode.DirectoryNode -> {\n                            output.appendLine(\"$indent$prefix── ${child.name}/\")\n                            renderTree(child, depth + 1, output)\n                        }\n\n                        is TreeNode.CompressedNode -> {\n                            output.appendLine(\"$indent$prefix── {${child.subdirNames.joinToString(\",\")}}/\")\n                        }\n\n                        is TreeNode.ParallelDirsNode -> {\n                            // 以更紧凑的格式显示并列目录结构\n                            val dirs = child.dirNames.sorted().joinToString(\",\")\n                            output.appendLine(\"$indent$prefix── {$dirs}/${child.commonChildName}/\")\n                        }\n                    }\n                }\n            }\n\n            else -> {} // 其他类型节点在这里不需要单独处理\n        }\n    }\n\n    /**\n     * 判断目录是否应被排除\n     */\n    private fun isExcluded(project: Project, directory: PsiDirectory): Boolean {\n        val excludedDirs = setOf(\".idea\", \"build\", \"target\", \".gradle\", \"node_modules\")\n        if (directory.name in excludedDirs) return true\n\n        val status = FileStatusManager.getInstance(project).getStatus(directory.virtualFile)\n        return status == FileStatus.IGNORED\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/execute/command/FileFuncShireCommand.kt",
    "content": "package com.phodal.shirelang.compiler.execute.command\n\nimport com.phodal.shirelang.compiler.parser.SHIRE_ERROR\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.roots.ProjectFileIndex\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.phodal.shirelang.completion.dataprovider.FileFunc\nimport com.phodal.shirecore.canBeAdded\nimport com.phodal.shirelang.completion.dataprovider.BuiltinCommand\n\nclass FileFuncShireCommand(val myProject: Project, private val prop: String) : ShireCommand {\n    override val commandName = BuiltinCommand.FILE_FUNC\n\n    override suspend fun doExecute(): String {\n        val (functionName, args) = parseRegex(prop)\n            ?: return \"\"\"$SHIRE_ERROR: file-func is not in the format @file-func:<functionName>(<arg1>, <arg2>, ...)\n            |Example: @file-func:regex(\".*\\.kt\")\n        \"\"\".trimMargin()\n\n        val fileFunction = FileFunc.fromString(functionName) ?: return \"$SHIRE_ERROR: Unknown function: $functionName\"\n        when (fileFunction) {\n            FileFunc.Regex -> {\n                try {\n                    val regex = Regex(args[0])\n                    return regexFunction(regex, myProject).joinToString(\", \")\n                } catch (e: Exception) {\n                    return SHIRE_ERROR + \": ${e.message}\"\n                }\n            }\n        }\n    }\n\n    private fun regexFunction(regex: Regex, project: Project): MutableList<VirtualFile> {\n        val files: MutableList<VirtualFile> = mutableListOf()\n        ProjectFileIndex.getInstance(project).iterateContent {\n            if (it.canBeAdded(project)) {\n                if (regex.matches(it.path)) {\n                    files.add(it)\n                }\n            }\n\n            true\n        }\n\n        return files\n    }\n\n    companion object {\n        /**\n         * Parses a given property string to extract the function name and its arguments.\n         *\n         * The property string is in the format <functionName>(<arg1>, <arg2>, ...).\n         *\n         * @param prop The property string to parse.\n         * @return The function name and the list of arguments as a Pair object.\n         * @throws IllegalArgumentException if the property string has invalid regex pattern.\n         */\n        fun parseRegex(prop: String): Pair<String, List<String>>? {\n            val regexPattern = Regex(\"\"\"(\\w+)\\(([^)]+)\\)\"\"\")\n            val matchResult = regexPattern.find(prop)\n\n            if (matchResult != null && matchResult.groupValues.size == 3) {\n                val functionName = matchResult.groupValues[1]\n                val args = matchResult.groupValues[2].split(',').map { it.trim() }\n                return Pair(functionName, args)\n            } else {\n                return null\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/execute/command/FileShireCommand.kt",
    "content": "package com.phodal.shirelang.compiler.execute.command\n\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.intellij.openapi.vfs.readText\nimport com.intellij.psi.PsiManager\nimport com.phodal.shirecore.ShirelangNotifications\nimport com.phodal.shirecore.findFile\nimport com.phodal.shirecore.lookupFile\nimport com.phodal.shirelang.compiler.ast.LineInfo\nimport com.phodal.shirelang.completion.dataprovider.BuiltinCommand\n\n/**\n * FileAutoCommand is responsible for reading a file and returning its contents.\n *\n * @param myProject the Project in which the file operations are performed\n * @param prop the property string containing the file name and optional line range\n *\n */\nclass FileShireCommand(private val myProject: Project, private val prop: String) : ShireCommand {\n    override val commandName = BuiltinCommand.FILE\n\n    override suspend fun doExecute(): String? {\n        val range: LineInfo? = LineInfo.fromString(prop)\n\n        // prop name can be src/file.name#L1-L2\n        val filepath = prop.split(\"#\")[0]\n        var virtualFile: VirtualFile? = myProject.lookupFile(filepath)\n\n        if (virtualFile == null) {\n            val filename = filepath.split(\"/\").last()\n            virtualFile = myProject.findFile(filename, false)\n        }\n\n        val content = virtualFile?.readText()\n        if (content == null) {\n            ShirelangNotifications.warn(myProject, \"File not found: $prop\")\n            /// not show error message to just notify\n            return \"File not found: $prop\"\n        }\n\n        val lang = PsiManager.getInstance(myProject).findFile(virtualFile)?.language?.displayName ?: \"\"\n\n        val fileContent = if (range == null) {\n            content\n        } else {\n            try {\n                content.split(\"\\n\").slice(range.startLine - 1 until range.endLine)\n                    .joinToString(\"\\n\")\n            } catch (e: StringIndexOutOfBoundsException) {\n                content\n            }\n        }\n\n        val output = StringBuilder()\n        // add file path\n        output.append(\"// File: $prop\\n\")\n        output.append(\"\\n```$lang\\n\")\n        output.append(fileContent)\n        output.append(\"\\n```\\n\")\n        return output.toString()\n    }\n\n    companion object {\n        fun file(project: Project, path: String): VirtualFile? {\n            val filename = path.split(\"#\")[0]\n            val virtualFile = project.lookupFile(filename)\n            return virtualFile\n        }\n    }\n}\n\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/execute/command/GotoShireCommand.kt",
    "content": "package com.phodal.shirelang.compiler.execute.command\n\nimport com.intellij.execution.filters.OpenFileHyperlinkInfo\nimport com.intellij.openapi.application.runReadAction\nimport com.intellij.openapi.editor.ScrollType\nimport com.intellij.openapi.fileEditor.FileEditorManager\nimport com.intellij.openapi.project.Project\nimport com.phodal.shirecore.lookupFile\nimport com.phodal.shirecore.provider.shire.ShireSymbolProvider\nimport com.phodal.shirelang.compiler.ast.LineInfo\nimport com.phodal.shirelang.compiler.parser.SHIRE_ERROR\nimport com.phodal.shirelang.completion.dataprovider.BuiltinCommand\nimport com.phodal.shirelang.psi.ShireUsed\n\n/**\n * The goto command will open the file and move the cursor to the specified line.\n * For example:\n *\n * ```shire\n * /goto:src/main/shire/com/phodal/shirelang/compiler/execute/command/GotoShireCommand.kt#L10C8\n * ```\n *\n * means to open the file `src/main/shire/com/phodal/shirelang/compiler/execute/command/GotoShireCommand.kt`\n * and move the cursor to line 10, column 8.\n */\nclass GotoShireCommand(val myProject: Project, private val argument: String, val used: ShireUsed) : ShireCommand {\n    override val commandName = BuiltinCommand.GOTO\n\n    override suspend fun doExecute(): String {\n        if (argument.contains(\".\")) {\n            return gotoSymbol()\n        }\n\n        val range: LineInfo? = LineInfo.fromString(used.text)\n        return gotoFile(range)\n    }\n\n    private fun gotoSymbol(): String {\n        val symbols = ShireSymbolProvider.all().map {\n            it.resolveSymbol(myProject, argument)\n        }.flatten()\n\n        if (symbols.isEmpty()) {\n            return \"$SHIRE_ERROR: Symbol not found: $argument\"\n        }\n\n        val results: List<String> = symbols.map { symbol ->\n            val hyperlinkInfo = OpenFileHyperlinkInfo(myProject, symbol.containingFile.virtualFile, symbol.textOffset)\n            hyperlinkInfo.navigate(myProject)\n            symbol.containingFile.virtualFile.path\n        }\n\n        return results.joinToString(\"\\n\")\n    }\n\n    private fun gotoFile(range: LineInfo?): String {\n        val virtualFile = runReadAction { myProject.lookupFile(argument) }\n        if (virtualFile == null) {\n            return \"$SHIRE_ERROR: File not found: $argument\"\n        }\n\n        val editor =\n            FileEditorManager.getInstance(myProject).selectedTextEditor ?: return \"$SHIRE_ERROR: Editor not found\"\n        val line = range?.startLine ?: 0\n        val column = range?.startColumn ?: 0\n\n        runReadAction {\n            val offset = editor.document.let {\n                val lineStartOffset = it.getLineStartOffset(line.coerceIn(0, it.lineCount - 1))\n                (lineStartOffset + column).coerceAtMost(it.textLength)\n            }\n\n            editor.caretModel.moveToOffset(offset)\n            editor.scrollingModel.scrollToCaret(ScrollType.CENTER)\n        }\n\n        return \"Navigated to $argument at line $line, column $column\"\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/execute/command/LocalSearchShireCommand.kt",
    "content": "package com.phodal.shirelang.compiler.execute.command\n\nimport com.intellij.openapi.application.runReadAction\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.roots.ProjectFileIndex\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.phodal.shirecore.canBeAdded\nimport com.phodal.shirecore.relativePath\nimport com.phodal.shirelang.completion.dataprovider.BuiltinCommand\nimport kotlin.collections.component1\nimport kotlin.collections.component2\n\n/**\n * Todo: Spike different search API in Intellij\n * - [com.intellij.util.indexing.FileBasedIndex]\n * - [com.intellij.find.FindManager] or [com.intellij.find.impl.FindInProjectUtil]\n * - [com.intellij.psi.search.PsiSearchHelper]\n * - [com.intellij.structuralsearch.StructuralSearchUtil] (Structural search API)\n * - [com.intellij.find.EditorSearchSession]\n *\n * ```java\n * EditorSearchSession.start(editor,project).setTextInField(\"Your Text to search\");\n * ```\n *\n */\nclass LocalSearchShireCommand(val myProject: Project, private val scope: String, val text: String?) : ShireCommand {\n    override val commandName = BuiltinCommand.LOCAL_SEARCH\n\n    private val MAX_LINE_SIZE = 180\n    private val OVERLAP = 4\n\n    override suspend fun doExecute(): String {\n        val text = (text ?: scope).trim()\n        /// check text length if less then 3 return alert slowly\n        if (text.length < 3) {\n            throw IllegalArgumentException(\"Text length should be more than 4\")\n        }\n\n        val textSearch = runReadAction { search(myProject, text, OVERLAP) }\n        return textSearch.map { (file, lines) ->\n            val filePath = file.relativePath(myProject)\n            val linesWithContext = lines.joinToString(\"\\n\")\n            \"file: $filePath\\n$linesWithContext\"\n        }.joinToString(\"\\n\")\n    }\n\n    /**\n     * Searches for occurrences of a specified keyword within the content of files in the project.\n     * For each occurrence, it retrieves a specified number of lines before and after the matched line,\n     * providing context around the keyword. The results are grouped by the file in which the keyword was found.\n     *\n     * @param project The project in which to search for the keyword. This is used to access the project's file index.\n     * @param keyword The keyword to search for within the files. The search is case-sensitive.\n     * @param overlap The number of lines to retrieve before and after each matched line. This determines the context size around the keyword.\n     * @return A map where each key is a `VirtualFile` containing the keyword, and the value is a list of strings representing\n     *         the lines of context around the keyword in that file. The context includes the matched line and the specified\n     *         number of lines before and after it.\n     */\n    private fun search(project: Project, keyword: String, overlap: Int = OVERLAP): Map<VirtualFile, List<String>> {\n        val result = mutableMapOf<VirtualFile, List<String>>()\n\n        ProjectFileIndex.getInstance(project).iterateContent { file ->\n            if (!file.canBeAdded(project) || !ProjectFileIndex.getInstance(project).isInContent(file)) {\n                return@iterateContent true\n            }\n\n            if (ProjectFileIndex.getInstance(project).isUnderIgnored(file)) return@iterateContent true\n            /// skip for .idea/\n            if (file.path.contains(\".idea\")) return@iterateContent true\n\n            val content = file.contentsToByteArray().toString(Charsets.UTF_8).lines()\n            val matchedIndices = content.withIndex()\n                .filter { (_, line) ->\n                    line.length < MAX_LINE_SIZE && line.contains(keyword)\n                }\n                .map { it.index }\n\n            if (matchedIndices.isNotEmpty()) {\n                val linesWithContext = matchedIndices.flatMap { index ->\n                    val start = (index - overlap).coerceAtLeast(0)\n                    val end = (index + overlap).coerceAtMost(content.size - 1)\n                    content.subList(start, end + 1).mapIndexed { offset, line ->\n                        \"${start + offset + 1} $line\"\n                    }\n                }.distinct()\n\n                result[file] = linesWithContext\n            }\n            true\n        }\n\n        return result\n    }\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/execute/command/OpenShireCommand.kt",
    "content": "package com.phodal.shirelang.compiler.execute.command\n\nimport com.intellij.openapi.fileEditor.FileDocumentManager\nimport com.intellij.openapi.fileEditor.FileEditorManager\nimport com.intellij.openapi.project.Project\nimport com.phodal.shirecore.lookupFile\nimport com.phodal.shirelang.completion.dataprovider.BuiltinCommand\n\nclass OpenShireCommand(val myProject: Project, private val filename: String) : ShireCommand {\n    override val commandName = BuiltinCommand.OPEN\n\n    override suspend fun doExecute(): String? {\n        FileDocumentManager.getInstance().saveAllDocuments()\n\n        val file = myProject.lookupFile(filename)\n        if (file != null) {\n            FileEditorManager.getInstance(myProject).openFile(file, true)\n            return \"Opening $filename...\"\n        } else {\n            return \"File not found: $filename\"\n        }\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/execute/command/PatchShireCommand.kt",
    "content": "package com.phodal.shirelang.compiler.execute.command\n\nimport com.intellij.openapi.application.ApplicationManager\nimport com.intellij.openapi.diff.impl.patch.FilePatch\nimport com.intellij.openapi.diff.impl.patch.PatchReader\nimport com.intellij.openapi.fileEditor.FileDocumentManager\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.vcs.changes.patch.AbstractFilePatchInProgress\nimport com.intellij.openapi.vcs.changes.patch.ApplyPatchDefaultExecutor\nimport com.intellij.openapi.vcs.changes.patch.MatchPatchPaths\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.intellij.util.containers.MultiMap\nimport com.phodal.shirelang.completion.dataprovider.BuiltinCommand\n\nclass PatchShireCommand(val myProject: Project, private val prop: String, private val codeContent: String) :\n    ShireCommand {\n    override val commandName = BuiltinCommand.PATCH\n\n    override suspend fun doExecute(): String {\n        FileDocumentManager.getInstance().saveAllDocuments()\n\n        val shelfExecutor = ApplyPatchDefaultExecutor(myProject)\n\n        val myReader = PatchReader(codeContent)\n        myReader.parseAllPatches()\n\n        val filePatches: MutableList<FilePatch> = myReader.allPatches\n\n        ApplicationManager.getApplication().invokeLater {\n            val matchedPatches =\n                MatchPatchPaths(myProject).execute(filePatches, true)\n\n            val patchGroups = MultiMap<VirtualFile, AbstractFilePatchInProgress<*>>()\n            for (patchInProgress in matchedPatches) {\n                patchGroups.putValue(patchInProgress.base, patchInProgress)\n            }\n\n            val additionalInfo = myReader.getAdditionalInfo(ApplyPatchDefaultExecutor.pathsFromGroups(patchGroups))\n            shelfExecutor.apply(filePatches, patchGroups, null, prop, additionalInfo)\n        }\n\n        return \"Patch in Progress...\"\n    }\n\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/execute/command/PrintShireCommand.kt",
    "content": "package com.phodal.shirelang.compiler.execute.command\n\nimport com.phodal.shirelang.completion.dataprovider.BuiltinCommand\n\nclass PrintShireCommand(private val value: String) : ShireCommand {\n    override val commandName = BuiltinCommand.FILE_FUNC\n\n    override suspend fun doExecute(): String {\n        return value\n    }\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/execute/command/RefactorShireCommand.kt",
    "content": "package com.phodal.shirelang.compiler.execute.command\n\nimport com.intellij.lang.Language\nimport com.intellij.openapi.fileEditor.FileDocumentManager\nimport com.intellij.openapi.fileEditor.FileEditorManager\nimport com.intellij.openapi.project.Project\nimport com.intellij.psi.PsiFile\nimport com.intellij.psi.PsiManager\nimport com.phodal.shirelang.completion.dataprovider.BuiltinRefactorCommand\nimport com.phodal.shirecore.provider.shire.RefactoringTool\nimport com.phodal.shirelang.completion.dataprovider.BuiltinCommand\nimport com.phodal.shirelang.psi.ShireFile\n\n/**\n * `RefactorShireCommand` is a class that implements the `ShireCommand` interface. It is responsible for executing\n * refactoring commands within a project based on the provided argument and text segment.\n *\n * The class has three private properties:\n * - `myProject`: A `Project` instance representing the current project.\n * - `argument`: A `String` containing the refactoring command to be executed.\n * - `textSegment`: A `String` containing the text segment relevant to the refactoring command.\n *\n * The `execute` method is the main entry point for executing a refactoring command. It first attempts to parse the\n * `argument` into a `BuiltinRefactorCommand` using the `fromString` method. If the command is not recognized, a\n * message indicating that it is unknown is returned.\n *\n * Depending on the type of refactoring command, the `execute` method performs different actions:\n * - For `BuiltinRefactorCommand.RENAME`: The method splits the `textSegment` using \" to \" and assigns the result to\n *   the variables `from` and `to`. It then performs a rename operation on a class in Java or Kotlin. The actual\n *   implementation of the rename operation is not provided in the code snippet, but it suggests using `RenameQuickFix`.\n * @see com.intellij.jvm.analysis.quickFix.RenameQuickFix for kotlin\n * @see com.intellij.spellchecker.quickfixes.RenameTo for by typos rename which will be better\n * - For `BuiltinRefactorCommand.SAFEDELETE`: The method checks the usage of the symbol before deleting it. It\n *   suggests using `SafeDeleteFix` as an example.\n * @see org.jetbrains.kotlin.idea.inspections.SafeDeleteFix for Kotlin\n * @see com.intellij.codeInsight.daemon.impl.quickfix.SafeDeleteFix for Java\n * - For `BuiltinRefactorCommand.DELETE`: The method does not perform any specific action, but it is expected to be\n *   implemented to handle the deletion of elements.\n * @see com.intellij.codeInspection.LocalQuickFixOnPsiElement\n * - For `BuiltinRefactorCommand.MOVE`: The method suggests using ` as an example for moving elements move package fix to a different package.\n * @see com.intellij.codeInspection.MoveToPackageFix\n *\n *\n * The `execute` method always returns `null`, indicating that the refactoring command has been executed, but the\n * actual result of the refactoring is not returned.\n *\n * This class is designed to be used within a refactoring tool or plugin that provides built-in refactoring commands.\n * It demonstrates how to handle different refactoring scenarios\n */\nclass RefactorShireCommand(val myProject: Project, private val argument: String, private val textSegment: String) :\n    ShireCommand {\n    override val commandName = BuiltinCommand.REFACTOR\n\n    override suspend fun doExecute(): String? {\n        var currentEditFile: PsiFile? = null\n        val editor = FileEditorManager.getInstance(myProject).selectedTextEditor\n        if (editor != null) {\n            val currentFile = FileDocumentManager.getInstance().getFile(editor.document) ?: return \"File not found\"\n            val currentPsiFile = PsiManager.getInstance(myProject).findFile(currentFile)\n\n            // will not handle the case where the current file is not a ShireFile\n            currentEditFile = if (currentPsiFile is ShireFile) {\n                null\n            } else {\n                currentPsiFile\n            }\n        }\n\n        val language = currentEditFile?.language ?: Language.findLanguageByID(\"JAVA\") ?: return \"Language not found\"\n        val refactoringTool = RefactoringTool.forLanguage(language)\n            ?: return \"Refactoring tool not found for Java\"\n\n        val command = BuiltinRefactorCommand.fromString(argument) ?: return \"Unknown refactor command: $argument\"\n\n        when (command) {\n            BuiltinRefactorCommand.RENAME -> {\n                val (from, to) = textSegment.split(\" to \")\n                refactoringTool.rename(from.trim(), to.trim(), currentEditFile)\n            }\n\n            BuiltinRefactorCommand.SAFE_DELETE -> {\n                val psiFile = refactoringTool.lookupFile(textSegment.trim()) ?: return \"File not found\"\n                refactoringTool.safeDelete(psiFile)\n            }\n\n            BuiltinRefactorCommand.DELETE -> {\n                val psiFile = refactoringTool.lookupFile(textSegment.trim()) ?: return \"File not found\"\n                refactoringTool.safeDelete(psiFile)\n            }\n\n            BuiltinRefactorCommand.MOVE -> {\n                val (from, to) = textSegment.split(\" to \")\n                val psiFile = refactoringTool.lookupFile(from.trim()) ?: return \"File not found\"\n                refactoringTool.move(psiFile, to.trim())\n            }\n        }\n\n        return null\n    }\n}\n\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/execute/command/RelatedSymbolInsCommand.kt",
    "content": "package com.phodal.shirelang.compiler.execute.command\n\nimport com.intellij.openapi.project.Project\nimport com.phodal.shirecore.provider.psi.RelatedClassesProvider\nimport com.phodal.shirecore.provider.shire.ShireSymbolProvider\nimport com.phodal.shirelang.completion.dataprovider.BuiltinCommand\n\nclass RelatedSymbolInsCommand(val myProject: Project, private val symbol: String) : ShireCommand {\n    override val commandName = BuiltinCommand.RELATED\n\n    override suspend fun doExecute(): String? {\n        val elements = ShireSymbolProvider.all().map {\n            it.resolveSymbol(myProject, symbol)\n        }.flatten()\n\n        if (elements.isEmpty()) return null\n\n        val psiElements = elements.mapNotNull {\n            RelatedClassesProvider.provide(it.language)?.lookup(it)\n        }.flatten()\n\n        if (psiElements.isEmpty()) return null\n\n        return psiElements.joinToString(\"\\n\") { it.text }\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/execute/command/RevShireCommand.kt",
    "content": "package com.phodal.shirelang.compiler.execute.command\n\nimport com.intellij.openapi.project.Project\nimport com.phodal.shirecore.provider.shire.RevisionProvider\nimport com.phodal.shirelang.completion.dataprovider.BuiltinCommand\n\n/**\n * RevAutoCommand is used to execute a command that retrieves the committed change list for a given revision using Git.\n *\n * @param myProject the Project instance associated with the command\n * @param revision the Git revision for which the committed change list is to be retrieved\n *\n */\nclass RevShireCommand(private val myProject: Project, private val revision: String) : ShireCommand {\n    override val commandName = BuiltinCommand.REV\n\n    override suspend fun doExecute(): String {\n        return RevisionProvider.provide()?.let {\n            val changes = it.fetchChanges(myProject, revision)\n            if (changes != null) {\n                return changes\n            } else {\n                return \"No changes found for revision $revision\"\n            }\n        } ?: \"No revision provider found\"\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/execute/command/RipgrepSearchShireCommand.kt",
    "content": "package com.phodal.shirelang.compiler.execute.command\n\nimport com.intellij.openapi.project.Project\nimport com.phodal.shirelang.compiler.execute.command.search.RipgrepSearcher\nimport com.phodal.shirelang.completion.dataprovider.BuiltinCommand\n\nclass RipgrepSearchShireCommand(\n    val myProject: Project, private val scope: String, val text: String?,\n) : ShireCommand {\n    override val commandName = BuiltinCommand.RIPGREP_SEARCH\n\n    override fun isApplicable(): Boolean {\n        return RipgrepSearcher.findRipgrepBinary() != null\n    }\n\n    override suspend fun doExecute(): String? {\n        val searchDirectory = myProject.baseDir!!.path\n        return RipgrepSearcher.searchFiles(myProject, searchDirectory, text ?: scope, null).get()\n    }\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/execute/command/RunShireCommand.kt",
    "content": "package com.phodal.shirelang.compiler.execute.command\n\nimport com.intellij.openapi.project.Project\nimport com.intellij.psi.PsiFile\nimport com.intellij.psi.PsiManager\nimport com.phodal.shirecore.lookupFile\nimport com.phodal.shirecore.provider.TestingService\nimport com.phodal.shirecore.provider.shire.ProjectRunService\nimport com.phodal.shirelang.compiler.parser.SHIRE_ERROR\nimport com.phodal.shirelang.completion.dataprovider.BuiltinCommand\n\n/**\n * The `RunAutoCommand` class is responsible for executing an auto command on a given project.\n *\n * @property myProject The project to execute the auto command on.\n * @property argument The name of the file to find and run tests for.\n *\n */\nclass RunShireCommand(val myProject: Project, private val argument: String) : ShireCommand {\n    override val commandName = BuiltinCommand.RUN\n\n    override suspend fun doExecute(): String {\n        val task = ProjectRunService.all().mapNotNull { projectRun ->\n            val hasTasks = projectRun.tasks(myProject).any { task -> task.contains(argument) }\n            if (hasTasks) projectRun else null\n        }\n\n        if (task.isNotEmpty()) {\n            task.first().run(myProject, argument)\n            return \"Task run successfully: $argument\"\n        }\n\n        val virtualFile = myProject.lookupFile(argument.trim()) ?: return \"$SHIRE_ERROR: [RunShireCommand] File not found: $argument\"\n        try {\n            val psiFile: PsiFile =\n                PsiManager.getInstance(myProject).findFile(virtualFile)\n                    ?: return \"$SHIRE_ERROR: [RunShireCommand] File not found: $argument\"\n\n            val testService =\n                TestingService.context(psiFile)\n                    ?: return \"$SHIRE_ERROR: [RunShireCommand] No test service found for file: $argument\"\n            testService.runFile(myProject, virtualFile, null)\n\n            return \"Tests run successfully for file: $argument\"\n        } catch (e: Exception) {\n            return \"$SHIRE_ERROR: ${e.message}\"\n        }\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/execute/command/ShellShireCommand.kt",
    "content": "package com.phodal.shirelang.compiler.execute.command\n\nimport com.intellij.execution.RunnerAndConfigurationSettings\nimport com.phodal.shirelang.compiler.parser.SHIRE_ERROR\nimport com.intellij.openapi.application.ApplicationManager\nimport com.intellij.openapi.project.Project\nimport com.intellij.psi.PsiManager\nimport com.intellij.sh.psi.ShFile\nimport com.intellij.sh.run.ShRunner\nimport com.phodal.shirecore.lookupFile\nimport com.phodal.shirelang.completion.dataprovider.BuiltinCommand\nimport com.phodal.shirelang.runner.ShellFileRunService\n\n/**\n * A class that implements the `InsCommand` interface to execute a shell command within the IntelliJ IDEA environment.\n *\n * This class is designed to run a shell command specified by a given `prop` string, which is assumed to be the path to a file within the project.\n * The command is executed in a shell runner service provided by IntelliJ IDEA, using the specified file's path and its parent directory as the working directory.\n *\n * @param myProject The current project context.\n * @param argument The path to the file within the project whose content should be executed as a shell command.\n */\nclass ShellShireCommand(val myProject: Project, private val argument: String) : ShireCommand {\n    override val commandName = BuiltinCommand.SHELL\n\n    override suspend fun doExecute(): String {\n        val virtualFile = myProject.lookupFile(argument.trim()) ?: return \"$SHIRE_ERROR: File not found: $argument\"\n        val psiFile = PsiManager.getInstance(myProject).findFile(virtualFile) as? ShFile\n        val shellRunService = ShellFileRunService()\n\n        val settings: RunnerAndConfigurationSettings? =\n            shellRunService.createRunSettings(myProject, virtualFile, psiFile)\n\n        if (settings != null) {\n            shellRunService.runFile(myProject, virtualFile, psiFile)\n            return \"Running shell file: $argument\"\n        }\n\n        val workingDirectory = virtualFile.parent.path\n        val shRunner = ApplicationManager.getApplication().getService(ShRunner::class.java)\n            ?: return \"$SHIRE_ERROR: Shell runner not found\"\n\n        if (shRunner.isAvailable(myProject)) {\n            shRunner.run(myProject, virtualFile.path, workingDirectory, \"RunShireShell\", true)\n        }\n\n        return \"Running shell command: $argument\"\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/execute/command/ShireCommand.kt",
    "content": "package com.phodal.shirelang.compiler.execute.command\n\nimport com.phodal.shirelang.completion.dataprovider.BuiltinCommand\n\ninterface ShireCommand {\n    val commandName: BuiltinCommand\n\n    fun isApplicable(): Boolean = true\n\n    suspend fun doExecute(): String?\n}\n\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/execute/command/StructureShireCommand.kt",
    "content": "package com.phodal.shirelang.compiler.execute.command\n\nimport com.intellij.openapi.application.ApplicationManager\nimport com.intellij.openapi.application.runReadAction\nimport com.intellij.openapi.diagnostic.logger\nimport com.intellij.openapi.project.Project\nimport com.intellij.psi.PsiFile\nimport com.intellij.psi.PsiManager\nimport com.phodal.shirecore.provider.codemodel.FileStructureProvider\nimport com.phodal.shirelang.completion.dataprovider.BuiltinCommand\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.withContext\n\nclass StructureShireCommand(val myProject: Project, val prop: String) : ShireCommand {\n    override val commandName = BuiltinCommand.STRUCTURE\n\n    override suspend fun doExecute(): String? {\n        val virtualFile = FileShireCommand.file(myProject, prop)\n        if (virtualFile == null) {\n            logger<StructureShireCommand>().warn(\"File not found: $prop\")\n            return null\n        }\n\n        val psiFile: PsiFile = withContext(Dispatchers.IO) {\n            ApplicationManager.getApplication().executeOnPooledThread<PsiFile?> {\n                runReadAction {\n                    PsiManager.getInstance(myProject).findFile(virtualFile)\n                }\n            }.get()\n        } ?: return null\n\n        FileStructureProvider.from(psiFile).let {\n            return it?.format()\n        }\n    }\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/execute/command/SymbolShireCommand.kt",
    "content": "package com.phodal.shirelang.compiler.execute.command\n\nimport com.phodal.shirelang.compiler.parser.SHIRE_ERROR\nimport com.intellij.openapi.project.Project\nimport com.phodal.shirecore.provider.shire.ShireSymbolProvider\nimport com.phodal.shirelang.completion.dataprovider.BuiltinCommand\n\nclass SymbolShireCommand(val myProject: Project, private val prop: String) :\n    ShireCommand {\n    override val commandName = BuiltinCommand.SYMBOL\n\n    override suspend fun doExecute(): String {\n        val result = ShireSymbolProvider.all().mapNotNull {\n            val found = it.resolveSymbol(myProject, prop)\n            if (found.isEmpty()) return@mapNotNull null\n            \"```${it.language}\\n${found.map { namedElement -> namedElement.name }.joinToString { \"\\n\" }}\\n```\\n\"\n        }\n\n        if (result.isEmpty()) {\n            return \"$SHIRE_ERROR No symbol found: $prop\"\n        }\n\n        return result.joinToString(\"\\n\")\n    }\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/execute/command/WriteShireCommand.kt",
    "content": "package com.phodal.shirelang.compiler.execute.command\n\nimport com.phodal.shirelang.compiler.parser.SHIRE_ERROR\nimport com.phodal.shirelang.compiler.ast.LineInfo\nimport com.intellij.openapi.application.runReadAction\nimport com.intellij.openapi.application.runWriteAction\nimport com.intellij.openapi.command.WriteCommandAction\nimport com.intellij.openapi.editor.Document\nimport com.intellij.openapi.fileEditor.FileDocumentManager\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.project.guessProjectDir\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.intellij.psi.PsiDocumentManager\nimport com.intellij.psi.PsiFile\nimport com.intellij.psi.PsiManager\nimport com.phodal.shirecore.lookupFile\nimport com.phodal.shirelang.psi.ShireUsed\nimport com.phodal.shirelang.completion.dataprovider.BuiltinCommand\n\nclass WriteShireCommand(val myProject: Project, val argument: String, val content: String, private val used: ShireUsed) :\n    ShireCommand {\n    override val commandName = BuiltinCommand.WRITE\n\n    private val pathSeparator = \"/\"\n\n    override suspend fun doExecute(): String {\n        val range: LineInfo? = LineInfo.fromString(used.text)\n        val filepath = argument.split(\"#\")[0]\n        val projectDir = myProject.guessProjectDir() ?: return \"$SHIRE_ERROR: Project directory not found\"\n\n        val virtualFile = runReadAction { myProject.lookupFile(filepath) }\n        if (virtualFile == null) {\n            // filepath maybe just a file name, so we need to create parent directory\n            val hasChildPath = filepath.contains(pathSeparator)\n            if (hasChildPath) {\n                val parentPath = filepath.substringBeforeLast(pathSeparator)\n                var parentDir = projectDir.findChild(parentPath)\n                if (parentDir == null) {\n                    // parentDir maybe multiple level, so we need to create all parent directory\n                    val parentDirs = parentPath.split(pathSeparator)\n                    parentDir = projectDir\n                    for (dir in parentDirs) {\n                        if (dir.isEmpty()) continue\n\n                        //check child folder if exist? if not, create it\n                        parentDir = if (parentDir?.findChild(dir) == null) {\n                            runWriteAction { parentDir?.createChildDirectory(this, dir) }\n                        } else {\n                            parentDir.findChild(dir)\n                        }\n                    }\n\n                    if (parentDir == null) {\n                        return \"$SHIRE_ERROR: Create Directory failed: $parentPath\"\n                    }\n                }\n\n                return runWriteAction {\n                    createNewContent(parentDir, filepath, content)\n                        ?: return@runWriteAction \"$SHIRE_ERROR: Create File failed: $argument\"\n\n                    return@runWriteAction \"Create file: $argument\"\n                }\n            } else {\n                return runWriteAction {\n                    createNewContent(projectDir, filepath, content)\n                        ?: return@runWriteAction \"$SHIRE_ERROR: Create File failed: $argument\"\n\n                    return@runWriteAction \"Create file: $argument\"\n                }\n            }\n        }\n\n        val psiFile = PsiManager.getInstance(myProject).findFile(virtualFile)\n            ?: return \"$SHIRE_ERROR: File not found: $argument\"\n\n        return executeInsert(psiFile, range, content)\n    }\n\n    private fun createNewContent(parentDir: VirtualFile, filepath: String, content: String): Document? {\n        val newFile = parentDir.createChildData(this, filepath.substringAfterLast(pathSeparator))\n        val document = FileDocumentManager.getInstance().getDocument(newFile) ?: return null\n\n        document.setText(content)\n        return document\n    }\n\n    private fun executeInsert(\n        psiFile: PsiFile,\n        range: LineInfo?,\n        content: String\n    ): String {\n        val document = runReadAction {\n            PsiDocumentManager.getInstance(myProject).getDocument(psiFile)\n        } ?: return \"$SHIRE_ERROR: File not found: $argument\"\n\n        val startLine = range?.startLine ?: 0\n        val endLine = if (document.lineCount == 0) 1 else range?.endLine ?: document.lineCount\n\n        try {\n            val startOffset = document.getLineStartOffset(startLine)\n            val endOffset = document.getLineEndOffset(endLine - 1)\n            WriteCommandAction.runWriteCommandAction(myProject) {\n                document.replaceString(startOffset, endOffset, content)\n            }\n\n            return \"Writing to file: $argument\"\n        } catch (e: Exception) {\n            return \"$SHIRE_ERROR: ${e.message}\"\n        }\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/execute/command/search/RipgrepOutputProcessor.kt",
    "content": "package com.phodal.shirelang.compiler.execute.command.search\n\nimport com.google.gson.JsonParser\nimport com.intellij.execution.process.ProcessAdapter\nimport com.intellij.execution.process.ProcessEvent\nimport com.intellij.execution.process.ProcessOutputTypes\nimport com.intellij.openapi.util.Key\nimport kotlinx.serialization.Serializable\n\n@Serializable\npublic data class RipgrepSearchResult(\n    var filePath: String? = null,\n    var line: Int = 0,\n    var column: Int = 0,\n    var match: String? = null,\n    var beforeContext: MutableList<String?> = ArrayList<String?>(),\n    var afterContext: MutableList<String?> = ArrayList<String?>()\n)\n\nclass RipgrepOutputProcessor : ProcessAdapter() {\n    private val results: MutableList<RipgrepSearchResult> = ArrayList<RipgrepSearchResult>()\n    private var currentResult: RipgrepSearchResult? = null\n\n    override fun onTextAvailable(event: ProcessEvent, outputType: Key<*>) {\n        if (outputType === ProcessOutputTypes.STDOUT) {\n            parseJsonLine(event.text)\n        }\n    }\n\n    private val jsonBuffer = StringBuilder()\n\n    fun parseJsonLine(line: String) {\n        if (line.isBlank()) {\n            return\n        }\n\n        jsonBuffer.append(line)\n\n        // Try to parse the buffer as JSON\n        val json = try {\n            JsonParser.parseString(jsonBuffer.toString())\n        } catch (e: Exception) {\n            // If parsing fails, it might be because the JSON is incomplete\n            // So we just return and wait for more lines\n            return\n        }\n\n        // If parsing succeeds, clear the buffer and process the JSON\n        jsonBuffer.clear()\n\n        if (json.isJsonObject) {\n            val jsonObject = json.asJsonObject\n            val type = jsonObject.get(\"type\").asString\n\n            when (type) {\n                \"match\" -> {\n                    val data = jsonObject.getAsJsonObject(\"data\")\n                    val path = data.getAsJsonObject(\"path\").get(\"text\").asString\n                    val lines = data.getAsJsonObject(\"lines\").get(\"text\").asString\n                    val lineNumber = data.get(\"line_number\").asInt\n                    val absoluteOffset = data.get(\"absolute_offset\").asInt\n                    val submatches = data.getAsJsonArray(\"submatches\")\n\n                    currentResult = RipgrepSearchResult(\n                        filePath = path,\n                        line = lineNumber,\n                        column = absoluteOffset,\n                        match = lines.trim()\n                    )\n\n                    submatches.forEach { submatch ->\n                        val submatchObj = submatch.asJsonObject\n                        val matchText = submatchObj.get(\"match\").asJsonObject.get(\"text\").asString\n                        currentResult?.match = matchText\n                    }\n\n                    results.add(currentResult!!)\n                }\n\n                \"context\" -> {\n                    val data = jsonObject.getAsJsonObject(\"data\")\n                    val lines = data.getAsJsonObject(\"lines\").get(\"text\").asString\n                    val lineNumber = data.get(\"line_number\").asInt\n\n                    if (currentResult != null) {\n                        if (lineNumber < currentResult!!.line) {\n                            currentResult!!.beforeContext.add(lines.trim())\n                        } else {\n                            currentResult!!.afterContext.add(lines.trim())\n                        }\n                    }\n                }\n            }\n        }\n    }\n\n    fun getResults(): MutableList<RipgrepSearchResult> {\n        if (currentResult != null) {\n            results.add(currentResult!!)\n        }\n\n        return results\n    }\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/execute/command/search/RipgrepSearcher.kt",
    "content": "// Copyright 2024 Cline Bot Inc. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage com.phodal.shirelang.compiler.execute.command.search\n\nimport com.intellij.execution.configurations.GeneralCommandLine\nimport com.intellij.execution.process.*\nimport com.intellij.openapi.diagnostic.Logger\nimport com.intellij.openapi.project.Project\nimport org.jetbrains.annotations.NonNls\nimport org.jetbrains.annotations.SystemIndependent\nimport java.io.IOException\nimport java.nio.charset.StandardCharsets\nimport java.nio.file.Path\nimport java.nio.file.Paths\nimport java.util.*\nimport java.util.concurrent.CompletableFuture\nimport java.util.concurrent.TimeUnit\n\n/**\n * 使用Ripgrep进行文件搜索\n * Inspired by: https://github.com/cline/cline/blob/main/src/services/ripgrep/index.ts Apache-2.0\n */\nobject RipgrepSearcher {\n    private val LOG = Logger.getInstance(RipgrepSearcher::class.java)\n\n    fun searchFiles(\n        project: Project,\n        searchDirectory: String,\n        regexPattern: String,\n        filePattern: String?\n    ): CompletableFuture<String?> {\n        return CompletableFuture.supplyAsync<String> {\n            try {\n                val rgPath = findRipgrepBinary() ?: throw IOException(\"Ripgrep binary not found\")\n                val results = executeRipgrep(\n                    project,\n                    rgPath,\n                    searchDirectory,\n                    regexPattern,\n                    filePattern\n                )\n                return@supplyAsync formatResults(results, project.basePath!!)\n            } catch (e: Exception) {\n                LOG.error(\"Search failed\", e)\n                return@supplyAsync \"Search error: \" + e.message\n            }\n        }\n    }\n\n    @Throws(IOException::class)\n    fun findRipgrepBinary(): Path? {\n        val osName = System.getProperty(\"os.name\").lowercase(Locale.getDefault())\n        val binName = if (osName.contains(\"win\")) \"rg.exe\" else \"rg\"\n\n        val pb = ProcessBuilder(\"which\", binName)\n        val process = pb.start()\n        try {\n            if (process.waitFor(1, TimeUnit.SECONDS) && process.exitValue() == 0) {\n                val path = String(process.inputStream.readAllBytes(), StandardCharsets.UTF_8).trim { it <= ' ' }\n                return Paths.get(path)\n            }\n        } catch (_: InterruptedException) {\n            return null\n        }\n\n        return null\n    }\n\n    @Throws(IOException::class)\n    private fun executeRipgrep(project: Project, rgPath: Path, directory: String, regex: String, filePattern: String?):\n            MutableList<RipgrepSearchResult> {\n        val cmd = getCommandLine(rgPath, regex, filePattern, directory, project.basePath)\n\n        val handler: OSProcessHandler = ColoredProcessHandler(cmd)\n        val processor = RipgrepOutputProcessor()\n        handler.addProcessListener(processor)\n\n        handler.startNotify()\n        handler.waitFor()\n\n        return processor.getResults()\n    }\n\n    fun getCommandLine(\n        rgPath: Path,\n        regex: String? = null,\n        filePattern: String? = null,\n        directory: String? = null,\n        basePath: @SystemIndependent @NonNls String? = null,\n    ): GeneralCommandLine {\n        val cmd = GeneralCommandLine(rgPath.toString())\n        cmd.withWorkDirectory(basePath)\n\n        cmd.addParameters(\"--json\")\n\n        if (regex != null) {\n            cmd.addParameters(\"-e\", regex)\n        }\n\n        if (filePattern != null) {\n            cmd.addParameters(\"--glob\", filePattern)\n        }\n\n        cmd.addParameters(\"--context\", \"1\")\n\n        if (directory != null) {\n            cmd.addParameters(directory)\n        }\n\n        cmd.charset = StandardCharsets.UTF_8\n        return cmd\n    }\n\n    private fun formatResults(results: MutableList<RipgrepSearchResult>, basePath: String): String {\n        val output = StringBuilder()\n        val grouped: MutableMap<String?, MutableList<RipgrepSearchResult?>?> =\n            LinkedHashMap<String?, MutableList<RipgrepSearchResult?>?>()\n\n        for (result in results) {\n            val relPath = getRelativePath(basePath, result.filePath!!)\n            grouped.computeIfAbsent(relPath) { k: String? -> ArrayList<RipgrepSearchResult?>() }!!.add(result)\n        }\n\n        for (entry in grouped.entries) {\n            output.append(\"### filepath: \").append(entry.key).append(\"\\n\")\n            val filePath = Paths.get(basePath, entry.key)\n            val content = filePath.toFile().readLines()\n\n            val lineNumbers = entry.value!!.map { it!!.line }\n\n            val displayLines = mutableSetOf<Int>()\n            for (lineNumber in lineNumbers) {\n                val start = 1.coerceAtLeast(lineNumber - 4)\n                val end = content.size.coerceAtMost(lineNumber + 4)\n                for (i in start..end) {\n                    displayLines.add(i)\n                }\n            }\n\n            val sortedDisplayLines = displayLines.sorted()\n            for (lineNumber in sortedDisplayLines) {\n                val line = content.getOrNull(lineNumber - 1)\n                if (line != null) {\n                    output.append(lineNumber).append(\" \").append(line).append(\"\\n\")\n                }\n            }\n\n            output.append(\"\\n\")\n        }\n\n        return output.toString()\n    }\n\n    private fun getRelativePath(basePath: String, absolutePath: String): String {\n        val base = Paths.get(basePath)\n        val target = Paths.get(absolutePath)\n        return base.relativize(target).toString().replace('\\\\', '/')\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/execute/command/status/ShireCommandStatus.kt",
    "content": "package com.phodal.shirelang.compiler.execute.command.status\n\nenum class ShireCommandStatus {\n    SUCCESS,\n    FAILED,\n    RUNNING\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/execute/command/status/ShireCommandStatusListener.kt",
    "content": "package com.phodal.shirelang.compiler.execute.command.status\n\nimport com.intellij.openapi.application.ApplicationManager\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.intellij.util.messages.Topic\nimport com.phodal.shirelang.compiler.execute.command.ShireCommand\n\n/**\n * Provide for listening to the status of InsCommand\n */\ninterface ShireCommandStatusListener {\n    fun onFinish(command: ShireCommand, status: ShireCommandStatus, file: VirtualFile?)\n\n    companion object {\n        val TOPIC = Topic.create(\"shire.command.status\", ShireCommandStatusListener::class.java)\n\n        fun notify(command: ShireCommand, status: ShireCommandStatus, file: VirtualFile?) {\n            ApplicationManager.getApplication().messageBus\n                .syncPublisher(TOPIC)\n                .onFinish(command, status, file)\n        }\n    }\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/execute/processor/ApprovalExecuteProcessor.kt",
    "content": "package com.phodal.shirelang.compiler.execute.processor\n\nimport com.intellij.ide.DataManager\nimport com.intellij.openapi.application.runInEdt\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.ui.popup.JBPopupFactory\nimport com.phodal.shirelang.compiler.ast.patternaction.PatternActionFuncDef\nimport com.phodal.shirelang.compiler.ast.patternaction.PatternProcessor\nimport com.phodal.shirelang.compiler.execute.processor.ui.PendingApprovalPanel\nimport java.util.concurrent.CompletableFuture\n\nobject ApprovalExecuteProcessor: PatternProcessor {\n    override val type: PatternActionFuncDef = PatternActionFuncDef.APPROVAL_EXECUTE\n\n    fun execute(\n        myProject: Project,\n        filename: Any,\n        variableNames: Array<String>,\n        variableTable: MutableMap<String, Any?>,\n        approve: ((Any) -> Unit)? = null,\n        reject: (() -> Unit?)? = null\n    ): Any {\n        val dataContext = DataManager.getInstance().dataContextFromFocusAsync.blockingGet(10000)\n            ?: throw IllegalStateException(\"No data context\")\n\n        val panel = PendingApprovalPanel()\n\n        val future = CompletableFuture<Any>()\n\n        runInEdt {\n            val popup = JBPopupFactory.getInstance()\n                .createComponentPopupBuilder(panel, null)\n                .setResizable(true)\n                .setMovable(true)\n                .setFocusable(true)\n                .setRequestFocus(true)\n                .setCancelOnClickOutside(false)\n                .setCancelOnOtherWindowOpen(false)\n                .setCancelOnWindowDeactivation(false)\n                .setKeyboardActions(listOf())\n                .createPopup()\n\n            panel.setupKeyShortcuts(popup,\n                {\n                    popup.closeOk(null)\n                    approve?.invoke(\"\")\n                    future.complete(\"\")\n                },\n                {\n                    popup.cancel()\n                    reject?.invoke()\n                    future.complete(\"\")\n                })\n\n            popup.showInBestPositionFor(dataContext)\n        }\n\n        return future.get()\n    }\n}\n\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/execute/processor/BatchProcessor.kt",
    "content": "package com.phodal.shirelang.compiler.execute.processor\n\nimport com.intellij.openapi.application.runReadAction\nimport com.intellij.openapi.diagnostic.logger\nimport com.intellij.openapi.project.Project\nimport com.phodal.shirelang.ShireActionStartupActivity\nimport com.phodal.shirelang.actions.ShireRunFileAction\nimport com.phodal.shirelang.compiler.execute.FunctionStatementProcessor\nimport com.phodal.shirelang.compiler.ast.patternaction.PatternActionFuncDef\nimport com.phodal.shirelang.compiler.ast.patternaction.PatternProcessor\n\nobject BatchProcessor : PatternProcessor {\n    override val type: PatternActionFuncDef = PatternActionFuncDef.BATCH\n    fun execute(\n        myProject: Project,\n        filename: String,\n        inputs: List<String>,\n        batchSize: Int,\n        variableTable: MutableMap<String, Any?>,\n    ): Any {\n        val file = runReadAction {\n            ShireActionStartupActivity.findShireFile(myProject, filename)\n        }\n\n        if (file == null) {\n            logger<FunctionStatementProcessor>().error(\"execute shire error: file not found\")\n            return \"\"\n        }\n\n        var files = inputs\n        /// maybe inputs [\"a.txt\\nb.txt\", \"c.txt\\nd.txt\"] or [\"a.txt\", \"b.txt\", \"c.txt\", \"d.txt\"] we need to split it\n        if (inputs.size == 1) {\n            files = inputs[0].split(\"\\n\")\n        }\n\n\n        return files.forEach { chunk: String ->\n            try {\n                val variableNames = arrayOf(\"input\")\n                variableTable[\"input\"] = chunk\n                ShireRunFileAction.suspendExecuteFile(myProject, file, variableNames, variableTable) ?: \"\"\n            } catch (e: Exception) {\n                logger<FunctionStatementProcessor>().error(\"execute shire error: $e\")\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/execute/processor/CaptureProcessor.kt",
    "content": "package com.phodal.shirelang.compiler.execute.processor\n\nimport com.intellij.openapi.application.ReadAction\nimport com.intellij.openapi.project.Project\nimport com.intellij.psi.PsiFile\nimport com.intellij.psi.PsiManager\nimport com.phodal.shirecore.lookupFile\nimport com.phodal.shirecore.provider.psi.PsiCapture\nimport com.phodal.shirelang.compiler.ast.patternaction.PatternActionFuncDef\nimport com.phodal.shirelang.compiler.ast.patternaction.PatternProcessor\n\nobject CaptureProcessor: PatternProcessor {\n    override val type: PatternActionFuncDef = PatternActionFuncDef.CAPTURE\n\n    fun execute(myProject: Project, fileName: String, nodeType: String): Any {\n        // first lookup file in the file system\n        val lookupFile = myProject.lookupFile(fileName) ?: return \"File not found: $fileName\"\n\n        // convert to psi\n        val psiFile = ReadAction.compute<PsiFile?, Throwable> {\n            PsiManager.getInstance(myProject).findFile(lookupFile)\n        } ?: return \"Failed to find PSI file for $fileName\"\n\n        val language = psiFile.language\n\n        PsiCapture.provide(language)?.let {\n            val text = ReadAction.compute<String, Throwable> {\n                psiFile.text\n            }\n\n            return it.capture(text, nodeType)\n        }\n\n        // execute the capture function\n        val result = psiFile.children.filter {\n            it.node.elementType.toString() == nodeType\n        }\n\n        return result\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/execute/processor/CrawlProcessor.kt",
    "content": "package com.phodal.shirelang.compiler.execute.processor\n\nimport com.phodal.shirecore.agent.agenttool.browse.BrowseTool\nimport com.phodal.shirelang.compiler.ast.patternaction.PatternActionFuncDef\nimport com.phodal.shirelang.compiler.ast.patternaction.PatternProcessor\nimport kotlinx.coroutines.coroutineScope\nimport kotlinx.coroutines.runBlocking\n\nobject CrawlProcessor: PatternProcessor {\n    override val type: PatternActionFuncDef = PatternActionFuncDef.CRAWL\n\n    suspend fun doExecute(url: String): String? {\n        /// todo: parse github README.md if it's a github repo\n        return BrowseTool.parse(url).body\n    }\n\n    fun execute(urls: Array<out String>): List<String> {\n        val results = runBlocking {\n            coroutineScope {\n                urls.mapNotNull {\n                    try {\n                        doExecute(it)\n                    } catch (e: Exception) {\n                        null\n                    }\n                }\n            }\n        }\n\n        return results\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/execute/processor/ExecuteProcessor.kt",
    "content": "package com.phodal.shirelang.compiler.execute.processor\n\nimport com.intellij.openapi.application.runReadAction\nimport com.intellij.openapi.diagnostic.logger\nimport com.intellij.openapi.project.Project\nimport com.phodal.shirecore.lookupFile\nimport com.phodal.shirecore.provider.shire.FileRunService\nimport com.phodal.shirecore.workerThread\nimport com.phodal.shirelang.ShireActionStartupActivity\nimport com.phodal.shirelang.actions.ShireRunFileAction\nimport com.phodal.shirelang.compiler.parser.SHIRE_ERROR\nimport com.phodal.shirelang.compiler.execute.command.RunShireCommand\nimport com.phodal.shirelang.compiler.ast.patternaction.PatternActionFuncDef\nimport com.phodal.shirelang.compiler.ast.patternaction.PatternProcessor\nimport kotlinx.coroutines.CoroutineScope\nimport kotlinx.coroutines.launch\n\nobject ExecuteProcessor : PatternProcessor {\n    override val type: PatternActionFuncDef = PatternActionFuncDef.EXECUTE\n\n    private val logger = logger<ExecuteProcessor>()\n\n    fun execute(\n        myProject: Project,\n        filename: Any,\n        variableNames: Array<String>,\n        variableTable: MutableMap<String, Any?>,\n    ): Any {\n        val file = filename.toString()\n        if (file.endsWith(\".shire\")) {\n            return executeShireFile(myProject, filename, variableNames, variableTable)\n        }\n\n        if (file.startsWith(\":\")) {\n            CoroutineScope(workerThread).launch {\n                RunShireCommand(myProject, file).doExecute()\n            }\n        }\n\n        val virtualFile = myProject.lookupFile(file) ?: return \"$SHIRE_ERROR: File not found: $filename\"\n\n        val runService = FileRunService.provider(myProject, virtualFile)\n        return runService?.runFileAsync(myProject, virtualFile, null)\n            ?: \"$SHIRE_ERROR: [ExecuteProcessor] No run service found for file: $filename\"\n    }\n\n    private fun executeShireFile(\n        myProject: Project,\n        filename: Any,\n        variableNames: Array<String>,\n        variableTable: MutableMap<String, Any?>,\n    ): String {\n        try {\n            val file = runReadAction {\n                ShireActionStartupActivity.findShireFile(myProject, filename.toString())\n            }\n\n            if (file == null) {\n                logger.warn(\"execute shire error: file not found\")\n                return \"\"\n            }\n\n            return ShireRunFileAction.suspendExecuteFile(myProject, file, variableNames, variableTable) ?: \"\"\n        } catch (e: Exception) {\n            logger.warn(\"execute shire error: $e\")\n            return \"\"\n        }\n    }\n\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/execute/processor/ForeignFunctionProcessor.kt",
    "content": "package com.phodal.shirelang.compiler.execute.processor\n\nimport com.intellij.openapi.application.runReadAction\nimport com.intellij.openapi.project.Project\nimport com.phodal.shirecore.findFile\nimport com.phodal.shirecore.provider.shire.FileRunService\nimport com.phodal.shirelang.compiler.parser.SHIRE_ERROR\nimport com.phodal.shirelang.compiler.ast.ForeignFunction\n\nobject ForeignFunctionProcessor {\n    fun execute(\n        project: Project, funcName: String, args: List<Any>, allVariables: Map<String, Any?>, func: ForeignFunction,\n    ): Any {\n        val filename = func.funcPath\n\n        val virtualFile = runReadAction {\n            project.findFile(filename)\n        } ?: return \"$SHIRE_ERROR: File not found: $filename\"\n\n\n        /// last args will be file path, should be skip\n        val argList: List<String> = args.dropLast(1).map {\n            // handle for arrayList and map type\n            when (it) {\n                is List<*> -> it.joinToString(\",\")\n                is Map<*, *> -> it.entries.joinToString(\",\") { (k, v) -> \"$k=$v\" }\n                else -> it.toString()\n            }\n        }\n\n        return FileRunService.runInCli(project, virtualFile, argList)\n            ?: \"$SHIRE_ERROR: [ForeignFunctionProcessor] No run service found for file: $filename\"\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/execute/processor/JsonPathProcessor.kt",
    "content": "package com.phodal.shirelang.compiler.execute.processor\n\nimport com.intellij.execution.console.ConsoleViewWrapperBase\nimport com.intellij.execution.ui.ConsoleViewContentType\nimport com.intellij.execution.ui.RunContentManager\nimport com.intellij.openapi.diagnostic.logger\nimport com.intellij.openapi.project.Project\nimport com.nfeld.jsonpathkt.JsonPath\nimport com.nfeld.jsonpathkt.extension.read\nimport com.phodal.shirelang.compiler.ast.patternaction.PatternActionFunc\nimport com.phodal.shirelang.compiler.ast.patternaction.PatternActionFuncDef\nimport com.phodal.shirelang.compiler.ast.patternaction.PatternProcessor\nimport com.phodal.shirelang.compiler.execute.FunctionStatementProcessor\n\nobject JsonPathProcessor : PatternProcessor {\n    override val type: PatternActionFuncDef = PatternActionFuncDef.JSONPATH\n\n    fun execute(\n        project: Project,\n        jsonStr: String,\n        action: PatternActionFunc.JsonPath,\n    ): String? {\n        if (action.sseMode || jsonStr.startsWith(\"data: \")) {\n            return parseSSEResult(jsonStr, action.path.trim())\n        }\n\n        val result: String = try {\n            JsonPath.parse(jsonStr)?.read<Any>(action.path.trim()).toString()\n        } catch (e: Exception) {\n            console(project,\"jsonpath error: $e\")\n            return null\n        }\n\n        if (result == \"null\") {\n            console(project,\"jsonpath error: $result for $jsonStr\")\n            return null\n        }\n\n        return result\n    }\n\n    fun console(project: Project, str: String) {\n        val contentManager = RunContentManager.getInstance(project)\n        val console = contentManager.selectedContent?.executionConsole as? ConsoleViewWrapperBase ?: return\n\n        console.print(str, ConsoleViewContentType.ERROR_OUTPUT)\n    }\n\n\n    /**\n     * Parses the server-sent events (SSE) result from a given input string using a specified JSON path expression.\n     * This method is useful for extracting relevant information from a stream of SSE data.\n     *\n     * @param input The raw input string containing one or more SSE data lines.\n     * @param jsonPath The JSON path expression used to query the JSON data within each data line.\n     * @return A string containing the results of applying the JSON path to each data line, separated by newline characters.\n     *\n     * The method processes the input string as follows:\n     * input example\n     * ```bash\n     * data: {\"event\": \"agent_thought\", \"conversation_id\": \"48929266-a58f-46cc-a5eb-33145e6a96ef\", \"message_id\": \"91ad550b-1109-4062-88f8-07be18238e0e\", \"created_at\": 1725437154, \"task_id\": \"4f846104-8571-42f1-b04c-f6f034b2fe9e\", \"id\": \"cf621bc0-3daa-45ae-9346-9a386f9c73b0\", \"position\": 1, \"thought\": \"\", \"observation\": \"\", \"tool\": \"\", \"tool_labels\": {}, \"tool_input\": \"\", \"message_files\": []}\n     * data: {\"event\": \"agent_message\", \"conversation_id\": \"48929266-a58f-46cc-a5eb-33145e6a96ef\", \"message_id\": \"91ad550b-1109-4062-88f8-07be18238e0e\", \"created_at\": 1725437154, \"task_id\": \"4f846104-8571-42f1-b04c-f6f034b2fe9e\", \"id\": \"91ad550b-1109-4062-88f8-07be18238e0e\", \"answer\": \"The\"}\n     * data: {\"event\": \"message_end\", \"conversation_id\": \"48929266-a58f-46cc-a5eb-33145e6a96ef\", \"message_id\": \"91ad550b-1109-4062-88f8-07be18238e0e\", \"created_at\": 1725437154, \"task_id\": \"4f846104-8571-42f1-b04c-f6f034b2fe9e\", \"id\": \"91ad550b-1109-4062-88f8-07be18238e0e\", \"metadata\": {\"usage\": {\"prompt_tokens\": 20, \"prompt_unit_price\": \"0.15\", \"prompt_price_unit\": \"0.000001\", \"prompt_price\": \"0.0000030\", \"completion_tokens\": 481, \"completion_unit_price\": \"0.60\", \"completion_price_unit\": \"0.000001\", \"completion_price\": \"0.0002886\", \"total_tokens\": 501, \"total_price\": \"0.0002916\", \"currency\": \"USD\", \"latency\": 0.46675555396359414}}}\n     * data: {\"event\": \"tts_message_end\", \"conversation_id\": \"48929266-a58f-46cc-a5eb-33145e6a96ef\", \"message_id\": \"91ad550b-1109-4062-88f8-07be18238e0e\", \"created_at\": 1725437154, \"task_id\": \"4f846104-8571-42f1-b04c-f6f034b2fe9e\", \"audio\": \"\"}\n     */\n    fun parseSSEResult(input: String, jsonPath: String): String {\n        val lines = input.split(\"\\n\")\n        val dataLines = lines.filter {\n            it.startsWith(\"data: \")\n        }.map {\n            it.substring(6)\n        }.mapNotNull {\n            try {\n                JsonPath.parse(it)?.read<Any>(jsonPath)\n            } catch (e: Exception) {\n                logger<FunctionStatementProcessor>().warn(\"jsonpath error: $e\")\n                null\n            }\n        }\n\n        return dataLines.joinToString(separator = \"\")\n    }\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/execute/processor/RedactProcessor.kt",
    "content": "package com.phodal.shirelang.compiler.execute.processor\n\nimport com.intellij.openapi.project.Project\nimport com.phodal.shirecore.function.guard.scanner.SecretPatternsScanner\n\nobject RedactProcessor {\n    fun execute(project: Project, lastResult: Any): Any {\n        if (lastResult is String) {\n            return project.getService(SecretPatternsScanner::class.java).maskInput(lastResult)\n        }\n\n        return lastResult\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/execute/processor/ThreadProcessor.kt",
    "content": "package com.phodal.shirelang.compiler.execute.processor\n\nimport com.intellij.openapi.application.ApplicationManager\nimport com.intellij.openapi.application.ReadAction\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.vfs.readText\nimport com.intellij.psi.PsiFile\nimport com.intellij.psi.PsiManager\nimport com.intellij.sh.psi.ShFile\nimport com.intellij.sh.run.ShRunner\nimport com.phodal.shirecore.lookupFile\nimport com.phodal.shirecore.provider.http.HttpHandler\nimport com.phodal.shirecore.provider.http.HttpHandlerType\nimport com.phodal.shirecore.provider.shire.FileRunService\nimport com.phodal.shirelang.actions.ShireRunFileAction\nimport com.phodal.shirelang.compiler.parser.SHIRE_ERROR\nimport com.phodal.shirelang.compiler.execute.processor.shell.ShireShellCommandRunner\nimport com.phodal.shirelang.compiler.ast.patternaction.PatternActionFuncDef\nimport com.phodal.shirelang.compiler.ast.patternaction.PatternProcessor\nimport com.phodal.shirelang.psi.ShireFile\nimport java.util.concurrent.CompletableFuture\n\n\nobject ThreadProcessor : PatternProcessor {\n    override val type: PatternActionFuncDef = PatternActionFuncDef.THREAD\n\n    fun execute(\n        myProject: Project, fileName: String, variablesName: Array<String>, variableTable: MutableMap<String, Any?>,\n    ): String {\n        val file = myProject.lookupFile(fileName) ?: return \"File not found: $fileName\"\n\n        val filename = file.name.lowercase()\n        val content = file.readText()\n\n        // if ends with .cURL.sh, try call cURL service\n        if (filename.endsWith(\".curl.sh\")) {\n            val execute = HttpHandler.provide(HttpHandlerType.CURL)\n                ?.execute(myProject, content, variablesName, variableTable)\n\n            if (execute != null) {\n                return execute\n            }\n        }\n\n        val psiFile = ReadAction.compute<PsiFile, Throwable> {\n            PsiManager.getInstance(myProject).findFile(file)\n        } ?: return \"Failed to find PSI file for $fileName\"\n\n        when (psiFile) {\n            is ShireFile -> {\n                return when (val output = variableTable[\"output\"]) {\n                    is List<*> -> {\n                        val results = output.mapNotNull {\n                            try {\n                                variableTable[\"output\"] = it\n                                executeTask(myProject, variablesName, variableTable, psiFile)\n                            } catch (e: Exception) {\n                                null\n                            }\n                        }\n\n                        results.joinToString(\"\\n\")\n                    }\n\n                    is Array<*> -> {\n                        output.joinToString(\"\\n\") {\n                            variableTable[\"output\"] = it\n                            executeTask(myProject, variablesName, variableTable, psiFile)\n                                ?: \"$SHIRE_ERROR - Thread: No run service found\"\n                        }\n                    }\n\n                    else -> {\n                        return executeTask(myProject, variablesName, variableTable, psiFile)\n                            ?: \"$SHIRE_ERROR - Thread: No run service found\"\n                    }\n                }\n            }\n\n            is ShFile -> {\n                val processVariables: Map<String, String> =\n                    variablesName.associateWith { (variableTable[it] as? String ?: \"\") }\n                return executeShFile(psiFile, myProject, processVariables)\n            }\n\n            else -> {\n                val fileRunService = FileRunService.provider(myProject, file)\n                    ?: return \"$SHIRE_ERROR No run service found for $psiFile, $fileName\"\n\n                return fileRunService.runFileAsync(myProject, file, psiFile)\n                    ?: \"$SHIRE_ERROR Run service failure: $fileName\"\n            }\n        }\n    }\n\n    private fun executeShFile(psiFile: ShFile, myProject: Project, processVariables: Map<String, String>): String {\n        val virtualFile = psiFile.virtualFile\n        val shRunner = ApplicationManager.getApplication().getService(ShRunner::class.java)\n            ?: return \"$SHIRE_ERROR: Shell runner not found\"\n\n        val future = CompletableFuture<String>()\n        ApplicationManager.getApplication().invokeLater {\n            if (shRunner.isAvailable(myProject)) {\n                try {\n                    val output = ShireShellCommandRunner.runShellCommand(virtualFile, myProject, processVariables)\n                    future.complete(output)\n                } catch (t: Throwable) {\n                    future.completeExceptionally(t)\n                }\n            }\n        }\n\n        return future.get()\n    }\n\n    private fun executeTask(\n        myProject: Project,\n        variables: Array<String>,\n        variableTable: MutableMap<String, Any?>,\n        psiFile: ShireFile,\n    ): String? {\n        return ShireRunFileAction.suspendExecuteFile(myProject, psiFile, variables, variableTable)\n    }\n}\n\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/execute/processor/TokenizerProcessor.kt",
    "content": "package com.phodal.shirelang.compiler.execute.processor\n\nimport com.huaban.analysis.jieba.JiebaSegmenter\nimport com.huaban.analysis.jieba.JiebaSegmenter.SegMode\nimport com.intellij.openapi.project.Project\nimport com.phodal.shirecore.search.tokenizer.*\nimport com.phodal.shirelang.compiler.ast.patternaction.PatternActionFunc\nimport com.phodal.shirelang.compiler.ast.patternaction.PatternActionFuncDef\nimport com.phodal.shirelang.compiler.ast.patternaction.PatternProcessor\n\n/**\n * For [PatternActionFuncDef.TOKENIZER]\n */\nobject TokenizerProcessor : PatternProcessor {\n    override val type: PatternActionFuncDef = PatternActionFuncDef.TOKENIZER\n\n    fun execute(project: Project, action: PatternActionFunc.Tokenizer): Any {\n        if (action.tokType.startsWith(\"/\") && action.tokType.endsWith(\"/\")) {\n            val regex = action.tokType.substring(1, action.tokType.length - 1)\n            val tokenizer = RegexpTokenizer(\n                object : RegexTokenizerOptions {\n                    override val pattern: Regex get() = Regex(regex)\n                    override val discardEmpty: Boolean get() = true\n                    override val gaps: Boolean? get() = false\n                }\n            )\n\n            return tokenizer.tokenize(action.text).distinct()\n        }\n\n        when (action.tokType) {\n            \"word\" -> {\n                val tokenizer = WordTokenizer()\n                return tokenizer.tokenize(action.text).distinct()\n            }\n\n            \"naming\" -> {\n                val tokenizer = CodeNamingTokenizer()\n                return tokenizer.tokenize(action.text).distinct()\n            }\n\n            \"stopwords\" -> {\n                return StopwordsBasedTokenizer.instance().tokenize(action.text).distinct()\n            }\n\n            \"jieba\" -> {\n                return JiebaSegmenter().process(action.text, SegMode.SEARCH).mapNotNull {\n                    val result = it.word.trim()\n                    result.ifEmpty {\n                        null\n                    }\n                }\n            }\n\n            else -> {\n                val tokenizer = WordTokenizer()\n                return tokenizer.tokenize(action.text).distinct()\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/execute/processor/shell/ShireShellCommandRunner.kt",
    "content": "package com.phodal.shirelang.compiler.execute.processor.shell\n\nimport com.intellij.execution.configurations.GeneralCommandLine\nimport com.intellij.execution.process.CapturingProcessHandler\nimport com.intellij.execution.process.OSProcessHandler\nimport com.intellij.openapi.application.ApplicationManager\nimport com.intellij.openapi.application.runReadAction\nimport com.intellij.openapi.diagnostic.logger\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.util.io.FileUtil\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.intellij.openapi.vfs.readText\nimport com.intellij.psi.search.ProjectScope\nimport com.phodal.shire.json.ShireEnvReader\nimport com.phodal.shire.json.ShireEnvVariableFiller\nimport java.io.File\nimport java.nio.charset.StandardCharsets\n\n\nobject ShireShellCommandRunner {\n    private const val DEFAULT_TIMEOUT: Int = 30000\n\n    fun fill(project: Project, file: VirtualFile, processVariables: Map<String, String>): String {\n        return runReadAction {\n            val scope = ProjectScope.getContentScope(project)\n\n            val envName =\n                ShireEnvReader.getAllEnvironments(project, scope).firstOrNull() ?: ShireEnvReader.DEFAULT_ENV_NAME\n            val envObject = ShireEnvReader.getEnvObject(envName, scope, project)\n\n            val content = file.readText()\n\n            val envVariables: List<Set<String>> = ShireEnvReader.fetchEnvironmentVariables(envName, scope)\n            val filledContent = ShireEnvVariableFiller.fillVariables(content, envVariables, envObject, processVariables)\n\n            filledContent\n        }\n    }\n\n    fun runShellCommand(virtualFile: VirtualFile, myProject: Project, processVariables: Map<String, String>): String {\n        val workingDirectory = virtualFile.parent.path\n\n        val fileContent = fill(myProject, virtualFile, processVariables)\n        val tempFile = File.createTempFile(\"tempScript\", \".sh\");\n        tempFile.writeText(fileContent)\n\n        val commandLine: GeneralCommandLine = GeneralCommandLine()\n            .withParentEnvironmentType(GeneralCommandLine.ParentEnvironmentType.CONSOLE)\n            .withWorkDirectory(workingDirectory)\n            .withCharset(StandardCharsets.UTF_8)\n            .withExePath(\"sh\")\n            .withParameters(tempFile.path)\n\n        val future = ApplicationManager.getApplication().executeOnPooledThread<String> {\n            val processOutput = runCatching {\n                CapturingProcessHandler(commandLine).runProcess(DEFAULT_TIMEOUT)\n            }.apply { deleteFileOnTermination(commandLine, tempFile) }.getOrThrow()\n\n\n            val exitCode = processOutput.exitCode\n            if (exitCode != 0) {\n                throw RuntimeException(\"Cannot execute ${commandLine}: exit code $exitCode, error output: ${processOutput.stderr}\")\n            }\n            processOutput.stdout\n        }\n\n        return try {\n            future.get() // 阻塞获取结果，可以选择添加超时控制\n        } catch (e: Exception) {\n            logger<ShireShellCommandRunner>().error(\"Command execution failed\", e)\n            throw RuntimeException(\"Execution failed: ${e.message}\", e)\n        }\n    }\n\n    /**\n     * We need to ensure that the file is deleted after the process is executed.\n     * for example,the file also needs to be deleted when [create-process][OSProcessHandler.startProcess] fails.\n     */\n    private fun deleteFileOnTermination(commandLine: GeneralCommandLine, tempFile: File) {\n//        OSProcessHandler.deleteFileOnTermination(commandLine, tempFile)  // is Internal API\n        try {\n            FileUtil.delete(tempFile)\n        } catch (e: Exception) {\n            // ignore\n        }\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/execute/processor/ui/PendingApprovalPanel.kt",
    "content": "package com.phodal.shirelang.compiler.execute.processor.ui\n\nimport com.intellij.openapi.ui.popup.JBPopup\nimport com.intellij.ui.dsl.builder.panel\nimport com.intellij.util.ui.JBUI\nimport java.awt.event.InputEvent\nimport java.awt.event.KeyEvent\nimport javax.swing.JButton\nimport javax.swing.JPanel\nimport javax.swing.KeyStroke\n\nclass PendingApprovalPanel : JPanel() {\n    private val approveButton = JButton(\"Approve\")\n    private val rejectButton = JButton(\"Reject\")\n\n    init {\n        val layoutBuilder = panel {\n            row {\n                label(getShortcutLabel(\"⌘ + ↵\", \"Ctrl + ↵\"))\n                cell(approveButton)\n\n                label(getShortcutLabel(\"⌘ + ⌦\", \"Ctrl + Del\"))\n                cell(rejectButton)\n            }\n        }\n\n\n        layoutBuilder.border = JBUI.Borders.empty(0, 10)\n        this.add(layoutBuilder)\n    }\n\n    private fun getShortcutLabel(shortcutForMac: String, shortcutForOthers: String): String {\n        return if (System.getProperty(\"os.name\").contains(\"Mac\")) {\n            shortcutForMac\n        } else {\n            shortcutForOthers\n        }\n    }\n\n    fun setupKeyShortcuts(popup: JBPopup, approve: (Any) -> Unit, reject: (Any) -> Unit) {\n        approveButton.addActionListener(approve)\n        approveButton.registerKeyboardAction(\n            {\n                popup.closeOk(null)\n                approveButton.doClick()\n            }, KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, InputEvent.CTRL_DOWN_MASK), WHEN_IN_FOCUSED_WINDOW\n        )\n\n        rejectButton.addActionListener(reject)\n        rejectButton.registerKeyboardAction(\n            {\n                popup.closeOk(null)\n                rejectButton.doClick()\n            }, KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, InputEvent.CTRL_DOWN_MASK), WHEN_IN_FOCUSED_WINDOW\n        )\n    }\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/execute/searcher/PatternSearcher.kt",
    "content": "package com.phodal.shirelang.compiler.execute.searcher\n\nimport com.intellij.openapi.diagnostic.logger\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.project.guessProjectDir\nimport com.intellij.openapi.vfs.VfsUtilCore\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.intellij.openapi.vfs.VirtualFileManager\nimport com.intellij.openapi.vfs.VirtualFileVisitor\nimport com.intellij.util.io.URLUtil\nimport java.util.regex.Pattern\n\nobject PatternSearcher {\n    private val cache: MutableMap<String, List<VirtualFile>> = mutableMapOf()\n    /**\n     * This function is used to find files in a given project that match a specified regular expression.\n     *\n     * @param project The project within which to search for files. This is an instance of the Project class.\n     * @param regex The regular expression to match file names against. This is a string.\n     *\n     * The function first checks if the regular expression is already present in the cache. If it is, it returns the corresponding list of files.\n     * If the regular expression is not in the cache, the function compiles the regular expression into a pattern.\n     * It then refreshes the file system and gets the base directory of the project.\n     * If the base directory is not null, it refreshes the file system and finds the file by path.\n     * It then creates a visitor for each file in the base directory. If the file name matches the pattern, it is added to the list of matching files.\n     * The function finally returns the list of matching files.\n     *\n     * @return A list of VirtualFile objects that match the specified regular expression. If no matching files are found, an empty list is returned.\n     */\n    fun findFilesByRegex(project: Project, regex: String): List<VirtualFile> {\n        if (cache.containsKey(regex)) {\n            return cache[regex]!!\n        }\n\n        val pattern: Pattern = try {\n            Pattern.compile(regex)\n        } catch (e: Exception) {\n            logger<PatternSearcher>().error(\"Invalid regex: $regex\")\n            return emptyList()\n        }\n\n        val baseDir: VirtualFile = project.guessProjectDir() ?: return emptyList()\n\n        val matchingFiles: MutableList<VirtualFile> = ArrayList()\n\n        VirtualFileManager.getInstance().getFileSystem(URLUtil.FILE_PROTOCOL).refresh(false)\n\n        VfsUtilCore.visitChildrenRecursively(baseDir, object : VirtualFileVisitor<Any>() {\n            override fun visitFile(file: VirtualFile): Boolean {\n                if (pattern.matcher(file.name).matches()) {\n                    matchingFiles.add(file)\n                }\n                return true\n            }\n\n        })\n\n        return matchingFiles\n    }\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/execute/shireql/ShireDateSchema.kt",
    "content": "package com.phodal.shirelang.compiler.execute.shireql\n\nimport kotlinx.datetime.*\nimport kotlinx.datetime.TimeZone\nimport java.util.*\nimport java.time.format.DateTimeFormatter\n\nclass ShireDateSchema : ShireQLSchema {\n    private val date: Instant = Clock.System.now()\n\n    fun now(): Long {\n        return date.toEpochMilliseconds()\n    }\n\n    fun dayOfWeek(): Int {\n        return Calendar.getInstance().get(Calendar.DAY_OF_WEEK)\n    }\n\n    fun year(): Int {\n        return Calendar.getInstance().get(Calendar.YEAR)\n    }\n\n    fun month(): Int {\n        return Calendar.getInstance().get(Calendar.MONTH) + 1\n    }\n\n    fun dayOfMonth(): Int {\n        return Calendar.getInstance().get(Calendar.DAY_OF_MONTH)\n    }\n\n    /**\n     * Formats the date using the specified output format.\n     *\n     * ```shire\n     * format(\"yyyy-MM-dd HH:mm:ss\")\n     * ```\n     */\n    fun format(outputFormat: String) : String {\n        val localDateTime = date.toLocalDateTime(TimeZone.currentSystemDefault())\n        val formatter = DateTimeFormatter.ofPattern(outputFormat)\n        return localDateTime.toJavaLocalDateTime().format(formatter)\n    }\n\n    override fun toString(): String {\n        return \"ShireDate(date=$date)\"\n    }\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/execute/shireql/ShireQLProcessor.kt",
    "content": "package com.phodal.shirelang.compiler.execute.shireql\n\nimport com.intellij.openapi.application.runReadAction\nimport com.intellij.openapi.diagnostic.logger\nimport com.intellij.openapi.project.Project\nimport com.intellij.psi.PsiElement\nimport com.phodal.shirecore.provider.variable.ShireQLInterpreter\nimport com.phodal.shirelang.compiler.ast.MethodCall\nimport com.phodal.shirelang.compiler.ast.hobbit.HobbitHole\nimport com.phodal.shirelang.compiler.execute.FunctionStatementProcessor\nimport java.lang.reflect.Method\nimport java.util.*\n\nclass ShireQLProcessor(override val myProject: Project, hole: HobbitHole) :\n    FunctionStatementProcessor(myProject, hole) {\n\n    override fun <T : Any> invokeMethodOrField(methodCall: MethodCall, element: T): Any? {\n        val methodName = methodCall.methodName.display()\n        val objectName = methodCall.objectName.display()\n\n        val methodArgs = methodCall.arguments\n        if (element is PsiElement) {\n            ShireQLInterpreter.provide(element.language)?.let { psiQLInterpreter ->\n                val hasPqlInterpreter = psiQLInterpreter.supportsMethod(element.language, methodName).any {\n                    it == methodName\n                }\n\n                if (hasPqlInterpreter) {\n                    return runReadAction {\n                        psiQLInterpreter.resolveCall(element, methodName, methodCall.parameters() ?: emptyList())\n                    }\n                }\n            }\n        }\n\n        val isField = methodArgs == null\n\n        if (isField) {\n            val field = element.javaClass.fields.find {\n                it.name == methodName\n            }\n\n            if (field != null) {\n                return field.get(element)\n            }\n        }\n\n        // use reflection to call method\n        val allMethods = element.javaClass.methods\n        val method = allMethods.find {\n            it.name == methodName\n        }\n        if (method != null) {\n            if (methodArgs == null) {\n                return method.invoke(element)\n            }\n\n            return method.invoke(element, methodArgs)\n        }\n\n        if (isField) {\n            // maybe getter, we try to find getter, first upper case method name first letter\n            val getterName = \"get${\n                methodName.replaceFirstChar {\n                    if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString()\n                }\n            }\"\n            val getter = allMethods.find {\n                it.name == getterName\n            }\n\n            if (getter != null) {\n                return getter.invoke(element)\n            }\n        }\n\n        // if not found, show error log\n        return showErrorLog(allMethods, element, methodName, objectName)\n    }\n\n    private fun <T : Any> showErrorLog(\n        allMethods: Array<out Method>,\n        element: T,\n        methodName: String,\n        objectName: String,\n    ): Nothing? {\n        val supportMethodNames: List<String> = allMethods.map { it.name }\n        val supportFieldNames: List<String> = element.javaClass.fields.map { it.name }\n\n        logger<ShireQLProcessor>().error(\n            \"\"\"\n            method or field not found: $objectName.$methodName\n            supported methods: $supportMethodNames\n            supported fields: $supportFieldNames\n            \"\"\".trimIndent()\n        )\n        return null\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/execute/shireql/ShireQLSchema.kt",
    "content": "package com.phodal.shirelang.compiler.execute.shireql\n\ninterface ShireQLSchema {\n\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/execute/variable/ShireQLFromType.kt",
    "content": "package com.phodal.shirelang.compiler.execute.variable\n\nenum class ShireQLFromType(val typeName: String) {\n    // PSI Query\n    PsiFile(\"PsiFile\"),\n    PsiPackage(\"PsiPackage\"),\n    PsiClass(\"PsiClass\"),\n    PsiMethod(\"PsiMethod\"),\n    PsiField(\"PsiField\"),\n\n    // GitQuery\n    GitCommit(\"GitCommit\"),\n    GitBranch(\"GitBranch\"),\n\n    // Others\n    Date(\"Date\"),\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/execute/variable/ShireQLVariableBuilder.kt",
    "content": "package com.phodal.shirelang.compiler.execute.variable\n\nimport com.intellij.openapi.project.Project\nimport com.intellij.psi.PsiElement\nimport com.phodal.shirecore.provider.shire.ShireQLDataProvider\nimport com.phodal.shirecore.provider.shire.ShireSymbolProvider\nimport com.phodal.shirecore.variable.vcs.ShireGitCommit\nimport com.phodal.shirelang.compiler.ast.hobbit.HobbitHole\nimport com.phodal.shirelang.compiler.ast.VariableElement\nimport com.phodal.shirelang.compiler.execute.shireql.ShireDateSchema\nimport com.phodal.shirelang.compiler.ast.patternaction.PatternActionFunc\n\n\nclass ShireQLVariableBuilder(val myProject: Project, hole: HobbitHole) {\n    fun buildVariables(fromStmt: PatternActionFunc.From): Map<String, List<Any>> {\n        return fromStmt.variables.associate {\n            when {\n                it.variableType.startsWith(\"Psi\") -> {\n                    it.value to lookupElement(it)\n                }\n                it.variableType.startsWith(\"Git\") -> {\n                    it.value to lookupVcsCommit(it)\n                }\n                it.variableType == ShireQLFromType.Date.typeName -> {\n                    it.value to createDateFunc(it)\n                }\n                else -> {\n                    it.value to lookupElement(it)\n                }\n            }\n        }\n    }\n\n    // cache\n    private val cache = mutableMapOf<String, List<PsiElement>>()\n\n    private fun lookupElement(it: VariableElement): List<PsiElement> {\n        if (cache.containsKey(it.variableType)) {\n            return cache[it.variableType] ?: emptyList()\n        }\n\n        val elements: List<PsiElement> = ShireSymbolProvider.all().flatMap { provider ->\n            provider.lookupElementByName(myProject, it.variableType) ?: emptyList()\n        }\n\n        cache[it.variableType] = elements\n        return elements\n    }\n\n\n    private fun lookupVcsCommit(it: VariableElement): List<ShireGitCommit> {\n        val elements: List<ShireGitCommit> = ShireQLDataProvider.all().flatMap { provider ->\n            provider.lookup(myProject, it.variableType) ?: emptyList()\n        }\n\n        return elements\n    }\n\n    private fun createDateFunc(it: VariableElement): List<ShireDateSchema> {\n        return listOf(ShireDateSchema())\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/execute/variable/VariableEvaluator.kt",
    "content": "package com.phodal.shirelang.compiler.execute.variable\n\nimport com.phodal.shirelang.compiler.ast.FrontMatterType\n\nclass VariableEvaluator(val element: Any) {\n    val valued: MutableMap<FrontMatterType, Any?> = mutableMapOf()\n\n    fun putValue(key: FrontMatterType, value: Any?) {\n        valued[key] = value\n    }\n\n    fun getValue(key: FrontMatterType): Any? {\n        return valued[key]\n    }\n}\n\nclass VariableContainerManager {\n    val variables: MutableMap<Any, VariableEvaluator> = mutableMapOf()\n\n    fun putValue(key: Any, prop: FrontMatterType, value: Any) {\n        if (!variables.containsKey(key)) {\n            variables[key] = VariableEvaluator(value)\n        }\n\n        variables[key]?.putValue(prop, value)\n    }\n\n    fun getValue(key: Any, prop: FrontMatterType): Any? {\n        return variables[key].let {\n            it?.getValue(prop)\n        }\n    }\n\n    fun isNotEmpty(): Boolean {\n        return variables.isNotEmpty()\n    }\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/parser/HobbitHoleParser.kt",
    "content": "package com.phodal.shirelang.compiler.parser\n\nimport com.intellij.openapi.application.runReadAction\nimport com.intellij.openapi.diagnostic.logger\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.TokenType.WHITE_SPACE\nimport com.intellij.psi.util.PsiTreeUtil\nimport com.intellij.psi.util.elementType\nimport com.phodal.shirelang.compiler.ast.*\nimport com.phodal.shirelang.compiler.ast.hobbit.HobbitHole\nimport com.phodal.shirelang.compiler.ast.action.RuleBasedPatternAction\nimport com.phodal.shirelang.compiler.ast.patternaction.PatternActionFunc\nimport com.phodal.shirelang.psi.*\n\nobject HobbitHoleParser {\n    private val logger = logger<HobbitHoleParser>()\n\n    fun hasFrontMatter(file: ShireFile): Boolean {\n        return PsiTreeUtil.getChildrenOfTypeAsList(file, ShireFrontMatterHeader::class.java).isNotEmpty()\n    }\n\n    fun frontMatterOffset(file: ShireFile): Int {\n        val frontMatterHeader = PsiTreeUtil.getChildrenOfTypeAsList(file, ShireFrontMatterHeader::class.java).firstOrNull()\n        return frontMatterHeader?.textOffset ?: 0\n    }\n\n    /**\n     * Parses the given ShireFrontMatterHeader and returns a FrontMatterShireConfig object.\n     *\n     * @param psiElement the ShireFrontMatterHeader to be parsed\n     * @return a FrontMatterShireConfig object if parsing is successful, null otherwise\n     */\n    fun parse(psiElement: ShireFrontMatterHeader): HobbitHole? {\n        return psiElement.children.firstOrNull()?.let {\n            val fm = processFrontMatter(it.children)\n            HobbitHole.from(fm)\n        }\n    }\n\n    fun parse(file: ShireFile): HobbitHole? {\n        return runReadAction {\n            PsiTreeUtil.getChildrenOfTypeAsList(file, ShireFrontMatterHeader::class.java)?.firstOrNull()?.let {\n                parse(it)\n            }\n        }\n    }\n\n    private fun processFrontMatter(frontMatterEntries: Array<PsiElement>): MutableMap<String, FrontMatterType> {\n        val frontMatter: MutableMap<String, FrontMatterType> = mutableMapOf()\n        var lastKey = \"\"\n\n        frontMatterEntries.forEach { entry ->\n            entry.children.forEach { child ->\n                when (child.elementType) {\n                    ShireTypes.LIFECYCLE_ID,\n                    ShireTypes.FRONT_MATTER_KEY,\n                        -> {\n                        lastKey = child.text\n                    }\n\n                    ShireTypes.FRONT_MATTER_VALUE -> {\n                        frontMatter[lastKey] = parseFrontMatterValue(child)\n                            ?: FrontMatterType.STRING(\"FrontMatter value parsing failed: ${child.text}\")\n                    }\n\n                    ShireTypes.PATTERN_ACTION -> {\n                        frontMatter[lastKey] = parsePatternAction(child)\n                            ?: FrontMatterType.STRING(\"Pattern action parsing failed: ${child.text}\")\n                    }\n\n                    ShireTypes.LOGICAL_AND_EXPR -> {\n                        frontMatter[lastKey] = parseLogicAndExprToType(child as ShireLogicalAndExpr)\n                            ?: FrontMatterType.STRING(\"Logical expression parsing failed: ${child.text}\")\n                    }\n\n                    ShireTypes.LOGICAL_OR_EXPR -> {\n                        frontMatter[lastKey] = parseLogicOrExprToType(child as ShireLogicalOrExpr)\n                            ?: FrontMatterType.STRING(\"Logical expression parsing failed: ${child.text}\")\n                    }\n\n                    ShireTypes.CALL_EXPR -> {\n                        parseExpr(child)?.let {\n                            frontMatter[lastKey] = FrontMatterType.EXPRESSION(it)\n                        }\n                    }\n\n                    ShireTypes.FUNCTION_STATEMENT -> {\n                        frontMatter[lastKey] = parseFunction(child as ShireFunctionStatement)\n                    }\n\n                    /**\n                     * For blocked when condition\n                     *\n                     * ```shire\n                     * when: { $filePath.contains(\"src/main/java\") && $fileName.contains(\".java\") }\n                     * ```\n                     */\n                    ShireTypes.VARIABLE_EXPR -> {\n                        val childExpr = (child as? ShireVariableExpr)?.expr\n                        if (childExpr != null) {\n                            parseExpr(childExpr)?.let {\n                                frontMatter[lastKey] = FrontMatterType.EXPRESSION(it)\n                            }\n                        }\n                    }\n\n                    ShireTypes.LITERAL_EXPR -> {\n                        parseExpr(child)?.let {\n                            frontMatter[lastKey] = FrontMatterType.EXPRESSION(it)\n                        }\n                    }\n\n                    ShireTypes.FOREIGN_FUNCTION -> {\n                        parseForeignFunc(lastKey, child as ShireForeignFunction)?.let {\n                            frontMatter[lastKey] = FrontMatterType.EXPRESSION(it)\n                        }\n                    }\n\n                    else -> {\n                        logger.warn(\"processFrontMatter, Unknown FrontMatter type: ${child.elementType}, value: $child\")\n                    }\n                }\n            }\n        }\n\n        return frontMatter\n    }\n\n    private fun parseForeignFunc(fmKey: String, function: ShireForeignFunction): Statement {\n        val funcPath = function.foreignPath.quoteString.text.removeSurrounding(\"\\\"\")\n        val accessFuncName = function.foreignFuncName?.text ?: \"\"\n        val inputTypes = function.foreignTypeList.map { it.text }\n        val returnVars = function.foreignOutput?.children?.associate { it.text to \"\" } ?: emptyMap()\n\n        return ForeignFunctionStmt(fmKey, funcPath, accessFuncName, inputTypes, returnVars)\n    }\n\n    private fun parseFunction(statement: ShireFunctionStatement): FrontMatterType {\n        return when (val body = statement.functionBody?.firstChild) {\n            is ShireQueryStatement -> {\n                ShireAstQLParser.parse(body)\n            }\n\n            is ShireActionBody -> {\n                val expressions = body.actionExprList.mapNotNull {\n                    parseExpr(it)\n                }.map {\n                    FrontMatterType.EXPRESSION(it)\n                }\n\n                FrontMatterType.ARRAY(expressions)\n            }\n\n            null -> {\n                FrontMatterType.EMPTY()\n            }\n\n            else -> {\n                val expr = parseExpr(body)\n                if (expr is Statement) {\n                    return FrontMatterType.EXPRESSION(expr)\n                }\n\n                logger.error(\"parseFunction, Unknown function type: ${body.elementType}\")\n                FrontMatterType.STRING(\"Unknown function type: ${body.elementType}\")\n            }\n        }\n    }\n\n    private fun parseLogicAndExprToType(child: ShireLogicalAndExpr): FrontMatterType? {\n        val logicalExpression = parseLogicAndExpr(child) ?: return null\n        return FrontMatterType.EXPRESSION(logicalExpression)\n    }\n\n    private fun parseLogicAndExpr(child: ShireLogicalAndExpr): LogicalExpression? {\n        val left = child.exprList?.firstOrNull() ?: return null\n        val right = child.exprList?.lastOrNull() ?: return null\n\n        val leftStmt = parseExpr(left) ?: return null\n        val rightStmt = parseExpr(right) ?: return null\n\n        val logicalExpression = LogicalExpression(\n            left = leftStmt,\n            operator = OperatorType.And,\n            right = rightStmt\n        )\n\n        return logicalExpression\n    }\n\n    private fun parseLogicOrExprToType(child: ShireLogicalOrExpr): FrontMatterType? {\n        val logicOrExpr = parseLogicOrExpr(child) ?: return null\n        return FrontMatterType.EXPRESSION(logicOrExpr)\n    }\n\n    private fun parseLogicOrExpr(child: ShireLogicalOrExpr): LogicalExpression? {\n        val left = child.exprList.firstOrNull() ?: return null\n        val right = child.exprList.lastOrNull() ?: return null\n\n        val leftStmt = parseExpr(left) ?: return null\n        val rightStmt = parseExpr(right) ?: return null\n\n        val logicOrExpr = LogicalExpression(\n            left = leftStmt,\n            operator = OperatorType.Or,\n            right = rightStmt\n        )\n        return logicOrExpr\n    }\n\n    /**\n     * This function is used to parse an expression of type PsiElement into a Statement. The type of Statement returned depends on the type of the expression.\n     *\n     * @param expr The PsiElement expression to be parsed. This expression can be of type CALL_EXPR, EQ_COMPARISON_EXPR, INEQ_COMPARISON_EXPR, or any other type.\n     *\n     * If the expression is of type CALL_EXPR, the function finds the first child of type ShireExpr and builds a method call with the found ShireExpr and the list of expressions in the ShireCallExpr.\n     *\n     * If the expression is of type EQ_COMPARISON_EXPR, the function parses the first and last child of the expression into a Comparison statement with an equal operator.\n     *\n     * If the expression is of type INEQ_COMPARISON_EXPR, the function parses the first and last child of the expression into a Comparison statement with an operator determined by the ineqComparisonOp text of the ShireIneqComparisonExpr.\n     *\n     * If the expression is of any other type, the function logs a warning and returns a Comparison statement with an equal operator and empty string operands.\n     *\n     * @return A Statement parsed from the given expression. The type of Statement depends on the type of the expression.\n     */\n    fun parseExpr(expr: PsiElement): Statement? = when (expr) {\n        is ShireCallExpr -> {\n            val expressionList = expr.expressionList\n            val hasParentheses = expressionList?.prevSibling?.text == \"(\"\n\n            buildMethodCall(expr.refExpr, expressionList?.children, hasParentheses)\n        }\n\n        is ShireEqComparisonExpr -> {\n            val variable = parseRefExpr(expr.children.firstOrNull())\n            val value = parseRefExpr(expr.children.lastOrNull())\n            Comparison(variable, Operator(OperatorType.Equal), value)\n        }\n\n        is ShireIneqComparisonExpr -> {\n            val variable = parseRefExpr(expr.children.firstOrNull())\n            val value = parseRefExpr(expr.children.lastOrNull())\n            val operatorType = OperatorType.fromString(expr.ineqComparisonOp.text)\n            Comparison(variable, Operator(operatorType), value)\n        }\n\n        is ShireLogicalAndExpr -> {\n            parseLogicAndExpr(expr)\n                ?: Comparison(FrontMatterType.STRING(\"\"), Operator(OperatorType.Equal), FrontMatterType.STRING(\"\"))\n        }\n\n        is ShireLogicalOrExpr -> {\n            parseLogicOrExpr(expr)\n                ?: Comparison(FrontMatterType.STRING(\"\"), Operator(OperatorType.Equal), FrontMatterType.STRING(\"\"))\n        }\n\n        is ShireRefExpr -> {\n            if (expr.expr == null) {\n                Value(FrontMatterType.IDENTIFIER(expr.identifier.text))\n            } else {\n                val methodCall = buildMethodCall(expr, null, false)\n                methodCall\n            }\n        }\n\n        is ShireLiteralExpr -> {\n            Value(parseLiteral(expr))\n        }\n\n        is ShireActionExpr -> {\n            when (expr.firstChild) {\n                is ShireFuncCall -> {\n                    val args = parseParameters(expr.funcCall)\n                    MethodCall(FrontMatterType.IDENTIFIER(expr.funcCall!!.funcName.text), FrontMatterType.EMPTY(), args)\n                }\n\n                is ShireCaseBody -> {\n                    parseExprCaseBody(expr.firstChild as ShireCaseBody)\n                }\n\n                else -> {\n                    logger.warn(\"parseExpr, Unknown action expression type: ${expr.firstChild.elementType}\")\n                    null\n                }\n            }\n        }\n\n        is ShireConditionStatement -> {\n            val condition = parseLiteral(expr.caseCondition)\n            val body = parseRefExpr(expr.expr)\n\n            when (body) {\n                is FrontMatterType.EXPRESSION -> {\n                    CaseKeyValue(condition, body)\n                }\n\n                is FrontMatterType.STRING -> {\n                    CaseKeyValue(condition, FrontMatterType.EXPRESSION(Value(body)))\n                }\n\n                else -> {\n                    logger.warn(\"parseExpr, Unknown condition type: ${expr.expr?.elementType}\")\n                    null\n                }\n            }\n        }\n\n        else -> {\n            logger.warn(\"parseExpr, Unknown expression type: ${expr.elementType}\")\n            null\n        }\n    }\n\n    private fun parseExprCaseBody(caseBody: ShireCaseBody): ConditionCase? {\n        val condition = caseBody.conditionFlag?.conditionStatementList?.mapNotNull {\n            val condition = parseExpr(it)\n            if (condition != null) {\n                FrontMatterType.EXPRESSION(condition)\n            } else {\n                logger.warn(\"parseExprCaseBody, Unknown condition type: ${it.elementType}\")\n                null\n            }\n        } ?: emptyList()\n\n        val body = caseBody.casePatternActionList.mapNotNull {\n            val key = parseLiteral(it.caseCondition)\n            val processor = parseActionBodyFuncCall(it.actionBody.actionExprList)\n            FrontMatterType.EXPRESSION(CaseKeyValue(key, FrontMatterType.EXPRESSION(processor)))\n        }\n\n        return ConditionCase(condition, body)\n    }\n\n    private fun parseRefExpr(expr: PsiElement?): FrontMatterType {\n        return when (expr) {\n            is ShireLiteralExpr -> {\n                parseLiteral(expr)\n            }\n\n            // fake refExpr ::= expr? '.' IDENTIFIER\n            is ShireRefExpr -> {\n                if (expr.expr == null) {\n                    FrontMatterType.IDENTIFIER(expr.identifier.text)\n                } else {\n                    val methodCall = buildMethodCall(expr, null, false)\n                    FrontMatterType.EXPRESSION(methodCall)\n                }\n            }\n\n            is ShireCallExpr -> {\n                val expressionList = expr.expressionList\n                val hasParentheses = expressionList?.prevSibling?.text == \"(\"\n\n                val methodCall = buildMethodCall(expr.refExpr, expressionList?.children, hasParentheses)\n                FrontMatterType.EXPRESSION(methodCall)\n            }\n\n            is ShireIneqComparisonExpr -> {\n                val variable = parseRefExpr(expr.children.firstOrNull())\n                val value = parseRefExpr(expr.children.lastOrNull())\n                val operator = Operator(OperatorType.fromString(expr.ineqComparisonOp.text))\n\n                val comparison = Comparison(variable, operator, value)\n                FrontMatterType.EXPRESSION(comparison)\n            }\n\n            is ShireLogicalAndExpr -> {\n                parseLogicAndExpr(expr)?.let {\n                    FrontMatterType.EXPRESSION(it)\n                } ?: FrontMatterType.ERROR(\"cannot parse ShireLogicalAndExpr: ${expr.text}\")\n            }\n\n            is ShireLogicalOrExpr -> {\n                parseLogicOrExpr(expr)?.let {\n                    FrontMatterType.EXPRESSION(it)\n                } ?: FrontMatterType.ERROR(\"cannot parse ShireLogicalOrExpr: ${expr.text}\")\n            }\n\n            is ShireEqComparisonExpr -> {\n                val variable = parseRefExpr(expr.children.firstOrNull())\n                val value = parseRefExpr(expr.children.lastOrNull())\n                val operator = Operator(OperatorType.Equal)\n\n                val comparison = Comparison(variable, operator, value)\n                FrontMatterType.EXPRESSION(comparison)\n            }\n\n            else -> {\n                logger.warn(\"parseRefExpr, Unknown expression type: ${expr?.elementType}\")\n                FrontMatterType.STRING(\"\")\n            }\n        }\n    }\n\n\n    private fun buildMethodCall(\n        refExpr: ShireRefExpr,\n        expressionList: Array<PsiElement>?,\n        hasParentheses: Boolean,\n    ): MethodCall {\n        val left = if (refExpr.expr == null) {\n            FrontMatterType.IDENTIFIER(refExpr.identifier.text)\n        } else {\n            parseRefExpr(refExpr.expr)\n        }\n\n        val id = refExpr.expr?.nextSibling?.nextSibling\n        val right = FrontMatterType.IDENTIFIER(id?.text ?: \"\")\n\n        var args = expressionList?.map {\n            parseRefExpr(it)\n        }\n\n        // fix for () lost in display()\n        if (hasParentheses && args == null) {\n            args = emptyList()\n        }\n\n        return MethodCall(left, right, args)\n    }\n\n    private fun parseLiteral(ref: PsiElement): FrontMatterType {\n        val firstChild = ref.firstChild\n        return when (firstChild.elementType) {\n            ShireTypes.IDENTIFIER -> {\n                FrontMatterType.IDENTIFIER(ref.text)\n            }\n\n            ShireTypes.NUMBER -> {\n                FrontMatterType.NUMBER(ref.text.toInt())\n            }\n\n            ShireTypes.QUOTE_STRING -> {\n                val value = ref.text.substring(1, ref.text.length - 1)\n                FrontMatterType.STRING(value)\n            }\n\n            ShireTypes.VARIABLE_START -> {\n                val next = ref.lastChild\n                FrontMatterType.VARIABLE(next.text)\n            }\n\n            ShireTypes.DEFAULT -> {\n                FrontMatterType.IDENTIFIER(ref.text)\n            }\n\n            else -> {\n                logger.warn(\"parseLiteral, Unknown ref type: ${firstChild.elementType}\")\n                FrontMatterType.STRING(ref.text)\n            }\n        }\n    }\n\n    private fun parseFrontMatterValue(element: PsiElement): FrontMatterType? {\n        when (element) {\n            is ShireObjectKeyValue -> {\n                val map: MutableMap<String, FrontMatterType> = mutableMapOf()\n                element.children.mapNotNull {\n                    if (it.elementType == ShireTypes.KEY_VALUE) {\n                        processFrontMatter(it.children)\n                    } else {\n                        null\n                    }\n                }.forEach {\n                    map.putAll(it)\n                }\n\n                return FrontMatterType.OBJECT(map)\n            }\n        }\n\n        return when (element.firstChild.elementType) {\n            ShireTypes.IDENTIFIER -> {\n                FrontMatterType.IDENTIFIER(element.text)\n            }\n\n            ShireTypes.DATE -> {\n                FrontMatterType.DATE(element.text)\n            }\n\n            ShireTypes.QUOTE_STRING -> {\n                val value = element.text.substring(1, element.text.length - 1)\n                FrontMatterType.STRING(value)\n            }\n\n            ShireTypes.NUMBER -> {\n                FrontMatterType.NUMBER(element.text.toInt())\n            }\n\n            ShireTypes.BOOLEAN -> {\n                FrontMatterType.BOOLEAN(element.text.toBoolean())\n            }\n\n            ShireTypes.FRONT_MATTER_ARRAY -> {\n                val array: List<FrontMatterType> = parseArray(element)\n                FrontMatterType.ARRAY(array)\n            }\n\n            ShireTypes.NEWLINE -> {\n                return parseFrontMatterValue(element.firstChild.nextSibling)\n            }\n\n            ShireTypes.LBRACKET,\n            ShireTypes.RBRACKET,\n            ShireTypes.COMMA,\n            WHITE_SPACE,\n            null,\n                -> {\n                null\n            }\n\n            else -> {\n                logger.warn(\"parseFrontMatterValue, Unknown frontmatter type: ${element.firstChild}\")\n                null\n            }\n        }\n    }\n\n    private fun parsePatternAction(element: PsiElement): FrontMatterType? {\n        val pattern = element.children.firstOrNull()?.text ?: return null\n\n        val actionBlock = PsiTreeUtil.getChildOfType(element, ShireActionBlock::class.java)\n        val actionBody = actionBlock?.actionBody ?: return null\n\n        val processor: List<PatternActionFunc> = parseActionBodyFuncCall(actionBody.actionExprList).processors\n        return FrontMatterType.PATTERN(RuleBasedPatternAction(pattern, processor))\n    }\n\n    private fun parseActionBodyFuncCall(shireActionExprs: List<ShireActionExpr>?): Processor {\n        val processor: MutableList<PatternActionFunc> = mutableListOf()\n        shireActionExprs?.forEach { expr: ShireActionExpr ->\n            expr.funcCall?.let { funcCall ->\n                parseActionBodyFunCall(funcCall)?.let {\n                    processor.add(it)\n                }\n            }\n            expr.caseBody?.let { caseBody ->\n                parseExprCaseBody(caseBody)?.let { conditionCase ->\n                    val cases = conditionCase.cases.map {\n                        (it as FrontMatterType.EXPRESSION).value as CaseKeyValue\n                    }\n\n                    processor.add(PatternActionFunc.CaseMatch(cases))\n                }\n            }\n        }\n\n        return Processor(processor)\n    }\n\n    private fun parseActionBodyFunCall(funcCall: ShireFuncCall?): PatternActionFunc? {\n        val args = parseParameters(funcCall) ?: emptyList()\n        val funcName = funcCall?.funcName?.text ?: return null\n        return PatternActionFunc.from(funcName, args)\n    }\n\n    private fun parseParameters(funcCall: ShireFuncCall?): List<String>? = runReadAction {\n        PsiTreeUtil.findChildOfType(funcCall, ShirePipelineArgs::class.java)\n            ?.let {\n                it.pipelineArgList.map { arg -> arg }\n            }?.map {\n                when (it.firstChild.elementType) {\n                    ShireTypes.QUOTE_STRING -> it.text\n                        .removeSurrounding(\"\\\"\")\n                        .removeSurrounding(\"'\")\n\n                    ShireTypes.IDENTIFIER -> it.text.removeSurrounding(\"\\\"\")\n                    else -> it.text\n                }\n            }\n    }\n\n    private fun parseArray(element: PsiElement): List<FrontMatterType> {\n        val array = mutableListOf<FrontMatterType>()\n        var arrayElement: PsiElement? = element.children.firstOrNull()?.firstChild\n        while (arrayElement != null) {\n            parseFrontMatterValue(arrayElement)?.let {\n                array.add(it)\n            }\n            arrayElement = arrayElement.nextSibling\n        }\n\n        return array\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/parser/ShireAstQLParser.kt",
    "content": "package com.phodal.shirelang.compiler.parser\n\nimport com.phodal.shirelang.compiler.ast.FrontMatterType\nimport com.phodal.shirelang.compiler.ast.ShirePsiQueryStatement\nimport com.phodal.shirelang.compiler.ast.Statement\nimport com.phodal.shirelang.compiler.ast.VariableElement\nimport com.phodal.shirelang.psi.ShireFromClause\nimport com.phodal.shirelang.psi.ShireQueryStatement\nimport com.phodal.shirelang.psi.ShireSelectClause\nimport com.phodal.shirelang.psi.ShireWhereClause\n\nobject ShireAstQLParser {\n    fun parse(statement: ShireQueryStatement): FrontMatterType {\n        val value = ShirePsiQueryStatement(\n            parseFrom(statement.fromClause),\n            parseWhere(statement.whereClause)!!,\n            parseSelect(statement.selectClause)\n        )\n\n        return FrontMatterType.QUERY_STATEMENT(value)\n    }\n\n    private fun parseFrom(fromClause: ShireFromClause): List<VariableElement> {\n        return fromClause.psiElementDecl.psiVarDeclList.map {\n            VariableElement(it.psiType.identifier.text, it.identifier.text)\n        }\n    }\n\n    private fun parseWhere(whereClause: ShireWhereClause): Statement? {\n        return HobbitHoleParser.parseExpr(whereClause.expr)\n    }\n\n    private fun parseSelect(selectClause: ShireSelectClause): List<Statement> {\n        return selectClause.exprList.mapNotNull {\n            HobbitHoleParser.parseExpr(it)\n        }\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/parser/ShireError.kt",
    "content": "package com.phodal.shirelang.compiler.parser\n\nconst val SHIRE_ERROR = \"<ShireError>\""
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/parser/ShireParsedResult.kt",
    "content": "package com.phodal.shirelang.compiler.parser\n\nimport com.phodal.shirecore.agent.CustomAgent\nimport com.phodal.shirelang.compiler.ast.hobbit.HobbitHole\nimport com.phodal.shirelang.compiler.variable.VariableTable\nimport com.phodal.shirelang.psi.ShireFile\n\ndata class ShireParsedResult(\n    /**\n     * The origin Shire content\n     */\n    var sourceCode: String = \"\",\n\n    /**\n     * Output String of a compiler result, not the final result\n     */\n    var shireOutput: String = \"\",\n\n    /**\n     * Is local command only\n     */\n    var isLocalCommand: Boolean = false,\n\n    /**\n     * Has error\n     */\n    var hasError: Boolean = false,\n\n    /**\n     * Execute agent\n     */\n    var executeAgent: CustomAgent? = null,\n\n    /**\n     * Next job file to be executed\n     */\n    var nextJob: ShireFile? = null,\n\n    /**\n     * The frontmatter of the file, which contains the configuration of Shire\n     */\n    var config: HobbitHole? = null,\n\n    /**\n     * Symbol table for all variables\n     */\n    var variableTable: VariableTable = VariableTable(),\n)\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/parser/ShireSyntaxAnalyzer.kt",
    "content": "package com.phodal.shirelang.compiler.parser\n\nimport com.intellij.lang.parser.GeneratedParserUtilBase.DUMMY_BLOCK\nimport com.intellij.openapi.application.runReadAction\nimport com.intellij.openapi.diagnostic.logger\nimport com.intellij.openapi.editor.Document\nimport com.intellij.openapi.editor.Editor\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.project.guessProjectDir\nimport com.intellij.psi.PsiDocumentManager\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.TokenType.WHITE_SPACE\nimport com.intellij.psi.util.PsiTreeUtil\nimport com.intellij.psi.util.elementType\nimport com.phodal.shirecore.agent.CustomAgent\nimport com.phodal.shirelang.compiler.execute.command.*\nimport com.phodal.shirelang.compiler.template.TemplateCompiler\nimport com.phodal.shirelang.compiler.variable.VariableTable\nimport com.phodal.shirelang.completion.dataprovider.BuiltinCommand\nimport com.phodal.shirelang.completion.dataprovider.CustomCommand\nimport com.phodal.shirelang.parser.CodeBlockElement\nimport com.phodal.shirelang.psi.*\nimport com.phodal.shirelang.psi.ShireTypes.MARKDOWN_HEADER\nimport kotlinx.coroutines.runBlocking\nimport java.util.*\n\n\nval CACHED_COMPILE_RESULT = mutableMapOf<String, ShireParsedResult>()\n\n/**\n * ShireCompiler class is responsible for compiling Shire files by processing different elements such as text segments, newlines, code blocks, used commands, comments, agents, variables, and builtin commands.\n * It takes a Project, ShireFile, Editor, and PsiElement as input parameters.\n * The compile() function processes the elements in the ShireFile and generates a ShireCompiledResult object containing the compiled output.\n * The processUsed() function handles the processing of used commands, agents, and variables within the ShireFile.\n * The processingCommand() function executes the specified builtin command with the provided properties and updates the output accordingly.\n * The lookupNextCode() function looks up the next code block element following a used command.\n * The lookupNextTextSegment() function looks up the next text segment following a used command.\n */\nclass ShireSyntaxAnalyzer(\n    private val myProject: Project,\n    private val file: ShireFile,\n    private val editor: Editor? = null,\n    private val element: PsiElement? = null,\n) {\n    private var skipNextCode: Boolean = false\n    private val logger = logger<ShireSyntaxAnalyzer>()\n    private val result = ShireParsedResult()\n    private val output: StringBuilder = StringBuilder()\n\n    private val variableTable = VariableTable()\n\n    companion object {\n        const val FLOW_FALG = \"[flow]:\"\n    }\n\n    /**\n     * @return ShireCompiledResult object containing the compiled result\n     */\n    fun parseAndExecuteLocalCommand(): ShireParsedResult {\n        result.sourceCode = file.text\n        val iterator = file.children.iterator()\n\n        while (iterator.hasNext()) {\n            val psiElement = iterator.next()\n\n            when (psiElement.elementType) {\n                ShireTypes.TEXT_SEGMENT -> output.append(psiElement.text)\n                ShireTypes.NEWLINE -> output.append(\"\\n\")\n                ShireTypes.CODE -> {\n                    if (skipNextCode) {\n                        skipNextCode = false\n                        continue\n                    }\n\n                    output.append(psiElement.text)\n                }\n\n                ShireTypes.USED -> processUsed(psiElement as ShireUsed)\n                ShireTypes.COMMENTS -> {\n                    if (psiElement.text.startsWith(FLOW_FALG)) {\n                        val fileName = psiElement.text.substringAfter(FLOW_FALG).trim()\n                        val content =\n                            myProject.guessProjectDir()?.findFileByRelativePath(fileName)?.let { virtualFile ->\n                                virtualFile.inputStream.bufferedReader().use { reader -> reader.readText() }\n                            }\n\n                        if (content != null) {\n                            val shireFile = ShireFile.fromString(myProject, content)\n                            result.nextJob = shireFile\n                        }\n                    }\n                }\n\n                ShireTypes.FRONTMATTER_START -> {\n                    val nextElement = PsiTreeUtil.findChildOfType(\n                        psiElement.parent, ShireFrontMatterHeader::class.java\n                    ) ?: continue\n                    result.config = HobbitHoleParser.parse(nextElement)\n                }\n\n                ShireTypes.FRONT_MATTER_HEADER -> {\n                    result.config = HobbitHoleParser.parse(psiElement as ShireFrontMatterHeader)\n                }\n\n                WHITE_SPACE, DUMMY_BLOCK -> output.append(psiElement.text)\n                ShireTypes.VELOCITY_EXPR -> {\n                    processVelocityExpr(psiElement as ShireVelocityExpr)\n                    logger.info(\"Velocity expression found: ${psiElement.text}\")\n                }\n\n                MARKDOWN_HEADER -> {\n                    output.append(\"#[[${psiElement.text}]]#\")\n                }\n\n                else -> {\n                    output.append(psiElement.text)\n                    logger.warn(\"Unknown element type: ${psiElement.elementType}, text: ${psiElement.text}\")\n                }\n            }\n        }\n\n        result.shireOutput = output.toString()\n        result.variableTable = variableTable\n\n        CACHED_COMPILE_RESULT[file.name] = result\n        return result\n    }\n\n    private fun processVelocityExpr(velocityExpr: ShireVelocityExpr) {\n        handleNextSiblingForChild(velocityExpr) { next ->\n            if (next is ShireIfExpr) {\n                handleNextSiblingForChild(next) {\n                    when (it) {\n                        is ShireIfClause, is ShireElseifClause, is ShireElseClause -> {\n                            handleNextSiblingForChild(it, ::processIfClause)\n                        }\n\n                        else -> output.append(it.text)\n                    }\n                }\n            } else {\n                output.append(next.text)\n            }\n        }\n    }\n\n    private fun processIfClause(clauseContent: PsiElement) {\n        when (clauseContent) {\n            is ShireExpr -> {\n                addVariable(clauseContent)\n                if (!result.hasError) output.append(clauseContent.text)\n            }\n\n            is ShireVelocityBlock -> {\n                ShireFile.fromString(myProject, clauseContent.text).let { file ->\n                    ShireSyntaxAnalyzer(myProject, file).parseAndExecuteLocalCommand().let {\n                        output.append(it.shireOutput)\n                        variableTable.addVariable(it.variableTable)\n                        result.hasError = it.hasError\n                    }\n                }\n\n            }\n\n            else -> {\n                output.append(clauseContent.text)\n            }\n        }\n    }\n\n    private fun addVariable(psiElement: PsiElement?) {\n        if (psiElement == null) return\n        val queue = LinkedList<PsiElement>()\n        queue.push(psiElement)\n        while (!queue.isEmpty() && !result.hasError) {\n            val e = queue.pop()\n            if (e.firstChild.elementType == ShireTypes.VARIABLE_START) {\n                processVariable(e.firstChild)\n            } else {\n                e.children.forEach {\n                    queue.push(it)\n                }\n            }\n        }\n    }\n\n    private fun handleNextSiblingForChild(element: PsiElement?, handle: (PsiElement) -> Unit) {\n        var child: PsiElement? = element?.firstChild\n        while (child != null && !result.hasError) {\n            handle(child)\n            child = child.nextSibling\n        }\n    }\n\n    private fun processUsed(used: ShireUsed) {\n        val firstChild = used.firstChild\n        val id = firstChild.nextSibling\n\n        when (firstChild.elementType) {\n            ShireTypes.COMMAND_START -> {\n                val command = BuiltinCommand.fromString(id?.text ?: \"\")\n                if (command == null) {\n                    CustomCommand.fromString(myProject, id?.text ?: \"\")?.let { cmd ->\n                        ShireFile.fromString(myProject, cmd.content).let { file ->\n                            ShireSyntaxAnalyzer(myProject, file).parseAndExecuteLocalCommand().let {\n                                output.append(it.shireOutput)\n                                result.hasError = it.hasError\n                            }\n                        }\n\n                        return\n                    }\n\n\n                    output.append(used.text)\n                    logger.warn(\"Unknown command: ${id?.text}\")\n                    result.hasError = true\n                    return\n                }\n\n                if (!command.requireProps) {\n                    processingCommand(command, \"\", used, fallbackText = used.text)\n                    return\n                }\n\n                val propElement = id.nextSibling?.nextSibling\n                val isProp = (propElement.elementType == ShireTypes.COMMAND_PROP)\n                if (!isProp) {\n                    output.append(used.text)\n                    logger.warn(\"No command prop found: ${used.text}\")\n                    result.hasError = true\n                    return\n                }\n\n                processingCommand(command, propElement!!.text, used, fallbackText = used.text)\n            }\n\n            ShireTypes.AGENT_START -> {\n                val shireAgentId = id as ShireAgentId\n                val configs = CustomAgent.loadFromProject(myProject).filter {\n                    it.name == shireAgentId.quoteString?.text?.removeSurrounding(\"\\\"\")?.removeSurrounding(\"'\")\n                            || it.name == shireAgentId.identifier?.text\n                }\n\n                if (configs.isNotEmpty()) {\n                    result.executeAgent = configs.first()\n                }\n            }\n\n            ShireTypes.VARIABLE_START -> {\n                processVariable(firstChild)\n                if (!result.hasError) output.append(used.text)\n            }\n\n            else -> {\n                logger.warn(\"Unknown [cc.unitmesh.devti.language.psi.ShireUsed] type: ${firstChild.elementType}\")\n                output.append(used.text)\n            }\n        }\n    }\n\n    private fun processVariable(variableStart: PsiElement) {\n        if (variableStart.elementType != ShireTypes.VARIABLE_START) {\n            logger.warn(\"Illegal type: ${variableStart.elementType}\")\n            return\n        }\n        val variableId = variableStart.nextSibling?.text\n\n        val currentEditor = editor ?: TemplateCompiler.defaultEditor(myProject)\n        val currentElement = element ?: TemplateCompiler.defaultElement(myProject, currentEditor)\n\n        if (currentElement == null) {\n            output.append(\"$SHIRE_ERROR No element found for variable: ${variableStart.text}\")\n            result.hasError = true\n            return\n        }\n\n        val lineNo = try {\n            runReadAction {\n                val containingFile = currentElement.containingFile\n                val document: Document? =     PsiDocumentManager.getInstance(variableStart.project).getDocument(containingFile)\n                document?.getLineNumber(variableStart.textRange.startOffset) ?: 0\n            }\n        } catch (e: Exception) {\n            0\n        }\n\n        variableTable.addVariable(variableId ?: \"\", VariableTable.VariableType.String, lineNo)\n    }\n\n    private fun processingCommand(commandNode: BuiltinCommand, prop: String, used: ShireUsed, fallbackText: String) {\n        val command: ShireCommand = when (commandNode) {\n            BuiltinCommand.FILE -> {\n                FileShireCommand(myProject, prop)\n            }\n\n            BuiltinCommand.REV -> {\n                RevShireCommand(myProject, prop)\n            }\n\n            BuiltinCommand.SYMBOL -> {\n                result.isLocalCommand = true\n                SymbolShireCommand(myProject, prop)\n            }\n\n            BuiltinCommand.WRITE -> {\n                result.isLocalCommand = true\n                val shireCode: CodeBlockElement? = lookupNextCode(used)\n                if (shireCode == null) {\n                    PrintShireCommand(\"/\" + commandNode.commandName + \":\" + prop)\n                } else {\n                    WriteShireCommand(myProject, prop, shireCode.codeText(), used)\n                }\n            }\n\n            BuiltinCommand.PATCH -> {\n                result.isLocalCommand = true\n                val shireCode: CodeBlockElement? = lookupNextCode(used)\n                if (shireCode == null) {\n                    PrintShireCommand(\"/\" + commandNode.commandName + \":\" + prop)\n                } else {\n                    PatchShireCommand(myProject, prop, shireCode.codeText())\n                }\n            }\n\n            BuiltinCommand.COMMIT -> {\n                result.isLocalCommand = true\n                val shireCode: CodeBlockElement? = lookupNextCode(used)\n                if (shireCode == null) {\n                    PrintShireCommand(\"/\" + commandNode.commandName + \":\" + prop)\n                } else {\n                    CommitShireCommand(myProject, shireCode.codeText())\n                }\n            }\n\n            BuiltinCommand.RUN -> {\n                result.isLocalCommand = true\n                RunShireCommand(myProject, prop)\n            }\n\n            BuiltinCommand.FILE_FUNC -> {\n                result.isLocalCommand = true\n                FileFuncShireCommand(myProject, prop)\n            }\n\n            BuiltinCommand.SHELL -> {\n                result.isLocalCommand = true\n                ShellShireCommand(myProject, prop)\n            }\n\n            BuiltinCommand.BROWSE -> {\n                result.isLocalCommand = true\n                BrowseShireCommand(myProject, prop)\n            }\n\n            BuiltinCommand.REFACTOR -> {\n                result.isLocalCommand = true\n                val nextTextSegment = lookupNextTextSegment(used)\n                RefactorShireCommand(myProject, prop, nextTextSegment)\n            }\n\n            BuiltinCommand.GOTO -> {\n                result.isLocalCommand = true\n                GotoShireCommand(myProject, prop, used)\n            }\n\n            BuiltinCommand.STRUCTURE -> {\n                result.isLocalCommand = true\n                StructureShireCommand(myProject, prop)\n            }\n\n            BuiltinCommand.DATABASE -> {\n                result.isLocalCommand = true\n                val shireCode: String? = lookupNextCode(used)?.text\n                DatabaseShireCommand(myProject, prop, shireCode)\n            }\n\n            BuiltinCommand.DIR -> {\n                result.isLocalCommand = true\n                DirShireCommand(myProject, prop)\n            }\n\n            BuiltinCommand.LOCAL_SEARCH -> {\n                result.isLocalCommand = true\n                val shireCode: String? = lookupNextCode(used)?.text\n                LocalSearchShireCommand(myProject, prop, shireCode)\n            }\n\n            BuiltinCommand.RELATED -> {\n                result.isLocalCommand = true\n                RelatedSymbolInsCommand(myProject, prop)\n            }\n\n            BuiltinCommand.OPEN -> {\n                result.isLocalCommand = true\n                OpenShireCommand(myProject, prop)\n            }\n\n            BuiltinCommand.RIPGREP_SEARCH -> {\n                result.isLocalCommand = true\n                val shireCode: String? = lookupNextCode(used)?.text\n                RipgrepSearchShireCommand(myProject, prop, shireCode)\n            }\n\n        }\n\n        val execResult = runBlocking {\n            command.doExecute()\n        }\n\n        val isSucceed = execResult?.contains(SHIRE_ERROR) == false\n        val result = if (isSucceed) {\n            val hasReadCodeBlock = commandNode in listOf(\n                BuiltinCommand.WRITE,\n                BuiltinCommand.PATCH,\n                BuiltinCommand.COMMIT,\n                BuiltinCommand.DATABASE,\n            )\n\n            if (hasReadCodeBlock) {\n                skipNextCode = true\n            }\n\n            execResult\n        } else {\n            execResult ?: fallbackText\n        }\n\n        output.append(result)\n    }\n\n    private fun lookupNextCode(used: ShireUsed): CodeBlockElement? {\n        val shireCode: CodeBlockElement?\n        var next: PsiElement? = used\n        while (true) {\n            next = next?.nextSibling\n            if (next == null) {\n                shireCode = null\n                break\n            }\n\n            if (next.elementType == ShireTypes.CODE) {\n                shireCode = next as CodeBlockElement\n                break\n            }\n        }\n\n        return shireCode\n    }\n\n    private fun lookupNextTextSegment(used: ShireUsed): String {\n        val textSegment: StringBuilder = StringBuilder()\n        var next: PsiElement? = used\n        while (true) {\n            next = next?.nextSibling\n            if (next == null) {\n                break\n            }\n\n            if (next.elementType == ShireTypes.TEXT_SEGMENT) {\n                textSegment.append(next.text)\n                break\n            }\n        }\n\n        return textSegment.toString()\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/template/ShireVariableTemplateCompiler.kt",
    "content": "package com.phodal.shirelang.compiler.template\n\nimport com.intellij.openapi.application.runReadAction\nimport com.intellij.openapi.editor.Editor\nimport com.intellij.openapi.project.Project\nimport com.intellij.psi.PsiManager\nimport com.phodal.shirelang.compiler.ast.hobbit.HobbitHole\nimport com.phodal.shirelang.compiler.variable.VariableTable\nimport com.phodal.shirelang.compiler.variable.resolver.CompositeVariableResolver\nimport com.phodal.shirelang.compiler.variable.resolver.base.VariableResolverContext\n\n/**\n * The `ShireTemplateCompiler` class is responsible for compiling templates in a Kotlin project.\n * It takes a `Project`, a `HobbitHole`, a `SymbolTable`, and an `input` string as parameters.\n */\nclass ShireVariableTemplateCompiler(\n    private val myProject: Project,\n    private val hole: HobbitHole?,\n    private val variableTable: VariableTable,\n    private val input: String,\n    private val editor: Editor?,\n) {\n    private val customVariables: MutableMap<String, String> = mutableMapOf()\n\n    var compiledVariables: Map<String, Any> = mapOf()\n\n    suspend fun compile(): String {\n        val currentEditor = editor ?: TemplateCompiler.defaultEditor(myProject)\n\n        if (currentEditor != null) {\n            val prompt = doExecuteCompile(currentEditor)\n            return cleanUp(prompt)\n        }\n\n        return input\n    }\n\n    suspend fun doExecuteCompile(editor: Editor): String {\n//        val record = VariableSnapshotRecorder.getInstance(myProject)\n        val additionalMap: Map<String, Any> = compileVariable(editor, customVariables)\n//        record.printSnapshot()\n\n        compiledVariables = additionalMap.mapValues { it.value.toString() }\n\n        val file = runReadAction {\n            PsiManager.getInstance(myProject).findFile(editor.virtualFile ?: return@runReadAction null)\n        }\n\n        val templateCompiler = TemplateCompiler(file?.language, file)\n\n        templateCompiler.putAll(additionalMap)\n        templateCompiler.putAll(customVariables)\n\n        return templateCompiler.compile(input)\n    }\n\n    suspend fun compileVariable(editor: Editor, customVariables: MutableMap<String, String>): Map<String, Any> {\n        val context = VariableResolverContext(myProject, editor, hole, variableTable, null)\n        return CompositeVariableResolver(context).resolve(customVariables)\n    }\n\n\n    fun putCustomVariable(varName: String, varValue: String) {\n        customVariables[varName] = varValue\n    }\n\n    private fun cleanUp(prompt: String) = prompt.trim().replace(\"\\n\\n\\n\", \"\\n\\n\")\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/template/TemplateCompiler.kt",
    "content": "package com.phodal.shirelang.compiler.template\n\nimport com.intellij.lang.Language\nimport com.intellij.openapi.application.ReadAction\nimport com.intellij.openapi.diagnostic.logger\nimport com.intellij.openapi.editor.Editor\nimport com.intellij.openapi.fileEditor.FileEditorManager\nimport com.intellij.openapi.project.Project\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.PsiFile\nimport com.intellij.psi.PsiManager\nimport org.apache.velocity.VelocityContext\nimport org.apache.velocity.app.Velocity\nimport java.io.StringWriter\n\nclass TemplateCompiler(\n    val language: Language?,\n    val file: PsiFile?,\n) {\n    private val logger = logger<TemplateCompiler>()\n    private val variableMap: MutableMap<String, Any> = mutableMapOf()\n\n    fun putAll(map: Map<String, Any>) {\n        variableMap.putAll(map)\n    }\n\n    fun compile(template: String): String {\n        val oldContextClassLoader = Thread.currentThread().contextClassLoader\n        Thread.currentThread().contextClassLoader = TemplateCompiler::class.java.classLoader\n\n        // for compatibility with older versions of AutoDev\n        val context = VelocityContext(variableMap as Map<String, Any>?)\n        val sw = StringWriter()\n        try {\n            // for compatibility with older versions of AutoDev\n            context.put(\"context\", variableMap)\n            Velocity.evaluate(context, sw, \"#\" + this.javaClass.name, template)\n        } catch (e: Exception) {\n            logger.error(\"Failed to compile template: $template\", e)\n            sw.write(template)\n        }\n\n        Thread.currentThread().contextClassLoader = oldContextClassLoader\n        return sw.toString()\n    }\n\n    companion object {\n        /**\n         * This function returns the default editor for the given project.\n         * It takes a Project object as a parameter and returns an Editor object.\n         * It uses the FileEditorManager to get the selected text editor for the project.\n         * If no editor is selected, it returns null.\n         */\n        fun defaultEditor(myProject: Project): Editor? {\n            return FileEditorManager.getInstance(myProject).selectedTextEditor\n        }\n\n        /**\n         * This function returns the PsiElement at the current caret position in the editor.\n         *\n         * @param myProject the project to which the editor belongs\n         * @param currentEditor the current editor where the caret position is located\n         * @return the PsiElement at the current caret position, or null if not found\n         */\n        fun defaultElement(myProject: Project, currentEditor: Editor?): PsiElement? =\n            ReadAction.compute<PsiElement?, Throwable> {\n                currentEditor?.caretModel?.currentCaret?.offset?.let {\n                    val psiFile = currentEditor.let { editor ->\n                        val psiFile = editor.virtualFile?.let { file ->\n                            PsiManager.getInstance(myProject).findFile(file)\n                        }\n\n                        psiFile\n                    } ?: return@let null\n\n                    psiFile.findElementAt(it) ?: return@let psiFile\n                }\n            }\n    }\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/variable/CompositeVariableProvider.kt",
    "content": "package com.phodal.shirelang.compiler.variable\n\nimport com.phodal.shirecore.provider.variable.model.ContextVariable\nimport com.phodal.shirecore.provider.variable.model.PsiContextVariable\nimport com.phodal.shirecore.provider.variable.model.ToolchainVariable\nimport com.phodal.shirecore.provider.variable.model.toolchain.VcsToolchainVariable\nimport com.phodal.shirecore.provider.variable.model.SystemInfoVariable\nimport com.phodal.shirecore.provider.variable.model.toolchain.DatabaseToolchainVariable\n\ndata class VariableDisplay(\n    val name: String,\n    val description: String,\n    val priority: Double = 0.0\n)\n\nobject CompositeVariableProvider {\n    fun all(): List<VariableDisplay> {\n        val results = mutableListOf<VariableDisplay>()\n\n        ContextVariable.entries.forEach {\n            results.add(VariableDisplay(it.variableName, it.description, 99.0))\n        }\n\n        PsiContextVariable.entries.forEach {\n            results.add(VariableDisplay(it.variableName, it.description ?: \"\", 90.0))\n        }\n\n        VcsToolchainVariable.entries.forEach {\n            results.add(VariableDisplay(it.variableName, it.description, 80.0))\n        }\n\n        DatabaseToolchainVariable.entries.forEach {\n            results.add(VariableDisplay(it.variableName, it.description, 70.0))\n        }\n\n        ToolchainVariable.all().forEach {\n            results.add(VariableDisplay(it.variableName, it.description, 70.0))\n        }\n\n        SystemInfoVariable.all().forEach {\n            results.add(VariableDisplay(it.variableName, it.description, 60.0))\n        }\n\n        return results\n    }\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/variable/VariableTable.kt",
    "content": "package com.phodal.shirelang.compiler.variable\n\nclass VariableTable {\n    private val table: MutableMap<String, VariableInfo> = mutableMapOf()\n\n    fun addVariable(name: String, varType: VariableType, lineDeclared: Int, scope: VariableScope = VariableScope.BuiltIn) {\n        var varName = name;\n        // {context.frameworkContext}\n        if (varName.startsWith(\"{\") && varName.endsWith(\"}\")) {\n            varName = varName.substring(1, varName.length - 1)\n        }\n\n        // remove the context prefix\n        if (varName.startsWith(\"context.\")) {\n            varName = varName.substring(8)\n        }\n\n        if (!table.containsKey(varName)) {\n            table[varName] = VariableInfo(varType, scope, lineDeclared)\n        } else {\n            // Ignore duplicate keys to avoid startup failures.\n            // Affected: the lineDeclared value is only valid for the first time, but it is not used anywhere yet.\n//            throw Exception(\"Variable $varName already declared.\")\n        }\n    }\n\n    fun addVariable(variableTable: VariableTable) {\n        variableTable.getAllVariables().forEach {\n            table[it.key] = it.value\n        }\n    }\n\n    fun getVariable(name: String): VariableInfo {\n        return table[name] ?: throw Exception(\"Variable $name not found.\")\n    }\n\n    fun updateVariable(name: String, newType: VariableType? = null, newScope: VariableScope? = null, newLineDeclared: Int? = null) {\n        val variable = table[name] ?: throw Exception(\"Variable $name not found.\")\n        val updatedVariable = variable.copy(\n            type = newType ?: variable.type,\n            scope = newScope ?: variable.scope,\n            lineDeclared = newLineDeclared ?: variable.lineDeclared\n        )\n        table[name] = updatedVariable\n    }\n\n    fun removeVariable(name: String) {\n        if (table.containsKey(name)) {\n            table.remove(name)\n        } else {\n            throw Exception(\"Variable $name not found.\")\n        }\n    }\n\n    fun getAllVariables(): Map<String, VariableInfo> = table.toMap()\n\n    data class VariableInfo(\n        val type: VariableType,\n        val scope: VariableScope,\n        val lineDeclared: Int\n    )\n\n    enum class VariableType {\n        String,\n        Boolean,\n        Number,\n    }\n\n    enum class VariableScope {\n        BuiltIn,\n        UserDefined\n    }\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/variable/resolver/CompositeVariableResolver.kt",
    "content": "package com.phodal.shirelang.compiler.variable.resolver\n\nimport com.intellij.openapi.application.ReadAction\nimport com.intellij.psi.PsiElement\nimport com.phodal.shirecore.middleware.select.SelectElementStrategy\nimport com.phodal.shirelang.compiler.variable.resolver.base.VariableResolver\nimport com.phodal.shirelang.compiler.variable.resolver.base.VariableResolverContext\n\nclass CompositeVariableResolver(private val context: VariableResolverContext) : VariableResolver {\n    init {\n        context.element = ReadAction.compute<PsiElement?, Throwable> {\n            SelectElementStrategy.resolvePsiElement(context.myProject, context.editor)\n        }\n    }\n\n    override suspend fun resolve(initVariables: Map<String, Any>): Map<String, Any> {\n        val resolverList = listOf(\n            PsiContextVariableResolver(context),\n            ToolchainVariableResolver(context),\n            ContextVariableResolver(context),\n            SystemInfoVariableResolver(context),\n            UserCustomVariableResolver(context),\n        )\n\n        val initial = initVariables.toMutableMap()\n\n        return resolverList.fold(initial) { acc: MutableMap<String, Any>, resolver: VariableResolver ->\n            acc.putAll(resolver.resolve(acc))\n            acc\n        }\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/variable/resolver/ContextVariableResolver.kt",
    "content": "package com.phodal.shirelang.compiler.variable.resolver\n\nimport com.intellij.lang.Language\nimport com.intellij.openapi.application.ReadAction\nimport com.intellij.openapi.editor.CaretModel\nimport com.intellij.psi.PsiNameIdentifierOwner\nimport com.phodal.shirelang.compiler.variable.resolver.base.VariableResolver\nimport com.phodal.shirelang.compiler.variable.resolver.base.VariableResolverContext\nimport com.phodal.shirecore.provider.variable.model.ContextVariable\nimport com.phodal.shirecore.provider.variable.model.ContextVariable.*\n\nclass ContextVariableResolver(\n    private val context: VariableResolverContext,\n) : VariableResolver {\n    fun all(): List<ContextVariable> = entries\n\n    override suspend fun resolve(initVariables: Map<String, Any>): Map<String, String> = ReadAction.compute<Map<String, String>, Throwable> {\n        val file = context.element?.containingFile\n        val caretModel = context.editor.caretModel\n\n        all().associate { variable ->\n            variable.variableName to when (variable) {\n                SELECTION -> context.editor.selectionModel.selectedText\n                    ?: context.editor.document.text.substring(caretModel.offset)\n\n                SELECTION_WITH_NUM -> {\n                    lineNumberedSelection(caretModel)\n                }\n\n                BEFORE_CURSOR -> file?.text?.substring(0, caretModel.offset)\n                    ?: context.editor.document.text.substring(0, caretModel.offset)\n\n                AFTER_CURSOR -> file?.text?.substring(caretModel.offset)\n                    ?: context.editor.document.text.substring(caretModel.offset)\n\n                FILE_NAME -> file?.name ?: \"\"\n                FILE_PATH -> file?.virtualFile?.path ?: \"\"\n                METHOD_NAME -> when (context.element) {\n                    is PsiNameIdentifierOwner -> (context.element as PsiNameIdentifierOwner).nameIdentifier?.text\n                        ?: \"\"\n\n                    else -> \"\"\n                }\n\n                LANGUAGE -> context.element?.language?.displayName ?: \"\"\n                COMMENT_SYMBOL -> getCommentSymbol(context.element?.language)\n\n                ALL -> file?.text ?: context.editor.document.text ?: \"\"\n            }\n        }\n    }\n\n    private fun lineNumberedSelection(caretModel: CaretModel): String {\n        val selection = context.editor.selectionModel.selectedText\n            ?: context.editor.document.text.substring(caretModel.offset)\n\n        var lineNo = caretModel.logicalPosition.line + 1\n        return selection.split(\"\\n\").joinToString(\"\\n\") {\n            val line = \"$lineNo: $it\"\n            lineNo++\n            line\n        }\n    }\n\n\n    private fun getCommentSymbol(language: Language?): String {\n        return when (language?.displayName?.lowercase()) {\n            \"java\", \"kotlin\" -> \"//\"\n            \"python\" -> \"#\"\n            \"javascript\" -> \"//\"\n            \"typescript\" -> \"//\"\n            \"go\" -> \"//\"\n            \"c\", \"c++\", \"c#\" -> \"//\"\n            \"rust\" -> \"//\"\n            \"ruby\" -> \"#\"\n            \"shell\" -> \"#\"\n            \"php\" -> \"//\"\n            \"perl\" -> \"#\"\n            \"swift\" -> \"//\"\n            \"r\" -> \"#\"\n            \"scala\" -> \"//\"\n            \"groovy\" -> \"//\"\n            \"lua\" -> \"--\"\n            else -> \"-\"\n        }\n    }\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/variable/resolver/PsiContextVariableResolver.kt",
    "content": "package com.phodal.shirelang.compiler.variable.resolver\n\nimport com.intellij.openapi.application.runReadAction\nimport com.intellij.openapi.diagnostic.logger\nimport com.intellij.psi.PsiManager\nimport com.phodal.shirecore.provider.variable.PsiContextVariableProvider\nimport com.phodal.shirecore.provider.variable.impl.DefaultPsiContextVariableProvider\nimport com.phodal.shirecore.provider.variable.model.PsiContextVariable\nimport com.phodal.shirelang.compiler.variable.resolver.base.VariableResolver\nimport com.phodal.shirelang.compiler.variable.resolver.base.VariableResolverContext\n\n/**\n * Include ToolchainVariableProvider and PsiContextVariableProvider\n */\nclass PsiContextVariableResolver(private val context: VariableResolverContext) : VariableResolver {\n    private val variableProvider: PsiContextVariableProvider\n\n    init {\n        val psiFile = runReadAction {\n            PsiManager.getInstance(context.myProject).findFile(context.editor.virtualFile ?: return@runReadAction null)\n        }\n\n        variableProvider = if (psiFile?.language != null) {\n            PsiContextVariableProvider.provide(psiFile.language)\n        } else {\n            DefaultPsiContextVariableProvider()\n        }\n    }\n\n    override suspend fun resolve(initVariables: Map<String, Any>): Map<String, Any> {\n        val result = mutableMapOf<String, Any>()\n        context.variableTable.getAllVariables().forEach {\n            val psiContextVariable = PsiContextVariable.from(it.key)\n            if (psiContextVariable != null) {\n                result[it.key] = try {\n                    runReadAction {\n                        variableProvider.resolve(psiContextVariable, context.myProject, context.editor, context.element)\n                    }\n                } catch (e: Exception) {\n                    logger<CompositeVariableResolver>().error(\"Failed to resolve variable: ${it.key}\", e)\n                    \"\"\n                }\n\n                return@forEach\n            }\n        }\n\n        return result\n    }\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/variable/resolver/SystemInfoVariableResolver.kt",
    "content": "package com.phodal.shirelang.compiler.variable.resolver\n\nimport com.phodal.shirecore.provider.variable.model.SystemInfoVariable\nimport com.phodal.shirelang.compiler.variable.resolver.base.VariableResolver\nimport com.phodal.shirelang.compiler.variable.resolver.base.VariableResolverContext\n\n/**\n * SystemInfoVariableResolver is a class that provides a way to resolve system information variables.\n */\nclass SystemInfoVariableResolver(\n    private val context: VariableResolverContext,\n) : VariableResolver {\n    override suspend fun resolve(initVariables: Map<String, Any>): Map<String, Any> {\n        return SystemInfoVariable.all().associate {\n            it.variableName to it.value!!\n        }\n    }\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/variable/resolver/ToolchainVariableResolver.kt",
    "content": "package com.phodal.shirelang.compiler.variable.resolver\n\nimport com.intellij.openapi.diagnostic.logger\nimport com.phodal.shirecore.provider.variable.*\nimport com.phodal.shirecore.provider.variable.model.ToolchainVariable\nimport com.phodal.shirelang.compiler.variable.resolver.base.VariableResolver\nimport com.phodal.shirelang.compiler.variable.resolver.base.VariableResolverContext\n\n/**\n * Include ToolchainVariableProvider and PsiContextVariableProvider\n */\nclass ToolchainVariableResolver(\n    private val context: VariableResolverContext,\n) : VariableResolver {\n    override suspend fun resolve(initVariables: Map<String, Any>): Map<String, Any> {\n        val result = mutableMapOf<String, Any>()\n        context.variableTable.getAllVariables().forEach {\n            val variable = ToolchainVariable.from(it.key) ?: return@forEach\n            val provider = ToolchainVariableProvider\n                .provide(variable, context.element, context.myProject) ?: return@forEach\n\n            result[it.key] = try {\n                val resolvedValue = provider.resolve(variable, context.myProject, context.editor, context.element)\n                val value = (resolvedValue as? ToolchainVariable)?.value ?: resolvedValue\n                logger<ToolchainVariableResolver>().info(\"start to resolve variable: $value\")\n                value\n            } catch (e: Exception) {\n                logger<ToolchainVariableResolver>().error(\"Failed to resolve variable: ${it.key}\", e)\n                \"\"\n            }\n\n        }\n\n        return result\n    }\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/variable/resolver/UserCustomVariableResolver.kt",
    "content": "package com.phodal.shirelang.compiler.variable.resolver\n\nimport com.phodal.shirelang.compiler.execute.PatternActionProcessor\nimport com.phodal.shirelang.compiler.variable.resolver.base.VariableResolver\nimport com.phodal.shirelang.compiler.variable.resolver.base.VariableResolverContext\nimport com.phodal.shirelang.debugger.snapshot.VariableSnapshotRecorder\n\nclass UserCustomVariableResolver(\n    private val context: VariableResolverContext,\n) : VariableResolver {\n    private val record = VariableSnapshotRecorder.getInstance(context.myProject)\n    override suspend fun resolve(initVariables: Map<String, Any>): Map<String, String> {\n        record.clear()\n\n        val vars: MutableMap<String, Any?> = initVariables.toMutableMap()\n        return context.hole?.variables?.mapValues {\n            PatternActionProcessor(context.myProject, context.hole, vars).execute(it.value)\n        } ?: emptyMap()\n    }\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/variable/resolver/base/VariableResolver.kt",
    "content": "package com.phodal.shirelang.compiler.variable.resolver.base\n\n/**\n * The `VariableResolver` interface is designed to provide a mechanism for resolving variables.\n */\ninterface VariableResolver {\n    suspend fun resolve(initVariables: Map<String, Any>): Map<String, Any>\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/compiler/variable/resolver/base/VariableResolverContext.kt",
    "content": "package com.phodal.shirelang.compiler.variable.resolver.base\n\nimport com.intellij.openapi.editor.Editor\nimport com.intellij.openapi.project.Project\nimport com.intellij.psi.PsiElement\nimport com.phodal.shirelang.compiler.variable.VariableTable\nimport com.phodal.shirelang.compiler.ast.hobbit.HobbitHole\n\ndata class VariableResolverContext(\n    val myProject: Project,\n    val editor: Editor,\n    val hole: HobbitHole?,\n    val variableTable: VariableTable,\n    var element: PsiElement?\n)"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/completion/ShireCompletionContributor.kt",
    "content": "package com.phodal.shirelang.completion\n\nimport com.intellij.codeInsight.completion.CompletionContributor\nimport com.intellij.codeInsight.completion.CompletionType\nimport com.intellij.patterns.ElementPattern\nimport com.intellij.patterns.PlatformPatterns\nimport com.intellij.patterns.PsiElementPattern\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.tree.IElementType\nimport com.phodal.shirelang.completion.dataprovider.BuiltinCommand\nimport com.phodal.shirelang.completion.provider.*\nimport com.phodal.shirelang.psi.ShireFrontMatterEntry\nimport com.phodal.shirelang.psi.ShireTypes\nimport com.phodal.shirelang.psi.ShireUsed\n\nclass ShireCompletionContributor : CompletionContributor() {\n    init {\n        extend(\n            CompletionType.BASIC,\n            PlatformPatterns.psiElement(ShireTypes.LANGUAGE_IDENTIFIER),\n            CodeFenceLanguageCompletion()\n        )\n\n        extend(CompletionType.BASIC, identifierAfter(ShireTypes.AGENT_START), CustomAgentCompletion())\n\n        extend(CompletionType.BASIC, identifierAfter(ShireTypes.VARIABLE_START), VariableCompletionProvider())\n\n        extend(CompletionType.BASIC, identifierAfter(ShireTypes.VARIABLE_START), AgentToolOverviewCompletion())\n\n        extend(CompletionType.BASIC, identifierAfter(ShireTypes.COMMAND_START), BuiltinCommandCompletion())\n\n        extend(CompletionType.BASIC, hobbitHoleKey(), HobbitHoleKeyCompletion())\n        extend(CompletionType.BASIC, hobbitHolePattern(), HobbitHoleValueCompletion())\n\n        extend(CompletionType.BASIC, identifierAfter(ShireTypes.PIPE), PostProcessorCompletion())\n\n        extend(CompletionType.BASIC, whenConditionPattern(), WhenConditionCompletionProvider())\n        extend(CompletionType.BASIC, whenConditionFuncPattern(), WhenConditionFunctionCompletionProvider())\n\n        // command completion\n        extend(\n            CompletionType.BASIC,\n            (valuePatterns(listOf(\n                BuiltinCommand.FILE, BuiltinCommand.RUN, BuiltinCommand.WRITE, BuiltinCommand.STRUCTURE\n            ))),\n            FileReferenceLanguageProvider()\n        )\n        extend(\n            CompletionType.BASIC,\n            commandPropPattern(BuiltinCommand.REV.commandName),\n            RevisionReferenceLanguageProvider()\n        )\n        extend(\n            CompletionType.BASIC,\n            commandPropPattern(BuiltinCommand.SYMBOL.commandName),\n            SymbolReferenceLanguageProvider()\n        )\n        extend(\n            CompletionType.BASIC,\n            commandPropPattern(BuiltinCommand.FILE_FUNC.commandName),\n            FileFunctionProvider()\n        )\n        extend(\n            CompletionType.BASIC,\n            commandPropPattern(BuiltinCommand.REFACTOR.commandName),\n            RefactoringFuncProvider()\n        )\n        extend(\n            CompletionType.BASIC,\n            commandPropPattern(BuiltinCommand.RUN.commandName),\n            ProjectRunProvider()\n        )\n    }\n\n    private inline fun <reified I : PsiElement> psiElement() = PlatformPatterns.psiElement(I::class.java)\n\n    private fun baseUsedPattern(): PsiElementPattern.Capture<PsiElement> =\n        PlatformPatterns.psiElement()\n            .inside(psiElement<ShireUsed>())\n\n    private fun identifierAfter(type: IElementType): ElementPattern<out PsiElement> =\n        PlatformPatterns.psiElement(ShireTypes.IDENTIFIER)\n            .afterLeaf(PlatformPatterns.psiElement().withElementType(type))\n\n    private fun commandPropPattern(text: String): PsiElementPattern.Capture<PsiElement> =\n        baseUsedPattern()\n            .withElementType(ShireTypes.COMMAND_PROP)\n            .afterLeafSkipping(\n                PlatformPatterns.psiElement(ShireTypes.COLON),\n                PlatformPatterns.psiElement().withText(text)\n            )\n\n    private fun hobbitHolePattern(): ElementPattern<out PsiElement> {\n        return PlatformPatterns.psiElement()\n            .inside(psiElement<ShireFrontMatterEntry>())\n            .afterLeafSkipping(\n                PlatformPatterns.psiElement().withElementType(ShireTypes.FRONT_MATTER_KEY),\n                PlatformPatterns.psiElement(ShireTypes.COLON)\n            )\n    }\n\n    private fun whenConditionPattern(): ElementPattern<out PsiElement> {\n        return PlatformPatterns.psiElement()\n            .inside(psiElement<ShireFrontMatterEntry>())\n            .afterLeaf(PlatformPatterns.psiElement().withText(\"$\"))\n    }\n\n    private fun whenConditionFuncPattern(): ElementPattern<out PsiElement> {\n        return PlatformPatterns.psiElement(ShireTypes.IDENTIFIER)\n            .inside(psiElement<ShireFrontMatterEntry>())\n            .afterLeafSkipping(\n                PlatformPatterns.psiElement(ShireTypes.IDENTIFIER),\n                PlatformPatterns.psiElement(ShireTypes.DOT),\n            )\n    }\n\n    private fun hobbitHoleKey(): PsiElementPattern.Capture<PsiElement> {\n        val excludedElements = listOf(\n            ShireTypes.COLON,\n            ShireTypes.DOT,\n            ShireTypes.AGENT_START,\n            ShireTypes.VARIABLE_START,\n            ShireTypes.COMMAND_START\n        ).map { PlatformPatterns.psiElement().afterLeaf(PlatformPatterns.psiElement(it)) }\n\n        return excludedElements.fold(\n            PlatformPatterns.psiElement(ShireTypes.IDENTIFIER)\n        ) { pattern, excludedPattern ->\n            pattern.andNot(excludedPattern)\n        }\n    }\n\n    private fun valuePatterns(listOf: List<BuiltinCommand>): ElementPattern<out PsiElement> {\n        val patterns = listOf.map { commandPropPattern(it.commandName) }\n        return PlatformPatterns.or(*patterns.toTypedArray())\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/completion/UserCustomCompletionContributor.kt",
    "content": "package com.phodal.shirelang.completion\n\nimport com.intellij.codeInsight.completion.CompletionContributor\nimport com.intellij.codeInsight.completion.CompletionType\nimport com.intellij.patterns.PlatformPatterns\nimport com.phodal.shirelang.completion.provider.CustomCommandCompletion\nimport com.phodal.shirelang.psi.ShireTypes\n\nclass UserCustomCompletionContributor : CompletionContributor() {\n    init {\n        extend(CompletionType.BASIC, PlatformPatterns.psiElement(ShireTypes.COMMAND_ID), CustomCommandCompletion())\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/completion/dataprovider/BuiltinCommand.kt",
    "content": "package com.phodal.shirelang.completion.dataprovider\n\nimport com.intellij.icons.AllIcons\nimport com.phodal.shirelang.ShireIcons\nimport java.nio.charset.StandardCharsets\nimport javax.swing.Icon\n\nenum class BuiltinCommand(\n    val commandName: String,\n    val description: String,\n    val icon: Icon,\n    val hasCompletion: Boolean = false,\n    val requireProps: Boolean = false,\n    val enableInSketch: Boolean = true,\n) {\n    FILE(\"file\", \"Read the content of a file by project relative path\", AllIcons.Actions.Copy, true, true),\n    REV(\n        \"rev\",\n        \"Read git changes by sha hash; For other git operations, it is recommended to use native git commands\",\n        AllIcons.Vcs.History,\n        true,\n        true,\n        enableInSketch = false\n    ),\n\n    /**\n     * Every language will have a symbol completion, which is the most basic completion, for example,\n     * - Java: [com.intellij.codeInsight.completion.JavaKeywordCompletion]\n     * - Kotlin: [org.jetbrains.kotlin.idea.completion.KotlinCompletionContributor]\n     * - Python: [com.jetbrains.python.codeInsight.completion.PyClassNameCompletionContributor]\n     */\n    SYMBOL(\n        \"symbol\",\n        \"Read content by Java/Kotlin canonical name, such as package name, class name.\",\n        AllIcons.Toolwindows.ToolWindowStructure,\n        true,\n        true\n    ),\n    WRITE(\n        \"write\",\n        \"Write content to a file with markdown code block, /write:path/to/file:L1-L2\",\n        AllIcons.Actions.Edit,\n        true,\n        true\n    ),\n    PATCH(\n        \"patch\",\n        \"Apply GNU unified diff format structure patch to a file, /patch:path/to/file\",\n        AllIcons.Vcs.Patch_file,\n        false\n    ),\n    RUN(\"run\", \"Run the IDE's built-in command, like build tool, test.\", AllIcons.Actions.Execute, true, true),\n    SHELL(\n        \"shell\",\n        \"Execute a shell command and collect (ProcessBuild) the result\",\n        AllIcons.Debugger.Console,\n        true,\n        true\n    ),\n    COMMIT(\"commit\", \"Do commit with current workspace with some messages.\", AllIcons.Vcs.CommitNode, false),\n    FILE_FUNC(\n        \"file-func\",\n        \"Read the name of a file, support for: \" + FileFunc.values().joinToString(\",\") { it.funcName },\n        AllIcons.Actions.GroupByFile,\n        true,\n        true,\n        enableInSketch = false,\n    ),\n    BROWSE(\"browse\", \"Fetch the content of a given URL.\", AllIcons.Toolwindows.WebToolWindow, false, true),\n    REFACTOR(\n        \"refactor\",\n        \"Refactor the content of a file, only support for rename, safeDelete and move.\",\n        ShireIcons.Idea,\n        true,\n        true\n    ),\n    GOTO(\"goto\", \"Goto the content of a file\", AllIcons.Actions.Forward, true, true, enableInSketch = false),\n    STRUCTURE(\n        \"structure\",\n        \"Get the structure of a file with AST/PSI\",\n        AllIcons.Toolwindows.ToolWindowStructure,\n        true,\n        true\n    ),\n    DIR(\"dir\", \"List files and directories in a tree-like structure\", AllIcons.Actions.ProjectDirectory, true, true),\n    DATABASE(\n        \"database\",\n        \"Read the content of a database, /database:query\\n```sql\\nSELECT * FROM table\\n```\",\n        AllIcons.Toolwindows.ToolWindowHierarchy,\n        true,\n        true\n    ),\n    LOCAL_SEARCH(\n        \"localSearch\",\n        \"Search text in the scope (current only support project) will return 5 line before and after\",\n        AllIcons.Actions.Search,\n        false,\n        true\n    ),\n    RELATED(\n        \"related\",\n        \"Get related code by AST (abstract syntax tree) for the current file\",\n        AllIcons.Actions.Find,\n        false,\n        true\n    ),\n    OPEN(\"open\", \"Open a file in the editor\", AllIcons.Actions.MenuOpen, false, true),\n    RIPGREP_SEARCH(\"ripgrepSearch\", \"Search text in the project with ripgrep\", AllIcons.Actions.Regex, false, true),\n    ;\n\n    companion object {\n        fun all(): List<BuiltinCommand> {\n            return entries\n        }\n\n        fun example(command: BuiltinCommand): String {\n            val commandName = command.commandName\n            val inputStream = BuiltinCommand::class.java.getResourceAsStream(\"/docs/agentExamples/$commandName.shire\")\n                ?: throw IllegalStateException(\"Example file not found: $commandName.shire\")\n\n            return inputStream.use {\n                it.readAllBytes().toString(StandardCharsets.UTF_8)\n            }\n        }\n\n        fun fromString(agentName: String): BuiltinCommand? = values().find { it.commandName == agentName }\n\n        val READ_COMMANDS = setOf(\n            DIR,\n            LOCAL_SEARCH,\n            FILE,\n            REV,\n            STRUCTURE,\n            SYMBOL,\n            DATABASE,\n            RELATED,\n            RIPGREP_SEARCH\n        )\n    }\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/completion/dataprovider/BuiltinRefactorCommand.kt",
    "content": "package com.phodal.shirelang.completion.dataprovider\n\nenum class BuiltinRefactorCommand(val funcName: String, val description: String) {\n    RENAME(\"rename\", \"Rename a file\"),\n    SAFE_DELETE(\"safe-delete\", \"Safe delete a file\"),\n    DELETE(\"delete\", \"Delete a file\"),\n    MOVE(\"move\", \"Move a file\"),\n    ;\n\n    companion object {\n        fun all(): List<BuiltinRefactorCommand> {\n            return values().toList()\n        }\n\n        fun fromString(command: String): BuiltinRefactorCommand? {\n            return values().find { it.name.equals(command, ignoreCase = true) }\n        }\n    }\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/completion/dataprovider/CustomCommand.kt",
    "content": "package com.phodal.shirelang.completion.dataprovider\n\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.phodal.shirelang.ShireIcons\nimport javax.swing.Icon\n\ndata class CustomCommand(\n    val commandName: String,\n    val content: String,\n    val icon: Icon = ShireIcons.COMMAND\n) {\n    companion object {\n        fun all(project: Project): List<CustomCommand> {\n            return listOf()\n        }\n\n        /**\n         *  Read the content from the given file and create a CustomCommand object with the file name and content.\n         *  @param file the VirtualFile from which the content will be read\n         *  @return CustomCommand object containing the name of the file without extension and the content of the file\n         */\n        private fun fromFile(file: VirtualFile): CustomCommand {\n            val content = file.inputStream.readBytes().toString(Charsets.UTF_8)\n            return CustomCommand(file.nameWithoutExtension, content)\n        }\n\n        fun fromString(project: Project, agentName: String): CustomCommand? {\n            return all(project).find { it.commandName == agentName }\n        }\n    }\n}\n\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/completion/dataprovider/FileFunc.kt",
    "content": "package com.phodal.shirelang.completion.dataprovider\n\nimport com.intellij.icons.AllIcons\nimport javax.swing.Icon\n\nenum class FileFunc(val funcName: String, val description: String, val icon: Icon) {\n    Regex(\"regex\", \"Read the content of a file by regex\", AllIcons.Actions.Regex),\n    ;\n\n    companion object {\n        fun all(): List<FileFunc> {\n            return values().toList()\n        }\n\n        fun fromString(funcName: String): FileFunc? {\n            return values().find { it.funcName == funcName }\n        }\n    }\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/completion/dataprovider/ToolHubVariable.kt",
    "content": "package com.phodal.shirelang.completion.dataprovider\n\nimport com.phodal.shirecore.agent.CustomAgent\n\n/**\n * The tool hub provides a list of tools - agents and commands for the AI Agents to decide which one to call\n * For example, you prompt could be:\n * ```shire\n * Here is the tools you can use:\n * $agents\n * ```\n *\n * Or\n *\n * ```shire\n * Here is the tools you can use:\n * $commands\n * ```\n */\nenum class ToolHubVariable(val hubName: String, val type: String, val description: String) {\n    AGENTS(\"agents\", CustomAgent::class.simpleName.toString(), \"Shire all agent for AI Agents to call\"),\n    COMMANDS(\"commands\", BuiltinCommand::class.simpleName.toString(), \"Shire all commands for AI Agents to call\"),\n    ;\n\n    companion object {\n        fun all(): List<ToolHubVariable> {\n            return values().toList()\n        }\n\n        /**\n         * @param variableId should be one of the [ToolHubVariable] name\n         */\n        fun lookup(variableId: String?): List<String> {\n            return when (variableId) {\n                COMMANDS.hubName -> ToolHubVariable.all().map {\n                    \"- \" + it.hubName + \". \" + it.description\n                }\n                else -> emptyList()\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/completion/provider/AgentToolOverviewCompletion.kt",
    "content": "package com.phodal.shirelang.completion.provider\n\nimport com.phodal.shirelang.completion.dataprovider.ToolHubVariable\nimport com.intellij.codeInsight.completion.CompletionParameters\nimport com.intellij.codeInsight.completion.CompletionProvider\nimport com.intellij.codeInsight.completion.CompletionResultSet\nimport com.intellij.codeInsight.completion.PrioritizedLookupElement\nimport com.intellij.codeInsight.lookup.LookupElementBuilder\nimport com.intellij.util.ProcessingContext\nimport com.phodal.shirelang.ShireIcons\n\nclass AgentToolOverviewCompletion : CompletionProvider<CompletionParameters>() {\n    override fun addCompletions(\n        parameters: CompletionParameters,\n        context: ProcessingContext,\n        result: CompletionResultSet\n    ) {\n        ToolHubVariable.all().forEach { toolHub ->\n            val elements = LookupElementBuilder.create(toolHub.hubName)\n                .withIcon(ShireIcons.DEFAULT)\n                .withTypeText(\"(${toolHub.description})\", true)\n                .withPresentableText(toolHub.hubName)\n                .withTailText(toolHub.type, true)\n\n            result.addElement(PrioritizedLookupElement.withPriority(elements, 0.0))\n        }\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/completion/provider/BuiltinCommandCompletion.kt",
    "content": "package com.phodal.shirelang.completion.provider\n\nimport com.phodal.shirelang.completion.dataprovider.BuiltinCommand\nimport com.intellij.codeInsight.AutoPopupController\nimport com.intellij.codeInsight.completion.*\nimport com.intellij.codeInsight.lookup.LookupElementBuilder\nimport com.intellij.util.ProcessingContext\n\nclass BuiltinCommandCompletion : CompletionProvider<CompletionParameters>() {\n    override fun addCompletions(\n        parameters: CompletionParameters,\n        context: ProcessingContext,\n        result: CompletionResultSet,\n    ) {\n        BuiltinCommand.all().forEach {\n            val lookupElement = createCommandCompletionCandidate(it)\n            result.addElement(lookupElement)\n        }\n    }\n\n    private fun createCommandCompletionCandidate(it: BuiltinCommand) =\n        PrioritizedLookupElement.withPriority(\n            LookupElementBuilder.create(it.commandName)\n                .withIcon(it.icon)\n                .withTypeText(it.description, true)\n                .withInsertHandler { context, _ ->\n                    if (!it.hasCompletion) return@withInsertHandler\n\n                    context.document.insertString(context.tailOffset, \":\")\n                    context.editor.caretModel.moveCaretRelatively(1, 0, false, false, false)\n\n                    val editor = context.editor\n                    AutoPopupController.getInstance(editor.project!!).scheduleAutoPopup(editor)\n                },\n            // before custom\n            99.0\n        )\n}\n\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/completion/provider/CodeFenceLanguageCompletion.kt",
    "content": "package com.phodal.shirelang.completion.provider\n\nimport com.intellij.codeInsight.completion.*\nimport com.intellij.codeInsight.lookup.LookupElement\nimport com.intellij.codeInsight.lookup.LookupElementBuilder\nimport com.intellij.lang.Language\nimport com.intellij.lang.LanguageUtil\nimport com.intellij.ui.DeferredIconImpl\nimport com.intellij.util.ProcessingContext\nimport com.phodal.shirelang.markdown.CodeFenceLanguageAliases\nimport javax.swing.Icon\n\nclass CodeFenceLanguageCompletion : CompletionProvider<CompletionParameters>() {\n    override fun addCompletions(\n        parameters: CompletionParameters,\n        context: ProcessingContext,\n        result: CompletionResultSet,\n    ) {\n        for (language in LanguageUtil.getInjectableLanguages()) {\n            val alias = CodeFenceLanguageAliases.findMainAlias(language.id)\n            val handler = LookupElementBuilder.create(alias)\n                .withIcon(createLanguageIcon(language))\n                .withTypeText(language.displayName, true)\n                .withInsertHandler(MyInsertHandler())\n\n            result.addElement(handler)\n        }\n    }\n\n    private fun createLanguageIcon(language: Language): Icon {\n        return DeferredIconImpl(null, language, true) { it.associatedFileType?.icon }\n    }\n\n    private class MyInsertHandler : InsertHandler<LookupElement> {\n        override fun handleInsert(context: InsertionContext, item: LookupElement) {\n            context.document.insertString(context.tailOffset, \"\\n\\n```\\n\")\n            context.editor.caretModel.moveCaretRelatively(1, 0, false, false, false)\n        }\n    }\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/completion/provider/CustomAgentCompletion.kt",
    "content": "package com.phodal.shirelang.completion.provider\n\nimport com.intellij.codeInsight.completion.CompletionParameters\nimport com.intellij.codeInsight.completion.CompletionProvider\nimport com.intellij.codeInsight.completion.CompletionResultSet\nimport com.intellij.codeInsight.lookup.LookupElementBuilder\nimport com.intellij.util.ProcessingContext\nimport com.phodal.shirecore.agent.CustomAgent\n\nclass CustomAgentCompletion : CompletionProvider<CompletionParameters>() {\n    override fun addCompletions(\n        parameters: CompletionParameters,\n        context: ProcessingContext,\n        result: CompletionResultSet,\n    ) {\n        val configs: List<CustomAgent> = CustomAgent.loadFromProject(parameters.originalFile.project)\n        configs.forEach { config ->\n            result.addElement(\n                LookupElementBuilder.create(config.name)\n                    .withInsertHandler { context, _ ->\n                        context.document.insertString(context.tailOffset, \" \")\n                        context.editor.caretModel.moveCaretRelatively(1, 0, false, true, false)\n                    }\n                    .withTypeText(config.description, true))\n        }\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/completion/provider/CustomCommandCompletion.kt",
    "content": "package com.phodal.shirelang.completion.provider\n\nimport com.phodal.shirelang.completion.dataprovider.CustomCommand\nimport com.intellij.codeInsight.completion.CompletionParameters\nimport com.intellij.codeInsight.completion.CompletionProvider\nimport com.intellij.codeInsight.completion.CompletionResultSet\nimport com.intellij.codeInsight.completion.PrioritizedLookupElement\nimport com.intellij.codeInsight.lookup.LookupElementBuilder\nimport com.intellij.util.ProcessingContext\n\nclass CustomCommandCompletion : CompletionProvider<CompletionParameters>() {\n    override fun addCompletions(\n        parameters: CompletionParameters,\n        context: ProcessingContext,\n        result: CompletionResultSet\n    ) {\n        val project = parameters.originalFile.project ?: return\n        CustomCommand.all(project).forEach {\n            val element = LookupElementBuilder.create(it.commandName)\n                .withIcon(it.icon)\n                .withTypeText(it.content, true)\n\n            result.addElement(PrioritizedLookupElement.withPriority(element, 0.0))\n        }\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/completion/provider/FileFunctionProvider.kt",
    "content": "package com.phodal.shirelang.completion.provider\n\nimport com.phodal.shirelang.completion.dataprovider.FileFunc\nimport com.intellij.codeInsight.completion.CompletionParameters\nimport com.intellij.codeInsight.completion.CompletionProvider\nimport com.intellij.codeInsight.completion.CompletionResultSet\nimport com.intellij.codeInsight.lookup.LookupElementBuilder\nimport com.intellij.util.ProcessingContext\n\nclass FileFunctionProvider : CompletionProvider<CompletionParameters>() {\n    override fun addCompletions(\n        parameters: CompletionParameters,\n        context: ProcessingContext,\n        result: CompletionResultSet\n    ) {\n        FileFunc.all().forEach {\n            val element = LookupElementBuilder.create(it.funcName)\n                .withIcon(it.icon)\n                .withTypeText(it.description, true)\n\n            result.addElement(element)\n        }\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/completion/provider/FileReferenceLanguageProvider.kt",
    "content": "package com.phodal.shirelang.completion.provider\n\nimport com.intellij.codeInsight.completion.CompletionParameters\nimport com.intellij.codeInsight.completion.CompletionProvider\nimport com.intellij.codeInsight.completion.CompletionResultSet\nimport com.intellij.codeInsight.lookup.LookupElement\nimport com.intellij.codeInsight.lookup.LookupElementBuilder\nimport com.intellij.ide.presentation.VirtualFilePresentation\nimport com.intellij.openapi.fileEditor.impl.EditorHistoryManager\nimport com.intellij.openapi.project.guessProjectDir\nimport com.intellij.openapi.roots.ProjectFileIndex\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.intellij.util.ProcessingContext\nimport com.phodal.shirecore.canBeAdded\nimport com.phodal.shirecore.completion.ShireLookupElement\nimport org.jetbrains.annotations.NonNls\nimport java.io.File\n\nclass FileReferenceLanguageProvider : CompletionProvider<CompletionParameters>() {\n    override fun addCompletions(\n        parameters: CompletionParameters,\n        context: ProcessingContext,\n        result: CompletionResultSet,\n    ) {\n        val project = parameters.position.project\n        val basePath = project.guessProjectDir()?.path ?: return\n\n        /**\n         * Recent open files\n         */\n        EditorHistoryManager.getInstance(project).fileList.forEach {\n            if (!it.canBeAdded(project)) return@forEach\n            result.addElement(buildElement(it, basePath, 99.0))\n        }\n\n        /**\n         * Project Files\n         */\n        ProjectFileIndex.getInstance(project).iterateContent {\n            if (!it.canBeAdded(project)) return@iterateContent true\n            // if relative to the same extention can be high priority, if can be added to project\n            result.addElement(buildElement(it, basePath, 1.0))\n            true\n        }\n    }\n\n    private fun buildElement(\n        virtualFile: VirtualFile,\n        basePath: @NonNls String,\n        priority: Double,\n    ): LookupElement {\n        val removePrefix = virtualFile.path.removePrefix(basePath)\n        val relativePath: String = removePrefix.removePrefix(File.separator)\n\n        val elementBuilder = LookupElementBuilder.create(relativePath)\n            .withIcon(VirtualFilePresentation.getIcon(virtualFile))\n            .withInsertHandler { context, _ ->\n                context.editor.caretModel.moveCaretRelatively(1, 1, false, false, false)\n            }\n\n        return ShireLookupElement.withPriority(elementBuilder, priority, virtualFile)\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/completion/provider/HobbitHoleKeyCompletion.kt",
    "content": "package com.phodal.shirelang.completion.provider\n\nimport com.intellij.codeInsight.completion.CompletionParameters\nimport com.intellij.codeInsight.completion.CompletionProvider\nimport com.intellij.codeInsight.completion.CompletionResultSet\nimport com.intellij.codeInsight.completion.PrioritizedLookupElement\nimport com.intellij.codeInsight.lookup.LookupElementBuilder\nimport com.intellij.util.ProcessingContext\nimport com.phodal.shirelang.ShireIcons\nimport com.phodal.shirelang.compiler.ast.hobbit.HobbitHole\n\nclass HobbitHoleKeyCompletion : CompletionProvider<CompletionParameters>() {\n    override fun addCompletions(\n        parameters: CompletionParameters,\n        context: ProcessingContext,\n        result: CompletionResultSet,\n    ) {\n        HobbitHole.keys().forEach {\n            val element = LookupElementBuilder.create(it.key)\n                .withIcon(ShireIcons.Idea)\n                .withTypeText(it.value, true)\n\n            result.addElement(PrioritizedLookupElement.withPriority(element, 0.0))\n        }\n    }\n\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/completion/provider/HobbitHoleValueCompletion.kt",
    "content": "package com.phodal.shirelang.completion.provider\n\nimport com.intellij.codeInsight.completion.CompletionParameters\nimport com.intellij.codeInsight.completion.CompletionProvider\nimport com.intellij.codeInsight.completion.CompletionResultSet\nimport com.intellij.codeInsight.completion.InsertionContext\nimport com.intellij.codeInsight.lookup.LookupElement\nimport com.intellij.codeInsight.lookup.LookupElementBuilder\nimport com.intellij.psi.util.PsiTreeUtil\nimport com.intellij.util.ProcessingContext\nimport com.phodal.shirecore.config.ShireActionLocation\nimport com.phodal.shirecore.config.InteractionType\nimport com.phodal.shirecore.middleware.post.PostProcessor\nimport com.phodal.shirecore.middleware.select.SelectElementStrategy\nimport com.phodal.shirelang.ShireIcons\nimport com.phodal.shirelang.compiler.ast.hobbit.HobbitHole\n\nclass HobbitHoleValueCompletion : CompletionProvider<CompletionParameters>() {\n    private val HOBBIT = \"hobbit\"\n\n    override fun addCompletions(\n        parameters: CompletionParameters,\n        context: ProcessingContext,\n        result: CompletionResultSet,\n    ) {\n        val position = parameters.originalPosition ?: parameters.position\n        val psiElement = PsiTreeUtil.prevVisibleLeaf(position)?.let {\n            PsiTreeUtil.prevLeaf(it, true)\n        } ?: return\n\n        when (psiElement.text) {\n            HOBBIT -> {\n                hobbitHeroes(result)\n            }\n\n            HobbitHole.ACTION_LOCATION -> {\n                ShireActionLocation.all().forEach {\n                    result.addElement(\n                        LookupElementBuilder\n                            .create(it.location)\n                            .withIcon(ShireIcons.DEFAULT)\n                            .withInsertHandler { context, _ ->\n                                context.document.insertString(context.startOffset, \" \")\n                            }\n                            .withTypeText(it.description, true)\n                    )\n                }\n            }\n\n            HobbitHole.INTERACTION -> {\n                InteractionType.entries.forEach {\n                    result.addElement(\n                        LookupElementBuilder\n                            .create(it.name)\n                            .withIcon(ShireIcons.Idea)\n                            .withInsertHandler { context, _ ->\n                                context.document.insertString(context.startOffset, \" \")\n                            }\n                            .withTypeText(it.description, true)\n                    )\n                }\n            }\n\n            HobbitHole.STRATEGY_SELECTION -> {\n                SelectElementStrategy.all().forEach {\n                    result.addElement(\n                        LookupElementBuilder\n                            .create(it)\n                            .withIcon(ShireIcons.Idea)\n                            .withInsertHandler { context, _ ->\n                                context.document.insertString(context.startOffset, \" \")\n                            }\n                    )\n                }\n            }\n\n            HobbitHole.ON_STREAMING_END -> {\n                PostProcessor.allNames().forEach {\n                    result.addElement(\n                        LookupElementBuilder\n                            .create(it)\n                            .withIcon(ShireIcons.DEFAULT)\n                            .withInsertHandler { context: InsertionContext, item: LookupElement ->\n                                val offset = context.editor.caretModel.offset\n                                val startOffset = offset - item.lookupString.length\n                                context.document.deleteString(startOffset, offset)\n                                // insert value inside `{ }`, for example, if user select $demo, the insert will be `{ $demo }`\n                                val value = \" { $it  }\"\n                                context.document.insertString(context.startOffset, value)\n\n                                context.editor.caretModel.moveToOffset(startOffset + value.length - 2)\n                            }\n                    )\n                }\n            }\n        }\n    }\n\n    private fun hobbitHeroes(result: CompletionResultSet) {\n        listOf(\"Frodo\", \"Sam\", \"Merry\", \"Pippin\").forEach {\n            result.addElement(LookupElementBuilder.create(it))\n        }\n    }\n\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/completion/provider/PostProcessorCompletion.kt",
    "content": "package com.phodal.shirelang.completion.provider\n\nimport com.intellij.codeInsight.completion.CompletionParameters\nimport com.intellij.codeInsight.completion.CompletionProvider\nimport com.intellij.codeInsight.completion.CompletionResultSet\nimport com.intellij.codeInsight.completion.InsertionContext\nimport com.intellij.codeInsight.lookup.LookupElement\nimport com.intellij.codeInsight.lookup.LookupElementBuilder\nimport com.intellij.util.ProcessingContext\nimport com.phodal.shirecore.middleware.post.PostProcessor\nimport com.phodal.shirelang.ShireIcons\n\nclass PostProcessorCompletion : CompletionProvider<CompletionParameters>() {\n    override fun addCompletions(\n        parameters: CompletionParameters,\n        context: ProcessingContext,\n        result: CompletionResultSet,\n    ) {\n        PostProcessor.allNames().forEach {\n            result.addElement(\n                LookupElementBuilder\n                    .create(it)\n                    .withIcon(ShireIcons.DEFAULT)\n                    .withInsertHandler { context: InsertionContext, item: LookupElement ->\n\n                    }\n            )\n        }\n    }\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/completion/provider/ProjectRunProvider.kt",
    "content": "package com.phodal.shirelang.completion.provider\n\nimport com.intellij.codeInsight.completion.CompletionParameters\nimport com.intellij.codeInsight.completion.CompletionProvider\nimport com.intellij.codeInsight.completion.CompletionResultSet\nimport com.intellij.util.ProcessingContext\nimport com.phodal.shirecore.provider.shire.ProjectRunService\n\nclass ProjectRunProvider : CompletionProvider<CompletionParameters>() {\n    override fun addCompletions(\n        parameters: CompletionParameters,\n        context: ProcessingContext,\n        result: CompletionResultSet,\n    ) {\n        ProjectRunService.all().forEach { provider ->\n            provider\n                .lookupAvailableTask(parameters.editor.project!!, parameters, result)\n                .forEach {\n                    result.addElement(it)\n                }\n        }\n    }\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/completion/provider/QueryStatementCompletion.kt",
    "content": "package com.phodal.shirelang.completion.provider\n\nimport com.intellij.codeInsight.completion.CompletionParameters\nimport com.intellij.codeInsight.completion.CompletionProvider\nimport com.intellij.codeInsight.completion.CompletionResultSet\nimport com.intellij.codeInsight.lookup.LookupElementBuilder\nimport com.intellij.util.ProcessingContext\n\nclass QueryStatementCompletion : CompletionProvider<CompletionParameters>() {\n    /** will auto insert code\n     * ```shire\n     *   from {\n     *     PsiClass clazz / * sample * /\n     *   }\n     *   where {\n     *     // your code here\n     *   }\n     *   select {\n     *     // output selection\n     *   }\n     * ```\n     */\n    override fun addCompletions(\n        parameters: CompletionParameters,\n        context: ProcessingContext,\n        result: CompletionResultSet\n    ) {\n        // 添加代码补全项\n        result.addElement(\n            LookupElementBuilder.create(\"from { ... } where { ... } select { ... }\")\n                .withInsertHandler { context, item ->\n                    val document = context.document\n                    val startOffset = context.startOffset\n                    val endOffset = context.tailOffset\n\n                    document.replaceString(startOffset, endOffset, \"from {\\n} where {\\n} select {\\n}\")\n\n                    // 移动光标到 from 后面\n                    context.editor.caretModel.moveToOffset(startOffset + 5)\n                }\n        )\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/completion/provider/RefactoringFuncProvider.kt",
    "content": "package com.phodal.shirelang.completion.provider\n\nimport com.phodal.shirelang.completion.dataprovider.BuiltinRefactorCommand\nimport com.intellij.codeInsight.completion.CompletionParameters\nimport com.intellij.codeInsight.completion.CompletionProvider\nimport com.intellij.codeInsight.completion.CompletionResultSet\nimport com.intellij.codeInsight.lookup.LookupElementBuilder\nimport com.intellij.util.ProcessingContext\n\nclass RefactoringFuncProvider : CompletionProvider<CompletionParameters>() {\n    override fun addCompletions(\n        parameters: CompletionParameters,\n        context: ProcessingContext,\n        result: CompletionResultSet\n    ) {\n        BuiltinRefactorCommand.all().forEach {\n            val element = LookupElementBuilder.create(it.funcName)\n                .withTypeText(it.description, true)\n\n            result.addElement(element)\n        }\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/completion/provider/RevisionReferenceLanguageProvider.kt",
    "content": "package com.phodal.shirelang.completion.provider\n\nimport com.intellij.codeInsight.completion.CompletionParameters\nimport com.intellij.codeInsight.completion.CompletionProvider\nimport com.intellij.codeInsight.completion.CompletionResultSet\nimport com.intellij.openapi.project.Project\nimport com.intellij.util.ProcessingContext\nimport com.phodal.shirecore.provider.shire.RevisionProvider\n\n\nclass RevisionReferenceLanguageProvider : CompletionProvider<CompletionParameters>() {\n    override fun addCompletions(\n        parameters: CompletionParameters,\n        context: ProcessingContext,\n        result: CompletionResultSet,\n    ) {\n        val project: Project = parameters.editor.project ?: return\n\n        RevisionProvider.provide()?.fetchCompletions(project, result)\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/completion/provider/SymbolReferenceLanguageProvider.kt",
    "content": "package com.phodal.shirelang.completion.provider\n\nimport com.intellij.codeInsight.completion.CompletionParameters\nimport com.intellij.codeInsight.completion.CompletionProvider\nimport com.intellij.codeInsight.completion.CompletionResultSet\nimport com.intellij.util.ProcessingContext\nimport com.phodal.shirecore.provider.shire.ShireSymbolProvider\n\nclass SymbolReferenceLanguageProvider : CompletionProvider<CompletionParameters>() {\n    override fun addCompletions(\n        parameters: CompletionParameters,\n        context: ProcessingContext,\n        result: CompletionResultSet\n    ) {\n        ShireSymbolProvider.all().forEach { completionProvider ->\n            val elements = completionProvider.lookupSymbol(parameters.editor.project!!, parameters, result)\n            elements.forEach {\n                result.addElement(it)\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/completion/provider/VariableCompletionProvider.kt",
    "content": "package com.phodal.shirelang.completion.provider\n\nimport com.intellij.codeInsight.completion.CompletionParameters\nimport com.intellij.codeInsight.completion.CompletionProvider\nimport com.intellij.codeInsight.completion.CompletionResultSet\nimport com.intellij.codeInsight.completion.PrioritizedLookupElement\nimport com.intellij.codeInsight.lookup.LookupElementBuilder\nimport com.intellij.util.ProcessingContext\nimport com.phodal.shirelang.ShireIcons\nimport com.phodal.shirelang.compiler.variable.CompositeVariableProvider\n\nclass VariableCompletionProvider : CompletionProvider<CompletionParameters>() {\n    override fun addCompletions(\n        parameters: CompletionParameters,\n        context: ProcessingContext,\n        result: CompletionResultSet,\n    ) {\n        CompositeVariableProvider.all().forEach {\n            val withTypeText =\n                PrioritizedLookupElement.withPriority(\n                    LookupElementBuilder.create(it.name)\n                        .withIcon(ShireIcons.Variable)\n                        .withTypeText(it.description, true),\n                    it.priority\n                )\n            result.addElement(withTypeText)\n        }\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/completion/provider/WhenConditionCompletionProvider.kt",
    "content": "package com.phodal.shirelang.completion.provider\n\nimport com.intellij.codeInsight.completion.CompletionParameters\nimport com.intellij.codeInsight.completion.CompletionProvider\nimport com.intellij.codeInsight.completion.CompletionResultSet\nimport com.intellij.codeInsight.completion.PrioritizedLookupElement\nimport com.intellij.codeInsight.lookup.LookupElementBuilder\nimport com.intellij.util.ProcessingContext\nimport com.phodal.shirelang.ShireIcons\nimport com.phodal.shirecore.provider.variable.model.ConditionPsiVariable\nimport com.phodal.shirelang.compiler.ast.ExpressionBuiltInMethod\n\nclass WhenConditionCompletionProvider : CompletionProvider<CompletionParameters>() {\n    override fun addCompletions(\n        parameters: CompletionParameters,\n        context: ProcessingContext,\n        result: CompletionResultSet,\n    ) {\n        ConditionPsiVariable.values().forEach {\n            val withTypeText =\n                PrioritizedLookupElement.withPriority(\n                    LookupElementBuilder\n                        .create(it.name)\n                        .withIcon(ShireIcons.DEFAULT)\n                        .withTypeText(it.description, true),\n                    199.0\n                )\n\n            result.addElement(withTypeText)\n        }\n    }\n}\n\nclass WhenConditionFunctionCompletionProvider : CompletionProvider<CompletionParameters>() {\n    override fun addCompletions(\n        parameters: CompletionParameters,\n        context: ProcessingContext,\n        result: CompletionResultSet,\n    ) {\n        ExpressionBuiltInMethod.completionProvider().forEach {\n            val elementBuilder = LookupElementBuilder.create(it.methodName)\n                .withTypeText(it.description, true)\n                .withInsertHandler { context, _ ->\n                    context.document.insertString(context.tailOffset, it.postInsertString)\n                    context.editor.caretModel.moveCaretRelatively(it.moveCaret, 0, false, false, false)\n                }\n\n            val withTypeText =\n                PrioritizedLookupElement.withPriority(\n                    elementBuilder, 99.0\n                )\n\n            result.addElement(withTypeText)\n        }\n    }\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/debugger/ShireBreakpoint.kt",
    "content": "package com.phodal.shirelang.debugger\n\nimport com.intellij.openapi.fileTypes.FileTypeRegistry\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.intellij.xdebugger.breakpoints.XBreakpointHandler\nimport com.intellij.xdebugger.breakpoints.XBreakpointProperties\nimport com.intellij.xdebugger.breakpoints.XLineBreakpoint\nimport com.intellij.xdebugger.breakpoints.XLineBreakpointType\nimport com.phodal.shirelang.ShireFileType\n\nclass ShireBreakpointHandler(val process: ShireDebugProcess) :\n    XBreakpointHandler<XLineBreakpoint<ShireBpProperties>>(ShireLineBreakpointType::class.java) {\n    override fun registerBreakpoint(breakpoint: XLineBreakpoint<ShireBpProperties>) {\n        process.addBreakpoint(breakpoint)\n    }\n\n    override fun unregisterBreakpoint(breakpoint: XLineBreakpoint<ShireBpProperties>, temporary: Boolean) {\n        process.removeBreakpoint(breakpoint)\n    }\n}\n\nclass ShireBpProperties : XBreakpointProperties<ShireBpProperties>() {\n    override fun getState(): ShireBpProperties = this\n    override fun loadState(state: ShireBpProperties) {}\n}\n\nclass ShireLineBreakpointType : XLineBreakpointType<ShireBpProperties>(ID, TITLE) {\n    override fun canPutAt(file: VirtualFile, line: Int, project: Project): Boolean {\n        return canPutAt(project, file, line)\n    }\n\n    override fun createBreakpointProperties(file: VirtualFile, line: Int): ShireBpProperties = ShireBpProperties()\n\n\n    fun canPutAt(project: Project, file: VirtualFile, line: Int): Boolean {\n        // val shireFile = PsiManager.getInstance(project).findFile(file) as? ShireFile ?: return false\n        //        val findLeafElementAt = shireFile.node.findLeafElementAt(line)?.elementType\n        //        findLeafElementAt?.let {\n        //            return true\n        //        }\n        //\n        //        return false\n        return (FileTypeRegistry.getInstance().isFileOfType(file, ShireFileType.INSTANCE))\n    }\n\n    companion object {\n        private const val ID = \"the-shire-line\"\n        private const val TITLE = \"Shire Breakpoints\"\n    }\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/debugger/ShireDebugProcess.kt",
    "content": "package com.phodal.shirelang.debugger\n\nimport com.intellij.execution.process.ProcessEvent\nimport com.intellij.execution.runners.ExecutionEnvironment\nimport com.intellij.execution.ui.ConsoleViewContentType\nimport com.intellij.execution.ui.ExecutionConsole\nimport com.intellij.execution.ui.RunnerLayoutUi\nimport com.intellij.execution.ui.layout.PlaceInGrid\nimport com.intellij.icons.AllIcons\nimport com.intellij.openapi.Disposable\nimport com.intellij.openapi.application.ApplicationManager\nimport com.intellij.ui.content.Content\nimport com.intellij.util.Alarm\nimport com.intellij.xdebugger.XDebugProcess\nimport com.intellij.xdebugger.XDebugSession\nimport com.intellij.xdebugger.XSourcePosition\nimport com.intellij.xdebugger.breakpoints.XBreakpointHandler\nimport com.intellij.xdebugger.breakpoints.XLineBreakpoint\nimport com.intellij.xdebugger.evaluation.XDebuggerEditorsProvider\nimport com.intellij.xdebugger.frame.XSuspendContext\nimport com.intellij.xdebugger.ui.XDebugTabLayouter\nimport com.phodal.shirecore.ShireCoroutineScope\nimport com.phodal.shirelang.psi.ShireFile\nimport com.phodal.shirelang.run.*\nimport com.phodal.shirelang.run.runner.ShireRunner\nimport com.phodal.shirelang.run.runner.ShireRunnerContext\nimport kotlinx.coroutines.launch\nimport kotlinx.coroutines.runBlocking\n\nclass ShireDebugProcess(private val session: XDebugSession, private val environment: ExecutionEnvironment) :\n    XDebugProcess(session), Disposable {\n    private val debuggableConfiguration: ShireConfiguration get() = session.runProfile as ShireConfiguration\n    private val runProfileState =\n        debuggableConfiguration.getState(environment.executor, environment) as ShireRunConfigurationProfileState\n\n    private val connection = ApplicationManager.getApplication().messageBus.connect(this)\n    private val myRequestsScheduler: Alarm\n\n    init {\n        myRequestsScheduler = Alarm(Alarm.ThreadToUse.POOLED_THREAD, this)\n    }\n\n    private val breakpointHandlers = arrayOf<XBreakpointHandler<*>>(ShireBreakpointHandler(this))\n\n    override fun getBreakpointHandlers(): Array<XBreakpointHandler<*>> = breakpointHandlers\n    override fun createTabLayouter(): XDebugTabLayouter = ShireDebugTabLayouter(runProfileState.console)\n    override fun createConsole(): ExecutionConsole = runProfileState.console\n\n    var shireRunnerContext: ShireRunnerContext? = null\n\n    fun start() {\n        ShireCoroutineScope.scope(session.project).launch {\n            runBlocking {\n                val psiFile: ShireFile = ShireFile.lookup(session.project, debuggableConfiguration.getScriptPath())\n                    ?: return@runBlocking\n\n                shireRunnerContext = ShireRunner.compileOnly(session.project, psiFile, mapOf(), null)\n                session.positionReached(ShireSuspendContext(this@ShireDebugProcess, session.project))\n            }\n        }\n\n        val processAdapter = ShireProcessAdapter(debuggableConfiguration, runProfileState.console)\n        processHandler.addProcessListener(processAdapter)\n        runProfileState.console.print(\"Waiting for resume...\", ConsoleViewContentType.NORMAL_OUTPUT)\n    }\n\n    override fun resume(context: XSuspendContext?) {\n        connection.subscribe(ShireRunListener.TOPIC, object : ShireRunListener {\n            override fun runFinish(\n                allOutput: String,\n                llmOutput: String,\n                event: ProcessEvent,\n                scriptPath: String,\n                consoleView: ShireConsoleView?,\n            ) {\n                this@ShireDebugProcess.stop()\n            }\n        })\n\n        runProfileState.execute(environment.executor, environment.runner)\n    }\n\n    override fun runToPosition(position: XSourcePosition, context: XSuspendContext?) {\n        runProfileState.execute(environment.executor, environment.runner)\n    }\n\n    override fun startStepOut(context: XSuspendContext?) {\n        runProfileState.execute(environment.executor, environment.runner)\n    }\n\n    override fun startStepInto(context: XSuspendContext?) {\n        runProfileState.execute(environment.executor, environment.runner)\n    }\n\n    override fun startStepOver(context: XSuspendContext?) {\n        runProfileState.execute(environment.executor, environment.runner)\n    }\n\n    override fun startForceStepInto(context: XSuspendContext?) {\n        runProfileState.execute(environment.executor, environment.runner)\n    }\n\n    override fun startPausing() {\n\n    }\n\n    override fun stop() {\n        connection.disconnect()\n        processHandler.destroyProcess()\n        session.stop()\n    }\n\n    override fun dispose() {\n        connection.disconnect()\n    }\n\n    fun addBreakpoint(breakpoint: XLineBreakpoint<ShireBpProperties>) {\n\n    }\n\n    fun removeBreakpoint(breakpoint: XLineBreakpoint<ShireBpProperties>) {\n\n    }\n\n    override fun getEditorsProvider(): XDebuggerEditorsProvider {\n        return ShireDebuggerEditorsProvider()\n    }\n}\n\nclass ShireDebugTabLayouter(val console: ShireConsoleView) : XDebugTabLayouter() {\n    override fun registerConsoleContent(ui: RunnerLayoutUi, console: ExecutionConsole): Content {\n        val content = ui\n            .createContent(\n                \"DebuggedConsoleContent\", console.component, \"Shire Debugged Console\",\n                AllIcons.Debugger.Console, console.preferredFocusableComponent\n            )\n\n        content.isCloseable = false\n        ui.addContent(content, 1, PlaceInGrid.bottom, false)\n        return content\n    }\n}\n\n\nval RUNNER_ID: String = \"ShireProgramRunner\""
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/debugger/ShireDebugRunner.kt",
    "content": "package com.phodal.shirelang.debugger\n\nimport com.intellij.execution.configurations.RunProfile\nimport com.intellij.execution.configurations.RunProfileState\nimport com.intellij.execution.configurations.RunnerSettings\nimport com.intellij.execution.executors.DefaultDebugExecutor\nimport com.intellij.execution.runners.ExecutionEnvironment\nimport com.intellij.execution.runners.GenericProgramRunner\nimport com.intellij.execution.ui.RunContentDescriptor\nimport com.intellij.xdebugger.XDebugProcess\nimport com.intellij.xdebugger.XDebugProcessStarter\nimport com.intellij.xdebugger.XDebugSession\nimport com.intellij.xdebugger.XDebuggerManager\nimport com.phodal.shirelang.run.ShireConfiguration\n\n/// refs to: https://github.com/KronicDeth/intellij-elixir/pull/643/files#diff-b1ba5c87ca6f66a455e4c1539cb2d99a62722d067a3d9e8043b290426cea5470\nclass ShireDebugRunner : GenericProgramRunner<RunnerSettings>() {\n    override fun canRun(executorId: String, profile: RunProfile): Boolean {\n        return (executorId == DefaultDebugExecutor.EXECUTOR_ID) && profile is ShireConfiguration\n    }\n\n    override fun getRunnerId(): String = RUNNER_ID\n\n    override fun doExecute(state: RunProfileState, environment: ExecutionEnvironment): RunContentDescriptor? {\n        val xDebuggerManager = XDebuggerManager.getInstance(environment.project)\n        return xDebuggerManager.startSession(environment, object : XDebugProcessStarter() {\n            override fun start(session: XDebugSession): XDebugProcess {\n                val shireDebugProcess = ShireDebugProcess(session, environment)\n                shireDebugProcess.start()\n                return shireDebugProcess\n            }\n        }).runContentDescriptor\n    }\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/debugger/ShireDebugSettings.kt",
    "content": "package com.phodal.shirelang.debugger\n\nimport com.intellij.openapi.Disposable\nimport com.intellij.openapi.options.Configurable\nimport com.intellij.openapi.options.ConfigurableUi\nimport com.intellij.openapi.options.SimpleConfigurable\nimport com.intellij.openapi.util.Getter\nimport com.intellij.ui.components.JBCheckBox\nimport com.intellij.ui.dsl.builder.Panel\nimport com.intellij.ui.dsl.builder.panel\nimport com.intellij.util.xmlb.XmlSerializerUtil\nimport com.intellij.xdebugger.settings.DebuggerSettingsCategory\nimport com.intellij.xdebugger.settings.XDebuggerSettings\nimport javax.swing.JComponent\n\nclass ShireDebugSettings : XDebuggerSettings<ShireDebugSettings>(\"shire\"), Getter<ShireDebugSettings> {\n    override fun get(): ShireDebugSettings = this\n    override fun getState()= this\n    override fun loadState(state: ShireDebugSettings) {\n        XmlSerializerUtil.copyBean(state, this)\n    }\n\n    var breakOnPanic: Boolean = true\n    override fun createConfigurables(category: DebuggerSettingsCategory): MutableCollection<out Configurable> {\n        val config = SimpleConfigurable.create(\n            \"ShireDebugSettings\",\n            \"Shire Debugger\",\n            ShireDebugSettingsConfigurableUi::class.java,\n            this\n        )\n        return mutableListOf(config)\n    }\n\n    companion object {\n        @JvmStatic\n        fun getInstance(): ShireDebugSettings = getInstance(ShireDebugSettings::class.java)\n    }\n}\n\nclass ShireDebugSettingsConfigurableUi : ConfigurableUi<ShireDebugSettings>, Disposable {\n    private val components: List<ShireDebuggerUiComponent> = run {\n        val components = mutableListOf<ShireDebuggerUiComponent>()\n        components.add(RsBreakOnPanicConfigurableUi())\n        components\n    }\n\n    override fun isModified(settings: ShireDebugSettings): Boolean = components.any { it.isModified(settings) }\n\n    override fun reset(settings: ShireDebugSettings) {\n        components.forEach { it.reset(settings) }\n    }\n\n    override fun apply(settings: ShireDebugSettings) {\n        components.forEach { it.apply(settings) }\n    }\n\n    override fun getComponent(): JComponent {\n        return panel {\n            for (component in components) {\n                component.buildUi(this)\n            }\n        }\n    }\n\n    override fun dispose() {\n        components.forEach { it.dispose() }\n    }\n}\n\nabstract class ShireDebuggerUiComponent: ConfigurableUi<ShireDebugSettings>, Disposable {\n    abstract fun buildUi(panel: Panel)\n\n    override fun getComponent(): JComponent {\n        return panel {\n            buildUi(this)\n        }\n    }\n\n    override fun dispose() {}\n}\n\nclass RsBreakOnPanicConfigurableUi : ShireDebuggerUiComponent() {\n    private val breakOnPanicCheckBox: JBCheckBox = JBCheckBox(\"Debug\", ShireDebugSettings.getInstance().breakOnPanic)\n\n    override fun reset(settings: ShireDebugSettings) {\n        breakOnPanicCheckBox.isSelected = settings.breakOnPanic\n    }\n\n    override fun isModified(settings: ShireDebugSettings): Boolean\n            = settings.breakOnPanic != breakOnPanicCheckBox.isSelected\n\n    override fun apply(settings: ShireDebugSettings) {\n        settings.breakOnPanic = breakOnPanicCheckBox.isSelected\n    }\n\n    override fun buildUi(panel: Panel) {\n        with(panel) {\n            row { cell(breakOnPanicCheckBox) }\n        }\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/debugger/ShireDebuggerEditorsProvider.kt",
    "content": "package com.phodal.shirelang.debugger\n\nimport com.intellij.openapi.fileTypes.FileType\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.PsiFile\nimport com.intellij.psi.impl.PsiManagerEx\nimport com.intellij.testFramework.LightVirtualFile\nimport com.intellij.xdebugger.XDebuggerUtil\nimport com.intellij.xdebugger.evaluation.XDebuggerEditorsProviderBase\nimport com.phodal.shirelang.ShireFileType\nimport com.phodal.shirelang.psi.ShireFile\n\nclass ShireDebuggerEditorsProvider: XDebuggerEditorsProviderBase() {\n    override fun getFileType(): FileType = ShireFileType.INSTANCE\n\n    override fun createExpressionCodeFragment(project: Project, text: String, context: PsiElement?, isPhysical: Boolean): PsiFile {\n        val name = \"fragment\" + \".\" + ShireFileType.INSTANCE.defaultExtension\n        val viewProvider = PsiManagerEx.getInstanceEx(project).fileManager.createFileViewProvider(\n            LightVirtualFile(name, ShireFileType.INSTANCE, text), isPhysical)\n        return ShireFile(viewProvider)\n    }\n\n    override fun getContextElement(virtualFile: VirtualFile, offset: Int, project: Project): PsiElement? {\n        return XDebuggerUtil.getInstance().findContextElement(virtualFile, offset, project, true)\n    }\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/debugger/ShireStackFrame.kt",
    "content": "package com.phodal.shirelang.debugger\n\nimport com.intellij.icons.AllIcons\nimport com.intellij.openapi.Disposable\nimport com.intellij.openapi.application.ApplicationManager\nimport com.intellij.openapi.project.Project\nimport com.intellij.ui.ColoredTextContainer\nimport com.intellij.ui.SimpleTextAttributes\nimport com.intellij.xdebugger.XExpression\nimport com.intellij.xdebugger.XSourcePosition\nimport com.intellij.xdebugger.evaluation.XDebuggerEvaluator\nimport com.intellij.xdebugger.frame.*\nimport com.phodal.shirelang.debugger.snapshot.UserCustomVariableSnapshot\nimport com.phodal.shirelang.debugger.snapshot.VariableSnapshotRecorder\nimport org.jetbrains.concurrency.Promise\n\n\nclass ShireExecutionStack(private val process: ShireDebugProcess, project: Project) :\n    XExecutionStack(\"Variables\") {\n    private val stackFrames: MutableList<ShireStackFrame> = mutableListOf()\n\n    init {\n        stackFrames.add(ShireStackFrame(process, project, null))\n        val variableSnapshots = VariableSnapshotRecorder.getInstance(project).all()\n        variableSnapshots.forEach {\n            stackFrames.add(ShireStackFrame(process, project, it))\n        }\n\n        stackFrames.reverse()\n    }\n\n    override fun getTopFrame(): XStackFrame? = stackFrames.firstOrNull()\n\n    override fun computeStackFrames(firstFrameIndex: Int, container: XStackFrameContainer) {\n        container.addStackFrames(stackFrames, true)\n    }\n}\n\nclass ShireStackFrame(\n    val process: ShireDebugProcess,\n    val project: Project,\n    private val snapshot: UserCustomVariableSnapshot? = null,\n) : XStackFrame(), Disposable {\n    private var snapshotValue: ShireDebugValue? = null\n    override fun customizePresentation(component: ColoredTextContainer) {\n        if (snapshot == null) {\n            component.append(\"Init\", SimpleTextAttributes.REGULAR_ATTRIBUTES)\n            component.setIcon(AllIcons.Debugger.Frame)\n            return\n        }\n\n        val variableOperation = snapshot.operations.firstOrNull()\n        if (variableOperation == null) {\n            component.append(\n                snapshot.variableName + \" -> \" + \"Init\" + \"(\" + \")\",\n                SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES\n            )\n            component.setIcon(AllIcons.Debugger.Frame)\n        } else {\n            val functionName = variableOperation.functionName\n            val value = variableOperation.value.toString()\n            snapshotValue = ShireDebugValue(snapshot.variableName, \"String\", value, snapshot)\n            component.append(\n                snapshot.variableName + \" -> \" + functionName + \"(\" + value + \")\",\n                SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES\n            )\n            component.setIcon(AllIcons.Debugger.Frame)\n        }\n    }\n\n    override fun computeChildren(node: XCompositeNode) {\n        val root = XValueChildrenList()\n        snapshotValue?.let {\n            root.add(it)\n        }\n\n        node.addChildren(root, false)\n\n        val filteredChildren = XValueChildrenList()\n        process.shireRunnerContext?.compiledVariables?.forEach {\n            filteredChildren.add(ShireDebugValue(it.key, \"String\", it.value.toString()))\n        }\n\n        node.addChildren(filteredChildren, true)\n    }\n\n    override fun getEvaluator(): XDebuggerEvaluator? {\n        return ShireDebugEvaluator()\n    }\n\n    override fun dispose() {\n\n    }\n}\n\nclass ShireDebugEvaluator : XDebuggerEvaluator() {\n    override fun evaluate(expr: String, callback: XEvaluationCallback, expressionPosition: XSourcePosition?) {\n        ApplicationManager.getApplication().executeOnPooledThread {\n            val expression = expr.trim()\n            if (expression.isEmpty()) {\n                callback.evaluated(getNone())\n                return@executeOnPooledThread\n            }\n\n\n            val value = ShireDebugValue(expression, \"String\", expression)\n            callback.evaluated(value)\n        }\n    }\n\n    private fun getNone(): XValue = ShireDebugValue(\"\", \"None\", \"\")\n}\n\nclass ShireDebugValue(\n    private val myName: String,\n    val type: String = \"String\",\n    val value: String,\n    val snapshot: UserCustomVariableSnapshot? = null,\n) : XNamedValue(myName) {\n    override fun computePresentation(node: XValueNode, place: XValuePlace) {\n        if (snapshot != null) {\n            node.setPresentation(AllIcons.Debugger.Value, myName, value, true)\n            return\n        }\n\n        node.setPresentation(AllIcons.Debugger.Value, myName, value, false)\n    }\n\n    override fun calculateEvaluationExpression(): Promise<XExpression> {\n        return super.calculateEvaluationExpression()\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/debugger/ShireSuspendContext.kt",
    "content": "package com.phodal.shirelang.debugger\n\nimport com.intellij.openapi.project.Project\nimport com.intellij.xdebugger.frame.XExecutionStack\nimport com.intellij.xdebugger.frame.XSuspendContext\n\nclass ShireSuspendContext(val process: ShireDebugProcess, project: Project) : XSuspendContext() {\n    private val shireExecutionStacks: Array<XExecutionStack> = arrayOf(\n        ShireExecutionStack(process, project)\n    )\n\n    override fun getActiveExecutionStack(): XExecutionStack? = shireExecutionStacks.firstOrNull()\n    override fun getExecutionStacks(): Array<XExecutionStack> = shireExecutionStacks\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/debugger/snapshot/ShireFileSnapshot.kt",
    "content": "package com.phodal.shirelang.debugger.snapshot\n\nimport com.intellij.openapi.vfs.VirtualFile\nimport kotlinx.datetime.Clock\n\n/**\n * the snapshot of TimeTravel Debugger\n * ```shire\n * ---\n * name: \"Context Variable\"\n * description: \"Here is a description of the action.\"\n * interaction:  RunPanel\n * variables:\n *   \"contextVariable\": /ContextVariable\\.kt/ { cat }\n *   \"psiContextVariable\": /PsiContextVariable\\.kt/ { cat }\n * onStreamingEnd: { parseCode | saveFile(\"docs/shire/shire-builtin-variable.md\") }\n * ---\n *\n * 根据如下的信息，编写对应的 ContextVariable 相关信息的 markdown 文档。\n * ```\n */\nclass ShireFileSnapshot(\n    val file: VirtualFile,\n    val rnd: Int, // seed for random number generator\n    var variables: Map<String, UserCustomVariableSnapshot>,\n    var allCode: String = \"\",\n    /**\n     * execute to current code line\n     */\n    var executedCode: String = \"\",\n    val metadata: SnapshotMetadata = SnapshotMetadata(Clock.System.now(), \"0.0.1\", file),\n) {\n\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/debugger/snapshot/UserCustomVariableSnapshot.kt",
    "content": "package com.phodal.shirelang.debugger.snapshot\n\nimport com.intellij.openapi.util.UserDataHolderBase\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.phodal.shirelang.compiler.variable.resolver.base.VariableResolver\nimport kotlinx.datetime.Instant\n\ndata class SnapshotMetadata(\n    val createdAt: Instant,          // 创建时间\n    val version: String,             // 版本号或其他标识\n    val file: VirtualFile,           // 文件的虚拟路径\n)\n\n/**\n * Variable Snapshot will store all change flow of a variable. For example:\n * ```shire\n * ---\n * variables:\n *   \"controllers\": /.*.java/ { cat | grep(\"class\\s+([a-zA-Z]*Controller)\")  }\n * ---\n * ```\n *\n * The variable snapshot should store:\n *\n * - the value after cat function\n * - the value after grep function\n */\ndata class VariableOperation(\n    val functionName: String,\n    val timestamp: Long,\n    val value: Any?,\n)\n\nclass UserCustomVariableSnapshot(\n    val variableName: String,\n    val value: Any? = null,\n    val className: String? = VariableResolver::class.java.name,\n    val operations: List<VariableOperation> = mutableListOf(),\n    private val context: ExecutionContext = ExecutionContext(),\n) : UserDataHolderBase() {\n    private val valueHistory = mutableListOf<Any>()\n    private var currentValue: Any? = null\n\n    fun recordValue(value: Any, functionIndex: Int = -1) {\n        currentValue = value\n        valueHistory.add(value)\n    }\n\n    fun getCurrentValue(): Any? = currentValue\n\n    fun getHistory(): List<Any> = valueHistory.toList()\n}\n\ndata class ExecutionContext(\n    val variables: MutableMap<String, Any> = mutableMapOf(),\n    val environment: MutableMap<String, String> = mutableMapOf(),\n    val metadata: MutableMap<String, Any> = mutableMapOf(),\n)\n\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/debugger/snapshot/VariableSnapshotRecorder.kt",
    "content": "\npackage com.phodal.shirelang.debugger.snapshot\n\nimport com.intellij.openapi.components.Service\nimport com.intellij.openapi.project.Project\n\n@Service(Service.Level.PROJECT)\nclass VariableSnapshotRecorder {\n    private val snapshots = mutableListOf<UserCustomVariableSnapshot>()\n    private val listeners = mutableListOf<VariableSnapshotListener>()\n\n    fun addSnapshot(variableName: String, value: Any, operation: String? = null, operationArg: Any? = null) {\n        val operationList = mutableListOf<VariableOperation>()\n        if (operation != null) {\n            operationList.add(VariableOperation(operation, System.currentTimeMillis(), operationArg))\n        }\n\n        val result = when (value) {\n            is Array<*> -> {\n                value.joinToString(\", \")\n            }\n\n            is List<*> -> {\n                value.joinToString(\", \")\n            }\n\n            else -> {\n                value.toString()\n            }\n        }\n\n        snapshots.add(UserCustomVariableSnapshot(variableName, result, operations = operationList))\n        listeners.forEach { it.onSnapshot(variableName, result, operationList) }\n    }\n\n    fun clear() {\n        snapshots.clear()\n    }\n\n    fun all(): List<UserCustomVariableSnapshot> {\n        return snapshots\n    }\n\n    fun addListener(listener: VariableSnapshotListener) {\n        listeners.add(listener)\n    }\n\n    companion object {\n        fun getInstance(project: Project): VariableSnapshotRecorder {\n            return project.getService(VariableSnapshotRecorder::class.java)\n        }\n    }\n}\n\ninterface VariableSnapshotListener {\n    fun onSnapshot(variableName: String, value: String, operations: List<VariableOperation>)\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/documentation/ShireDocumentationProvider.kt",
    "content": "package com.phodal.shirelang.documentation\n\nimport com.intellij.lang.documentation.AbstractDocumentationProvider\nimport com.intellij.openapi.editor.Editor\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.PsiFile\nimport com.intellij.psi.util.elementType\nimport com.phodal.shirelang.psi.ShireTypes\nimport com.phodal.shirecore.agent.CustomAgent\nimport com.phodal.shirecore.utils.markdown.MarkdownUtil\nimport com.phodal.shirelang.ShireLanguage\nimport com.phodal.shirelang.compiler.ast.patternaction.PatternActionFunc\nimport com.phodal.shirelang.completion.dataprovider.BuiltinCommand\nimport com.phodal.shirelang.compiler.variable.CompositeVariableProvider\n\nclass ShireDocumentationProvider : AbstractDocumentationProvider() {\n    override fun generateDoc(element: PsiElement?, originalElement: PsiElement?): String? {\n        val project = element?.project ?: return null\n        val markdownDoc = when (element.elementType) {\n            ShireTypes.IDENTIFIER -> {\n               when (element.parent.elementType) {\n                    ShireTypes.AGENT_ID -> {\n                        val agentConfigs = CustomAgent.loadFromProject(project).filter {\n                            it.name == element.text\n                        }\n\n                        if (agentConfigs.isEmpty()) return null\n                        agentConfigs.joinToString(\"\\n\") { it.description }\n                    }\n\n                    ShireTypes.COMMAND_ID -> {\n                        val command = BuiltinCommand.all().find { it.commandName == element.text } ?: return null\n                        val example = BuiltinCommand.example(command)\n                        val lang = ShireLanguage.INSTANCE.displayName\n                        \"${command.description}\\nExample:\\n```$lang\\n$example\\n```\\n \"\n                    }\n\n                    ShireTypes.VARIABLE_ID -> {\n                        CompositeVariableProvider.all().find { it.name == element.text }?.description\n                    }\n\n                   ShireTypes.FUNC_NAME -> {\n                       val funcName = element.text\n                       PatternActionFunc.findDocByName(funcName) ?: return null\n                   }\n\n                    else -> null\n                }\n            }\n\n            ShireTypes.PATTERN_ACTION -> {\n                \"Pattern action is a way to define a pattern for the agent to match. It's a JSONPath expression.\"\n            }\n\n            else -> {\n                null\n            }\n        } ?: return null\n\n        return MarkdownUtil.toHtml(markdownDoc)\n    }\n\n    override fun getCustomDocumentationElement(\n        editor: Editor,\n        file: PsiFile,\n        contextElement: PsiElement?,\n        targetOffset: Int,\n    ): PsiElement? = contextElement ?: file.findElementAt(targetOffset)\n}\n\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/editor/FileFilterPopup.kt",
    "content": "package com.phodal.shirelang.editor\n\nimport com.intellij.codeInsight.AutoPopupController\nimport com.intellij.codeInsight.completion.InsertHandler\nimport com.intellij.codeInsight.lookup.LookupElement\nimport com.intellij.ide.presentation.VirtualFilePresentation\nimport com.intellij.openapi.application.ApplicationManager\nimport com.intellij.openapi.application.runReadAction\nimport com.intellij.openapi.editor.event.DocumentListener\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.project.guessProjectDir\nimport com.intellij.openapi.roots.ProjectFileIndex\nimport com.intellij.openapi.ui.popup.JBPopup\nimport com.intellij.openapi.ui.popup.JBPopupFactory\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.intellij.ui.TextFieldWithAutoCompletion\nimport com.intellij.ui.TextFieldWithAutoCompletionListProvider\nimport com.phodal.shirecore.canBeAdded\nimport java.awt.Component\nimport java.awt.Dimension\nimport javax.swing.Icon\n\n/**\n * @author lk\n */\nclass FileFilterPopup(project: Project, onSelect: (VirtualFile) -> Unit) {\n    private val textField: TextFieldWithAutoCompletion<VirtualFile>\n\n    private var popup: JBPopup? = null\n\n    init {\n        val fileList = mutableListOf<VirtualFile>()\n\n        ApplicationManager.getApplication().executeOnPooledThread {\n            runReadAction {\n                ProjectFileIndex.getInstance(project).iterateContent({ file ->\n                    fileList.add(file)\n                    true\n                }) { it.canBeAdded(project) }\n            }\n        }\n\n        val basePath = project.guessProjectDir()?.path ?: \"\"\n\n        textField = TextFieldWithAutoCompletion(\n            project, object : TextFieldWithAutoCompletionListProvider<VirtualFile>(fileList) {\n                override fun getLookupString(item: VirtualFile): String {\n                    return item.path.removePrefix(basePath)\n                }\n\n                override fun getIcon(item: VirtualFile): Icon? {\n                    return VirtualFilePresentation.getIcon(item)\n                }\n\n                override fun createInsertHandler(item: VirtualFile): InsertHandler<LookupElement> {\n                    return InsertHandler { _, _ -> onSelect(item); popup?.cancel() }\n                }\n\n            }, false, null\n        )\n\n\n        textField.addDocumentListener(object : DocumentListener {\n            override fun documentChanged(e: com.intellij.openapi.editor.event.DocumentEvent) {\n                if (e.oldLength > e.newLength && textField.editor != null) {\n                    AutoPopupController.getInstance(project).autoPopupMemberLookup(textField.editor) { true }\n                }\n            }\n        })\n    }\n\n    fun show(component: Component) {\n        textField.preferredSize = Dimension(180, 30)\n        popup = JBPopupFactory.getInstance()\n            .createComponentPopupBuilder(textField, textField)\n            .setResizable(true)\n            .setMovable(false)\n            .setRequestFocus(true)\n            .createPopup()\n        popup?.showUnderneathOf(component)\n    }\n\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/editor/ShireFileEditorWithPreview.kt",
    "content": "package com.phodal.shirelang.editor\n\nimport com.intellij.icons.AllIcons\nimport com.intellij.ide.BrowserUtil\nimport com.intellij.openapi.actionSystem.*\nimport com.intellij.openapi.editor.event.VisibleAreaEvent\nimport com.intellij.openapi.editor.event.VisibleAreaListener\nimport com.intellij.openapi.editor.ex.util.EditorUtil\nimport com.intellij.openapi.editor.impl.EditorImpl\nimport com.intellij.openapi.fileEditor.TextEditor\nimport com.intellij.openapi.fileEditor.TextEditorWithPreview\nimport com.intellij.openapi.fileEditor.impl.text.TextEditorProvider\nimport com.intellij.openapi.project.DumbService\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.phodal.shirelang.ShireBundle\n\nclass ShireFileEditorWithPreview(\n    private val ourEditor: TextEditor,\n    @JvmField var preview: ShirePreviewEditor,\n    private val project: Project,\n) : TextEditorWithPreview(\n    ourEditor, preview,\n    \"Shire Split Editor\",\n    Layout.SHOW_EDITOR_AND_PREVIEW,\n) {\n    val virtualFile: VirtualFile = ourEditor.file\n\n    init {\n        // allow launching actions while in preview mode;\n        preview.setMainEditor(ourEditor.editor)\n        ourEditor.editor.scrollingModel.addVisibleAreaListener(MyVisibleAreaListener(), this)\n    }\n\n    override fun dispose() {\n        TextEditorProvider.getInstance().disposeEditor(ourEditor)\n    }\n\n    inner class MyVisibleAreaListener : VisibleAreaListener {\n        private var previousLine = 0\n\n        override fun visibleAreaChanged(event: VisibleAreaEvent) {\n            val editor = event.editor\n            val y = editor.scrollingModel.verticalScrollOffset\n            val currentLine = if (editor is EditorImpl) editor.yToVisualLine(y) else y / editor.lineHeight\n            if (currentLine == previousLine) {\n                return\n            }\n\n            previousLine = currentLine\n            preview.scrollToSrcOffset(EditorUtil.getVisualLineEndOffset(editor, currentLine))\n        }\n    }\n\n    override fun createToolbar(): ActionToolbar {\n        return ActionManager.getInstance()\n            .createActionToolbar(ActionPlaces.EDITOR_TOOLBAR, createActionGroup(project), true)\n            .also {\n                it.targetComponent = editor.contentComponent\n            }\n    }\n\n    private fun createActionGroup(project: Project): ActionGroup {\n        return DefaultActionGroup(\n            object : AnAction(ShireBundle.message(\"editor.preview\"), ShireBundle.message(\"editor.preview.tip\"), AllIcons.Actions.Preview) {\n                override fun getActionUpdateThread(): ActionUpdateThread = ActionUpdateThread.BGT\n                override fun update(e: AnActionEvent) {\n                    e.presentation.isEnabled = !DumbService.isDumb(project)\n                }\n\n                override fun actionPerformed(e: AnActionEvent) {\n                    DumbService.getInstance(project).runWhenSmart {\n                        preview.component.isVisible = true\n                        preview.updateDisplayedContent()\n                    }\n                }\n            },\n            object : AnAction(ShireBundle.message(\"editor.preview.refresh\"), ShireBundle.message(\"editor.preview.refresh\"), AllIcons.Actions.Refresh) {\n                override fun getActionUpdateThread(): ActionUpdateThread = ActionUpdateThread.BGT\n                override fun update(e: AnActionEvent) {\n                    e.presentation.isEnabled = !DumbService.isDumb(project)\n                }\n\n                override fun actionPerformed(e: AnActionEvent) {\n                    DumbService.getInstance(project).runWhenSmart {\n                        preview.updateDisplayedContent()\n                    }\n                }\n            },\n            Separator(),\n            object : AnAction(ShireBundle.message(\"editor.preview.help\"), ShireBundle.message(\"editor.preview.help\"), AllIcons.Actions.Help) {\n                override fun getActionUpdateThread(): ActionUpdateThread = ActionUpdateThread.BGT\n                override fun actionPerformed(e: AnActionEvent) {\n                    BrowserUtil.browse(ShireBundle.message(\"editor.preview.help.url\"))\n                }\n            }\n        )\n    }\n\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/editor/ShirePreviewEditor.kt",
    "content": "package com.phodal.shirelang.editor\n\nimport com.intellij.icons.AllIcons\nimport com.intellij.lang.Language\nimport com.intellij.openapi.actionSystem.AnAction\nimport com.intellij.openapi.actionSystem.AnActionEvent\nimport com.intellij.openapi.application.smartReadAction\nimport com.intellij.openapi.editor.Editor\nimport com.intellij.openapi.editor.ScrollType\nimport com.intellij.openapi.editor.ex.EditorEx\nimport com.intellij.openapi.fileEditor.FileDocumentManager\nimport com.intellij.openapi.fileEditor.FileEditor\nimport com.intellij.openapi.fileEditor.FileEditorState\nimport com.intellij.openapi.fileTypes.LanguageFileType\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.util.UserDataHolder\nimport com.intellij.openapi.util.UserDataHolderBase\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.intellij.psi.PsiManager\nimport com.intellij.testFramework.LightVirtualFile\nimport com.intellij.ui.JBColor\nimport com.intellij.ui.RoundedLineBorder\nimport com.intellij.ui.components.JBLabel\nimport com.intellij.ui.components.JBScrollPane\nimport com.intellij.ui.dsl.builder.Align\nimport com.intellij.ui.dsl.builder.panel\nimport com.intellij.util.ui.JBUI\nimport com.intellij.util.ui.UIUtil\nimport com.phodal.shirecore.ShireCoroutineScope\nimport com.phodal.shirecore.sketch.highlight.CodeHighlightSketch\nimport com.phodal.shirecore.sketch.highlight.EditorFragment\nimport com.phodal.shirecore.utils.markdown.CodeFenceLanguage\nimport com.phodal.shirelang.psi.ShireFile\nimport com.phodal.shirelang.run.runner.ShireRunner\nimport com.phodal.shirelang.run.runner.ShireRunnerContext\nimport kotlinx.coroutines.flow.MutableStateFlow\nimport kotlinx.coroutines.launch\nimport kotlinx.coroutines.runBlocking\nimport org.intellij.plugins.markdown.lang.MarkdownLanguage\nimport java.awt.BorderLayout\nimport java.awt.event.MouseAdapter\nimport java.awt.event.MouseEvent\nimport java.beans.PropertyChangeListener\nimport javax.swing.BorderFactory\nimport javax.swing.JComponent\nimport javax.swing.JPanel\nimport javax.swing.ScrollPaneConstants\nimport javax.swing.SwingConstants\n\n/**\n * Display shire file render prompt and have a sample file as view\n */\nopen class ShirePreviewEditor(\n    val project: Project,\n    val virtualFile: VirtualFile,\n) : UserDataHolder by UserDataHolderBase(), FileEditor {\n    val psiFile = PsiManager.getInstance(project).findFile(virtualFile)\n    private var mainEditor = MutableStateFlow<Editor?>(null)\n    private val mainPanel = JPanel(BorderLayout())\n    private val visualPanel: JBScrollPane = JBScrollPane(\n        mainPanel,\n        ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,\n        ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER\n    )\n\n    private var shireRunnerContext: ShireRunnerContext? = null\n    private val variablePanel = ShireVariableViewPanel(project)\n\n    private var highlightSketch: CodeHighlightSketch? = null\n    private var sampleEditor: Editor? = null\n    private var language: Language? = Language.findLanguageByID(\"JAVA\")\n    private val javaHelloWorld = \"\"\"\n        package com.phodal.shirelang;\n        \n        class HelloWorld {\n            public static void main(String[] args) {\n                System.out.println(\"Hello, World\");\n            }\n        }\n    \"\"\".trimIndent()\n\n    private var editorPanel: JPanel? = null\n\n    init {\n        val corePanel = panel {\n            row {\n                val label = JBLabel(\"Shire Preview (Experimental)\").apply {\n                    fontColor = UIUtil.FontColor.BRIGHTER\n                    background = JBColor(0xF5F5F5, 0x2B2D30)\n                    font = JBUI.Fonts.label(16.0f).asBold()\n                    border = JBUI.Borders.empty(0, 16)\n                    isOpaque = true\n                }\n\n                cell(label).align(Align.FILL).resizableColumn()\n            }\n            if (language != null) {\n                row {\n                    cell(JBLabel(\"Sample file for variable\").apply {\n                        fontColor = UIUtil.FontColor.BRIGHTER\n                        background = JBColor(0xF5F5F5, 0x2B2D30)\n                        font = JBUI.Fonts.label(14.0f).asBold()\n                        border = JBUI.Borders.empty(0, 16)\n                        isOpaque = true\n                    }).align(Align.FILL).resizableColumn()\n\n                    cell(JBLabel(\"(/shire.java)\", AllIcons.Actions.Edit, SwingConstants.LEADING).also {\n                        it.addMouseListener(object : MouseAdapter() {\n                            override fun mouseClicked(e: MouseEvent?) {\n                                FileFilterPopup(project) { file ->\n                                    it.text = \"(${file.name})\"\n                                    language = (file.fileType as? LanguageFileType)?.language\n                                    updatePreviewEditor(file)\n                                }.show(it)\n                            }\n                        })\n\n                    }).align(Align.FILL).resizableColumn()\n                    button(\"\", object : AnAction() {\n                        override fun actionPerformed(event: AnActionEvent) {\n                            updateDisplayedContent()\n                        }\n                    }).also {\n                        it.component.icon = AllIcons.Actions.Refresh\n                        it.component.preferredSize = JBUI.size(24, 24)\n                    }\n                }\n                row {\n                    val editor = CodeHighlightSketch.createCodeViewerEditor(\n                        project,\n                        javaHelloWorld,\n                        language,\n                        this@ShirePreviewEditor\n                    )\n\n                    setSampleEditor(editor) {\n                        editorPanel = JPanel(BorderLayout()).apply {\n                            add(it, BorderLayout.CENTER)\n                            cell(this).align(Align.FILL).resizableColumn()\n                        }\n                    }\n\n                }\n            }\n            row {\n                cell(JBLabel(\"Variables\").apply {\n                    fontColor = UIUtil.FontColor.BRIGHTER\n                    background = JBColor(0xF5F5F5, 0x2B2D30)\n                    font = JBUI.Fonts.label(14.0f).asBold()\n                    border = JBUI.Borders.empty(0, 16)\n                    isOpaque = true\n                }).align(Align.FILL).resizableColumn()\n            }\n            row {\n                cell(variablePanel).align(Align.FILL)\n            }\n            row {\n                cell(JBLabel(\"Prompt (some variable may be error)\").apply {\n                    fontColor = UIUtil.FontColor.BRIGHTER\n                    background = JBColor(0xF5F5F5, 0x2B2D30)\n                    font = JBUI.Fonts.label(14.0f).asBold()\n                    border = JBUI.Borders.empty(0, 16)\n                    isOpaque = true\n                }).align(Align.FILL).resizableColumn()\n            }\n            row {\n                highlightSketch = CodeHighlightSketch(project, \"\", MarkdownLanguage.INSTANCE, 18).apply {\n                    initEditor(\"Please refresh to see the result\")\n                }\n                highlightSketch?.editorFragment?.setCollapsed(true)\n                highlightSketch?.editorFragment?.updateExpandCollapseLabel()\n\n                val panel = JPanel(BorderLayout())\n                panel.border = BorderFactory.createCompoundBorder(\n                    BorderFactory.createEmptyBorder(12, 12, 12, 12),\n                    RoundedLineBorder(JBColor.border(), 8, 1)\n                )\n                highlightSketch?.let { panel.add(it, BorderLayout.CENTER) }\n\n                cell(panel).align(Align.FILL)\n            }\n        }\n\n        this.mainPanel.add(corePanel, BorderLayout.CENTER)\n    }\n\n    fun updateDisplayedContent() {\n        try {\n            ShireCoroutineScope.scope(project).launch {\n                runBlocking {\n                    val psiFile = smartReadAction(project) {\n                        PsiManager.getInstance(project).findFile(virtualFile) as? ShireFile\n                    } ?: return@runBlocking\n\n                    shireRunnerContext = ShireRunner.compileOnly(project, psiFile, mapOf(), sampleEditor)\n\n                    val variables = shireRunnerContext?.compiledVariables\n                    if (variables != null) {\n                        variablePanel.updateVariables(variables)\n                    }\n\n                    highlightSketch?.updateViewText(shireRunnerContext!!.finalPrompt)\n                    highlightSketch?.repaint()\n\n                    mainPanel.revalidate()\n                    mainPanel.repaint()\n                }\n            }\n        } catch (e: Exception) {\n            e.printStackTrace()\n        }\n    }\n\n    fun setMainEditor(editor: Editor) {\n        check(mainEditor.value == null)\n        mainEditor.value = editor\n    }\n\n    fun scrollToSrcOffset(offset: Int) {\n        val highlightEditor = highlightSketch?.editorFragment?.editor\n        if (highlightEditor == null) {\n            visualPanel.verticalScrollBar.value = offset\n            return\n        }\n\n        val position = highlightEditor.offsetToLogicalPosition(offset)\n        highlightEditor.scrollingModel.scrollTo(position, ScrollType.MAKE_VISIBLE)\n    }\n\n    private fun updatePreviewEditor(file: VirtualFile) {\n        FileDocumentManager.getInstance().getDocument(file)?.text?.let { text ->\n\n            val language = language ?: CodeFenceLanguage.findLanguage(\"Plain text\")\n            val lightFile = object : LightVirtualFile(file.name, language, text) {\n                override fun getPath() = file.path\n            }\n\n            val document = FileDocumentManager.getInstance().getDocument(lightFile) ?: return@let\n\n            val editor = CodeHighlightSketch.createCodeViewerEditor(\n                project, lightFile, document, this\n            )\n\n            setSampleEditor(editor) {\n                editorPanel?.removeAll()\n                editorPanel?.add(it, BorderLayout.CENTER)\n            }\n            updateDisplayedContent()\n        }\n    }\n\n    private fun setSampleEditor(editor: EditorEx, consume: (JComponent) -> Unit) {\n        editor.isViewer = false\n        editor.settings.isLineNumbersShown = true\n\n        val editorFragment = EditorFragment(editor)\n        editorFragment.setCollapsed(true)\n        editorFragment.updateExpandCollapseLabel()\n\n        sampleEditor = editor\n\n        consume(editorFragment.getContent())\n    }\n\n    override fun getComponent(): JComponent = visualPanel\n    override fun getName(): String = \"Shire Prompt Preview\"\n    override fun setState(state: FileEditorState) {}\n    override fun isModified(): Boolean = false\n    override fun isValid(): Boolean = true\n    override fun getFile(): VirtualFile = virtualFile\n    override fun getPreferredFocusedComponent(): JComponent? = null\n    override fun addPropertyChangeListener(listener: PropertyChangeListener) {}\n    override fun removePropertyChangeListener(listener: PropertyChangeListener) {}\n    override fun dispose() {}\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/editor/ShirePreviewEditorProvider.kt",
    "content": "package com.phodal.shirelang.editor\n\nimport com.intellij.openapi.fileEditor.AsyncFileEditorProvider\nimport com.intellij.openapi.fileEditor.FileEditor\nimport com.intellij.openapi.fileEditor.FileEditorPolicy\nimport com.intellij.openapi.fileEditor.WeighedFileEditorProvider\nimport com.intellij.openapi.fileTypes.FileTypeRegistry\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.phodal.shirelang.ShireFileType\n\nclass ShirePreviewEditorProvider : WeighedFileEditorProvider(), AsyncFileEditorProvider {\n    override fun accept(project: Project, file: VirtualFile): Boolean {\n        return FileTypeRegistry.getInstance().isFileOfType(file, ShireFileType.INSTANCE)\n    }\n\n    override fun createEditor(project: Project, virtualFile: VirtualFile): FileEditor {\n        return ShirePreviewEditor(project, virtualFile)\n    }\n\n    override fun createEditorAsync(project: Project, file: VirtualFile): AsyncFileEditorProvider.Builder {\n        return object : AsyncFileEditorProvider.Builder() {\n            override fun build(): FileEditor {\n                return ShirePreviewEditor(project, file)\n            }\n        }\n    }\n\n    override fun getEditorTypeId(): String = \"shire-preview-editor\"\n\n    override fun getPolicy(): FileEditorPolicy = FileEditorPolicy.PLACE_AFTER_DEFAULT_EDITOR\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/editor/ShireSnapshotViewPanel.kt",
    "content": "package com.phodal.shirelang.editor\n\nimport com.intellij.ui.JBColor\nimport com.intellij.ui.components.JBLabel\nimport com.intellij.ui.components.JBPanel\nimport com.intellij.ui.components.JBScrollPane\nimport com.intellij.ui.table.JBTable\nimport com.intellij.util.ui.JBUI\nimport com.intellij.util.ui.UIUtil\nimport com.phodal.shirelang.ShireBundle\nimport com.phodal.shirelang.debugger.snapshot.UserCustomVariableSnapshot\nimport java.awt.BorderLayout\nimport javax.swing.JPanel\nimport javax.swing.ScrollPaneConstants\nimport javax.swing.table.DefaultTableModel\n\nclass ShireSnapshotViewPanel : JPanel(BorderLayout()) {\n    private val contentPanel = JBPanel<JBPanel<*>>(BorderLayout())\n    private val tableModel = DefaultTableModel(arrayOf(\"Variable\", \"Operation\", \"Value\", \"Timestamp\"), 0)\n\n    init {\n        val table = JBTable(tableModel).apply {\n            tableHeader.reorderingAllowed = true\n            tableHeader.resizingAllowed = true\n            setShowGrid(true)\n            gridColor = JBColor.PanelBackground\n            intercellSpacing = JBUI.size(0, 0)\n\n            val columnModel = columnModel\n            columnModel.getColumn(0).preferredWidth = 80\n            columnModel.getColumn(1).preferredWidth = 60\n            columnModel.getColumn(2).preferredWidth = 300\n            columnModel.getColumn(3).preferredWidth = 80\n\n            autoResizeMode = JBTable.AUTO_RESIZE_LAST_COLUMN\n        }\n\n        val scrollPane = JBScrollPane(\n            table,\n            ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,\n            ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED\n        ).apply {\n            minimumSize = JBUI.size(0, 160)\n            preferredSize = JBUI.size(0, 160)\n        }\n\n        setupPanel()\n        add(scrollPane, BorderLayout.CENTER)\n    }\n\n    private fun setupPanel() {\n        contentPanel.background = JBColor(0xF5F5F5, 0x2B2D30)\n\n        val titleLabel = JBLabel(ShireBundle.message(\"editor.preview.variable.panel\")).apply {\n            font = JBUI.Fonts.label(14f).asBold()\n            fontColor = UIUtil.FontColor.BRIGHTER\n            background = JBColor(0xF5F5F5, 0x2B2D30)\n            font = JBUI.Fonts.label(14.0f).asBold()\n            border = JBUI.Borders.empty(0, 16)\n            isOpaque = true\n        }\n\n        contentPanel.add(titleLabel, BorderLayout.NORTH)\n        add(contentPanel, BorderLayout.NORTH)\n    }\n\n    fun updateSnapshots(snapshots: List<UserCustomVariableSnapshot>) {\n        if (snapshots.isEmpty()) {\n            isVisible = false\n            return\n        }\n\n        isVisible = true\n\n        tableModel.rowCount = 0\n        /// remove all rows\n        tableModel.dataVector.removeAllElements()\n\n        snapshots.forEach { snapshot ->\n            val operation = snapshot.operations.firstOrNull()\n            tableModel.addRow(\n                arrayOf(\n                    snapshot.variableName,\n                    operation?.functionName ?: \"\",\n                    snapshot.value.toString(),\n                    formatTimestamp(operation?.timestamp ?: 0)\n                )\n            )\n        }\n\n        revalidate()\n        repaint()\n    }\n\n    private fun formatTimestamp(timestamp: Long): String {\n        if (timestamp == 0L) {\n            return \"N/A\"\n        }\n\n        return java.text.SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss\").format(timestamp)\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/editor/ShireSplitEditorProvider.kt",
    "content": "package com.phodal.shirelang.editor\n\nimport com.intellij.openapi.fileEditor.*\nimport com.intellij.openapi.fileEditor.impl.text.TextEditorProvider\nimport com.intellij.openapi.fileTypes.FileTypeRegistry\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.intellij.testFramework.LightVirtualFile\nimport com.phodal.shirelang.ShireFileType\n\n\nclass ShireSplitEditorProvider : WeighedFileEditorProvider() {\n    override fun getEditorTypeId() = \"shire-split-editor\"\n    private val mainProvider: TextEditorProvider = TextEditorProvider.getInstance()\n    private val previewProvider: FileEditorProvider = ShirePreviewEditorProvider()\n\n    override fun accept(project: Project, file: VirtualFile) =\n        FileTypeRegistry.getInstance().isFileOfType(file, ShireFileType.INSTANCE)\n\n    override fun createEditor(project: Project, file: VirtualFile): FileEditor {\n        val editor = TextEditorProvider.getInstance().createEditor(project, file)\n        if (editor.file is LightVirtualFile) {\n            return editor\n        }\n\n        val mainEditor = mainProvider.createEditor(project, file) as TextEditor\n        val preview = previewProvider.createEditor(project, file) as ShirePreviewEditor\n        return ShireFileEditorWithPreview(mainEditor, preview, project)\n    }\n\n    override fun getPolicy() = FileEditorPolicy.HIDE_OTHER_EDITORS\n}\n\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/editor/ShireVariableViewPanel.kt",
    "content": "package com.phodal.shirelang.editor\n\nimport com.intellij.openapi.project.Project\nimport com.intellij.ui.JBColor\nimport com.intellij.ui.components.JBLabel\nimport com.intellij.ui.components.JBPanel\nimport com.intellij.ui.components.JBScrollPane\nimport com.intellij.ui.table.JBTable\nimport com.intellij.util.ui.JBUI\nimport com.phodal.shirecore.provider.variable.model.DebugValue\nimport com.phodal.shirecore.provider.variable.model.Variable\nimport com.phodal.shirelang.debugger.snapshot.VariableSnapshotRecorder\nimport java.awt.BorderLayout\nimport javax.swing.JPanel\nimport javax.swing.ScrollPaneConstants\nimport javax.swing.table.DefaultTableModel\n\nclass ShireVariableViewPanel(val project: Project) : JPanel(BorderLayout()) {\n    private val contentPanel = JBPanel<JBPanel<*>>(BorderLayout())\n    private val tableModel = DefaultTableModel(arrayOf(\"Name\", \"Description\", \"Value\"), 0).apply {\n        background = JBColor.WHITE\n    }\n\n    private val snapshotViewPanel = ShireSnapshotViewPanel()\n    private val snapshotRecorder = VariableSnapshotRecorder.getInstance(project)\n\n    init {\n        val table = JBTable(tableModel).apply {\n            tableHeader.reorderingAllowed = true\n            tableHeader.resizingAllowed = true\n            setShowGrid(true)\n            gridColor = JBColor.PanelBackground\n            intercellSpacing = JBUI.size(0, 0)\n\n            val columnModel = columnModel\n            columnModel.getColumn(0).preferredWidth = 150\n            columnModel.getColumn(1).preferredWidth = 450\n\n            autoResizeMode = JBTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS\n        }\n\n        val scrollPane = JBScrollPane(\n            table,\n            ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,\n            ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER\n        ).apply {\n            minimumSize = JBUI.size(0, 160)\n            preferredSize = JBUI.size(0, 160)\n        }\n\n        add(scrollPane, BorderLayout.CENTER)\n\n        add(snapshotViewPanel, BorderLayout.SOUTH)\n        setupPanel()\n    }\n\n    private fun setupPanel() {\n        contentPanel.background = JBColor(0xF5F5F5, 0x2B2D30)\n\n        val titleLabel = JBLabel(\"Variables\").apply {\n            font = JBUI.Fonts.label(14f).asBold()\n            border = JBUI.Borders.empty(4, 8)\n        }\n\n        contentPanel.add(titleLabel, BorderLayout.NORTH)\n    }\n\n    fun updateVariables(variables: Map<String, Any>) {\n        tableModel.rowCount = 0\n\n        val allVariables: MutableMap<String, Variable> = DebugValue.all().associateBy { it.variableName }.toMutableMap()\n\n        snapshotViewPanel.updateSnapshots(snapshotRecorder.all())\n\n        variables.toSortedMap().forEach { (key, value) ->\n            val valueStr = value.toString()\n            val description = DebugValue.description(key)\n\n            /// remove existing variables\n            if (allVariables.containsKey(key)) {\n                allVariables.remove(key)\n            }\n\n            tableModel.addRow(java.util.Vector<String>().apply {\n                add(key)\n                add(description)\n                add(valueStr)\n            })\n        }\n\n        allVariables.forEach { (_, value) ->\n            tableModel.addRow(java.util.Vector<String>().apply {\n                add(value.variableName)\n                add(value.description)\n                add(\"N/A\")\n            })\n        }\n\n        revalidate()\n        repaint()\n    }\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/folding/ShireFoldingBuilder.kt",
    "content": "package com.phodal.shirelang.folding\n\nimport com.intellij.lang.ASTNode\nimport com.intellij.lang.folding.FoldingBuilderEx\nimport com.intellij.lang.folding.FoldingDescriptor\nimport com.intellij.openapi.editor.Document\nimport com.intellij.openapi.util.TextRange\nimport com.intellij.openapi.util.text.StringUtil\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.util.PsiUtilCore\nimport com.intellij.psi.util.elementType\nimport com.phodal.shirelang.compiler.execute.command.FileShireCommand\nimport com.phodal.shirelang.completion.dataprovider.BuiltinCommand\nimport com.phodal.shirelang.psi.*\n\nclass ShireFoldingBuilder : FoldingBuilderEx() {\n    override fun isCollapsedByDefault(node: ASTNode): Boolean = true\n    override fun getPlaceholderText(node: ASTNode): String = node.text\n\n    override fun buildFoldRegions(root: PsiElement, document: Document, quick: Boolean): Array<FoldingDescriptor> {\n        val descriptors = mutableListOf<FoldingDescriptor>()\n        root.accept(ShireFoldingVisitor(descriptors))\n        return descriptors.toTypedArray()\n    }\n\n    override fun getPlaceholderText(node: ASTNode, range: TextRange): String {\n        val elementType = PsiUtilCore.getElementType(node)\n        when (elementType) {\n            ShireTypes.USED -> {\n                val commandId = (node.psi as ShireUsed).commandId\n                when (commandId?.text) {\n                    BuiltinCommand.FILE.commandName -> {\n                        val prop = (node.psi as ShireUsed).commandProp?.text ?: return \"\"\n                        val virtualFile = FileShireCommand.file((node.psi as ShireUsed).project, prop)\n                        return \"/${BuiltinCommand.FILE.commandName}:${virtualFile?.name}\"\n                    }\n                    BuiltinCommand.STRUCTURE.commandName -> {\n                        val prop = (node.psi as ShireUsed).commandProp?.text ?: return \"\"\n                        val virtualFile = FileShireCommand.file((node.psi as ShireUsed).project, prop)\n                        return \"/${BuiltinCommand.STRUCTURE.commandName}:${virtualFile?.name}\"\n                    }\n                }\n            }\n        }\n\n        val explicitName = foldedElementsPresentations[elementType]\n        val elementText = StringUtil.shortenTextWithEllipsis(node.text, 30, 5)\n        return explicitName?.let { \"$it: $elementText\" } ?: elementText\n    }\n\n    private val foldedElementsPresentations = hashMapOf(\n        ShireTypes.FRONT_MATTER_HEADER to \"Hobbit Hole\",\n        ShireTypes.CODE to \"Code Block\",\n        ShireTypes.QUERY_STATEMENT to \"Shire AstQL\",\n        ShireTypes.BLOCK_COMMENT to \"/* ... */\",\n    )\n\n    override fun isCollapsedByDefault(foldingDescriptor: FoldingDescriptor): Boolean {\n        return when (foldingDescriptor.element.elementType) {\n            ShireTypes.FRONT_MATTER_HEADER -> true\n            ShireTypes.CODE -> false\n            ShireTypes.USED -> true\n            else -> false\n        }\n    }\n}\n\nclass ShireFoldingVisitor(private val descriptors: MutableList<FoldingDescriptor>) : ShireVisitor() {\n    override fun visitElement(element: PsiElement) {\n        when (element.elementType) {\n            ShireTypes.FRONT_MATTER_HEADER -> {\n                descriptors.add(FoldingDescriptor(element.node, element.textRange))\n            }\n\n            ShireTypes.CODE -> {\n                descriptors.add(FoldingDescriptor(element.node, element.textRange))\n            }\n\n            ShireTypes.USED -> {\n                val commandId = (element as? ShireUsed)?.commandId\n                if (commandId?.text == BuiltinCommand.FILE.commandName) {\n                    descriptors.add(FoldingDescriptor(element.node, element.textRange))\n                }\n            }\n        }\n\n        element.acceptChildren(this)\n    }\n\n    override fun visitQueryStatement(o: ShireQueryStatement) {\n        descriptors.add(FoldingDescriptor(o.node, o.textRange))\n    }\n\n    override fun visitCaseBody(o: ShireCaseBody) {\n        descriptors.add(FoldingDescriptor(o.node, o.textRange))\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/formatter/ShireFormattingModelBuilder.kt",
    "content": "// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage com.phodal.shirelang.formatter\n\nimport com.intellij.formatting.*\nimport com.intellij.lang.ASTNode\nimport com.intellij.psi.PsiFile\nimport com.intellij.psi.codeStyle.CodeStyleSettings\nimport com.intellij.psi.formatter.DocumentBasedFormattingModel\nimport com.intellij.psi.formatter.common.AbstractBlock\nimport com.intellij.psi.tree.IElementType\nimport com.intellij.psi.util.PsiUtilCore\nimport com.intellij.util.SmartList\nimport com.intellij.util.containers.FactoryMap\nimport com.phodal.shirelang.psi.ShireElementType\nimport com.phodal.shirelang.psi.ShireTypes\n\nclass ShireFormattingModelBuilder : FormattingModelBuilder {\n    override fun createModel(formattingContext: FormattingContext): FormattingModel {\n        val file = formattingContext.containingFile\n        val settings = formattingContext.codeStyleSettings\n\n        val rootBlock = createBlock(ShireFormattingContext(settings, file), formattingContext.node)\n        return DocumentBasedFormattingModel(rootBlock, settings, file)\n    }\n\n    companion object {\n        fun createBlock(context: ShireFormattingContext, node: ASTNode): Block {\n            val nodeType = PsiUtilCore.getElementType(node)\n            return ShireFormattingBlock(context, node)\n        }\n    }\n}\n\nclass ShireFormattingBlock(private val myContext: ShireFormattingContext, val myNode: ASTNode) :\n    AbstractBlock(myNode, null, myContext.computeAlignment(myNode)) {\n    private val myIndent: Indent?\n\n    init {\n        myIndent = myContext.computeBlockIndent(myNode)\n    }\n\n    override fun getIndent(): Indent? {\n        return myIndent\n    }\n\n    override fun getSpacing(child1: Block?, child2: Block): Spacing? = myContext.computeSpacing(this, child1, child2)\n    override fun isLeaf(): Boolean = false\n    override fun buildChildren(): List<Block> = buildSubBlocks(myContext, myNode)\n\n    private fun buildSubBlocks(context: ShireFormattingContext, node: ASTNode): List<Block> {\n        val res: MutableList<Block> = SmartList()\n        var subNode = node.firstChildNode\n        while (subNode != null) {\n            val subNodeType = PsiUtilCore.getElementType(subNode)\n            if (ShireElementType.SPACE_ELEMENTS.contains(subNodeType)) {\n                // just skip them (comment processed above)\n            } else if (ShireTypes.QUOTE_STRING === subNodeType) {\n                res.addAll(buildSubBlocks(context, subNode))\n            } else if (ShireElementType.CONTAINERS.contains(subNodeType)) {\n                //\n            } else {\n                res.add(ShireFormattingModelBuilder.createBlock(context, subNode))\n            }\n            subNode = subNode.treeNext\n        }\n        return res\n    }\n}\n\nclass ShireFormattingContext(val mySettings: CodeStyleSettings, file: PsiFile) {\n    private val myChildIndentAlignments: Map<ASTNode, Alignment> = FactoryMap.create { node: ASTNode? ->\n        Alignment.createAlignment(true)\n    }\n    private val myChildValueAlignments: Map<ASTNode, Alignment> = FactoryMap.create { node: ASTNode? ->\n        Alignment.createAlignment(true)\n    }\n\n    fun computeAlignment(node: ASTNode): Alignment? {\n        val type: IElementType = PsiUtilCore.getElementType(node)\n        if (type === ShireTypes.COLON) {\n            return myChildValueAlignments[node.treeParent.treeParent]\n        }\n        if (type === ShireTypes.KEY_VALUE) {\n            return myChildIndentAlignments[node.treeParent]\n        }\n\n        return null\n    }\n\n    fun computeSpacing(shireFormattingBlock: ShireFormattingBlock, child1: Block?, child2: Block): Spacing? {\n        return null\n    }\n\n    private val DIRECT_NORMAL_INDENT: Indent = Indent.getNormalIndent(true)\n    private val SAME_AS_PARENT_INDENT: Indent = Indent.getSpaceIndent(0, true)\n    private val SAME_AS_INDENTED_ANCESTOR_INDENT: Indent = Indent.getSpaceIndent(0)\n\n    fun computeBlockIndent(node: ASTNode): Indent? {\n        val nodeType: IElementType = PsiUtilCore.getElementType(node) ?: return null\n\n        if (nodeType === ShireTypes.KEY_VALUE) {\n            return computeKeyValuePairIndent(node)\n        }\n\n        return null\n    }\n\n    private fun computeKeyValuePairIndent(node: ASTNode): Indent? {\n        val parentType = PsiUtilCore.getElementType(node.treeParent)\n        val grandParentType = if (parentType == null) null else PsiUtilCore.getElementType(node.treeParent.treeParent)\n\n        return DIRECT_NORMAL_INDENT\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/highlight/ShireErrorFilter.kt",
    "content": "package com.phodal.shirelang.highlight\n\nimport com.intellij.codeInsight.highlighting.HighlightErrorFilter\nimport com.phodal.shirelang.ShireLanguage\nimport com.intellij.psi.PsiErrorElement\nimport com.intellij.psi.PsiFile\n\nclass ShireErrorFilter : HighlightErrorFilter() {\n    override fun shouldHighlightErrorElement(element: PsiErrorElement): Boolean {\n        val containingFile: PsiFile = element.containingFile\n        return !(containingFile.language === ShireLanguage.INSTANCE && containingFile.context != null)\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/highlight/ShireHighlightingAnnotator.kt",
    "content": "package com.phodal.shirelang.highlight\n\nimport com.intellij.lang.annotation.AnnotationHolder\nimport com.intellij.lang.annotation.Annotator\nimport com.intellij.lang.annotation.HighlightSeverity\nimport com.intellij.openapi.editor.DefaultLanguageHighlighterColors\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.util.PsiUtilCore\nimport com.phodal.shirelang.psi.ShireTypes\n\nclass ShireHighlightingAnnotator : Annotator {\n    override fun annotate(element: PsiElement, holder: AnnotationHolder) {\n        when (PsiUtilCore.getElementType(element)) {\n            ShireTypes.IDENTIFIER -> {\n                holder.newSilentAnnotation(HighlightSeverity.INFORMATION)\n                    .textAttributes(DefaultLanguageHighlighterColors.IDENTIFIER)\n                    .create()\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/highlight/ShireSyntaxHighlighter.kt",
    "content": "package com.phodal.shirelang.highlight\n\nimport com.intellij.lexer.Lexer\nimport com.intellij.openapi.editor.DefaultLanguageHighlighterColors\nimport com.intellij.openapi.editor.colors.TextAttributesKey\nimport com.intellij.openapi.fileTypes.SyntaxHighlighter\nimport com.intellij.openapi.fileTypes.SyntaxHighlighterBase\nimport com.intellij.psi.tree.IElementType\nimport com.intellij.psi.tree.TokenSet\nimport com.phodal.shirelang.lexer.ShireLexerAdapter\nimport com.phodal.shirelang.psi.ShireTypes\n\nclass ShireSyntaxHighlighter : SyntaxHighlighterBase() {\n    override fun getHighlightingLexer(): Lexer = ShireLexerAdapter()\n\n    override fun getTokenHighlights(tokenType: IElementType?): Array<TextAttributesKey> {\n        return pack(ATTRIBUTES[tokenType])\n    }\n\n    companion object {\n        private val ATTRIBUTES: MutableMap<IElementType, TextAttributesKey> = HashMap()\n\n        private val KEYWORDS: TokenSet = TokenSet.create(\n            ShireTypes.CASE,\n            ShireTypes.DEFAULT,\n            ShireTypes.SELECT,\n            ShireTypes.WHERE,\n            ShireTypes.FROM,\n            ShireTypes.IF,\n            ShireTypes.ELSE,\n            ShireTypes.ELSEIF,\n            ShireTypes.END,\n            ShireTypes.ENDIF,\n            ShireTypes.AND,\n\n            // true and false\n            ShireTypes.BOOLEAN,\n\n            // lifecycle\n            ShireTypes.WHEN,\n            ShireTypes.BEFORE_STREAMING,\n            ShireTypes.ON_STREAMING,\n            ShireTypes.ON_STREAMING_END,\n            ShireTypes.AFTER_STREAMING,\n        )\n\n        init {\n            fillMap(\n                ATTRIBUTES,\n                KEYWORDS,\n                DefaultLanguageHighlighterColors.KEYWORD\n            )\n\n            ATTRIBUTES[ShireTypes.COMMENTS] = DefaultLanguageHighlighterColors.LINE_COMMENT\n            ATTRIBUTES[ShireTypes.CONTENT_COMMENTS] = DefaultLanguageHighlighterColors.LINE_COMMENT\n            ATTRIBUTES[ShireTypes.BLOCK_COMMENT] = DefaultLanguageHighlighterColors.BLOCK_COMMENT\n\n            ATTRIBUTES[ShireTypes.VARIABLE_START] = DefaultLanguageHighlighterColors.KEYWORD\n            ATTRIBUTES[ShireTypes.VARIABLE_ID] = DefaultLanguageHighlighterColors.CONSTANT\n\n            ATTRIBUTES[ShireTypes.FOREIGN_TYPE] = DefaultLanguageHighlighterColors.KEYWORD\n            ATTRIBUTES[ShireTypes.OUTPUT_VAR] = DefaultLanguageHighlighterColors.LOCAL_VARIABLE\n            ATTRIBUTES[ShireTypes.ACCESS] = DefaultLanguageHighlighterColors.KEYWORD\n            ATTRIBUTES[ShireTypes.PROCESS] = DefaultLanguageHighlighterColors.KEYWORD\n\n            ATTRIBUTES[ShireTypes.AGENT_START] = DefaultLanguageHighlighterColors.KEYWORD\n            ATTRIBUTES[ShireTypes.AGENT_ID] = DefaultLanguageHighlighterColors.CONSTANT\n\n            ATTRIBUTES[ShireTypes.COMMAND_START] = DefaultLanguageHighlighterColors.KEYWORD\n            ATTRIBUTES[ShireTypes.COMMAND_ID] = DefaultLanguageHighlighterColors.KEYWORD\n            ATTRIBUTES[ShireTypes.COMMAND_PROP] = DefaultLanguageHighlighterColors.STRING\n\n            ATTRIBUTES[ShireTypes.SHARP] = DefaultLanguageHighlighterColors.CONSTANT\n            ATTRIBUTES[ShireTypes.MARKDOWN_HEADER] = DefaultLanguageHighlighterColors.CONSTANT\n\n            ATTRIBUTES[ShireTypes.LINE_INFO] = DefaultLanguageHighlighterColors.NUMBER\n\n            ATTRIBUTES[ShireTypes.CODE_BLOCK_START] = DefaultLanguageHighlighterColors.KEYWORD\n            ATTRIBUTES[ShireTypes.CODE_BLOCK_END] = DefaultLanguageHighlighterColors.KEYWORD\n            ATTRIBUTES[ShireTypes.LANGUAGE_ID] = DefaultLanguageHighlighterColors.CONSTANT\n\n            ATTRIBUTES[ShireTypes.NUMBER] = DefaultLanguageHighlighterColors.NUMBER\n\n            ATTRIBUTES[ShireTypes.FRONTMATTER_START] = DefaultLanguageHighlighterColors.KEYWORD\n            ATTRIBUTES[ShireTypes.FRONTMATTER_END] = DefaultLanguageHighlighterColors.KEYWORD\n\n            ATTRIBUTES[ShireTypes.FRONT_MATTER_ID] = DefaultLanguageHighlighterColors.CONSTANT\n\n            // func name\n            ATTRIBUTES[ShireTypes.IDENTIFIER] = DefaultLanguageHighlighterColors.IDENTIFIER\n            ATTRIBUTES[ShireTypes.NUMBER] = DefaultLanguageHighlighterColors.KEYWORD\n            ATTRIBUTES[ShireTypes.QUOTE_STRING] = DefaultLanguageHighlighterColors.STRING\n            ATTRIBUTES[ShireTypes.DATE] = DefaultLanguageHighlighterColors.LABEL\n\n            ATTRIBUTES[ShireTypes.LBRACKET] = DefaultLanguageHighlighterColors.BRACKETS\n            ATTRIBUTES[ShireTypes.RBRACKET] = DefaultLanguageHighlighterColors.BRACKETS\n\n            ATTRIBUTES[ShireTypes.OPEN_BRACE] = DefaultLanguageHighlighterColors.BRACES\n            ATTRIBUTES[ShireTypes.CLOSE_BRACE] = DefaultLanguageHighlighterColors.BRACES\n\n            ATTRIBUTES[ShireTypes.LPAREN] = DefaultLanguageHighlighterColors.PARENTHESES\n            ATTRIBUTES[ShireTypes.RPAREN] = DefaultLanguageHighlighterColors.PARENTHESES\n        }\n    }\n\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/highlight/ShireSyntaxHighlighterFactory.kt",
    "content": "package com.phodal.shirelang.highlight\n\nimport com.intellij.openapi.fileTypes.SyntaxHighlighterFactory\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.vfs.VirtualFile\n\nclass ShireSyntaxHighlighterFactory : SyntaxHighlighterFactory() {\n    override fun getSyntaxHighlighter(project: Project?, virtualFile: VirtualFile?) = ShireSyntaxHighlighter()\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/highlight/braces/ShireBraceMatcher.kt",
    "content": "package com.phodal.shirelang.highlight.braces\n\nimport com.intellij.codeInsight.highlighting.PairedBraceMatcherAdapter\nimport com.intellij.lang.BracePair\nimport com.intellij.lang.PairedBraceMatcher\nimport com.intellij.psi.PsiFile\nimport com.intellij.psi.tree.IElementType\nimport com.phodal.shirelang.ShireLanguage\nimport com.phodal.shirelang.psi.ShireTypes\n\nclass ShireBraceMatcher : PairedBraceMatcherAdapter(\n    MyPairedBraceMatcher(), ShireLanguage.INSTANCE\n) {\n    class MyPairedBraceMatcher : PairedBraceMatcher {\n        override fun getPairs(): Array<BracePair> {\n            return arrayOf(\n                BracePair(ShireTypes.LPAREN, ShireTypes.RPAREN, false),\n                BracePair(ShireTypes.LBRACKET, ShireTypes.RBRACKET, false),\n                BracePair(ShireTypes.LT, ShireTypes.GT, false),\n                BracePair(ShireTypes.OPEN_BRACE, ShireTypes.CLOSE_BRACE, true),\n                BracePair(ShireTypes.CODE_BLOCK_START, ShireTypes.CODE_BLOCK_END, true)\n            )\n        }\n\n        override fun isPairedBracesAllowedBeforeType(lbraceType: IElementType, type: IElementType?): Boolean {\n            return type == null || type === ShireTypes.RPAREN || type === ShireTypes.RBRACKET || type === ShireTypes.GT\n        }\n\n        override fun getCodeConstructStart(file: PsiFile, openingBraceOffset: Int): Int {\n            return openingBraceOffset\n        }\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/highlight/braces/ShireQuoteHandler.kt",
    "content": "package com.phodal.shirelang.highlight.braces\n\nimport com.intellij.codeInsight.editorActions.SimpleTokenSetQuoteHandler\nimport com.phodal.shirelang.psi.ShireTypes\n\nclass ShireQuoteHandler : SimpleTokenSetQuoteHandler(\n    ShireTypes.QUOTE_STRING\n)\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/index/ShireIdentifierIndex.kt",
    "content": "package com.phodal.shirelang.index\n\nimport com.intellij.psi.PsiElement\nimport com.intellij.util.indexing.*\nimport com.intellij.util.io.DataExternalizer\nimport com.intellij.util.io.EnumeratorStringDescriptor\nimport com.intellij.util.io.KeyDescriptor\nimport com.phodal.shirelang.ShireFileType\nimport com.phodal.shirelang.compiler.ast.hobbit.HobbitHole\nimport com.phodal.shirelang.psi.ShireFrontMatterKey\nimport com.phodal.shirelang.psi.ShireVisitor\nimport java.io.DataInput\nimport java.io.DataOutput\n\ninternal val SHIRE_CONFIG_IDENTIFIER_INDEX_ID = ID.create<String, Int>(\"shire.index.name\")\n\ninternal val isIndexing = ThreadLocal<Boolean>()\n\nclass ShireIdentifierIndex: FileBasedIndexExtension<String, Int>() {\n    override fun getValueExternalizer() = object : DataExternalizer<Int> {\n        override fun save(out: DataOutput, value: Int) = out.writeInt(value)\n        override fun read(`in`: DataInput) = `in`.readInt()\n    }\n\n    override fun getIndexer() = DataIndexer<String, Int, FileContent> {\n        val result = mutableMapOf<String, Int>()\n        val visitor = object : ShireVisitor() {\n            override fun visitElement(element: PsiElement) {\n                if (element is ShireFrontMatterKey && element.text == HobbitHole.NAME) {\n                    result[element.text] = element.textOffset\n                }\n\n                super.visitElement(element)\n            }\n        }\n\n        isIndexing.set(true)\n        it.psiFile.accept(visitor)\n        isIndexing.set(false)\n        result\n    }\n\n    override fun getName() = SHIRE_CONFIG_IDENTIFIER_INDEX_ID\n    override fun getVersion() = 1\n    override fun dependsOnFileContent() = true\n    override fun getInputFilter() = inputFilter\n    override fun getKeyDescriptor(): KeyDescriptor<String> = EnumeratorStringDescriptor.INSTANCE\n\n    private val inputFilter = DefaultFileTypeSpecificInputFilter(ShireFileType.INSTANCE)\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/lexer/ShireLexerAdapter.kt",
    "content": "package com.phodal.shirelang.lexer\n\nimport com.intellij.lexer.FlexAdapter\n\n\nclass ShireLexerAdapter : FlexAdapter(_ShireLexer())\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/lexer/ShireTokenType.kt",
    "content": "package com.phodal.shirelang.lexer\n\nimport com.intellij.psi.tree.IElementType\nimport com.phodal.shirelang.ShireLanguage\nimport org.jetbrains.annotations.NonNls\n\nclass ShireTokenType(debugName: @NonNls String) : IElementType(debugName, ShireLanguage.INSTANCE) {\n    override fun toString(): String = \"ShireTokenType.\" + super.toString()\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/lints/ShireDuplicateAgentInspection.kt",
    "content": "package com.phodal.shirelang.lints\n\nimport com.intellij.codeInspection.LocalInspectionTool\nimport com.intellij.codeInspection.ProblemsHolder\nimport com.intellij.psi.PsiElementVisitor\nimport com.intellij.psi.util.elementType\nimport com.phodal.shirelang.ShireBundle\nimport com.phodal.shirelang.psi.ShireTypes\nimport com.phodal.shirelang.psi.ShireUsed\nimport com.phodal.shirelang.psi.ShireVisitor\n\nclass ShireDuplicateAgentInspection : LocalInspectionTool() {\n    override fun getGroupDisplayName() = ShireBundle.message(\"inspection.group.name\")\n\n    override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {\n        return ShireDuplicateAgentVisitor(holder)\n    }\n\n    private class ShireDuplicateAgentVisitor(val holder: ProblemsHolder) : ShireVisitor() {\n        private var agentIds: MutableSet<ShireUsed> = mutableSetOf()\n\n        override fun visitUsed(o: ShireUsed) {\n            if (o.firstChild.nextSibling.elementType == ShireTypes.AGENT_ID) {\n                agentIds.add(o)\n\n                if (agentIds.contains(o)) {\n                    agentIds.forEachIndexed { index, it ->\n                        if (index > 0) {\n                            holder.registerProblem(it, ShireBundle.message(\"inspection.duplicate.agent\"))\n                        }\n                    }\n                }\n            }\n        }\n    }   \n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/markdown/CodeFenceLanguageAliases.kt",
    "content": "// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage com.phodal.shirelang.markdown\n\nimport com.intellij.openapi.util.text.StringUtil\n\n/**\n * Service to work with Markdown code fence's info-string.\n *\n * [CodeFenceLanguageAliases] is able to find possible IntelliJ Language ID\n * for info string (including resolution of standard aliases) and\n * (backwards) suggest correct info string for IntelliJ Language ID\n */\nobject CodeFenceLanguageAliases {\n  private data class Entry(\n    val id: String,\n    val main: String,\n    val aliases: Set<String>\n  )\n\n  private val aliases = setOf(\n    Entry(\"go\", \"go\", setOf(\"golang\")),\n    Entry(\"HCL\", \"hcl\", setOf(\"hcl\")),\n    Entry(\"ApacheConfig\", \"apacheconf\", setOf(\"aconf\", \"apache\", \"apacheconfig\")),\n    Entry(\"Batch\", \"batch\", setOf(\"bat\", \"batchfile\")),\n    Entry(\"CoffeeScript\", \"coffeescript\", setOf(\"coffee\", \"coffee-script\")),\n    Entry(\"JavaScript\", \"javascript\", setOf(\"js\", \"node\")),\n    Entry(\"Markdown\", \"markdown\", setOf(\"md\")),\n    Entry(\"PowerShell\", \"powershell\", setOf(\"posh\", \"pwsh\")),\n    Entry(\"Python\", \"python\", setOf(\"python2\", \"python3\", \"py\")),\n    Entry(\"R\", \"r\", setOf(\"rlang\", \"rscript\")),\n    Entry(\"RegExp\", \"regexp\", setOf(\"regex\")),\n    Entry(\"Ruby\", \"ruby\", setOf(\"ruby\", \"rb\")),\n    Entry(\"Yaml\", \"yaml\", setOf(\"yml\")),\n    Entry(\"Kotlin\", \"kotlin\", setOf(\"kt\", \"kts\")),\n    Entry(\"HCL-Terraform\", \"terraform\", setOf(\"hcl-terraform\", \"tf\")),\n    Entry(\"C#\", \"csharp\", setOf(\"cs\", \"c#\")),\n    Entry(\"F#\", \"fsharp\", setOf(\"fs\", \"f#\")),\n    Entry(\"Shell Script\", \"shell\", setOf(\"shell script\", \"bash\", \"zsh\", \"sh\"))\n  )\n\n  /**\n   * Finds the registered entry based on the provided value.\n   *\n   * @param value the value to search for in the registered entries\n   * @return the ID of the entry if found, null otherwise\n   */\n  fun findRegisteredEntry(value: String): String? {\n    val lower = value.lowercase()\n    val entry = aliases.singleOrNull { lower == it.main || lower in it.aliases }\n    return entry?.id\n  }\n\n  /**\n   * Get recommended alias for [id]\n   * @return recommended alias if any or just [id]\n   */\n  fun findMainAlias(id: String): String {\n    return findMainAliasIfRegistered(id) ?: StringUtil.toLowerCase(id)\n  }\n\n  private fun findMainAliasIfRegistered(id: String): String? {\n    return aliases.singleOrNull { id == it.id }?.main\n  }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/navigation/ShireGotoDeclarationHandler.kt",
    "content": "package com.phodal.shirelang.navigation\n\nimport com.intellij.codeInsight.navigation.actions.GotoDeclarationHandler\nimport com.intellij.codeInsight.navigation.actions.GotoDeclarationHandlerBase\nimport com.intellij.openapi.application.runInEdt\nimport com.intellij.openapi.editor.Editor\nimport com.intellij.openapi.fileEditor.FileEditorManager\nimport com.intellij.openapi.project.Project\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.impl.source.tree.LeafPsiElement\nimport com.intellij.psi.util.PsiTreeUtil.getChildrenOfTypeAsList\nimport com.intellij.psi.util.elementType\nimport com.phodal.shirecore.findFile\nimport com.phodal.shirecore.lookupFile\nimport com.phodal.shirecore.middleware.post.PostProcessorType\nimport com.phodal.shirelang.compiler.ast.patternaction.PatternActionFuncDef\nimport com.phodal.shirelang.psi.*\n\nclass ShireGotoDeclarationHandler : GotoDeclarationHandlerBase(), GotoDeclarationHandler {\n    private val validFunctionNames = setOf(\n        PatternActionFuncDef.EXECUTE.funcName,\n        PatternActionFuncDef.THREAD.funcName,\n        PatternActionFuncDef.BATCH.funcName,\n        PostProcessorType.SaveFile.handleName,\n        PatternActionFuncDef.APPROVAL_EXECUTE.funcName,\n        \"mock\"\n    )\n\n    override fun getGotoDeclarationTarget(element: PsiElement?, editor: Editor?): PsiElement? {\n        if (element !is LeafPsiElement) return null\n        val project = element.project\n\n        gotoSourceFile(element, project)\n\n        return gotoToFunctionDecl(element)\n    }\n\n    private fun gotoToFunctionDecl(element: LeafPsiElement): ShireFrontMatterEntry? {\n        val psiFile = element.containingFile\n        // handle for foreign function\n        val func = element.parent as? ShireFuncName ?: return null\n        val header = getChildrenOfTypeAsList(psiFile, ShireFrontMatterHeader::class.java).firstOrNull()\n        val functionsNode = header?.frontMatterEntries?.frontMatterEntryList?.firstOrNull {\n            it.firstChild.text == \"functions\"\n        } ?: return null\n\n        val functionEntries: List<ShireFrontMatterEntry> =\n            functionsNode.frontMatterValue?.objectKeyValue?.keyValueList?.filter {\n                it.frontMatterEntry.foreignFunction != null\n            }?.map {\n                it.frontMatterEntry\n            } ?: return null\n\n        val funcName = func.text\n        val foreignFunc = functionEntries.find { it.frontMatterKey?.text == funcName } ?: return null\n        return foreignFunc\n    }\n\n    private fun gotoSourceFile(\n        element: LeafPsiElement,\n        project: Project,\n    ) {\n        if (element.elementType != ShireTypes.QUOTE_STRING) return\n        if (element.parent?.elementType != ShireTypes.PIPELINE_ARG) return\n\n        val funcCall = element.parent?.parent?.parent as? ShireFuncCall ?: return\n        val funcName = funcCall.funcName.text\n\n        if (funcName !in validFunctionNames) return\n\n        val fileName = element.text.removeSurrounding(\"\\\"\")\n        val file = project.lookupFile(fileName) ?: project.findFile(fileName) ?: return\n\n        runInEdt {\n            FileEditorManager.getInstance(project).openFile(file, true)\n        }\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/parser/CodeBlockElement.kt",
    "content": "// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage com.phodal.shirelang.parser\n\nimport com.intellij.extapi.psi.ASTWrapperPsiElement\nimport com.intellij.lang.ASTNode\nimport com.intellij.openapi.util.TextRange\nimport com.intellij.psi.ElementManipulators\nimport com.intellij.psi.LiteralTextEscaper\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.PsiLanguageInjectionHost\nimport com.intellij.psi.impl.source.tree.injected.InjectionBackgroundSuppressor\nimport com.intellij.psi.templateLanguages.OuterLanguageElement\nimport com.intellij.psi.tree.IElementType\nimport com.intellij.psi.util.*\nimport com.phodal.shirelang.psi.ShireExpr\nimport com.phodal.shirelang.psi.ShireTypes\nimport com.phodal.shirecore.utils.markdown.CodeFence\n\nclass CodeBlockElement(node: ASTNode) : ASTWrapperPsiElement(node), PsiLanguageInjectionHost,\n    InjectionBackgroundSuppressor {\n\n    override fun isValidHost(): Boolean {\n        return isAbleToAcceptInjections(this)\n    }\n\n    private fun isAbleToAcceptInjections(host: CodeBlockElement): Boolean {\n        val hasStartBlock = host.firstChild?.elementType != ShireTypes.CODE_BLOCK_START\n        val hasEndBlock = host.lastChild?.elementType != ShireTypes.CODE_BLOCK_END\n\n        return !(hasStartBlock && hasEndBlock)\n    }\n\n    override fun updateText(text: String): PsiLanguageInjectionHost {\n        return ElementManipulators.handleContentChange(this, text)\n    }\n\n    override fun createLiteralTextEscaper(): LiteralTextEscaper<out PsiLanguageInjectionHost> {\n        return CodeBlockLiteralTextEscaper(this)\n    }\n\n    fun getLanguageId(): PsiElement? {\n        return findChildByType(ShireTypes.LANGUAGE_ID)\n    }\n\n    fun codeText(): String {\n        return CodeFence.parse(this.text).text\n    }\n\n    fun isShireTemplateCodeBlock(): Boolean {\n        return PsiTreeUtil.findChildOfType(this, ShireExpr::class.java) != null\n    }\n\n    companion object {\n        fun obtainFenceContent(element: CodeBlockElement): List<PsiElement>? {\n            return CachedValuesManager.getCachedValue(element) {\n                CachedValueProvider.Result.create(getContent(element), element)\n            }\n        }\n\n        private fun getContent(host: CodeBlockElement): List<PsiElement>? {\n            val children = host.firstChild\n                ?.siblings(forward = true, withSelf = true) ?: return null\n\n            val elements =\n                children.filter {\n                    it !is OuterLanguageElement\n                            && (it.node.elementType == ShireTypes.CODE_CONTENTS || it == ShireTypes.NEWLINE)\n                }\n                    .toList()\n\n            if (elements.isNotEmpty() && elements.first() == ShireTypes.NEWLINE) {\n                elements.drop(1)\n            }\n            if (elements.isNotEmpty() && elements.last() == ShireTypes.NEWLINE) {\n                elements.dropLast(1)\n            }\n\n            return elements.takeIf { it.isNotEmpty() }\n        }\n\n        fun obtainRelevantTextRange(element: CodeBlockElement): TextRange {\n            val elements = obtainFenceContent(element) ?: return getEmptyRange(element)\n            val first = elements.first()\n            val last = elements.last()\n\n            return TextRange.create(first.startOffsetInParent, last.startOffsetInParent + last.textLength)\n        }\n\n        private fun getEmptyRange(host: CodeBlockElement): TextRange {\n            val start = host.children.find { it.hasType(ShireTypes.LANGUAGE_ID) }\n                ?: host.children.find { it.hasType(ShireTypes.CODE_BLOCK_START) }\n\n            return TextRange.from(start!!.startOffsetInParent + start.textLength + 1, 0)\n        }\n    }\n}\n\nfun PsiElement.hasType(type: IElementType): Boolean {\n    return PsiUtilCore.getElementType(this) == type\n}\n\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/parser/CodeBlockLiteralTextEscaper.kt",
    "content": "// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage com.phodal.shirelang.parser\n\nimport com.intellij.openapi.util.TextRange\nimport com.intellij.psi.LiteralTextEscaper\n\nclass CodeBlockLiteralTextEscaper(host: CodeBlockElement) : LiteralTextEscaper<CodeBlockElement>(host) {\n    override fun getRelevantTextRange() = CodeBlockElement.obtainRelevantTextRange(myHost)\n    override fun isOneLine(): Boolean = false;\n\n    override fun decode(rangeInsideHost: TextRange, outChars: StringBuilder): Boolean {\n        val elements = CodeBlockElement.obtainFenceContent(myHost) ?: return true\n        for (element in elements) {\n            val intersected = rangeInsideHost.intersection(element.textRangeInParent) ?: continue\n            outChars.append(intersected.substring(myHost.text))\n        }\n\n        return true\n    }\n\n    override fun getOffsetInHost(offsetInDecoded: Int, rangeInsideHost: TextRange): Int {\n        val elements = CodeBlockElement.obtainFenceContent(myHost) ?: return -1\n        var cur = 0\n        for (element in elements) {\n            val intersected = rangeInsideHost.intersection(element.textRangeInParent)\n            if (intersected == null || intersected.isEmpty) continue\n            if (cur + intersected.length == offsetInDecoded) {\n                return intersected.startOffset + intersected.length\n            } else if (cur == offsetInDecoded) {\n                return intersected.startOffset\n            } else if (cur < offsetInDecoded && cur + intersected.length > offsetInDecoded) {\n                return intersected.startOffset + (offsetInDecoded - cur)\n            }\n            cur += intersected.length\n        }\n\n        val last = elements[elements.size - 1]\n        val intersected = rangeInsideHost.intersection(last.textRangeInParent)\n        if (intersected == null || intersected.isEmpty) return -1\n        val result = intersected.startOffset + (offsetInDecoded - (cur - intersected.length))\n        return if (rangeInsideHost.startOffset <= result && result <= rangeInsideHost.endOffset) {\n            result\n        } else -1\n    }\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/parser/PatternElement.kt",
    "content": "package com.phodal.shirelang.parser\n\nimport com.intellij.extapi.psi.ASTWrapperPsiElement\nimport com.intellij.lang.ASTNode\nimport com.intellij.psi.ElementManipulators\nimport com.intellij.psi.LiteralTextEscaper\nimport com.intellij.psi.PsiLanguageInjectionHost\nimport com.intellij.psi.impl.source.tree.injected.InjectionBackgroundSuppressor\n\nclass PatternElement(node: ASTNode) : ASTWrapperPsiElement(node), PsiLanguageInjectionHost,\n    InjectionBackgroundSuppressor {\n    override fun isValidHost(): Boolean = true\n\n    override fun updateText(text: String): PsiLanguageInjectionHost {\n        return ElementManipulators.handleContentChange(this, text)\n    }\n\n    override fun createLiteralTextEscaper(): LiteralTextEscaper<out PsiLanguageInjectionHost> {\n        return LiteralTextEscaper.createSimple(this, false)\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/parser/ShireGrepFuncCall.kt",
    "content": "package com.phodal.shirelang.parser\n\nimport com.intellij.lang.ASTNode\nimport com.intellij.psi.ElementManipulators\nimport com.intellij.psi.LiteralTextEscaper\nimport com.intellij.psi.PsiLanguageInjectionHost\nimport com.intellij.psi.impl.source.tree.injected.InjectionBackgroundSuppressor\nimport com.phodal.shirelang.psi.impl.ShireFuncCallImpl\n\nclass ShireGrepFuncCall(node: ASTNode) : ShireFuncCallImpl(node), PsiLanguageInjectionHost,\n    InjectionBackgroundSuppressor {\n    override fun isValidHost(): Boolean = true\n\n    override fun updateText(text: String): PsiLanguageInjectionHost {\n        return ElementManipulators.handleContentChange(this, text)\n    }\n\n    override fun createLiteralTextEscaper(): LiteralTextEscaper<out PsiLanguageInjectionHost> {\n        return LiteralTextEscaper.createSimple(this, false)\n    }\n}\n\nclass ShireSedFuncCall(node: ASTNode) : ShireFuncCallImpl(node), PsiLanguageInjectionHost,\n    InjectionBackgroundSuppressor {\n    override fun isValidHost(): Boolean = true\n\n    override fun updateText(text: String): PsiLanguageInjectionHost {\n        return ElementManipulators.handleContentChange(this, text)\n    }\n\n    override fun createLiteralTextEscaper(): LiteralTextEscaper<out PsiLanguageInjectionHost> {\n        return LiteralTextEscaper.createSimple(this, false)\n    }\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/parser/ShireParserDefinition.kt",
    "content": "package com.phodal.shirelang.parser\n\nimport com.intellij.extapi.psi.ASTWrapperPsiElement\nimport com.intellij.lang.ASTNode\nimport com.intellij.lang.ParserDefinition\nimport com.intellij.lang.PsiParser\nimport com.intellij.lexer.Lexer\nimport com.intellij.openapi.project.Project\nimport com.intellij.psi.FileViewProvider\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.PsiFile\nimport com.intellij.psi.tree.IFileElementType\nimport com.intellij.psi.tree.TokenSet\nimport com.phodal.shirelang.ShireLanguage\nimport com.phodal.shirelang.lexer.ShireLexerAdapter\nimport com.phodal.shirelang.psi.ShireFile\nimport com.phodal.shirelang.psi.ShireTypes\nimport org.jetbrains.annotations.NotNull\n\n\ninternal class ShireParserDefinition : ParserDefinition {\n    @NotNull\n    override fun createLexer(project: Project?): Lexer = ShireLexerAdapter()\n\n    @NotNull\n    override fun getCommentTokens(): TokenSet = ShireTokenTypeSets.SHIRE_COMMENTS\n\n    @NotNull\n    override fun getStringLiteralElements(): TokenSet = TokenSet.EMPTY\n\n    override fun getWhitespaceTokens(): TokenSet = ShireTokenTypeSets.WHITESPACES\n\n    @NotNull\n    override fun createParser(project: Project?): PsiParser = ShireParser()\n\n    @NotNull\n    override fun getFileNodeType(): IFileElementType = FILE\n\n    @NotNull\n    override fun createFile(@NotNull viewProvider: FileViewProvider): PsiFile = ShireFile(viewProvider)\n\n    @NotNull\n    override fun createElement(node: ASTNode?): PsiElement {\n        return when (node!!.elementType) {\n            ShireTypes.CODE -> {\n                CodeBlockElement(node)\n            }\n\n            ShireTypes.PATTERN -> {\n                PatternElement(node)\n            }\n            ShireTypes.FUNC_CALL -> {\n                when (node.firstChildNode.text) {\n                    \"grep\" -> {\n                        ShireGrepFuncCall(node)\n                    }\n                    \"sed\" -> {\n                        ShireSedFuncCall(node)\n                    }\n                    else -> {\n                        ShireTypes.Factory.createElement(node)\n                    }\n                }\n            }\n            ShireTypes.CODE_CONTENTS -> {\n                ASTWrapperPsiElement(node)\n            }\n\n            else -> ShireTypes.Factory.createElement(node)\n        }\n    }\n\n    companion object {\n        val FILE: IFileElementType = IFileElementType(ShireLanguage.INSTANCE)\n    }\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/parser/ShireTokenTypeSets.kt",
    "content": "package com.phodal.shirelang.parser\n\nimport com.intellij.psi.TokenType\nimport com.intellij.psi.tree.TokenSet\nimport com.phodal.shirelang.psi.ShireTypes\n\nobject ShireTokenTypeSets {\n    // ShireTypes.NEWLINE\n    val WHITESPACES: TokenSet = TokenSet.create(TokenType.WHITE_SPACE)\n\n    val SHIRE_COMMENTS = TokenSet.create(ShireTypes.CONTENT_COMMENTS, ShireTypes.COMMENTS, ShireTypes.BLOCK_COMMENT)\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/provider/ChatBoxShireFileCreateService.kt",
    "content": "package com.phodal.shirelang.provider\n\nimport com.intellij.ide.scratch.ScratchFileService\nimport com.intellij.ide.scratch.ScratchRootType\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.phodal.shirecore.SHIRE_CHAT_BOX_FILE\nimport com.phodal.shirecore.config.ShireActionLocation\nimport com.phodal.shirecore.provider.shire.FileCreateService\nimport com.phodal.shirelang.actions.base.DynamicShireActionService\nimport org.intellij.lang.annotations.Language\n\nclass ChatBoxShireFileCreateService : FileCreateService {\n    override fun createFile(prompt: String, project: Project): VirtualFile? {\n        val actions = DynamicShireActionService.getInstance(project).getActions(ShireActionLocation.CHAT_BOX)\n        var baseContent = \"\"\n        if (actions.isNotEmpty()) {\n            baseContent = actions.first().shireFile.text ?: \"\"\n        }\n\n        if (baseContent.isNotEmpty()) {\n            return createInputWithBase(prompt, project, baseContent)\n        }\n\n        return createInputOnly(prompt, project)\n    }\n\n    private fun createInputOnly(\n        prompt: String,\n        project: Project,\n    ): VirtualFile? {\n        @Language(\"Shire\")\n        val header = \"\"\"\n                    |---\n                    |name: \"shire-temp\"\n                    |description: \"Shire Temp File\"\n                    |interaction: RightPanel\n                    |---\n                    |\n                \"\"\".trimMargin()\n\n        val content = header + prompt\n\n        val virtualFile = ScratchRootType.getInstance().createScratchFile(\n            project,\n            SHIRE_CHAT_BOX_FILE,\n            com.intellij.lang.Language.findLanguageByID(\"Shire\"),\n            content,\n            ScratchFileService.Option.create_if_missing\n        )\n\n        return virtualFile\n    }\n\n    private fun createInputWithBase(\n        prompt: String,\n        project: Project,\n        baseContent: String,\n    ): VirtualFile? {\n        val content = baseContent.replace(\"\\$chatPrompt\", prompt)\n\n        val virtualFile = ScratchRootType.getInstance().createScratchFile(\n            project,\n            SHIRE_CHAT_BOX_FILE,\n            com.intellij.lang.Language.findLanguageByID(\"Shire\"),\n            content,\n            ScratchFileService.Option.create_if_missing\n        )\n\n        return virtualFile\n    }\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/provider/ShireActionPromptBuilder.kt",
    "content": "package com.phodal.shirelang.provider\n\nimport com.intellij.openapi.project.Project\nimport com.phodal.shirecore.config.ShireActionLocation\nimport com.phodal.shirecore.provider.ide.ShirePromptBuilder\nimport com.phodal.shirecore.provider.streaming.OnStreamingService\nimport com.phodal.shirelang.actions.base.DynamicShireActionService\nimport com.phodal.shirelang.run.runner.ShireRunner\nimport kotlinx.coroutines.runBlocking\n\nclass ShireActionPromptBuilder : ShirePromptBuilder {\n    override fun build(project: Project, actionLocation: String, originPrompt: String): String {\n        val location: ShireActionLocation = ShireActionLocation.valueOf(actionLocation)\n\n        val action = DynamicShireActionService.getInstance(project).getActions(location)\n            .firstOrNull() ?: return originPrompt\n\n        val initVariables = mapOf(\"chatPrompt\" to originPrompt)\n        val finalPrompt = runBlocking {\n            val runnerContext = ShireRunner.compileOnly(project, action.shireFile, initVariables, null)\n\n            val service = project.getService(OnStreamingService::class.java)\n            service?.onStart(project, runnerContext.finalPrompt)\n\n            runnerContext\n        }.finalPrompt\n\n        return finalPrompt\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/provider/ShireLanguageToolchainProvider.kt",
    "content": "package com.phodal.shirelang.provider\n\nimport com.intellij.openapi.project.Project\nimport com.phodal.shirecore.provider.context.LanguageToolchainProvider\nimport com.phodal.shirecore.provider.context.ToolchainContextItem\nimport com.phodal.shirecore.provider.context.ToolchainPrepareContext\nimport com.phodal.shirelang.ShireLanguage\n\nclass ShireLanguageToolchainProvider : LanguageToolchainProvider {\n    override fun isApplicable(project: Project, context: ToolchainPrepareContext): Boolean {\n        return context.element?.language is ShireLanguage\n    }\n\n    override suspend fun collect(project: Project, context: ToolchainPrepareContext): List<ToolchainContextItem> {\n        val text = \"Shire is a DSL for building AI Agent for IDEs.\"\n        return listOf(ToolchainContextItem(ShireLanguageToolchainProvider::class, text))\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/provider/ShirePsiVariableProvider.kt",
    "content": "package com.phodal.shirelang.provider\n\nimport com.intellij.openapi.editor.Editor\nimport com.intellij.openapi.project.Project\nimport com.intellij.psi.PsiElement\nimport com.phodal.shirecore.provider.variable.PsiContextVariableProvider\nimport com.phodal.shirecore.provider.variable.model.PsiContextVariable\nimport com.phodal.shirelang.ShireLanguage\n\nclass ShirePsiVariableProvider : PsiContextVariableProvider {\n    override fun resolve(variable: PsiContextVariable, project: Project, editor: Editor, psiElement: PsiElement?): Any {\n        if (psiElement == null) return \"\"\n        if (psiElement.language != ShireLanguage.INSTANCE) return \"\"\n\n        return when (variable) {\n            PsiContextVariable.CURRENT_CLASS_NAME -> \"\"\n            PsiContextVariable.CURRENT_CLASS_CODE -> \"\"\n            PsiContextVariable.CURRENT_METHOD_NAME -> \"\"\n            PsiContextVariable.CURRENT_METHOD_CODE -> \"\"\n            PsiContextVariable.RELATED_CLASSES -> \"\"\n            PsiContextVariable.SIMILAR_TEST_CASE -> \"\"\n            PsiContextVariable.IMPORTS -> \"\"\n            PsiContextVariable.IS_NEED_CREATE_FILE -> \"\"\n            PsiContextVariable.TARGET_TEST_FILE_NAME -> \"\"\n            PsiContextVariable.UNDER_TEST_METHOD_CODE -> \"\"\n            PsiContextVariable.FRAMEWORK_CONTEXT -> {\n                collectFrameworkContext(psiElement, project)\n            }\n            PsiContextVariable.CODE_SMELL -> \"\"\n            PsiContextVariable.METHOD_CALLER -> \"\"\n            PsiContextVariable.CALLED_METHOD -> \"\"\n            PsiContextVariable.SIMILAR_CODE -> \"\"\n            PsiContextVariable.STRUCTURE -> \"\"\n            PsiContextVariable.CHANGE_COUNT -> calculateChangeCount(psiElement)\n            PsiContextVariable.LINE_COUNT -> calculateLineCount(psiElement)\n            PsiContextVariable.COMPLEXITY_COUNT -> calculateComplexityCount(psiElement)\n        }\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/provider/ShireToolchainFunctionProvider.kt",
    "content": "package com.phodal.shirelang.provider\n\nimport com.intellij.openapi.project.Project\nimport com.phodal.shirecore.middleware.post.PostProcessor\nimport com.phodal.shirecore.provider.function.ToolchainFunctionProvider\nimport com.phodal.shirelang.compiler.ast.patternaction.PatternActionFunc\nimport com.phodal.shirelang.compiler.variable.CompositeVariableProvider\n\nenum class ShireProvideType(val type: String) {\n    Variables(\"variables\"),\n    Functions(\"functions\"),\n    Processor(\"processors\")\n    ;\n\n    companion object {\n        fun fromString(value: String): ShireProvideType? {\n            return entries.firstOrNull { it.type == value }\n        }\n    }\n}\n\nenum class ShireToolchainFunction(val funName: String) {\n    /**\n     * The provider function offers, Built-in functions in Shire, Built-in variables in Shire, lifecycle in Shire\n     * for example: `provider(\"variable\")` will return all variables in tables\n     */\n    Provider(\"provider\");\n\n    companion object {\n        fun fromString(value: String): ShireToolchainFunction? {\n            return entries.firstOrNull { it.funName == value }\n        }\n    }\n}\n\nclass ShireToolchainFunctionProvider : ToolchainFunctionProvider {\n    override fun isApplicable(project: Project, funcName: String): Boolean {\n        return ShireToolchainFunction.entries.any { it.funName == funcName }\n    }\n\n    override fun execute(project: Project, funcName: String, args: List<Any>, allVariables: Map<String, Any?>): Any {\n        val shireFunc = ShireToolchainFunction.fromString(funcName)\n            ?: throw IllegalArgumentException(\"Shire[Toolchain]: Invalid Toolchain function name\")\n\n        when (shireFunc) {\n            ShireToolchainFunction.Provider -> {\n                val type = args.first() as String\n                val withExample = args.getOrNull(1) as? Boolean ?: false\n\n                return when (ShireProvideType.fromString(type)) {\n                    ShireProvideType.Variables -> {\n                        /// name and description to markdown table\n                        var result = \"| Name | Description |\"\n                        result += \"\\n| --- | --- |\"\n                        CompositeVariableProvider.all().forEach {\n                            result += \"\\n| ${it.name} | ${it.description} |\"\n                        }\n\n                        result\n                    }\n\n                    ShireProvideType.Functions -> {\n                        /// funcName and example to markdown table\n                        var result = \"| Function | Description |\"\n                        result += \"\\n| --- | --- |\"\n                        PatternActionFunc.all().forEach {\n                            result += \"\\n| ${it.funcName} | ${it.description} |\"\n                            if (withExample) {\n                                result += \"Example: `${it.example}` |\"\n                            }\n                        }\n\n                        result\n                    }\n\n                    ShireProvideType.Processor -> {\n                        var result = \"| Processor | Description |\"\n                        result += \"\\n| --- | --- |\"\n                        PostProcessor.all().map {\n                            result += \"\\n| ${it.processorName} | ${it.description} |\"\n                        }\n\n                        result\n                    }\n\n                    null -> \"\"\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/psi/ShireElementType.kt",
    "content": "package com.phodal.shirelang.psi\n\nimport com.intellij.psi.TokenType\nimport com.intellij.psi.tree.IElementType\nimport com.intellij.psi.tree.TokenSet\nimport com.phodal.shirelang.ShireLanguage\n\nclass ShireElementType(debugName: String) : IElementType(debugName, ShireLanguage.INSTANCE) {\n    companion object {\n        val SPACE_ELEMENTS: TokenSet = TokenSet.create(\n            TokenType.WHITE_SPACE,\n            ShireTypes.INDENT\n        )\n\n        val CONTAINERS: TokenSet = TokenSet.create(\n            ShireTypes.CODE_BLOCK,\n            ShireTypes.FRONT_MATTER_ENTRY,\n        )\n    }\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/psi/ShireFile.kt",
    "content": "package com.phodal.shirelang.psi\n\nimport com.intellij.extapi.psi.PsiFileBase\nimport com.intellij.openapi.application.runReadAction\nimport com.intellij.openapi.fileTypes.FileType\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.intellij.openapi.vfs.VirtualFileManager\nimport com.intellij.psi.*\nimport com.phodal.shirelang.ShireFileType\nimport com.phodal.shirelang.ShireLanguage\nimport java.util.*\n\nclass ShireFile(viewProvider: FileViewProvider) : PsiFileBase(viewProvider, ShireLanguage.INSTANCE) {\n    override fun getFileType(): FileType = ShireFileType.INSTANCE\n\n    override fun getOriginalFile(): ShireFile = super.getOriginalFile() as ShireFile\n\n    override fun toString(): String = \"ShireFile\"\n\n    override fun getStub(): ShireFileStub? = super.getStub() as ShireFileStub?\n\n    companion object {\n        private val shireFileCache = mutableMapOf<VirtualFile, ShireFile>()\n        /**\n         * Create a tempShireFile from a string.\n         */\n        fun fromString(project: Project, text: String): ShireFile {\n            val filename =\n                ShireLanguage.INSTANCE.displayName + \"-${UUID.randomUUID()}.\" + ShireFileType.INSTANCE.defaultExtension\n            val shireFile = runReadAction {\n                PsiFileFactory.getInstance(project)\n                    .createFileFromText(filename, ShireLanguage.INSTANCE, text) as ShireFile\n            }\n\n            return shireFile\n        }\n\n        fun lookup(project: Project, path: String) = VirtualFileManager.getInstance()\n            .findFileByUrl(\"file://$path\")\n            ?.let {\n                lookup(project, it)\n            }\n\n        fun lookup(\n            project: Project,\n            virtualFile: VirtualFile,\n        ): ShireFile? {\n            shireFileCache[virtualFile]?.let {\n                if (it.isValid) {\n                    return it\n                } else {\n                    shireFileCache.remove(virtualFile)\n                }\n            }\n\n            val psiFile = runReadAction {\n                PsiManager.getInstance(project).findFile(virtualFile) as? ShireFile\n            }\n\n            if (psiFile != null) {\n                shireFileCache[virtualFile] = psiFile\n            }\n\n            return psiFile\n        }\n    }\n}\n\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/psi/ShireFileStub.kt",
    "content": "package com.phodal.shirelang.psi\n\nimport com.intellij.psi.PsiFile\nimport com.intellij.psi.StubBuilder\nimport com.intellij.psi.stubs.*\nimport com.intellij.psi.tree.IStubFileElementType\nimport com.phodal.shirelang.ShireLanguage\n\nclass ShireFileStub(file: ShireFile?, private val flags: Int) : PsiFileStubImpl<ShireFile>(file) {\n    override fun getType() = Type\n\n    object Type : IStubFileElementType<ShireFileStub>(ShireLanguage.INSTANCE) {\n        override fun getStubVersion(): Int = 1\n\n        override fun getExternalId(): String = \"shire.file\"\n\n        override fun serialize(stub: ShireFileStub, dataStream: StubOutputStream) {\n            dataStream.writeByte(stub.flags)\n        }\n\n        override fun deserialize(dataStream: StubInputStream, parentStub: StubElement<*>?): ShireFileStub {\n            return ShireFileStub(null, dataStream.readUnsignedByte())\n        }\n\n        override fun getBuilder(): StubBuilder = object : DefaultStubBuilder() {\n            override fun createStubForFile(file: PsiFile): StubElement<*> {\n                return ShireFileStub(file as ShireFile, 0)\n            }\n        }\n    }\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/run/ShireBeforeRunProviderDelegate.kt",
    "content": "package com.phodal.shirelang.run\n\nimport com.intellij.execution.configurations.RunConfiguration\nimport com.intellij.execution.impl.RunConfigurationBeforeRunProviderDelegate\nimport com.intellij.execution.runners.ExecutionEnvironment\nimport com.intellij.openapi.util.Key\n\nclass ShireBeforeRunProviderDelegate : RunConfigurationBeforeRunProviderDelegate {\n    private val SHIRE_BEFORE_RUN_TASK_KEY: String = \"Shire.BeforeRunTask\"\n    private val KEY_MAP: MutableMap<String, Key<Boolean>> = HashMap()\n\n    override fun beforeRun(environment: ExecutionEnvironment) {\n        val settings = environment.runnerAndConfigurationSettings ?: return\n        val configuration = settings.configuration\n\n        if (configuration is ShireConfiguration) {\n            val userDataKey = getRunBeforeUserDataKey(configuration)\n            configuration.project.putUserData(userDataKey, true)\n        }\n    }\n\n    private fun getRunBeforeUserDataKey(runConfiguration: RunConfiguration): Key<Boolean> {\n        return KEY_MAP.computeIfAbsent(runConfiguration.name) { key: String ->\n            Key.create(\n                SHIRE_BEFORE_RUN_TASK_KEY + \"_\" + key\n            )\n        }\n    }\n\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/run/ShireConfiguration.kt",
    "content": "package com.phodal.shirelang.run\n\nimport com.phodal.shirelang.ShireIcons\nimport com.intellij.execution.Executor\nimport com.intellij.execution.configurations.*\nimport com.intellij.execution.runners.ExecutionEnvironment\nimport com.intellij.openapi.options.SettingsEditor\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.util.io.FileUtil\nimport com.phodal.shirelang.ShireBundle\nimport org.jdom.Element\nimport javax.swing.Icon\n\nclass ShireConfiguration(project: Project, factory: ConfigurationFactory, name: String) :\n    LocatableConfigurationBase<ConfigurationFactory>(project, factory, name) {\n    override fun getIcon(): Icon = ShireIcons.DEFAULT\n\n    private var myScriptPath = \"\"\n    private val SCRIPT_PATH_TAG: String = \"SCRIPT_PATH\"\n\n    private var varMap: Map<String, String> = mutableMapOf()\n    private val VAR_MAP_TAG: String = \"VAR_MAP\"\n\n    override fun getState(executor: Executor, environment: ExecutionEnvironment): RunProfileState {\n        return ShireRunConfigurationProfileState(project, this)\n    }\n\n    override fun checkConfiguration() {\n        if (!FileUtil.exists(myScriptPath)) {\n            throw RuntimeConfigurationError(ShireBundle.message(\"shire.run.error.script.not.found\"))\n        }\n    }\n\n    override fun writeExternal(element: Element) {\n        super.writeExternal(element)\n        element.writeString(SCRIPT_PATH_TAG, myScriptPath)\n        element.writeString(VAR_MAP_TAG, varMap.toString())\n    }\n\n    override fun readExternal(element: Element) {\n        super.readExternal(element)\n        myScriptPath = element.readString(SCRIPT_PATH_TAG) ?: \"\"\n        varMap = mapStringToMap(element.readString(VAR_MAP_TAG) ?: \"\")\n    }\n\n    override fun getConfigurationEditor(): SettingsEditor<out RunConfiguration> = ShireSettingsEditor(project)\n\n    fun getScriptPath(): String = myScriptPath\n\n    fun setScriptPath(scriptPath: String) {\n        myScriptPath = scriptPath.trim { it <= ' ' }\n    }\n\n    private fun Element.writeString(name: String, value: String) {\n        val opt = Element(\"option\")\n        opt.setAttribute(\"name\", name)\n        opt.setAttribute(\"value\", value)\n        addContent(opt)\n    }\n\n    private fun Element.readString(name: String): String? =\n        children\n            .find { it.name == \"option\" && it.getAttributeValue(\"name\") == name }\n            ?.getAttributeValue(\"value\")\n\n    fun getVariables(): Map<String, String> = varMap\n\n    fun setVariables(variables: Map<String, String>) {\n        varMap = variables\n    }\n\n    companion object {\n        fun mapStringToMap(varMapString: String) = varMapString\n            .removePrefix(\"{\")\n            .removeSuffix(\"}\")\n            .split(\", \")\n            .map { it.split(\"=\") }\n            .filter { it.size >= 2 }\n            .associate { it[0] to it[1] }\n            .toMutableMap()\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/run/ShireConfigurationType.kt",
    "content": "package com.phodal.shirelang.run\n\nimport com.intellij.execution.configurations.ConfigurationTypeUtil.findConfigurationType\nimport com.intellij.execution.configurations.RunConfiguration\nimport com.intellij.execution.configurations.SimpleConfigurationType\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.util.NotNullLazyValue\nimport com.phodal.shirelang.ShireBundle\nimport com.phodal.shirelang.ShireIcons\nimport com.phodal.shirelang.ShireLanguage\n\nclass ShireConfigurationType : SimpleConfigurationType(\n    \"ShiresConfigurationType\",\n    ShireLanguage.INSTANCE.id,\n    ShireBundle.message(\"shire.line.marker.run.0\", ShireLanguage.INSTANCE.id),\n    NotNullLazyValue.lazy { ShireIcons.DEFAULT }\n) {\n    override fun isDumbAware(): Boolean = true\n\n    override fun isEditableInDumbMode(): Boolean = true\n\n    override fun createTemplateConfiguration(project: Project): RunConfiguration =\n        ShireConfiguration(project, this, \"ShireConfiguration\")\n\n    companion object {\n        fun getInstance(): ShireConfigurationType {\n            return findConfigurationType(ShireConfigurationType::class.java)\n        }\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/run/ShireConsoleView.kt",
    "content": "package com.phodal.shirelang.run\n\nimport com.intellij.build.BuildView\nimport com.intellij.build.DefaultBuildDescriptor\nimport com.intellij.build.events.BuildEvent\nimport com.intellij.execution.impl.ConsoleViewImpl\nimport com.intellij.execution.process.ProcessHandler\nimport com.intellij.execution.ui.ConsoleViewContentType\nimport com.intellij.openapi.editor.Editor\nimport com.intellij.openapi.externalSystem.model.ProjectSystemId\nimport com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskId\nimport com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskType\nimport com.intellij.openapi.externalSystem.service.execution.ExternalSystemRunConfigurationViewManager\nimport com.intellij.openapi.project.Project\nimport com.intellij.ui.components.panels.NonOpaquePanel\nimport com.phodal.shirecore.runner.ShireProcessHandler\nimport com.phodal.shirecore.runner.console.ShireConsoleViewBase\nimport com.phodal.shirelang.run.runner.ShireRunner\nimport java.awt.BorderLayout\nimport javax.swing.JComponent\n\nclass ShireConsoleView(private val executionConsole: ShireExecutionConsole) :\n    ShireConsoleViewBase(executionConsole) {\n\n    override fun getComponent(): JComponent = myPanel\n\n    private var myPanel: NonOpaquePanel = NonOpaquePanel(BorderLayout())\n\n    private var shireRunner: ShireRunner? = null\n    private val id = ProjectSystemId(\"Shire\")\n    private fun createTaskId() =\n        ExternalSystemTaskId.create(id, ExternalSystemTaskType.RESOLVE_PROJECT, executionConsole.project)\n\n    private val scriptPath = executionConsole.configuration.getScriptPath()\n\n    val task = createTaskId()\n    val buildDescriptor: DefaultBuildDescriptor =\n        DefaultBuildDescriptor(task.id, \"Shire\", scriptPath, System.currentTimeMillis())\n\n    val viewManager: ExternalSystemRunConfigurationViewManager =\n        executionConsole.project.getService(ExternalSystemRunConfigurationViewManager::class.java)\n\n    private val buildView: BuildView = object : BuildView(\n        executionConsole.project,\n        executionConsole,\n        buildDescriptor,\n        \"build.toolwindow.run.selection.state\",\n        viewManager\n    ) {\n        override fun onEvent(buildId: Any, event: BuildEvent) {\n            super.onEvent(buildId, event)\n            viewManager.onEvent(buildId, event)\n        }\n    }\n\n    init {\n        val baseComponent = buildView.component\n        myPanel.add(baseComponent, BorderLayout.EAST)\n\n        executionConsole.getProcessHandler()?.let {\n            buildView.attachToProcess(it)\n        }\n\n        myPanel.add(delegate.component, BorderLayout.CENTER)\n    }\n\n    fun output(clearAndStop: Boolean = true) = executionConsole.getOutput(clearAndStop)\n\n    override fun cancelCallback(callback: (String) -> Unit) {\n        shireRunner?.addCancelListener(callback)\n    }\n\n    fun getEditor(): Editor? {\n        return executionConsole.editor\n    }\n\n    override fun isCanceled(): Boolean = shireRunner?.isCanceled() ?: super.isCanceled()\n\n    fun bindShireRunner(runner: ShireRunner) {\n        shireRunner = runner\n    }\n\n    override fun dispose() {\n        super.dispose()\n        executionConsole.dispose()\n    }\n}\n\nclass ShireExecutionConsole(\n    project: Project,\n    viewer: Boolean,\n    private var isStopped: Boolean = false,\n    val configuration: ShireConfiguration,\n) : ConsoleViewImpl(project, viewer) {\n    private val outputBuilder = StringBuilder()\n    private var processHandler: ShireProcessHandler? = null\n\n    fun getProcessHandler(): ShireProcessHandler? {\n        return processHandler\n    }\n\n    override fun attachToProcess(processHandler: ProcessHandler) {\n        super.attachToProcess(processHandler)\n        this.processHandler = processHandler as ShireProcessHandler\n    }\n\n    override fun print(text: String, contentType: ConsoleViewContentType) {\n        super.print(text, contentType)\n        if (!isStopped) outputBuilder.append(text)\n    }\n\n    fun getOutput(clearAndStop: Boolean): String {\n        val output = outputBuilder.toString()\n        if (clearAndStop) {\n            isStopped = true\n            outputBuilder.clear()\n        }\n\n        return output\n    }\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/run/ShirePluginDisposable.kt",
    "content": "package com.phodal.shirelang.run\n\nimport com.intellij.openapi.Disposable\nimport com.intellij.openapi.application.ApplicationManager\nimport com.intellij.openapi.components.Service\nimport com.intellij.openapi.project.Project\n\n/**\n * The service is a parent disposable that represents the entire plugin lifecycle\n * and is intended to be used instead of the project/application as a parent disposable,\n * ensures that disposables registered using it as parents will be processed when the plugin is unloaded to avoid memory leaks.\n *\n * @author lk\n */\n@Service(Service.Level.APP, Service.Level.PROJECT)\nclass ShirePluginDisposable : Disposable {\n    override fun dispose() {\n    }\n\n    companion object {\n\n        fun getInstance(): ShirePluginDisposable {\n            return ApplicationManager.getApplication().getService(ShirePluginDisposable::class.java)\n        }\n\n        fun getInstance(project: Project): ShirePluginDisposable {\n            return project.getService(ShirePluginDisposable::class.java)\n        }\n\n    }\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/run/ShireProcessAdapter.kt",
    "content": "package com.phodal.shirelang.run\n\nimport com.intellij.execution.process.ProcessAdapter\nimport com.intellij.execution.process.ProcessEvent\nimport com.intellij.openapi.application.ApplicationManager\nimport com.intellij.openapi.util.Key\n\n/**\n * Adapter for the process of the Shire run configuration.\n * Will be used to get the result of the process and to notify the listeners when the process is terminated.\n */\nclass ShireProcessAdapter(val configuration: ShireConfiguration, val consoleView: ShireConsoleView?) :\n    ProcessAdapter() {\n    var result = \"\"\n    private var llmOutput: String = \"\"\n\n    override fun processTerminated(event: ProcessEvent) {\n        super.processTerminated(event)\n        ApplicationManager.getApplication().messageBus\n            .syncPublisher(ShireRunListener.TOPIC)\n            .runFinish(result, llmOutput, event, configuration.getScriptPath(), consoleView)\n    }\n\n    override fun onTextAvailable(event: ProcessEvent, outputType: Key<*>) {\n        super.onTextAvailable(event, outputType)\n        result = consoleView?.output().toString()\n    }\n\n    /**\n     * When the process is terminated, will use the given llmOutput to set the llmOutput of the adapter.\n     */\n    fun setLlmOutput(llmOutput: String?) {\n        if (llmOutput != null) {\n            this.llmOutput = llmOutput\n        }\n    }\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/run/ShireProgramRunner.kt",
    "content": "package com.phodal.shirelang.run\n\nimport com.intellij.execution.ExecutionResult\nimport com.intellij.execution.configurations.RunProfile\nimport com.intellij.execution.configurations.RunProfileState\nimport com.intellij.execution.configurations.RunnerSettings\nimport com.intellij.execution.executors.DefaultRunExecutor\nimport com.intellij.execution.process.ProcessEvent\nimport com.intellij.execution.runners.ExecutionEnvironment\nimport com.intellij.execution.runners.GenericProgramRunner\nimport com.intellij.execution.runners.showRunContent\nimport com.intellij.execution.ui.RunContentDescriptor\nimport com.intellij.openapi.Disposable\nimport com.intellij.openapi.application.ApplicationManager\nimport com.intellij.openapi.util.Disposer\nimport com.phodal.shirelang.run.flow.ShireProcessProcessor\nimport java.util.concurrent.atomic.AtomicReference\n\nclass ShireProgramRunner : GenericProgramRunner<RunnerSettings>(), Disposable {\n    private val connection = ApplicationManager.getApplication().messageBus.connect(this)\n\n    private var isSubscribed = false\n\n    init {\n        Disposer.register(ShirePluginDisposable.getInstance(), this)\n    }\n\n    override fun getRunnerId(): String = RUNNER_ID\n\n    override fun canRun(executorId: String, profile: RunProfile): Boolean {\n        return (executorId == DefaultRunExecutor.EXECUTOR_ID) && profile is ShireConfiguration\n    }\n\n    // environment.executor.id == Debug\n    override fun doExecute(state: RunProfileState, environment: ExecutionEnvironment): RunContentDescriptor? {\n        if (environment.runProfile !is ShireConfiguration) return null\n        val shireState = state as ShireRunConfigurationProfileState\n\n        var executeResult: ExecutionResult?\n\n        val result = AtomicReference<RunContentDescriptor>()\n\n        if (!isSubscribed) {\n            connection.subscribe(ShireRunListener.TOPIC, object : ShireRunListener {\n                override fun runFinish(\n                    allOutput: String,\n                    llmOutput: String,\n                    event: ProcessEvent,\n                    scriptPath: String,\n                    consoleView: ShireConsoleView?,\n                ) {\n                    environment.project.getService(ShireProcessProcessor::class.java)\n                        .process(allOutput, event, scriptPath, consoleView)\n                }\n            })\n\n            isSubscribed = true\n        }\n\n        ApplicationManager.getApplication().invokeAndWait {\n            executeResult = shireState.execute(environment.executor, this)\n\n            if (shireState.isShowRunContent) {\n                result.set(showRunContent(executeResult, environment))\n            }\n        }\n\n        return result.get()\n    }\n\n    override fun dispose() {\n        connection.disconnect()\n    }\n\n    companion object {\n        val RUNNER_ID: String = \"ShireProgramRunner\"\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/run/ShireRunConfigurationProducer.kt",
    "content": "package com.phodal.shirelang.run\n\nimport com.intellij.execution.actions.ConfigurationContext\nimport com.intellij.execution.actions.LazyRunConfigurationProducer\nimport com.intellij.openapi.util.Ref\nimport com.intellij.psi.PsiElement\nimport com.phodal.shirelang.psi.ShireFile\n\nclass ShireRunConfigurationProducer : LazyRunConfigurationProducer<ShireConfiguration>() {\n    override fun getConfigurationFactory() = ShireConfigurationType.getInstance()\n\n    override fun setupConfigurationFromContext(\n        configuration: ShireConfiguration,\n        context: ConfigurationContext,\n        sourceElement: Ref<PsiElement>,\n    ): Boolean {\n        val psiFile = sourceElement.get().containingFile as? ShireFile ?: return false\n        val virtualFile = psiFile.virtualFile ?: return false\n\n        configuration.name = virtualFile.presentableName\n        configuration.setScriptPath(virtualFile.path)\n\n        return true\n    }\n\n    override fun isConfigurationFromContext(\n        configuration: ShireConfiguration,\n        context: ConfigurationContext,\n    ): Boolean {\n        val psiLocation = context.psiLocation ?: return false\n        val psiFile = psiLocation.containingFile as? ShireFile ?: return false\n        val virtualFile = psiFile.virtualFile ?: return false\n\n        return virtualFile.path == configuration.getScriptPath()\n    }\n\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/run/ShireRunConfigurationProfileState.kt",
    "content": "package com.phodal.shirelang.run\n\nimport com.intellij.execution.DefaultExecutionResult\nimport com.intellij.execution.ExecutionResult\nimport com.intellij.execution.Executor\nimport com.intellij.execution.configurations.RunProfileState\nimport com.intellij.execution.process.ProcessEvent\nimport com.intellij.execution.process.ProcessListener\nimport com.intellij.execution.process.ProcessTerminatedListener\nimport com.intellij.execution.runners.ProgramRunner\nimport com.intellij.execution.ui.ConsoleViewContentType\nimport com.intellij.openapi.Disposable\nimport com.intellij.openapi.project.Project\nimport com.phodal.shirecore.ShireCoroutineScope\nimport com.phodal.shirecore.config.InteractionType\nimport com.phodal.shirecore.config.ShireActionLocation\nimport com.phodal.shirecore.provider.streaming.OnStreamingService\nimport com.phodal.shirecore.runner.ShireProcessHandler\nimport com.phodal.shirelang.psi.ShireFile\nimport com.phodal.shirelang.run.runner.ShireRunner\nimport kotlinx.coroutines.launch\n\n/**\n * ShireRunConfigurationProfileState is a class that represents the state of a run configuration profile in the Shire plugin for Kotlin.\n * It implements the RunProfileState interface.\n *\n */\nclass ShireRunConfigurationProfileState(\n    private val myProject: Project,\n    private val configuration: ShireConfiguration,\n) : RunProfileState, Disposable {\n    private var executionConsole: ShireExecutionConsole =\n        ShireExecutionConsole(myProject, true, configuration = configuration)\n    var console: ShireConsoleView = ShireConsoleView(executionConsole)\n\n    var isShowRunContent = true\n\n    override fun execute(executor: Executor?, runner: ProgramRunner<*>): ExecutionResult {\n        val processHandler = ShireProcessHandler(configuration.name)\n        ProcessTerminatedListener.attach(processHandler)\n\n        val processAdapter = ShireProcessAdapter(configuration, console)\n        processHandler.addProcessListener(processAdapter)\n\n        console.attachToProcess(processHandler)\n\n        val shireFile: ShireFile? = ShireFile.lookup(myProject, configuration.getScriptPath())\n        if (shireFile == null) {\n            console.print(\"File not found: ${configuration.getScriptPath()}\", ConsoleViewContentType.ERROR_OUTPUT)\n            processHandler.exitWithError()\n            return DefaultExecutionResult(console, processHandler)\n        }\n\n        val shireRunner = ShireRunner(\n            myProject, console, configuration, configuration.getVariables(), processHandler\n        ).also {\n            console.bindShireRunner(it)\n            processHandler.addProcessListener(object : ProcessListener {\n                override fun processTerminated(event: ProcessEvent) {\n                    it.cancel()\n                }\n            })\n        }\n\n        val parsedResult = ShireRunner.preAnalysisAndLocalExecute(shireFile, myProject)\n\n        val location = parsedResult.config?.actionLocation\n        if (location == ShireActionLocation.TERMINAL_MENU || location == ShireActionLocation.COMMIT_MENU) {\n            isShowRunContent = false\n        }\n\n        val interaction = parsedResult.config?.interaction\n        if (interaction == InteractionType.RightPanel) {\n            isShowRunContent = false\n        }\n\n        console.print(\"Prepare for running ${configuration.name}...\\n\", ConsoleViewContentType.NORMAL_OUTPUT)\n        ShireCoroutineScope.scope(myProject).launch {\n            try {\n                val llmOutput = shireRunner.execute(parsedResult)\n                processAdapter.setLlmOutput(llmOutput)\n\n                processAdapter.processTerminated(ProcessEvent(processHandler, 0))\n                myProject.getService(OnStreamingService::class.java)?.onDone(myProject)\n            } catch (e: Exception) {\n                console.print(\n                    \"Failed to run ${configuration.name}: ${e.message}\\n\",\n                    ConsoleViewContentType.LOG_ERROR_OUTPUT\n                )\n                console.print(e.stackTraceToString(), ConsoleViewContentType.ERROR_OUTPUT)\n            }\n        }\n\n        return DefaultExecutionResult(console, processHandler)\n    }\n\n    override fun dispose() {\n        console.dispose()\n        executionConsole.dispose()\n    }\n}\n\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/run/ShireRunLineMarkersProvider.kt",
    "content": "package com.phodal.shirelang.run\n\nimport com.phodal.shirelang.actions.ShireRunFileAction\nimport com.intellij.execution.lineMarker.RunLineMarkerContributor\nimport com.intellij.icons.AllIcons\nimport com.intellij.openapi.actionSystem.ActionManager\nimport com.intellij.openapi.actionSystem.AnAction\nimport com.intellij.openapi.project.DumbAware\nimport com.intellij.openapi.project.DumbService\nimport com.intellij.psi.PsiElement\nimport com.phodal.shirelang.ShireBundle\nimport com.phodal.shirelang.ShireLanguage\nimport com.phodal.shirelang.compiler.ast.hobbit.HobbitHole\nimport com.phodal.shirelang.psi.ShireFile\n\nclass ShireRunLineMarkersProvider : RunLineMarkerContributor(), DumbAware {\n    override fun getInfo(element: PsiElement): Info? {\n        if (DumbService.isDumb(element.project)) return null\n        if (element.language !is ShireLanguage) return null\n        val psiFile = element as? ShireFile ?: return null\n\n        val actions = arrayOf<AnAction>(ActionManager.getInstance().getAction(ShireRunFileAction.ID))\n\n        val displayName = HobbitHole.from(psiFile)?.name ?: psiFile.name\n\n        return Info(\n            AllIcons.RunConfigurations.TestState.Run,\n            { ShireBundle.message(\"shire.line.marker.run.0\", displayName) },\n            *actions\n        )\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/run/ShireRunListener.kt",
    "content": "package com.phodal.shirelang.run\n\nimport com.intellij.execution.process.ProcessEvent\nimport com.intellij.util.messages.Topic\nimport java.util.*\n\n@FunctionalInterface\ninterface ShireRunListener : EventListener {\n    /**\n     * Run finish event\n     *\n     * @param allOutput all output with Console and debug output, it's design for debug\n     * @param llmOutput LLM output\n     * @param event ProcessEvent\n     * @param scriptPath script path\n     * @param consoleView shire consoleView\n     */\n    fun runFinish(allOutput: String, llmOutput: String, event: ProcessEvent, scriptPath: String, consoleView: ShireConsoleView?)\n\n    companion object {\n        @Topic.AppLevel\n        val TOPIC: Topic<ShireRunListener> = Topic(\n            ShireRunListener::class.java, Topic.BroadcastDirection.TO_DIRECT_CHILDREN\n        )\n    }\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/run/ShireSettingsEditor.kt",
    "content": "package com.phodal.shirelang.run\n\nimport com.intellij.openapi.fileChooser.FileChooserDescriptorFactory\nimport com.intellij.openapi.options.SettingsEditor\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.ui.TextFieldWithBrowseButton\nimport com.intellij.ui.dsl.builder.AlignX\nimport com.intellij.ui.dsl.builder.panel\nimport com.phodal.shirelang.ShireBundle\nimport javax.swing.JComponent\n\nclass ShireSettingsEditor(val project: Project) : SettingsEditor<ShireConfiguration>() {\n    private val myScriptSelector: TextFieldWithBrowseButton = TextFieldWithBrowseButton()\n\n    init {\n        val descriptor = FileChooserDescriptorFactory.createSingleFileDescriptor()\n        val message = ShireBundle.message(\"shire.label.choose.file\")\n\n        myScriptSelector.addBrowseFolderListener(message, \"\", project, descriptor)\n    }\n\n    override fun createEditor(): JComponent = panel {\n        row {\n            cell(myScriptSelector).align(AlignX.FILL)\n        }\n    }\n\n    override fun resetEditorFrom(configuration: ShireConfiguration) {\n        myScriptSelector.text = configuration.getScriptPath()\n    }\n\n    override fun applyEditorTo(configuration: ShireConfiguration) {\n        configuration.setScriptPath(myScriptSelector.text)\n    }\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/run/ShireSyntaxLineMarkerProvider.kt",
    "content": "package com.phodal.shirelang.run\n\nimport com.intellij.codeInsight.daemon.LineMarkerInfo\nimport com.intellij.codeInsight.daemon.LineMarkerProvider\nimport com.intellij.openapi.editor.markup.GutterIconRenderer.Alignment.LEFT\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.util.elementType\nimport com.phodal.shirelang.ShireIcons\nimport com.phodal.shirelang.psi.*\n\nclass ShireSyntaxLineMarkerProvider : LineMarkerProvider {\n    override fun getLineMarkerInfo(element: PsiElement): LineMarkerInfo<*>? {\n        if (element !is ShireFrontMatterEntry) return null\n\n        // only leaf elements can have line markers, or IDEA will throw an exception\n        val leafElement = getLeafElement(element) ?: return null\n\n        val patternAction = element.patternAction\n        if (patternAction != null) {\n            val firstExpr = patternAction.actionBlock.actionBody.actionExprList.firstOrNull()\n            when (firstExpr?.firstChild) {\n                is ShireCaseBody -> {\n                    return LineMarkerInfo(leafElement, leafElement.textRange, ShireIcons.Case, null, null, LEFT)\n                    { \"\" }\n                }\n            }\n        }\n\n        when (element.functionStatement?.functionBody?.firstChild) {\n            is ShireQueryStatement -> {\n                return LineMarkerInfo(leafElement, leafElement.textRange, ShireIcons.PsiExpr, null, null, LEFT)\n                { \"\" }\n            }\n\n            is ShireActionBody -> {\n                return LineMarkerInfo(leafElement, leafElement.textRange, ShireIcons.Pipeline, null, null, LEFT)\n                { \"\" }\n            }\n        }\n\n        return null\n    }\n\n    private fun getLeafElement(element: ShireFrontMatterEntry): PsiElement? {\n        val firstChild = element.frontMatterKey?.firstChild\n        when (firstChild?.elementType) {\n            ShireTypes.PATTERN,\n            ShireTypes.FRONT_MATTER_ID -> {\n                return firstChild?.firstChild ?: firstChild\n            }\n            ShireTypes.IDENTIFIER -> {\n                return firstChild\n            }\n        }\n\n        return firstChild\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/run/executor/CustomRemoteAgentLlmExecutor.kt",
    "content": "package com.phodal.shirelang.run.executor\n\nimport com.intellij.execution.ui.ConsoleViewContentType\nimport com.intellij.openapi.application.ApplicationManager\nimport com.phodal.shirecore.agent.CustomAgent\nimport com.phodal.shirelang.ShireBundle\nimport com.phodal.shirelang.run.flow.ShireConversationService\nimport com.phodal.shirecore.ShireCoroutineScope\nimport com.phodal.shirecore.config.interaction.PostFunction\nimport com.phodal.shirecore.runner.console.cancelWithConsole\nimport com.phodal.shirecore.sse.CustomAgentSSEExecutor\nimport kotlinx.coroutines.flow.Flow\nimport kotlinx.coroutines.launch\nimport kotlinx.coroutines.runBlocking\n\nclass CustomRemoteAgentLlmExecutor(\n    override val context: ShireLlmExecutorContext,\n    private val agent: CustomAgent,\n) : ShireLlmExecutor(context) {\n    override fun execute(postFunction: PostFunction) {\n        ApplicationManager.getApplication().invokeLater {\n            val stringFlow: Flow<String>? = CustomAgentSSEExecutor(project = context.myProject).execute(context.prompt, agent)\n\n            val console = context.console\n            if (stringFlow == null) {\n                console?.print(\n                    \"CustomRemoteAgent:\" + ShireBundle.message(\"shire.llm.notfound\"),\n                    ConsoleViewContentType.ERROR_OUTPUT\n                )\n                context.processHandler.detachProcess()\n                postFunction(null, null)\n                return@invokeLater\n            }\n\n            ShireCoroutineScope.scope(context.myProject).launch {\n                val llmResult = StringBuilder()\n                runBlocking {\n                    stringFlow.cancelWithConsole(console).collect {\n                        llmResult.append(it)\n                        console?.print(it, ConsoleViewContentType.NORMAL_OUTPUT)\n                    }\n                }\n\n                console?.print(\"\\nDone!\", ConsoleViewContentType.SYSTEM_OUTPUT)\n                val llmResponse = llmResult.toString()\n                context.myProject.getService(ShireConversationService::class.java)\n                    .refreshLlmResponseCache(context.configuration.getScriptPath(), llmResponse)\n\n                postFunction(llmResponse, null)\n                context.processHandler.detachProcess()\n            }\n        }\n    }\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/run/executor/ShireDefaultLlmExecutor.kt",
    "content": "package com.phodal.shirelang.run.executor\n\nimport com.intellij.execution.ui.ConsoleViewContentType\nimport com.intellij.openapi.application.ApplicationManager\nimport com.intellij.openapi.application.ModalityState\nimport com.phodal.shirecore.ShireCoroutineScope\nimport com.phodal.shirecore.config.InteractionType\nimport com.phodal.shirecore.config.ShireActionLocation\nimport com.phodal.shirecore.config.interaction.PostFunction\nimport com.phodal.shirecore.llm.LlmProvider\nimport com.phodal.shirecore.provider.ide.LocationInteractionContext\nimport com.phodal.shirecore.provider.ide.LocationInteractionProvider\nimport com.phodal.shirecore.runner.console.cancelWithConsole\nimport com.phodal.shirelang.ShireBundle\nimport com.phodal.shirelang.run.flow.ShireConversationService\nimport kotlinx.coroutines.launch\nimport kotlinx.coroutines.runBlocking\n\nclass ShireDefaultLlmExecutor(\n    override val context: ShireLlmExecutorContext,\n    private val isLocalMode: Boolean,\n) : ShireLlmExecutor(context) {\n    override fun execute(postFunction: PostFunction) {\n        ApplicationManager.getApplication().invokeLater({\n            val console = context.console\n            if (isLocalMode && context.hole == null) {\n                console?.print(ShireBundle.message(\"shire.run.local.mode\"), ConsoleViewContentType.SYSTEM_OUTPUT)\n                context.processHandler.detachProcess()\n                return@invokeLater\n            }\n\n            val interaction = context.hole?.interaction\n            val interactionContext = LocationInteractionContext(\n                location = context.hole?.actionLocation ?: ShireActionLocation.RUN_PANEL,\n                interactionType = interaction ?: InteractionType.AppendCursorStream,\n                editor = context.editor,\n                project = context.myProject,\n                prompt = context.prompt,\n                console = console,\n            )\n\n            if (interaction != null) {\n                if (context.hole!!.interaction == InteractionType.OnPaste) {\n                    return@invokeLater\n                }\n                val interactionProvider = LocationInteractionProvider.provide(interactionContext)\n                if (interactionProvider != null) {\n                    interactionProvider.execute(interactionContext) { response, textRange ->\n                        postFunction(response, textRange)\n                        try {\n                            context.processHandler.detachProcess()\n                        } catch (e: Exception) {\n                            console?.print(e.message ?: \"Error\", ConsoleViewContentType.ERROR_OUTPUT)\n                        }\n                    }\n\n                    return@invokeLater\n                }\n            }\n\n            ShireCoroutineScope.scope(context.myProject).launch {\n                val llmResult = StringBuilder()\n                runBlocking {\n                    try {\n                        LlmProvider.provider(context.myProject)?.stream(context.prompt, \"\", false)\n                            ?.cancelWithConsole(console)?.collect {\n                            llmResult.append(it)\n                            console?.print(it, ConsoleViewContentType.NORMAL_OUTPUT)\n                        } ?: console?.print(\n                            \"DefaultLlm\" + ShireBundle.message(\"shire.llm.notfound\"),\n                            ConsoleViewContentType.ERROR_OUTPUT\n                        )\n                    } catch (e: Exception) {\n                        console?.print(e.message ?: \"Error\", ConsoleViewContentType.ERROR_OUTPUT)\n                        context.processHandler.detachProcess()\n                    }\n                }\n\n                console?.print(ShireBundle.message(\"shire.llm.done\"), ConsoleViewContentType.SYSTEM_OUTPUT)\n\n                val response = llmResult.toString()\n                context.myProject.getService(ShireConversationService::class.java)\n                    .refreshLlmResponseCache(context.configuration.getScriptPath(), response)\n\n                postFunction(response, null)\n                context.processHandler.detachProcess()\n            }\n        }, ModalityState.nonModal())\n    }\n}\n\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/run/executor/ShireLlmExecutor.kt",
    "content": "package com.phodal.shirelang.run.executor\n\nimport com.intellij.execution.process.ProcessHandler\nimport com.intellij.execution.ui.ConsoleView\nimport com.intellij.openapi.editor.Editor\nimport com.intellij.openapi.project.Project\nimport com.phodal.shirecore.config.interaction.PostFunction\nimport com.phodal.shirelang.compiler.ast.hobbit.HobbitHole\nimport com.phodal.shirelang.run.ShireConfiguration\n\ndata class ShireLlmExecutorContext(\n    val configuration: ShireConfiguration,\n    val processHandler: ProcessHandler,\n    val console: ConsoleView?,\n    val myProject: Project,\n    val hole: HobbitHole?,\n    val prompt: String,\n    val editor: Editor?,\n)\n\nabstract class ShireLlmExecutor(open val context: ShireLlmExecutorContext) {\n    abstract fun execute(postFunction: PostFunction)\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/run/flow/ShireConversationService.kt",
    "content": "package com.phodal.shirelang.run.flow\n\nimport com.intellij.execution.ui.ConsoleViewContentType\nimport com.intellij.openapi.components.Service\nimport com.intellij.openapi.project.Project\nimport com.phodal.shirecore.runner.console.cancelWithConsole\nimport com.phodal.shirecore.llm.LlmProvider\nimport com.phodal.shirelang.ShireBundle\nimport com.phodal.shirelang.compiler.parser.ShireParsedResult\nimport com.phodal.shirelang.run.ShireConsoleView\nimport kotlinx.coroutines.runBlocking\n\n@Service(Service.Level.PROJECT)\nclass ShireConversationService(val project: Project) {\n    private val cachedConversations: MutableMap<String, ShireProcessContext> = mutableMapOf()\n\n    fun createConversation(scriptPath: String, result: ShireParsedResult): ShireProcessContext {\n        val conversation = ShireProcessContext(scriptPath, result, \"\", \"\")\n        cachedConversations[scriptPath] = conversation\n        return conversation\n    }\n\n    fun getConversation(scriptPath: String): ShireProcessContext? {\n        return cachedConversations[scriptPath]\n    }\n\n    fun getLlmResponse(scriptPath: String): String {\n        return cachedConversations[scriptPath]?.llmResponse ?: \"\"\n    }\n\n    /**\n     * Updates the LLM response for a given script path in the cached conversations.\n     * If the script path exists in the cached conversations, the LLM response is updated with the provided value.\n     *\n     * @param scriptPath The script path for which the LLM response needs to be updated.\n     * @param llmResponse The new LLM response to be updated for the given script path.\n     */\n    fun refreshLlmResponseCache(scriptPath: String, llmResponse: String) {\n        cachedConversations[scriptPath]?.let {\n            cachedConversations[scriptPath] = it.copy(llmResponse = llmResponse)\n        }\n    }\n\n    /**\n     * Updates the IDE output for a conversation at the specified path.\n     *\n     * @param path The path of the conversation to update.\n     * @param ideOutput The new IDE output to set for the conversation.\n     */\n    fun refreshIdeOutput(path: String, ideOutput: String) {\n        cachedConversations[path]?.let {\n            cachedConversations[path] = it.copy(ideOutput = ideOutput)\n        }\n    }\n\n    /**\n     * Function to try re-running a conversation script.\n     *\n     * @param scriptPath The path of the script to re-run\n     */\n    fun retryScriptExecution(scriptPath: String, consoleView: ShireConsoleView?) {\n        if (cachedConversations.isEmpty()) return\n        val conversation = cachedConversations[scriptPath] ?: return\n        if (conversation.alreadyReRun) return\n        conversation.alreadyReRun = true\n\n        val prompt = StringBuilder()\n        val compiledResult = conversation.compiledResult\n        if (compiledResult.isLocalCommand) {\n            val message =\n                ShireBundle.message(\"shire.prompt.fix.command\", compiledResult.sourceCode, compiledResult.shireOutput)\n            prompt.append(message)\n        }\n\n        prompt.append(ShireBundle.message(\"shire.prompt.fix.run-result\", conversation.ideOutput))\n\n        val finalPrompt = prompt.toString()\n        if (consoleView != null) {\n            runBlocking {\n                try {\n                    LlmProvider.provider(project)\n                        ?.stream(finalPrompt, \"\", true)\n                        ?.cancelWithConsole(consoleView)\n                        ?.collect {\n                            consoleView.print(it, ConsoleViewContentType.NORMAL_OUTPUT)\n                        }\n                } catch (e: Exception) {\n                    consoleView.print(e.message ?: \"Error\", ConsoleViewContentType.ERROR_OUTPUT)\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/run/flow/ShireProcessContext.kt",
    "content": "package com.phodal.shirelang.run.flow\n\nimport com.phodal.shirecore.llm.ChatMessage\nimport com.phodal.shirelang.compiler.parser.ShireParsedResult\n\n/**\n * The `ShireProcessContext` class represents the context of a Shire process.\n */\ndata class ShireProcessContext(\n    val scriptPath: String,\n    val compiledResult: ShireParsedResult,\n    val llmResponse: String,\n    val ideOutput: String,\n    val chatMessages: MutableList<ChatMessage> = mutableListOf(),\n    var alreadyReRun: Boolean = false\n)"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/run/flow/ShireProcessProcessor.kt",
    "content": "package com.phodal.shirelang.run.flow\n\nimport com.intellij.execution.process.ProcessEvent\nimport com.intellij.execution.ui.ConsoleViewContentType\nimport com.intellij.openapi.application.runInEdt\nimport com.intellij.openapi.application.runReadAction\nimport com.intellij.openapi.components.Service\nimport com.intellij.openapi.project.Project\nimport com.intellij.psi.PsiComment\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.util.PsiUtilBase\nimport com.intellij.openapi.fileEditor.FileEditorManager\nimport com.phodal.shirecore.llm.LlmProvider\nimport com.phodal.shirecore.ShirelangNotifications\nimport com.phodal.shirecore.runner.console.cancelWithConsole\nimport com.phodal.shirecore.middleware.select.SelectElementStrategy\nimport com.phodal.shirelang.ShireLanguage\nimport com.phodal.shirelang.compiler.parser.ShireSyntaxAnalyzer\nimport com.phodal.shirelang.psi.ShireFile\nimport com.phodal.shirelang.psi.ShireVisitor\nimport com.phodal.shirelang.run.ShireConsoleView\nimport com.phodal.shirecore.utils.markdown.CodeFence\nimport kotlinx.coroutines.runBlocking\n\n@Service(Service.Level.PROJECT)\nclass ShireProcessProcessor(val project: Project) {\n    private val conversationService = project.getService(ShireConversationService::class.java)\n\n    /**\n     * This function takes a ShireFile as input and returns a list of PsiElements that are comments.\n     * It iterates through the ShireFile and adds any comments it finds to the list.\n     *\n     * @param shireFile the ShireFile to search for comments\n     * @return a list of PsiElements that are comments\n     */\n    private fun collectComments(shireFile: ShireFile): List<PsiComment> {\n        val comments = mutableListOf<PsiComment>()\n        shireFile.accept(object : ShireVisitor() {\n            override fun visitComment(comment: PsiComment) {\n                comments.add(comment)\n            }\n        })\n\n        return comments\n    }\n\n    /**\n     * Process the output of a script based on the exit code and flag comment.\n     * If LLM returns a Shire code, execute it.\n     * If the exit code is not 0, attempts to fix the script with LLM.\n     * If the exit code is 0 and there is a flag comment, process it.\n     *\n     * Flag comment format:\n     * ```shire\n     * [flow]:flowable.shire, means next step is flowable.shire\n     * ```\n     *\n     * @param output The output of the script\n     * @param event The process event containing the exit code\n     * @param scriptPath The path of the script file\n     */\n    fun process(output: String, event: ProcessEvent, scriptPath: String, consoleView: ShireConsoleView?) {\n        conversationService.refreshIdeOutput(scriptPath, output)\n\n        val code = CodeFence.parse(conversationService.getLlmResponse(scriptPath))\n        if (code.ideaLanguage == ShireLanguage.INSTANCE) {\n            runInEdt {\n                executeTask(ShireFile.fromString(project, code.text), consoleView)\n            }\n        }\n\n        when {\n            event.exitCode == 0 -> {\n                val shireFile: ShireFile = runReadAction { ShireFile.lookup(project, scriptPath) } ?: return\n                val firstComment = collectComments(shireFile).firstOrNull() ?: return\n                if (firstComment.textRange.startOffset == 0) {\n                    val text = firstComment.text\n                    if (text.startsWith(ShireSyntaxAnalyzer.FLOW_FALG)) {\n                        val nextScript = text.substring(ShireSyntaxAnalyzer.FLOW_FALG.length)\n                        val newScript = ShireFile.lookup(project, nextScript) ?: return\n                        this.executeTask(newScript, consoleView)\n                    }\n                }\n            }\n\n            event.exitCode != 0 -> {\n                conversationService.retryScriptExecution(scriptPath, consoleView)\n            }\n        }\n    }\n\n    /**\n     * This function is responsible for running a task with a new script.\n     * @param newScript The new script to be run.\n     */\n    private fun executeTask(newScript: ShireFile, consoleView: ShireConsoleView?) {\n        val shireCompiler = createCompiler(project, newScript)\n        val result = shireCompiler.parseAndExecuteLocalCommand()\n        if (result.shireOutput != \"\") {\n            ShirelangNotifications.info(project, result.shireOutput)\n        }\n\n        if (result.hasError) {\n            if (consoleView == null) return\n\n            runBlocking {\n                try {\n                    LlmProvider.provider(project)?.stream(result.shireOutput, \"Shirelang\", true)?.cancelWithConsole(consoleView)?.collect {\n                            consoleView.print(it, ConsoleViewContentType.NORMAL_OUTPUT)\n                        }\n                } catch (e: Exception) {\n                    consoleView.print(e.message ?: \"Error\", ConsoleViewContentType.ERROR_OUTPUT)\n                }\n            }\n        } else {\n            if (result.nextJob == null) return\n\n            val nextJob = result.nextJob!!\n            val nextResult = createCompiler(project, nextJob).parseAndExecuteLocalCommand()\n            if (nextResult.shireOutput != \"\") {\n                ShirelangNotifications.info(project, nextResult.shireOutput)\n            }\n        }\n    }\n\n    private fun createCompiler(project: Project, shireFile: ShireFile): ShireSyntaxAnalyzer {\n        val editor = FileEditorManager.getInstance(project).selectedTextEditor\n        val element: PsiElement? = editor?.caretModel?.currentCaret?.offset?.let {\n            val psiFile = PsiUtilBase.getPsiFileInEditor(editor, project) ?: return@let null\n            SelectElementStrategy.getElementAtOffset(psiFile, it)\n        }\n\n        return ShireSyntaxAnalyzer(project, shireFile, editor, element)\n    }\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/run/runner/ShireRunner.kt",
    "content": "package com.phodal.shirelang.run.runner\n\nimport com.intellij.execution.console.ConsoleViewWrapperBase\nimport com.intellij.execution.ui.ConsoleViewContentType\nimport com.intellij.openapi.application.runReadAction\nimport com.intellij.openapi.editor.Editor\nimport com.intellij.openapi.fileEditor.FileEditorManager\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.util.TextRange\nimport com.intellij.psi.PsiManager\nimport com.phodal.shirecore.config.ShireActionLocation\nimport com.phodal.shirecore.config.interaction.PostFunction\nimport com.phodal.shirecore.runner.console.cancelWithConsole\nimport com.phodal.shirecore.llm.LlmProvider\nimport com.phodal.shirecore.middleware.post.PostProcessorContext\nimport com.phodal.shirecore.provider.action.TerminalLocationExecutor\nimport com.phodal.shirecore.provider.context.ActionLocationEditor\nimport com.phodal.shirecore.workerThread\nimport com.phodal.shirelang.ShireBundle\nimport com.phodal.shirelang.compiler.parser.SHIRE_ERROR\nimport com.phodal.shirelang.compiler.parser.ShireParsedResult\nimport com.phodal.shirelang.compiler.template.ShireVariableTemplateCompiler\nimport com.phodal.shirelang.compiler.ast.hobbit.HobbitHole\nimport com.phodal.shirelang.psi.ShireFile\nimport com.phodal.shirelang.run.ShireConfiguration\nimport com.phodal.shirelang.run.ShireConsoleView\nimport com.phodal.shirecore.runner.ShireProcessHandler\nimport com.phodal.shirelang.run.executor.CustomRemoteAgentLlmExecutor\nimport com.phodal.shirelang.run.executor.ShireDefaultLlmExecutor\nimport com.phodal.shirelang.run.executor.ShireLlmExecutor\nimport com.phodal.shirelang.run.executor.ShireLlmExecutorContext\nimport com.phodal.shirelang.run.flow.ShireConversationService\nimport com.phodal.shirecore.provider.streaming.OnStreamingService\nimport com.phodal.shirelang.compiler.parser.ShireSyntaxAnalyzer\nimport kotlinx.coroutines.*\nimport java.util.concurrent.CompletableFuture\n\nclass ShireRunner(\n    private val project: Project,\n    private val console: ShireConsoleView?,\n    private val configuration: ShireConfiguration,\n    private val variableMap: Map<String, String>,\n    private val processHandler: ShireProcessHandler,\n) {\n    private var `compiledVariables`: Map<String, Any> = mapOf()\n    private val terminalLocationExecutor = TerminalLocationExecutor.provide(project)\n\n    private var isCanceled: Boolean = false\n\n    private val cancelListeners = mutableSetOf<(String) -> Unit>()\n\n    suspend fun execute(parsedResult: ShireParsedResult): String? {\n        prepareExecute(parsedResult, compiledVariables, project, console)\n\n        val runResult = CompletableFuture<String>()\n\n        val varsMap = variableFromPostProcessorContext(variableMap)\n\n        val runnerContext = processTemplateCompile(parsedResult, varsMap, project, configuration, console)\n        if (runnerContext.hasError) {\n            processHandler.exitWithError()\n            return null\n        }\n\n        val service = project.getService(OnStreamingService::class.java)\n        service?.onStart(project, runnerContext.finalPrompt)\n\n        this.compiledVariables = runnerContext.compiledVariables\n\n        project.getService(ShireConversationService::class.java)\n            .createConversation(configuration.getScriptPath(), runnerContext.compileResult)\n\n        if (runnerContext.hole?.actionLocation == ShireActionLocation.TERMINAL_MENU) {\n            executeTerminalUiTask(runnerContext) { response, textRange ->\n                runResult.complete(response)\n                executePostFunction(runnerContext, runnerContext.hole, response, textRange)\n            }\n        } else {\n            executeNormalUiTask(runnerContext) { response, textRange ->\n                runResult.complete(response)\n                executePostFunction(runnerContext, runnerContext.hole, response, textRange)\n            }\n        }\n\n        return withContext(Dispatchers.IO) {\n            runResult.get()\n        }\n    }\n\n    private fun executeTerminalUiTask(context: ShireRunnerContext, postFunction: PostFunction) {\n        CoroutineScope(workerThread).launch {\n            val handler = terminalLocationExecutor?.bundler(project, variableMap[\"input\"] ?: \"\")\n            if (handler == null) {\n                console?.print(\"Terminal not found\", ConsoleViewContentType.ERROR_OUTPUT)\n                processHandler.exitWithError()\n                return@launch\n            }\n\n            val llmResult = StringBuilder()\n            runBlocking {\n                try {\n                    LlmProvider.provider(project)?.stream(context.finalPrompt, \"\", false)\n                        ?.cancelWithConsole(console)?.collect {\n                            llmResult.append(it)\n                            handler.onChunk.invoke(it)\n                        } ?: console?.print(\n                        \"ShireRunner:\" + ShireBundle.message(\"shire.llm.notfound\"),\n                        ConsoleViewContentType.ERROR_OUTPUT\n                    )\n                } catch (e: Exception) {\n                    console?.print(e.message ?: \"Error\", ConsoleViewContentType.ERROR_OUTPUT)\n                    handler.onFinish?.invoke(null)\n                    processHandler.exitWithError()\n                }\n            }\n\n            val response = llmResult.toString()\n            handler.onFinish?.invoke(response)\n\n            postFunction(response, null)\n            processHandler.detachProcess()\n        }\n    }\n\n    private fun executeNormalUiTask(runData: ShireRunnerContext, postFunction: PostFunction) {\n        val agent = runData.compileResult.executeAgent\n        val hobbitHole = runData.hole\n\n        val shireLlmExecutorContext = ShireLlmExecutorContext(\n            configuration = configuration,\n            processHandler = processHandler,\n            console = console,\n            myProject = project,\n            hole = hobbitHole,\n            prompt = runData.finalPrompt,\n            editor = runData.editor,\n        )\n        val shireLlmExecutor: ShireLlmExecutor = when {\n            agent != null -> {\n                CustomRemoteAgentLlmExecutor(shireLlmExecutorContext, agent)\n            }\n\n            else -> {\n                val isLocalMode = runData.compileResult.isLocalCommand\n                ShireDefaultLlmExecutor(shireLlmExecutorContext, isLocalMode)\n            }\n        }\n\n        shireLlmExecutor.execute(postFunction)\n    }\n\n    fun executePostFunction(\n        runnerContext: ShireRunnerContext,\n        hobbitHole: HobbitHole?,\n        response: String?,\n        textRange: TextRange?,\n    ) {\n        if (console?.isCanceled() == true) return\n        val currentFile = runnerContext.editor?.virtualFile?.let {\n            runReadAction { PsiManager.getInstance(project).findFile(it) }\n        }\n        val context = PostProcessorContext(\n            currentFile = currentFile,\n            currentLanguage = currentFile?.language,\n            genText = response,\n            modifiedTextRange = textRange,\n            editor = runnerContext.editor,\n            lastTaskOutput = response,\n            compiledVariables = compiledVariables,\n            llmModelName = hobbitHole?.model,\n        )\n\n        PostProcessorContext.updateContextAndVariables(context)\n\n        val endStreamProcessor = hobbitHole?.executeStreamingEndProcessor(project, console, context, compiledVariables)\n        PostProcessorContext.updateOutput(endStreamProcessor)\n\n        val afterStreamHandler = hobbitHole?.executeAfterStreamingProcessor(project, console, context)\n        PostProcessorContext.updateOutput(afterStreamHandler)\n\n        try {\n            processHandler.detachProcess()\n        } catch (e: Exception) {\n//            console?.print(e.message ?: \"Error\", ConsoleViewContentType.ERROR_OUTPUT)\n        }\n    }\n\n    @Synchronized\n    fun addCancelListener(listener: (String) -> Unit) {\n        if (isCanceled) cancel(listener)\n        else cancelListeners.add(listener)\n    }\n\n    @Synchronized\n    fun cancel() {\n        if (!isCanceled) {\n            isCanceled = true\n            cancelListeners.forEach { cancel(it) }\n        }\n    }\n\n    fun isCanceled() = isCanceled\n\n    private fun cancel(cancel: (String) -> Unit) {\n        cancel(\"This job is canceled\")\n    }\n\n    companion object {\n        /**\n         * Thi api design for compile only\n         */\n        suspend fun compileOnly(\n            project: Project,\n            shireFile: ShireFile,\n            initVariables: Map<String, String>,\n            sampleEditor: Editor? = null\n        ): ShireRunnerContext {\n            val parsedResult = runReadAction {\n                preAnalysisAndLocalExecute(shireFile, project, sampleEditor)\n            }\n            prepareExecute(parsedResult, initVariables, project, null, userEditor = sampleEditor)\n\n            val variables = variableFromPostProcessorContext(initVariables)\n            val runnerContext = processTemplateCompile(parsedResult, variables, project, null, null,\n                userEditor = sampleEditor\n            )\n\n            val service = project.getService(OnStreamingService::class.java)\n            service?.onStart(project, runnerContext.finalPrompt)\n            return runnerContext\n        }\n\n        fun preAnalysisAndLocalExecute(\n            shireFile: ShireFile,\n            project: Project,\n            editor: Editor? = null,\n        ): ShireParsedResult {\n            val baseEditor = editor ?: ActionLocationEditor.defaultEditor(project)\n\n            val syntaxAnalyzer = ShireSyntaxAnalyzer(project, shireFile, baseEditor)\n            return syntaxAnalyzer.parseAndExecuteLocalCommand()\n        }\n\n        fun prepareExecute(\n            parsedResult: ShireParsedResult,\n            variables: Map<String, Any>,\n            project: Project,\n            consoleView: ShireConsoleView?,\n            userEditor: Editor? = null,\n        ): PostProcessorContext {\n            val hobbitHole = parsedResult.config\n            val editor = userEditor ?: FileEditorManager.getInstance(project).selectedTextEditor\n            hobbitHole?.pickupElement(project, editor)\n\n            val file = runReadAction {\n                editor?.let { PsiManager.getInstance(project).findFile(it.virtualFile) }\n            }\n\n            val context = PostProcessorContext.getData() ?: PostProcessorContext(\n                currentFile = file,\n                currentLanguage = file?.language,\n                editor = editor,\n                compiledVariables = variables,\n                llmModelName = hobbitHole?.model\n            )\n\n            PostProcessorContext.updateContextAndVariables(context)\n\n            val vars: MutableMap<String, Any?> = variables.toMutableMap()\n            hobbitHole?.executeBeforeStreamingProcessor(project, context, consoleView, vars)\n\n            val streamingService = project.getService(OnStreamingService::class.java)\n            streamingService.clearStreamingService()\n            hobbitHole?.onStreaming?.forEach {\n                streamingService.registerStreamingService(it, consoleView)\n            }\n\n            hobbitHole?.setupStreamingEndProcessor(project, context)\n\n            return context\n        }\n\n        private suspend fun processTemplateCompile(\n            compileResult: ShireParsedResult,\n            variableMap: Map<String, String>,\n            project: Project,\n            shireConfiguration: ShireConfiguration?,\n            shireConsoleView: ShireConsoleView?,\n            userEditor: Editor? = null,\n        ): ShireRunnerContext {\n            val hobbitHole = compileResult.config\n            val editor = userEditor ?: ActionLocationEditor.provide(project, hobbitHole?.actionLocation)\n\n            val templateCompiler =\n                ShireVariableTemplateCompiler(\n                    project,\n                    hobbitHole,\n                    compileResult.variableTable,\n                    compileResult.shireOutput,\n                    editor\n                )\n\n            variableMap.forEach { (key, value) ->\n                templateCompiler.putCustomVariable(key, value)\n            }\n\n            val promptTextTrim = templateCompiler.compile().trim()\n            val compiledVariables = templateCompiler.compiledVariables\n\n            PostProcessorContext.getData()?.lastTaskOutput?.let {\n                templateCompiler.putCustomVariable(\"output\", it)\n            }\n\n            if (shireConsoleView != null && shireConfiguration != null) {\n                printCompiledOutput(shireConsoleView, promptTextTrim, shireConfiguration)\n            }\n\n            var hasError = false\n\n            if (promptTextTrim.isEmpty()) {\n                shireConsoleView?.print(\"No content to run\", ConsoleViewContentType.ERROR_OUTPUT)\n                hasError = true\n            }\n\n            if (promptTextTrim.contains(SHIRE_ERROR)) {\n                hasError = true\n            }\n\n            return ShireRunnerContext(\n                hobbitHole,\n                editor = editor,\n                compileResult,\n                promptTextTrim,\n                hasError,\n                compiledVariables\n            )\n        }\n\n        private fun printCompiledOutput(\n            console: ConsoleViewWrapperBase,\n            promptText: String,\n            shireConfiguration: ShireConfiguration,\n        ) {\n            console.print(\"Shire Script: ${shireConfiguration.getScriptPath()}\\n\", ConsoleViewContentType.SYSTEM_OUTPUT)\n            console.print(\"Shire Script Compile output:\\n\", ConsoleViewContentType.SYSTEM_OUTPUT)\n            PostProcessorContext.getData()?.llmModelName?.let {\n                console.print(\"Used model: $it\\n\", ConsoleViewContentType.SYSTEM_OUTPUT)\n            }\n\n            promptText.split(\"\\n\").forEach {\n                when {\n                    it.contains(SHIRE_ERROR) -> {\n                        console.print(it, ConsoleViewContentType.LOG_ERROR_OUTPUT)\n                    }\n\n                    else -> {\n                        console.print(it, ConsoleViewContentType.USER_INPUT)\n                    }\n                }\n                console.print(\"\\n\", ConsoleViewContentType.NORMAL_OUTPUT)\n            }\n\n            console.print(\"\\n--------------------\\n\", ConsoleViewContentType.NORMAL_OUTPUT)\n        }\n\n        private fun variableFromPostProcessorContext(initValue: Map<String, String>): MutableMap<String, String> {\n            val varsMap = initValue.toMutableMap()\n            val data = PostProcessorContext.getData()\n            val variables = data?.compiledVariables\n            if (variables?.get(\"output\") != null && initValue[\"output\"] == null) {\n                varsMap[\"output\"] = variables[\"output\"].toString()\n            }\n\n            return varsMap\n        }\n    }\n}\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/run/runner/ShireRunnerContext.kt",
    "content": "package com.phodal.shirelang.run.runner\n\nimport com.intellij.openapi.editor.Editor\nimport com.phodal.shirelang.compiler.parser.ShireParsedResult\nimport com.phodal.shirelang.compiler.ast.hobbit.HobbitHole\n\nclass ShireRunnerContext(\n    val hole: HobbitHole?,\n    val editor: Editor?,\n    val compileResult: ShireParsedResult,\n    val finalPrompt: String = \"\",\n    val hasError: Boolean,\n    val compiledVariables: Map<String, Any>,\n)\n"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/runner/ShellFileRunService.kt",
    "content": "package com.phodal.shirelang.runner\n\nimport com.intellij.execution.RunManager\nimport com.intellij.execution.configurations.RunConfiguration\nimport com.intellij.execution.configurations.RunProfile\nimport com.intellij.openapi.application.runReadAction\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.intellij.psi.PsiManager\nimport com.intellij.sh.psi.ShFile\nimport com.intellij.sh.run.ShConfigurationType\nimport com.intellij.sh.run.ShRunConfiguration\nimport com.phodal.shirecore.provider.shire.FileRunService\n\nclass ShellFileRunService : FileRunService {\n    override fun isApplicable(project: Project, file: VirtualFile): Boolean {\n        return file.extension == \"sh\" || file.extension == \"bash\"\n    }\n\n    override fun runConfigurationClass(project: Project): Class<out RunProfile> = ShRunConfiguration::class.java\n\n    override fun createConfiguration(project: Project, virtualFile: VirtualFile): RunConfiguration? {\n        val configurationSetting = runReadAction {\n            val psiFile = PsiManager.getInstance(project).findFile(virtualFile) as? ShFile ?: return@runReadAction null\n            RunManager.getInstance(project)\n                .createConfiguration(psiFile.name, ShConfigurationType.getInstance())\n        } ?: return null\n\n        val configuration = configurationSetting.configuration as ShRunConfiguration\n        configuration.scriptPath = virtualFile.path\n        return configurationSetting.configuration\n    }\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/runner/ShireFileRunService.kt",
    "content": "package com.phodal.shirelang.runner\n\nimport com.intellij.execution.RunManager\nimport com.intellij.execution.RunnerAndConfigurationSettings\nimport com.intellij.execution.configurations.RunProfile\nimport com.intellij.openapi.Disposable\nimport com.intellij.openapi.application.runReadAction\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.intellij.psi.PsiElement\nimport com.phodal.shirecore.provider.shire.FileRunService\nimport com.phodal.shirelang.actions.ShireRunFileAction.Companion.executeFile\nimport com.phodal.shirelang.actions.base.DynamicShireActionConfig\nimport com.phodal.shirelang.psi.ShireFile\nimport com.phodal.shirelang.run.ShireConfiguration\nimport com.phodal.shirelang.run.ShireConfigurationType\n\nclass ShireFileRunService : FileRunService, Disposable {\n    override fun isApplicable(project: Project, file: VirtualFile): Boolean {\n        return file.extension == \"shire\"\n    }\n\n    override fun runConfigurationClass(project: Project): Class<out RunProfile> = ShireConfiguration::class.java\n\n    override fun createRunSettings(\n        project: Project,\n        virtualFile: VirtualFile,\n        testElement: PsiElement?,\n    ): RunnerAndConfigurationSettings? {\n        val runManager = RunManager.getInstance(project)\n\n        val psiFile = ShireFile.lookup(project, virtualFile) ?: return null\n\n        val setting = runReadAction {\n            runManager.createConfiguration(psiFile.name, ShireConfigurationType.getInstance())\n        }\n\n        val shireConfiguration = setting.configuration as ShireConfiguration\n        shireConfiguration.name = virtualFile.nameWithoutExtension\n        shireConfiguration.setScriptPath(virtualFile.path)\n\n        setting.isTemporary = true\n\n        runManager.setTemporaryConfiguration(setting)\n        runManager.selectedConfiguration = setting\n\n        return setting\n    }\n\n    override fun runFile(project: Project, virtualFile: VirtualFile, psiElement: PsiElement?): String? {\n        val settings = createRunSettings(project, virtualFile, psiElement) ?: return null\n        val psiFile = ShireFile.lookup(project, virtualFile) ?: return null\n\n        val config = DynamicShireActionConfig.from(psiFile)\n\n        executeFile(project, config, settings)\n        return \"Running Shire file: ${virtualFile.name}\"\n    }\n\n    override fun dispose() {\n\n    }\n}"
  },
  {
    "path": "shirelang/src/main/kotlin/com/phodal/shirelang/thirdparty/ShireSonarLintToolWindowListener.kt",
    "content": "package com.phodal.shirelang.thirdparty\n\nimport com.intellij.openapi.actionSystem.ActionManager\nimport com.intellij.openapi.actionSystem.ActionToolbar\nimport com.intellij.openapi.actionSystem.DefaultActionGroup\nimport com.intellij.openapi.ui.SimpleToolWindowPanel\nimport com.intellij.openapi.wm.ToolWindow\nimport com.intellij.openapi.wm.ex.ToolWindowManagerListener\n\nclass ShireSonarLintToolWindowListener : ToolWindowManagerListener {\n    override fun toolWindowShown(toolWindow: ToolWindow) {\n        if (toolWindow.id != \"SonarLint\") return\n\n        val action = ActionManager.getInstance().getAction(\"ShireSonarLintAction\")\n\n        val contentManager = toolWindow.contentManager\n        val content = contentManager.getContent(0) ?: return\n\n        val simpleToolWindowPanel = content.component as? SimpleToolWindowPanel\n        val actionToolbar = simpleToolWindowPanel?.toolbar?.components?.get(0) as? ActionToolbar ?: return\n        val actionGroup = actionToolbar.actionGroup as? DefaultActionGroup\n\n        if (actionGroup?.containsAction(action) == false) {\n            actionGroup.add(action)\n        }\n    }\n}"
  },
  {
    "path": "shirelang/src/main/resources/com.phodal.shirelang.xml",
    "content": "<idea-plugin package=\"com.phodal.shirelang\">\n    <resource-bundle>messages.ShireBundle</resource-bundle>\n    <!--suppress PluginXmlValidity -->\n    <dependencies>\n        <plugin id=\"org.intellij.plugins.markdown\"/>\n        <plugin id=\"com.jetbrains.sh\"/>\n    </dependencies>\n\n    <extensions defaultExtensionNs=\"com.intellij\">\n        <backgroundPostStartupActivity implementation=\"com.phodal.shirelang.ShireActionStartupActivity\"/>\n\n        <fileType name=\"ShireFile\" implementationClass=\"com.phodal.shirelang.ShireFileType\" fieldName=\"INSTANCE\"\n                  language=\"Shire\" extensions=\"shire\"/>\n\n        <fileBasedIndex implementation=\"com.phodal.shirelang.index.ShireIdentifierIndex\"/>\n\n        <lang.parserDefinition language=\"Shire\"\n                               implementationClass=\"com.phodal.shirelang.parser.ShireParserDefinition\"/>\n        <lang.syntaxHighlighterFactory language=\"Shire\"\n                                       implementationClass=\"com.phodal.shirelang.highlight.ShireSyntaxHighlighterFactory\"/>\n\n        <highlightErrorFilter implementation=\"com.phodal.shirelang.highlight.ShireErrorFilter\"/>\n\n        <braceMatcher filetype=\"Shire\" implementationClass=\"com.phodal.shirelang.highlight.braces.ShireBraceMatcher\"/>\n        <quoteHandler fileType=\"Shire\" className=\"com.phodal.shirelang.highlight.braces.ShireQuoteHandler\"/>\n\n        <annotator language=\"Shire\" implementationClass=\"com.phodal.shirelang.highlight.ShireHighlightingAnnotator\"/>\n\n        <lang.ast.factory language=\"Shire\"\n                          implementationClass=\"com.phodal.shirelang.ShireAstFactory\"/>\n\n        <typedHandler implementation=\"com.phodal.shirelang.ShireTypedHandler\"/>\n\n        <completion.contributor language=\"Shire\"\n                                id=\"ShireCompletionContributor\"\n                                order=\"first\"\n                                implementationClass=\"com.phodal.shirelang.completion.ShireCompletionContributor\"/>\n\n        <completion.contributor language=\"Shire\"\n                                order=\"after ShireCompletionContributor\"\n                                implementationClass=\"com.phodal.shirelang.completion.UserCustomCompletionContributor\"/>\n\n        <lang.foldingBuilder language=\"Shire\"\n                             implementationClass=\"com.phodal.shirelang.folding.ShireFoldingBuilder\"/>\n        <lang.commenter language=\"Shire\" implementationClass=\"com.phodal.shirelang.comment.ShireCommenter\"/>\n        <!--        <lang.formatter language=\"Shire\" implementationClass=\"com.phodal.shirelang.formatter.ShireFormattingModelBuilder\"/>-->\n\n        <languageInjector implementation=\"com.phodal.shirelang.ShireLanguageInjector\"/>\n        <!--        <multiHostInjector implementation=\"com.phodal.shirelang.ShireInCommentInjector\"/>-->\n\n        <configurationType implementation=\"com.phodal.shirelang.run.ShireConfigurationType\"/>\n        <programRunner implementation=\"com.phodal.shirelang.run.ShireProgramRunner\"/>\n\n        <!--   Debugger  -->\n        <programRunner implementation=\"com.phodal.shirelang.debugger.ShireDebugRunner\"/>\n        <xdebugger.breakpointType implementation=\"com.phodal.shirelang.debugger.ShireLineBreakpointType\"/>\n        <xdebugger.settings implementation=\"com.phodal.shirelang.debugger.ShireDebugSettings\"/>\n\n        <runConfigurationBeforeRunProviderDelegate\n                implementation=\"com.phodal.shirelang.run.ShireBeforeRunProviderDelegate\"/>\n        <runConfigurationProducer implementation=\"com.phodal.shirelang.run.ShireRunConfigurationProducer\"/>\n        <runLineMarkerContributor language=\"Shire\"\n                                  implementationClass=\"com.phodal.shirelang.run.ShireRunLineMarkersProvider\"/>\n\n        <codeInsight.lineMarkerProvider language=\"Shire\"\n                                        implementationClass=\"com.phodal.shirelang.run.ShireSyntaxLineMarkerProvider\"/>\n\n\n        <gotoDeclarationHandler implementation=\"com.phodal.shirelang.navigation.ShireGotoDeclarationHandler\"/>\n\n        <lang.documentationProvider language=\"Shire\"\n                                    id=\"shireDocumentationProvider\"\n                                    implementationClass=\"com.phodal.shirelang.documentation.ShireDocumentationProvider\"/>\n\n        <localInspection language=\"Shire\" groupPath=\"Shire\" groupName=\"Lints\"\n                         displayName=\"Duplicate agent declaration\"\n                         enabledByDefault=\"true\"\n                         level=\"ERROR\"\n                         implementationClass=\"com.phodal.shirelang.lints.ShireDuplicateAgentInspection\"/>\n\n        <intentionAction>\n            <className>com.phodal.shirelang.actions.intention.ShireIntentionHelper</className>\n            <categoryKey>shire.intention.category</categoryKey>\n        </intentionAction>\n\n        <copyPastePreProcessor implementation=\"com.phodal.shirelang.actions.copyPaste.ShireCopyPastePreProcessor\"/>\n\n        <vfs.asyncListener implementation=\"com.phodal.shirelang.actions.AsyncShireFileListener\"/>\n\n        <fileDocumentManagerListener implementation=\"com.phodal.shirelang.actions.ShireFileModificationListener\"/>\n\n        <editorFactoryDocumentListener implementation=\"com.phodal.shirelang.actions.ShireFileModificationListener\"/>\n\n        <fileEditorProvider implementation=\"com.phodal.shirelang.editor.ShireSplitEditorProvider\"/>\n    </extensions>\n\n    <actions>\n        <action id=\"runShireFileAction\"\n                class=\"com.phodal.shirelang.actions.ShireRunFileAction\"\n                use-shortcut-of=\"RunClass\"/>\n\n        <action id=\"shire.NewShireFile\" class=\"com.phodal.shirelang.actions.template.NewShireFileAction\"\n                icon=\"com.phodal.shirelang.ShireIcons.DEFAULT\">\n            <add-to-group group-id=\"NewGroup\" anchor=\"before\" relative-to-action=\"NewFromTemplate\"/>\n        </action>\n\n        <!-- Shire Context Action Group -->\n        <group id=\"ShireContextActionGroup\"\n               popup=\"true\" text=\"Shire Action\" description=\"Shire context action group\"\n               class=\"com.phodal.shirelang.actions.context.ShireContextMenuActionGroup\"\n               icon=\"com.phodal.shirelang.ShireIcons.DEFAULT\" searchable=\"false\">\n\n            <add-to-group group-id=\"EditorPopupMenu\" anchor=\"first\"/>\n        </group>\n\n        <!-- When multiple commit menu -->\n        <group id=\"ShireVcsActionGroup\"\n               class=\"com.phodal.shirelang.actions.vcs.ShireVcsActionGroup\"\n               icon=\"com.phodal.shirelang.ShireIcons.DEFAULT\"\n               description=\"Shire VCS Action\">\n\n            <add-to-group group-id=\"Vcs.MessageActionGroup\"/>\n        </group>\n\n        <!-- When one commit menu -->\n        <action id=\"ShireCommitMessage\"\n                class=\"com.phodal.shirelang.actions.vcs.ShireVcsSingleAction\"\n                icon=\"com.phodal.shirelang.ShireIcons.DEFAULT\"\n                text=\"Commit Message Action\"\n                description=\"Shire VCS Action\">\n\n            <add-to-group group-id=\"Vcs.MessageActionGroup\"/>\n        </action>\n\n        <action id=\"ShireCustomInputBox\"\n                class=\"com.phodal.shirelang.actions.input.ShireInputBoxAction\"\n                description=\"You can custom any assistant as you want!\"\n                icon=\"com.phodal.shirelang.ShireIcons.DEFAULT\"\n        >\n            <keyboard-shortcut keymap=\"$default\" first-keystroke=\"control BACK_SLASH\"/>\n        </action>\n\n        <action id=\"ShireTerminalAction\"\n                class=\"com.phodal.shirelang.actions.terminal.ShireTerminalAction\"\n                description=\"You can custom any assistant as you want!\"\n                text=\"Terminal Action\"\n                icon=\"com.phodal.shirelang.ShireIcons.DEFAULT\"\n        >\n        </action>\n\n        <action id=\"com.phodal.shirelang.actions.console.ShireConsoleAction\"\n                class=\"com.phodal.shirelang.actions.console.ShireConsoleAction\"\n                description=\"Ask AI fix this code\"\n                icon=\"com.phodal.shirelang.ShireIcons.DEFAULT\"\n        >\n            <add-to-group group-id=\"ConsoleEditorPopupMenu\" anchor=\"first\"/>\n        </action>\n\n        <!-- Shire Intentions Action Group -->\n        <group id=\"ShireIntentionsActionGroup\" class=\"com.phodal.shirelang.actions.intention.ShireIntentionsActionGroup\"\n               icon=\"com.phodal.shirelang.ShireIcons.DEFAULT\" searchable=\"false\">\n\n            <add-to-group group-id=\"ShowIntentionsGroup\" relative-to-action=\"ShowIntentionActions\" anchor=\"after\"/>\n            <add-to-group group-id=\"Floating.CodeToolbar\" anchor=\"first\"/>\n        </group>\n\n        <action id=\"ShireDatabaseAction\"\n                class=\"com.phodal.shirelang.actions.database.ShireDatabaseAction\"\n                icon=\"com.phodal.shirelang.ShireIcons.DEFAULT\"\n                text=\"Shire Database Action\"\n                description=\"Shire database action\">\n\n        </action>\n\n        <action id=\"ShireSonarLintAction\"\n                class=\"com.phodal.shirelang.actions.external.ShireSonarLintAction\"\n                icon=\"com.phodal.shirelang.ShireIcons.DEFAULT\"\n                text=\"Shire SonarLint Action\"\n                description=\"Shire SonarLint action\">\n\n        </action>\n\n        <action id=\"ShireVcsLogAction\"\n                class=\"com.phodal.shirelang.actions.vcs.ShireVcsLogAction\"\n                icon=\"com.phodal.shirelang.ShireIcons.DEFAULT\"\n                text=\"Shire Vcs Action\"\n                description=\"Shire vcs action\">\n\n        </action>\n\n        <group id=\"Shire.ToolWindow.Toolbar\">\n            <action id=\"Shire.ToolWindow.Toolbar.LanguageLabelAction\"\n                    class=\"com.phodal.shirecore.sketch.highlight.toolbar.ShireLanguageLabelAction\"/>\n            <action id=\"Shire.ToolWindow.Toolbar.CopyToClipboard\"\n                    icon=\"AllIcons.Actions.Copy\"\n                    class=\"com.phodal.shirecore.sketch.highlight.toolbar.ShireCopyToClipboardAction\"/>\n            <action id=\"Shire.ToolWindow.Toolbar.InsertCode\"\n                    icon=\"AllIcons.Actions.CheckOut\"\n                    class=\"com.phodal.shirecore.sketch.highlight.toolbar.ShireInsertCodeAction\"/>\n\n            <action id=\"Shire.ToolWindow.Snippet.RunCode\"\n                    icon=\"AllIcons.Actions.Execute\"\n                    class=\"com.phodal.shirecore.sketch.highlight.toolbar.ShireRunCodeAction\"/>\n        </group>\n    </actions>\n\n    <extensions defaultExtensionNs=\"com.phodal\">\n        <shirePromptBuilder implementation=\"com.phodal.shirelang.provider.ShireActionPromptBuilder\"/>\n\n        <shireFileRunService implementation=\"com.phodal.shirelang.runner.ShireFileRunService\"/>\n\n        <shireFileCreateService\n                language=\"Shire\"\n                implementationClass=\"com.phodal.shirelang.provider.ChatBoxShireFileCreateService\"/>\n\n        <shirePsiVariableProvider language=\"Shire\"\n                                  implementationClass=\"com.phodal.shirelang.provider.ShirePsiVariableProvider\"/>\n        <shireLanguageToolchainProvider language=\"Shire\"\n                                        implementationClass=\"com.phodal.shirelang.provider.ShireLanguageToolchainProvider\"/>\n\n        <shireToolchainFunctionProvider implementation=\"com.phodal.shirelang.provider.ShireToolchainFunctionProvider\"/>\n    </extensions>\n</idea-plugin>"
  },
  {
    "path": "shirelang/src/main/resources/docs/agentExamples/browse.shire",
    "content": "/browse:https://ide.unitmesh.cc"
  },
  {
    "path": "shirelang/src/main/resources/docs/agentExamples/commit.shire",
    "content": "/commit\n\n```markdown\n[//]: # (follow Conventional Commits, like feat: add 'graphiteWidth' option)\n```\n"
  },
  {
    "path": "shirelang/src/main/resources/docs/agentExamples/file-func.shire",
    "content": "/file-func:regex(\".*\\.txt\")"
  },
  {
    "path": "shirelang/src/main/resources/docs/agentExamples/file.shire",
    "content": "/file:.github/dependabot.yml#L1C1-L2C12"
  },
  {
    "path": "shirelang/src/main/resources/docs/agentExamples/patch.shire",
    "content": "/patch\n\n```patch\n// the patch to apply\n```\n"
  },
  {
    "path": "shirelang/src/main/resources/docs/agentExamples/refactor.shire",
    "content": "/refactor:rename cc.unitmesh.devti.language.run.DevInsProgramRunner to cc.unitmesh.devti.language.run.DevInsProgramRunnerImpl"
  },
  {
    "path": "shirelang/src/main/resources/docs/agentExamples/rev.shire",
    "content": "/rev:38d23de2"
  },
  {
    "path": "shirelang/src/main/resources/docs/agentExamples/run.shire",
    "content": "/run:src/main/cc/unitmesh/PythonPromptStrategyTest.kt"
  },
  {
    "path": "shirelang/src/main/resources/docs/agentExamples/shell.shire",
    "content": "/shell:execute.sh"
  },
  {
    "path": "shirelang/src/main/resources/docs/agentExamples/symbol.shire",
    "content": "/symbol:cc.unitmesh.devti.language.psi"
  },
  {
    "path": "shirelang/src/main/resources/docs/agentExamples/write.shire",
    "content": "/write:src/main/kotlin/cc/unitmesh/context/CppFileContextBuilder.kt\n\n```kotlin\n// some code\n```\n"
  },
  {
    "path": "shirelang/src/main/resources/fileTemplates/internal/Shire Action.shire.ft",
    "content": "---\nname: \"{{name}}\"\ndescription: \"Here is a description of the action.\"\ninteraction: AppendCursor\nactionLocation: ContextMenu\n---\n\n[notes]: For more options, see [the docs](https://shire.phodal.com/)\n\nYou are an experienced software development engineer skilled in using Test-Driven Development (TDD) to develop software.\nYou need to help users write test code based on their requirements.\n"
  },
  {
    "path": "shirelang/src/main/resources/inspectionDescriptions/ShireDuplicateAgent.html",
    "content": "<html>\n<body>\nDetects multiple @[AGENT_ID] annotations. Multiple @[AGENT_ID] are not allowed for current version.\n</body>\n</html>\n"
  },
  {
    "path": "shirelang/src/main/resources/messages/ShireBundle.properties",
    "content": "name=Shire\nshire.ref.loading=Loading git revision\nshire.line.marker.run.0=Run {0}\nshire.label.choose.file=Choice Shire File\nshire.run.error.script.not.found=Script not found\nshire.patch.cannot.read.patch=Cannot read a patch\ninspection.group.name=Shire language\ninspection.duplicate.agent=Duplicate agent calls detected. It is recommended to make only one call per agent. Please remove any duplicate agent calls.\nshire.intention.name=Shire Assistant\nshire.intention.category=Shire Intention\nshire.newFile=ShireAction\nshire.file=Shire file\nshire.run.local.mode=Local command detected, running in local mode\nshire.intention=Shire intention action\nshire.llm.notfound=No LLM provider found\nshire.llm.done=\\nDone!\nshire.prompt.fix.command=You are a top software developer in the world, which can help me to fix the issue.\\nWhen I use shell-like language and compile the script, I got an error, can you help me to fix it?\\n\\nOrigin script:\\n```\\n{0}\\n```\\n\\nScript with result:\\n####\\n{1}\\n####\nshire.prompt.fix.run-result=You are a top software developer in the world, which can help me to fix the issue.\\n\\nHere is the run result, can you help me to fix it?\\nRun result:\\n####\\n{0}\\n####\nintentions.request.background.process.title=Generate File\nshire.toolchain.function.not.found=No match function: {0}, If you using toolchain function, visit: https://shire.phodal.com/shire/shire-toolchain-function for more. TODO for User custom.\nintentions.assistant.name=Shire Intention Action\nshire.line.marker.run.comment=Run Shire\nshire.actions.chat-box=ChatBox\nshire.actions.run-from-chat-box=Run from Chatbox\neditor.preview.tip=Show Shire Prompt Preview\neditor.preview.refresh=Refresh Preview\neditor.preview=Show Preview\neditor.preview.help=Help\neditor.preview.help.url=https://shire.phodal.com/\neditor.preview.variable.panel=Custom Variable Snapshots\n"
  },
  {
    "path": "shirelang/src/test/kotlin/com/phodal/shirelang/ParsingNormalTest.kt",
    "content": "package com.phodal.shirelang\n\nimport com.intellij.testFramework.ParsingTestCase\nimport com.phodal.shirelang.parser.ShireParserDefinition\n\nclass ParsingNormalTest : ParsingTestCase(\"parser\", \"shire\", ShireParserDefinition()) {\n    override fun getTestDataPath(): String {\n        return \"src/test/testData\"\n    }\n\n    fun testBasicTest() {\n        doTest(true)\n    }\n\n    fun testJavaHelloWorld() {\n        doTest(true)\n    }\n\n    fun testEmptyCodeFence() {\n        doTest(true)\n    }\n\n    fun testJavaAnnotation() {\n        doTest(true)\n    }\n\n    fun testBlockStartOnly() {\n        doTest(true)\n    }\n\n    fun testComplexLangId() {\n        doTest(true)\n    }\n\n    fun testAutoCommand() {\n        doTest(true)\n    }\n\n    fun testCommandAndSymbol() {\n        doTest(true)\n    }\n\n    fun testBrowseWeb() {\n        doTest(true)\n    }\n\n    fun testAutoRefactor() {\n        doTest(true)\n    }\n\n    fun testFrontMatter() {\n        doTest(true)\n    }\n\n    fun testSingleComment() {\n        doTest(true)\n    }\n\n    fun testShireFmObject() {\n        doTest(true)\n    }\n\n    fun testPatternAction() {\n        doTest(true)\n    }\n\n    fun testPatternCaseAction() {\n        doTest(true)\n    }\n\n    fun testWhenCondition() {\n        doTest(true)\n    }\n\n    fun testVariableAccess() {\n        doTest(true)\n    }\n\n    fun testShirePsiQueryExpression() {\n        doTest(true)\n    }\n\n    fun testMultipleFMVariable() {\n        doTest(true)\n    }\n\n    fun testAfterStream() {\n        doTest(true)\n    }\n\n    fun testMarkdownCompatible() {\n        doTest(true)\n    }\n\n    fun testCustomFunctions() {\n        doTest(true)\n    }\n\n    fun testIfExpression() {\n        doTest(true)\n    }\n}\n"
  },
  {
    "path": "shirelang/src/test/kotlin/com/phodal/shirelang/ParsingRealWorldTest.kt",
    "content": "package com.phodal.shirelang\n\nimport com.intellij.testFramework.ParsingTestCase\nimport com.phodal.shirelang.parser.ShireParserDefinition\n\nclass ParsingRealWorldTest : ParsingTestCase(\"realworld\", \"shire\", ShireParserDefinition()) {\n    override fun getTestDataPath(): String {\n        return \"src/test/testData\"\n    }\n\n    fun testAutotest() {\n        doTest(true)\n    }\n\n    fun testLifeCycle() {\n        doTest(true)\n    }\n\n    fun testContentTee() {\n        doTest(true)\n    }\n\n    fun testWhenAfterStreaming() {\n        doTest(true)\n    }\n\n    fun testAfterStreamingOnly() {\n        doTest(true)\n    }\n\n    fun testOutputInVariable() {\n        doTest(true)\n    }\n\n    fun testOnPaste() {\n        doTest(true)\n    }\n\n    fun testLoginCommit() {\n        doTest(true)\n    }\n}\n\n"
  },
  {
    "path": "shirelang/src/test/kotlin/com/phodal/shirelang/ShireCompileTest.kt",
    "content": "package com.phodal.shirelang\n\nimport com.intellij.testFramework.fixtures.BasePlatformTestCase\nimport com.phodal.shirecore.config.ShireActionLocation\nimport com.phodal.shirecore.config.InteractionType\nimport com.phodal.shirelang.compiler.parser.HobbitHoleParser\nimport com.phodal.shirelang.compiler.parser.ShireSyntaxAnalyzer\nimport com.phodal.shirelang.compiler.ast.LogicalExpression\nimport com.phodal.shirelang.compiler.ast.patternaction.PatternActionFunc\nimport com.phodal.shirelang.compiler.execute.PatternActionProcessor\nimport com.phodal.shirelang.psi.ShireFile\nimport junit.framework.TestCase\nimport kotlinx.coroutines.runBlocking\nimport org.intellij.lang.annotations.Language\n\nclass ShireCompileTest : BasePlatformTestCase() {\n    fun testNormalString() {\n        val code = \"Normal String /\"\n        val file = myFixture.configureByText(\"test.shire\", code)\n\n        val compile = ShireSyntaxAnalyzer(project, file as ShireFile, myFixture.editor).parseAndExecuteLocalCommand()\n        assertEquals(\"Normal String /\", compile.shireOutput)\n    }\n\n    fun testWithFrontmatter() {\n        @Language(\"Shire\")\n        val code = \"\"\"\n            ---\n            name: Summary\n            description: \"Generate Summary\"\n            interaction: AppendCursor\n            actionLocation: ContextMenu\n            ---\n            \n            Summary webpage:\n            \n        \"\"\".trimIndent()\n\n        val file = myFixture.configureByText(\"test.shire\", code)\n\n        val compile = ShireSyntaxAnalyzer(project, file as ShireFile, myFixture.editor).parseAndExecuteLocalCommand()\n        assertEquals(\"\\n\\nSummary webpage:\\n\", compile.shireOutput)\n        compile.config!!.let {\n            assertEquals(\"Summary\", it.name)\n            assertEquals(\"Generate Summary\", it.description)\n            assertEquals(InteractionType.AppendCursor, it.interaction)\n            assertEquals(ShireActionLocation.CONTEXT_MENU, it.actionLocation)\n        }\n    }\n\n    fun testWithFrontMatterArray() {\n        @Language(\"Shire\")\n        val code = \"\"\"\n            ---\n            name: Summary\n            description: \"Generate Summary\"\n            interaction: AppendCursor\n            data: [\"a\", \"b\"]\n            ---\n            \n            Summary webpage:\n            \n        \"\"\".trimIndent()\n\n        val file = myFixture.configureByText(\"test.shire\", code)\n\n        val compile = ShireSyntaxAnalyzer(project, file as ShireFile, myFixture.editor).parseAndExecuteLocalCommand()\n        assertEquals(\"\\n\\nSummary webpage:\\n\", compile.shireOutput)\n    }\n\n    fun testShouldCheckFile() {\n        @Language(\"Shire\")\n        val code = \"\"\"\n            ---\n            name: Summary\n            description: \"Generate Summary\"\n            interaction: AppendCursor\n            data: [\"a\", \"b\"]\n            ---\n            \n            Summary webpage:\n            \n        \"\"\".trimIndent()\n\n        val file = myFixture.configureByText(\"test.shire\", code)\n\n        val isFrontMatterPresent = HobbitHoleParser.hasFrontMatter(file as ShireFile)\n        assertTrue(isFrontMatterPresent)\n    }\n\n    fun testShouldHandleForPatternAction() {\n        @Language(\"Shire\")\n        val code = \"\"\"\n            ---\n            name: Summary\n            description: \"Generate Summary\"\n            interaction: AppendCursor\n            data: [\"a\", \"b\"]\n            variables:\n              \"var1\": \"demo\"\n              \"var2\": /**.java/ { grep(\"error.log\") | sort | xargs(\"rm\")}\n            ---\n            \n            Summary webpage:\n        \"\"\".trimIndent()\n\n        val file = myFixture.configureByText(\"test.shire\", code)\n\n        val compile = ShireSyntaxAnalyzer(project, file as ShireFile, myFixture.editor).parseAndExecuteLocalCommand()\n        assertEquals(\"\\n\\nSummary webpage:\", compile.shireOutput)\n        val map = compile.config!!.variables\n\n\n        val var2 = map[\"var2\"]!!\n\n        val patterns = var2.patternActionFuncs\n        assertEquals(3, patterns.size)\n        assertEquals(\"grep\", patterns[0].funcName)\n        assertEquals(\"error.log\", (patterns[0] as PatternActionFunc.Grep).patterns[0])\n        assertEquals(\"sort\", patterns[1].funcName)\n        assertEquals(\"xargs\", patterns[2].funcName)\n    }\n\n    fun testShouldHandleForWhenCondition() {\n        @Language(\"Shire\")\n        val code = \"\"\"\n            ---\n            when: ${'$'}selection.length >= 1 && ${'$'}selection.first() == 'p'\n            ---\n            \n            Summary webpage:\n        \"\"\".trimIndent()\n\n        val file = myFixture.configureByText(\"test.shire\", code)\n\n        val compile = ShireSyntaxAnalyzer(project, file as ShireFile, myFixture.editor).parseAndExecuteLocalCommand()\n        assertEquals(\"\\n\\nSummary webpage:\", compile.shireOutput)\n        val when_ = compile.config?.when_\n\n        assertEquals(when_!!.display(), \"\\$selection.length >= 1 && \\$selection.first == \\\"p\\\"\")\n\n        val variables: Map<String, String> = mapOf(\n            \"selection\" to \"\"\"public class HelloWorld {\n    public static void main(String[] args) {\n        System.out.println(\"Hello, World\");\n    }\n}\"\"\"\n        )\n\n        val result = (when_.value as LogicalExpression).evaluate(variables)\n        assertTrue(result)\n    }\n\n    fun testShouldHandleForWhenConditionInVariableExpr() {\n        @Language(\"Shire\")\n        val code = \"\"\"\n            ---\n            when: { ${'$'}selection.length >= 1 && ${'$'}selection.first() == 'p' }\n            ---\n            \n            Summary webpage:\n        \"\"\".trimIndent()\n\n        val file = myFixture.configureByText(\"test.shire\", code)\n\n        val compile = ShireSyntaxAnalyzer(project, file as ShireFile, myFixture.editor).parseAndExecuteLocalCommand()\n        assertEquals(\"\\n\\nSummary webpage:\", compile.shireOutput)\n        val when_ = compile.config?.when_\n\n        assertEquals(when_!!.display(), \"\\$selection.length >= 1 && \\$selection.first == \\\"p\\\"\")\n\n        val variables: Map<String, String> = mapOf(\n            \"selection\" to \"\"\"public class HelloWorld {\n    public static void main(String[] args) {\n        System.out.println(\"Hello, World\");\n    }\n}\"\"\"\n        )\n\n        val result = (when_.value as LogicalExpression).evaluate(variables)\n        assertTrue(result)\n    }\n\n    fun testShouldHandleForWhenConditionForContains() {\n        @Language(\"Shire\")\n        val code = \"\"\"\n            ---\n            when: ${'$'}fileName.contains(\".java\") && ${'$'}filePath.contains(\"src/main/java\")\n            ---\n            \n            Summary webpage:\n        \"\"\".trimIndent()\n\n        val file = myFixture.configureByText(\"test.shire\", code)\n\n        val compile = ShireSyntaxAnalyzer(project, file as ShireFile, myFixture.editor).parseAndExecuteLocalCommand()\n        assertEquals(\"\\n\\nSummary webpage:\", compile.shireOutput)\n        val when_ = compile.config?.when_\n\n        assertEquals(when_!!.display(), \"\\$fileName.contains(\\\".java\\\") && \\$filePath.contains(\\\"src/main/java\\\")\")\n    }\n\n    fun testShouldHandleForWhenConditionForPattern() {\n        @Language(\"Shire\")\n        val code = \"\"\"\n            ---\n            when: ${'$'}fileName.matches(\"/.*.java/\")\n            ---\n            \n            Summary webpage:\n        \"\"\".trimIndent()\n\n        val file = myFixture.configureByText(\"test.shire\", code)\n\n        val compile = ShireSyntaxAnalyzer(project, file as ShireFile, myFixture.editor).parseAndExecuteLocalCommand()\n        assertEquals(\"\\n\\nSummary webpage:\", compile.shireOutput)\n        val when_ = compile.config?.when_\n\n        assertEquals(when_!!.display(), \"\\$fileName.matches(\\\"/.*.java/\\\")\")\n    }\n\n    fun testShouldGetSymbolTableValueFromCompileResult() {\n        @Language(\"Shire\")\n        val code = \"\"\"\n            ---\n            name: Summary\n            description: \"Generate Summary\"\n            interaction: AppendCursor\n            data: [\"a\", \"b\"]\n            when: ${'$'}fileName.matches(\"/.*.java/\")\n            variables:\n              \"var1\": \"demo\"\n              \"var2\": /.*.java/ { print(\"hello\") | sort }\n            ---\n            \n            Summary webpage: ${'$'}fileName\n        \"\"\".trimIndent()\n\n        val file = myFixture.configureByText(\"test.shire\", code)\n\n        val compile = ShireSyntaxAnalyzer(project, file as ShireFile, myFixture.editor).parseAndExecuteLocalCommand()\n        val table = compile.variableTable\n\n        val hole = compile.config!!\n\n        assertEquals(1, table.getAllVariables().size)\n        assertEquals(11, table.getVariable(\"fileName\").lineDeclared)\n\n        val results = runBlocking {\n            hole.variables.mapValues {\n                PatternActionProcessor(project, hole, mutableMapOf()).execute(it.value)\n            }\n        }\n\n        assertEquals(\"demo\", results[\"var1\"])\n        assertEquals(\"hello\", results[\"var2\"].toString())\n    }\n\n    fun testShouldLoadFile() {\n        @Language(\"Shire\")\n        val code = \"\"\"\n            ---\n            name: Summary\n            description: \"Generate Summary\"\n            interaction: AppendCursor\n            data: [\"a\", \"b\"]\n            when: ${'$'}fileName.matches(\"/.*.java/\")\n            variables:\n              \"var2\": /.*ple.shire/ { cat | find(\"fileName\") | sort }\n            ---\n            \n            Summary webpage: ${'$'}fileName\n        \"\"\".trimIndent()\n\n        val file = myFixture.addFileToProject(\"sample.shire\", code)\n\n        myFixture.openFileInEditor(file.virtualFile)\n\n        val compile = ShireSyntaxAnalyzer(project, file as ShireFile, myFixture.editor).parseAndExecuteLocalCommand()\n        val hole = compile.config!!\n\n        val results = runBlocking {\n            hole.variables.mapValues {\n                PatternActionProcessor(project, hole, mutableMapOf()).execute(it.value)\n            }\n        }\n\n        assertEquals(\n            \"  \\\"var2\\\": /.*ple.shire/ { cat | find(\\\"fileName\\\") | sort }\\n\" +\n                    \"Summary webpage: \\$fileName\\n\" +\n                    \"when: \\$fileName.matches(\\\"/.*.java/\\\")\", results[\"var2\"].toString()\n        )\n    }\n\n    fun testShouldComputePatterCaseResult() {\n        @Language(\"Shire\")\n        val code = \"\"\"\n            ---\n            variables:\n              \"var1\": /.*.shire/ {\n                case \"${'$'}0\" {\n                  \"error\" { grep(\"ERROR\") | sort | xargs(\"notify_admin\") }\n                  \"warn\" { grep(\"WARN\") | sort | xargs(\"notify_admin\") }\n                  \"info\" { grep(\"INFO\") | sort | xargs(\"notify_user\") }\n                  default  { grep(\"(.*).shire\") | sort }\n                }\n              }\n            ---\n\n            ${'$'}var1\n            \"\"\".trimIndent()\n\n        val file = myFixture.configureByText(\"test.shire\", code)\n\n        val compile = ShireSyntaxAnalyzer(project, file as ShireFile, myFixture.editor).parseAndExecuteLocalCommand()\n        val hole = compile.config!!\n\n        TestCase.assertEquals(1, hole.variables.size)\n\n        val results = runBlocking {\n            hole.variables.mapValues {\n                PatternActionProcessor(project, hole, mutableMapOf()).execute(it.value)\n            }\n        }\n\n        assertEquals(\"/src/test\", results[\"var1\"])\n    }\n\n    fun testShouldSupportCorrectGrep() {\n        @Language(\"Shire\")\n        val code = \"\"\"\n            ---\n            name: Summary\n            description: \"Generate Summary\"\n            interaction: AppendCursor\n            data: [\"a\", \"b\"]\n            when: ${'$'}fileName.matches(\"/.*.java/\")\n            variables:\n              \"phoneNumber\": \"086-1234567890\"\n              \"phoneNumber2\": \"088-1234567890\"\n              \"var2\": /.*ple.shire/ { cat | grep(\"([0-9]{3}-[0-9]{10})\") }\n            ---\n            \n            Summary webpage: ${'$'}fileName\n        \"\"\".trimIndent()\n\n        val file = myFixture.addFileToProject(\"sample.shire\", code)\n\n        myFixture.openFileInEditor(file.virtualFile)\n\n        val compile = ShireSyntaxAnalyzer(project, file as ShireFile, myFixture.editor).parseAndExecuteLocalCommand()\n        val hole = compile.config!!\n\n        val results = runBlocking {\n            hole.variables.mapValues {\n                PatternActionProcessor(project, hole, mutableMapOf()).execute(it.value)\n            }\n        }\n\n        assertEquals(\"086-1234567890\\n088-1234567890\", results[\"var2\"].toString())\n    }\n\n    fun testShouldHandleForDataRedact() {\n        @Language(\"Shire\")\n        val code = \"\"\"\n            ---\n            name: Summary\n            description: \"Generate Summary\"\n            interaction: AppendCursor\n            data: [\"a\", \"b\"]\n            when: ${'$'}fileName.matches(\"/.*.java/\")\n            variables:\n              \"phoneNumber\": \"086-1234567890\"\n              \"var2\": /.*ple.shire/ { cat | redact }\n            ---\n            \n            Summary webpage: ${'$'}fileName\n        \"\"\".trimIndent()\n\n        val file = myFixture.addFileToProject(\"sample.shire\", code)\n\n        myFixture.openFileInEditor(file.virtualFile)\n\n        val compile = ShireSyntaxAnalyzer(project, file as ShireFile, myFixture.editor).parseAndExecuteLocalCommand()\n        val hole = compile.config!!\n\n        val results = runBlocking {\n            hole.variables.mapValues {\n                PatternActionProcessor(project, hole, mutableMapOf()).execute(it.value)\n            }\n        }\n\n        assertEquals(\n            \"\"\"---\nname: Summary\ndescription: \"Generate Summary\"\ninteraction: AppendCursor\ndata: [\"a\", \"b\"]\nwhen: ${'$'}fileName.matches(\"/.*.java/\")\nvariables:\n  \"phoneNumber\": \"****\"\n  \"var2\": /.*ple.shire/ { cat | redact }\n---\n\nSummary webpage: ${'$'}fileName\"\"\", results[\"var2\"].toString()\n        )\n    }\n\n    fun testShouldConvertSourceCode() {\n        @Language(\"Shire\")\n        val code = \"\"\"\n            ---\n            name: \"添加测试\"\n            actionLocation: ContextMenu\n            variables:\n              \"sourceCode\": /any/ { print(${'$'}filePath) | sed(\"src/test\", \"src/main\") | sed(\"sample.shire\", \"sampleTest.shire\") | print }\n            onStreamingEnd: { parseCode | patch(${'$'}filePath, ${'$'}output) }\n            ---\n        \"\"\".trimIndent()\n\n        val file = myFixture.addFileToProject(\"sample.shire\", code)\n\n        myFixture.openFileInEditor(file.virtualFile)\n\n        val compile = ShireSyntaxAnalyzer(project, file as ShireFile, myFixture.editor).parseAndExecuteLocalCommand()\n        val hole = compile.config!!\n\n        val results = runBlocking {\n            hole.variables.mapValues {\n                PatternActionProcessor(\n                    project, hole, mutableMapOf(\n                        \"filePath\" to \"src/test/resources/sample.shire\"\n                    )\n                ).execute(it.value)\n            }\n        }\n\n        assertEquals(results[\"sourceCode\"], \"src/main/resources/sampleTest.shire\")\n    }\n\n    fun testShouldSupportForeignFunction() {\n        @Language(\"JavaScript\")\n        val jsMainWithArgs = \"\"\"\n            const args = process.argv.slice(2);\n            console.log(\"hello, world\");\n            console.log(args[0]);\n            \n            process.exit(0);\n        \"\"\".trimIndent()\n\n        myFixture.addFileToProject(\"hello.js\", jsMainWithArgs)\n\n        @Language(\"Shire\")\n        val code = \"\"\"\n            ---\n            name: Summary\n            description: \"Generate Summary\"\n            interaction: AppendCursor\n            functions:\n              normal: \"hello.js\"(string)\n            variables:\n              \"var2\": /.*ple.shire/ { normal }\n            ---\n            \n            Summary webpage: ${'$'}fileName\n        \"\"\".trimIndent()\n        val file = myFixture.addFileToProject(\"sample.shire\", code)\n        myFixture.openFileInEditor(file.virtualFile)\n\n        val compile = ShireSyntaxAnalyzer(project, file as ShireFile, myFixture.editor).parseAndExecuteLocalCommand()\n        val hole = compile.config!!\n\n        val results = runBlocking {\n            hole.variables.mapValues {\n                PatternActionProcessor(project, hole, mutableMapOf()).execute(it.value)\n            }\n        }\n\n//        assertEquals(\"\", results[\"var2\"].toString())\n        /// in 241 version, the result is not empty but don't know why\n        println(results[\"var2\"].toString())\n    }\n}\n"
  },
  {
    "path": "shirelang/src/test/kotlin/com/phodal/shirelang/ShireLifecycleTest.kt",
    "content": "package com.phodal.shirelang\n\nimport com.intellij.execution.ui.ConsoleView\nimport com.intellij.openapi.project.Project\nimport com.intellij.testFramework.fixtures.BasePlatformTestCase\nimport com.phodal.shirecore.middleware.post.PostProcessorContext\nimport com.phodal.shirecore.middleware.post.PostProcessor.Companion.handler\nimport com.phodal.shirecore.middleware.post.LifecycleProcessorSignature\nimport com.phodal.shirelang.compiler.parser.ShireSyntaxAnalyzer\nimport com.phodal.shirelang.psi.ShireFile\nimport junit.framework.TestCase\nimport org.intellij.lang.annotations.Language\nimport org.jetbrains.annotations.TestOnly\n\nclass ShireLifecycleTest : BasePlatformTestCase() {\n    fun testShouldHandleWhenStreamingEnd() {\n        @Language(\"Shire\")\n        val code = \"\"\"\n            ---\n            onStreamingEnd:  { parseCode | saveFile(\"demo.shire\") | verifyCode | runCode }\n            ---\n            \n            ${'$'}allController\n        \"\"\".trimIndent()\n\n        val file = myFixture.addFileToProject(\"sample.shire\", code)\n\n        myFixture.openFileInEditor(file.virtualFile)\n\n        val compile = ShireSyntaxAnalyzer(project, file as ShireFile, myFixture.editor).parseAndExecuteLocalCommand()\n        val hole = compile.config!!\n\n        val funcNode = hole.onStreamingEnd\n\n        assertEquals(funcNode.size, 4)\n        assertEquals(funcNode[0].funcName, \"parseCode\")\n        assertEquals(funcNode[0].args.size, 0)\n\n        assertEquals(funcNode[1].funcName, \"saveFile\")\n        assertEquals(funcNode[1].args[0], \"demo.shire\")\n\n        assertEquals(funcNode[2].funcName, \"verifyCode\")\n        assertEquals(funcNode[3].funcName, \"runCode\")\n\n        try {\n            val handleContext = PostProcessorContext(currentLanguage = ShireLanguage.INSTANCE, editor = null)\n            execute(project, funcNode, handleContext, null)\n        } catch (e: Exception) {\n            e.printStackTrace()\n        }\n    }\n\n    @TestOnly\n    fun execute(\n        project: Project,\n        funcNodes: List<LifecycleProcessorSignature>,\n        handleContext: PostProcessorContext,\n        console: ConsoleView?,\n    ) {\n        funcNodes.forEach { funNode ->\n            val handler = handler(funNode.funcName)\n            if (handler != null) {\n                handler.setup(handleContext)\n                handler.execute(project, handleContext, console, funNode.args)\n                handler.finish(handleContext)\n            }\n        }\n    }\n\n    fun testShouldHandleWhenAfterStreaming() {\n        @Language(\"Shire\")\n        val code = \"\"\"\n            ---\n            afterStreaming: {\n                condition {\n                  \"error\"       { output.length < 1 }\n                  \"success\"     { output.length > 1 }\n                  \"json-result\" { jsonpath(\"${'$'}.store.*\") }\n                }\n                case condition {\n                  \"error\"       { notify(\"Failed to Generate JSON\") }\n                  \"success\"     { notify(\"Success to Generate JSON\") }\n                  \"json-result\" { execute(\"sample2.shire\") }\n                  default       { notify(\"Failed to Generate JSON\") /* mean nothing */ }\n                }\n              }\n            ---\n            \n            ${'$'}allController\n        \"\"\".trimIndent()\n\n        val file = myFixture.addFileToProject(\"sample.shire\", code)\n\n        myFixture.openFileInEditor(file.virtualFile)\n\n        val compile = ShireSyntaxAnalyzer(project, file as ShireFile, myFixture.editor).parseAndExecuteLocalCommand()\n        val hole = compile.config!!\n\n        val funcNode = hole.afterStreaming!!\n\n        TestCase.assertEquals(funcNode.conditions.size, 3)\n        TestCase.assertEquals(funcNode.conditions[0].conditionKey, \"\\\"error\\\"\")\n\n        assertEquals(funcNode.conditions[2].valueExpression.display(), \"jsonpath(\\\"${'$'}.store.*\\\")\")\n\n        TestCase.assertEquals(funcNode.cases.size, 4)\n        TestCase.assertEquals(funcNode.cases[0].caseKey, \"\\\"error\\\"\")\n\n        val genJson = \"\"\"\n            {\n                \"store\": {\n                    \"book\": [\n                        {\n                            \"category\": \"reference\",\n                            \"author\": \"Nigel Rees\",\n                            \"title\": \"Sayings of the Century\",\n                            \"price\": 8.95\n                        }\n                    ]\n                }\n            }\n           \"\"\".trimIndent()\n        val handleContext = PostProcessorContext(\n            currentLanguage = ShireLanguage.INSTANCE,\n            genText = genJson,\n            editor = null\n        )\n\n        assertThrows(RuntimeException::class.java) {\n            hole.afterStreaming?.execute(myFixture.project, handleContext, hole)\n        }\n    }\n\n    fun testShouldSupportForBeforeStreaming() {\n        @Language(\"Shire\")\n        val code = \"\"\"\n            ---\n            beforeStreaming: { caching(\"disk\") | splitting | embedding }\n            ---\n            \n            ${'$'}allController\n        \"\"\".trimIndent()\n\n        val file = myFixture.addFileToProject(\"sample.shire\", code)\n\n        myFixture.openFileInEditor(file.virtualFile)\n\n        val compile = ShireSyntaxAnalyzer(project, file as ShireFile, myFixture.editor).parseAndExecuteLocalCommand()\n        val hole = compile.config!!.beforeStreaming!!\n\n        assertEquals(hole.processors.size, 3)\n        assertEquals(hole.processors[0].funcName, \"caching\")\n        assertEquals(hole.processors[1].funcName, \"splitting\")\n        assertEquals(hole.processors[2].funcName, \"embedding\")\n    }\n\n    fun testShouldExecuteProcessForOnStreamingEvent() {\n        @Language(\"Shire\")\n        val code = \"\"\"\n            ---\n            onStreaming: { logging() }\n            ---\n            \n            ${'$'}allController\n        \"\"\".trimIndent()\n\n        val file = myFixture.addFileToProject(\"sample.shire\", code)\n\n        myFixture.openFileInEditor(file.virtualFile)\n\n        val compile = ShireSyntaxAnalyzer(project, file as ShireFile, myFixture.editor).parseAndExecuteLocalCommand()\n        val funcSigns = compile.config!!.onStreaming\n\n        assertEquals(funcSigns.size, 1)\n        assertEquals(funcSigns[0].funcName, \"logging\")\n    }\n}"
  },
  {
    "path": "shirelang/src/test/kotlin/com/phodal/shirelang/ShirePatternPipelineTest.kt",
    "content": "package com.phodal.shirelang\n\nimport com.intellij.testFramework.fixtures.BasePlatformTestCase\nimport com.phodal.shirecore.middleware.post.PostProcessorContext\nimport com.phodal.shirelang.compiler.execute.PatternActionProcessor\nimport com.phodal.shirelang.compiler.parser.ShireSyntaxAnalyzer\nimport com.phodal.shirelang.compiler.template.ShireVariableTemplateCompiler\nimport com.phodal.shirelang.psi.ShireFile\nimport kotlinx.coroutines.runBlocking\nimport org.intellij.lang.annotations.Language\n\nclass ShirePatternPipelineTest : BasePlatformTestCase() {\n    fun testShouldSupportForTee() {\n        @Language(\"Shire\")\n        val code = \"\"\"\n            ---\n            name: Summary\n            description: \"Generate Summary\"\n            interaction: AppendCursor\n            data: [\"a\", \"b\"]\n            when: ${'$'}fileName.matches(\"/.*.java/\")\n            variables:\n              \"var2\": /.*ple.shire/ { cat | find(\"fileName\") | sort }\n            onStreamingEnd: { append(${'$'}var2) | saveFile(\"summary.md\") }\n            ---\n            \n            Summary webpage: ${'$'}fileName\n        \"\"\".trimIndent()\n\n        val file = myFixture.addFileToProject(\"sample.shire\", code)\n\n        myFixture.openFileInEditor(file.virtualFile)\n\n        val compile = ShireSyntaxAnalyzer(project, file as ShireFile, myFixture.editor).parseAndExecuteLocalCommand()\n        val hole = compile.config!!\n\n        val context = PostProcessorContext(\n            genText = \"User prompt:\\n\\n\",\n        )\n\n        runBlocking {\n            val shireTemplateCompiler = ShireVariableTemplateCompiler(project, hole, compile.variableTable, code, myFixture.editor)\n            val compiledVariables =\n                shireTemplateCompiler.compileVariable(myFixture.editor, mutableMapOf())\n\n            context.compiledVariables = compiledVariables\n\n            hole.variables.mapValues {\n                PatternActionProcessor(project, hole, mutableMapOf()).execute(it.value)\n            }\n\n            hole.setupStreamingEndProcessor(project, context = context)\n            hole.executeStreamingEndProcessor(project, null, context = context, compiledVariables)\n        }\n\n        assertEquals(\"User prompt:\\n\\n\" +\n                \"  \\\"var2\\\": /.*ple.shire/ { cat | find(\\\"fileName\\\") | sort }\\n\" +\n                \"Summary webpage: \\$fileName\\n\" +\n                \"when: \\$fileName.matches(\\\"/.*.java/\\\")\", context.genText)\n    }\n\n    fun testShouldSupportAfterStreamingPattern() {\n        @Language(\"Shire\")\n        val code = \"\"\"\n            ---\n            name: Summary\n            description: \"Generate Summary\"\n            interaction: AppendCursor\n            variables:\n              \"var2\": \"sample\"\n            afterStreaming: { \n                case condition {\n                  default { print(${'$'}output) }\n                }\n            }\n            ---\n            \n            Summary webpage: ${'$'}fileName\n        \"\"\".trimIndent()\n\n        val file = myFixture.addFileToProject(\"sample.shire\", code)\n\n        myFixture.openFileInEditor(file.virtualFile)\n\n        val compile = ShireSyntaxAnalyzer(project, file as ShireFile, myFixture.editor).parseAndExecuteLocalCommand()\n        val hole = compile.config!!\n\n        val context = PostProcessorContext(\n            genText = \"User prompt:\\n\\n\",\n        )\n\n        runBlocking {\n            val shireTemplateCompiler = ShireVariableTemplateCompiler(project, hole, compile.variableTable, code, myFixture.editor)\n            val compiledVariables =\n                shireTemplateCompiler.compileVariable(myFixture.editor, mutableMapOf())\n\n            context.compiledVariables = compiledVariables\n\n            hole.variables.mapValues {\n                PatternActionProcessor(project, hole, mutableMapOf()).execute(it.value)\n            }\n\n            hole.setupStreamingEndProcessor(project, context = context)\n            hole.executeAfterStreamingProcessor(project, null, context = context)\n        }\n\n        assertEquals(\"User prompt:\\n\\n\", context.lastTaskOutput)\n    }\n\n    fun testShouldUseSedReplaceContentInVariables() {\n        @Language(\"Shire\")\n        val code = \"\"\"\n            ---\n            name: Summary\n            description: \"Generate Summary\"\n            interaction: AppendCursor\n            variables:\n              \"openai\": \"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\"\n              \"var2\": /.*ple.shire/ { cat | find(\"openai\") | sed(\"(?i)\\b(sk-[a-zA-Z0-9]{20}T3BlbkFJ[a-zA-Z0-9]{20})(?:['|\\\"|\\n|\\r|\\s|\\x60|;]|${'$'})\", \"sk-***\") }\n            ---\n            \n            Summary webpage: ${'$'}var2\n        \"\"\".trimIndent()\n\n        val file = myFixture.addFileToProject(\"sample.shire\", code)\n\n        myFixture.openFileInEditor(file.virtualFile)\n\n        val compile = ShireSyntaxAnalyzer(project, file as ShireFile, myFixture.editor).parseAndExecuteLocalCommand()\n        val hole = compile.config!!\n\n        val context = PostProcessorContext(\n            genText = \"User prompt:\\n\\n\",\n        )\n\n        runBlocking {\n            val shireTemplateCompiler = ShireVariableTemplateCompiler(project, hole, compile.variableTable, code, myFixture.editor)\n            val compiledVariables =\n                shireTemplateCompiler.compileVariable(myFixture.editor, mutableMapOf())\n\n            context.compiledVariables = compiledVariables\n\n            hole.variables.mapValues {\n                PatternActionProcessor(project, hole, mutableMapOf()).execute(it.value)\n            }\n\n            hole.setupStreamingEndProcessor(project, context = context)\n            hole.executeAfterStreamingProcessor(project, null, context = context)\n        }\n\n        assertEquals(\"  \\\"openai\\\": \\\"sk-***\\n\" +\n                \"  \\\"var2\\\": /.*ple.shire/ { cat | find(\\\"openai\\\") | sed(\\\"(?i)\\\\b(sk-[a-zA-Z0-9]{20}T3BlbkFJ[a-zA-Z0-9]{20})(?:['|\\\\\\\"|\\\\n|\\\\r|\\\\s|\\\\x60|;]|\\$)\\\", \\\"sk-***\\\") }\", context.compiledVariables[\"var2\"]\n        )\n    }\n}\n"
  },
  {
    "path": "shirelang/src/test/kotlin/com/phodal/shirelang/ShireQueryExpressionTest.kt",
    "content": "package com.phodal.shirelang\n\nimport com.intellij.testFramework.fixtures.BasePlatformTestCase\nimport com.jetbrains.rd.util.first\nimport com.phodal.shirelang.compiler.parser.ShireSyntaxAnalyzer\nimport com.phodal.shirelang.compiler.ast.patternaction.PatternActionFunc\nimport com.phodal.shirelang.compiler.execute.PatternActionProcessor\nimport com.phodal.shirelang.psi.ShireFile\nimport junit.framework.TestCase\nimport kotlinx.coroutines.runBlocking\nimport org.intellij.lang.annotations.Language\n\nclass ShireQueryExpressionTest : BasePlatformTestCase() {\n    fun testShouldGetFromExpression() {\n        val sampleText = \"\"\"HelloWorld.txt\"\"\".trimIndent()\n\n        @Language(\"Shire\")\n        val code = \"\"\"\n            ---\n            variables:\n              \"allController\": {\n                from {\n                    File clazz // the class\n                }\n                where {\n                    clazz.text == \"HelloWorld.txt\"\n                }\n                select {\n                    clazz.toString(), \"code\"\n                }\n              }\n            ---\n            \n            ${'$'}allController\n        \"\"\".trimIndent()\n\n        myFixture.addFileToProject(\"HelloWorld.txt\", sampleText)\n        val file = myFixture.addFileToProject(\"sample.shire\", code)\n\n        val compile = ShireSyntaxAnalyzer(project, file as ShireFile, myFixture.editor).parseAndExecuteLocalCommand()\n        val hole = compile.config!!\n\n        val patternActionFuncs = hole.variables.first().value.patternActionFuncs\n        val whereDisplay = (patternActionFuncs[1] as PatternActionFunc.Where).statement.display()\n        val selectDisplay = (patternActionFuncs[2] as PatternActionFunc.Select).statements.map { it.display() }\n\n        assertEquals(whereDisplay, \"clazz.text == \\\"HelloWorld.txt\\\"\")\n        assertEquals(selectDisplay, listOf(\"clazz.toString\", \"\\\"code\\\"\"))\n\n        val results = runBlocking {\n            hole.variables.mapValues {\n                PatternActionProcessor(project, hole, mutableMapOf()).execute(it.value)\n            }\n        }\n\n        assertEquals(results[\"allController\"], \"\"\"\n            PsiFile(plain text):HelloWorld.txt\n            \"code\"\n            \"\"\".trimIndent())\n    }\n\n    fun testShouldTestForDayNow() {\n        @Language(\"Shire\")\n        val code = \"\"\"\n            ---\n            variables:\n              \"dayNow\": {\n                from {\n                    Date date\n                }\n                where {\n                    date.dayOfWeek() != 8\n                }\n                select {\n                    date.toString()\n                }\n              }\n            ---\n            \n            ${'$'}dayNow\n        \"\"\".trimIndent()\n\n        val file = myFixture.addFileToProject(\"sample.shire\", code)\n\n        myFixture.openFileInEditor(file.virtualFile)\n\n        val compile = ShireSyntaxAnalyzer(project, file as ShireFile, myFixture.editor).parseAndExecuteLocalCommand()\n        val hole = compile.config!!\n\n        val results = runBlocking {\n            hole.variables.mapValues {\n                PatternActionProcessor(project, hole, mutableMapOf()).execute(it.value)\n            }\n        }\n\n        val nowDate = results[\"dayNow\"]\n        TestCase.assertTrue(nowDate!!.startsWith(\"ShireDate(date=\"))\n    }\n}\n"
  },
  {
    "path": "shirelang/src/test/kotlin/com/phodal/shirelang/compiler/hobbit/execute/CrawlProcessorTest.kt",
    "content": "package com.phodal.shirelang.compiler.hobbit.execute\n\nimport com.intellij.testFramework.fixtures.BasePlatformTestCase\nimport com.phodal.shirelang.compiler.execute.processor.CrawlProcessor\n\nclass CrawlProcessorTest : BasePlatformTestCase() {\n    fun testShouldParseLink() {\n        val urls = arrayOf(\"https://shire.phodal.com/\")\n        val results = CrawlProcessor.execute(urls)\n        assertEquals(results.size, 1)\n    }\n}\n"
  },
  {
    "path": "shirelang/src/test/kotlin/com/phodal/shirelang/compiler/hobbit/execute/JsonPathProcessorTest.kt",
    "content": "package com.phodal.shirelang.compiler.hobbit.execute\n\nimport com.intellij.testFramework.fixtures.BasePlatformTestCase\nimport com.phodal.shirelang.compiler.ast.patternaction.PatternActionFunc\nimport com.phodal.shirelang.compiler.execute.processor.JsonPathProcessor\nimport junit.framework.TestCase.assertEquals\nimport junit.framework.TestCase.assertNull\nimport org.junit.Test\n\nclass JsonPathProcessorTest : BasePlatformTestCase() {\n\n    @Test\n    fun testShouldParseJsonStringWithValidJsonPath() {\n        // given\n        val jsonStr = \"{\\\"key\\\":\\\"value\\\"}\"\n        val action = PatternActionFunc.JsonPath(null, \"key\")\n\n        // when\n        val result = JsonPathProcessor.execute(project, jsonStr, action)\n\n        // then\n        assertEquals(\"value\", result)\n    }\n\n    @Test\n    fun testShouldReturnNullWhenJsonPathDoesNotExistInJsonString() {\n        // given\n        val jsonStr = \"{\\\"key\\\":\\\"value\\\"}\"\n        val action = PatternActionFunc.JsonPath(null, \"invalidKey\")\n\n        // when\n        val result = JsonPathProcessor.execute(project, jsonStr, action)\n\n        // then\n        assertNull(result)\n    }\n\n    @Test\n    fun testShouldParseSseResultWithValidJsonPath() {\n        // given\n        val sseInput =\n            \"data: {\\\"event\\\":\\\"agent_message\\\",\\\"conversation_id\\\":\\\"48929266-a58f-46cc-a5eb-33145e6a96ef\\\",\\\"message_id\\\":\\\"91ad550b-1109-4062-88f8-07be18238e0e\\\",\\\"created_at\\\":1725437154,\\\"task_id\\\":\\\"4f846104-8571-42f1-b04c-f6f034b2fe9e\\\",\\\"id\\\":\\\"91ad550b-1109-4062-88f8-07be18238e0e\\\",\\\"answer\\\":\\\"The\\\"}\\n\"\n        val jsonPath = \"answer\"\n\n        // when\n        val result = JsonPathProcessor.parseSSEResult(sseInput, jsonPath)\n\n        // then\n        assertEquals(\"The\", result)\n    }\n\n    @Test\n    fun testShouldReturnEmptyStringWhenJsonPathDoesNotExistInSseResult() {\n        // given\n        val sseInput =\n            \"data: {\\\"event\\\":\\\"agent_message\\\",\\\"conversation_id\\\":\\\"48929266-a58f-46cc-a5eb-33145e6a96ef\\\",\\\"message_id\\\":\\\"91ad550b-1109-4062-88f8-07be18238e0e\\\",\\\"created_at\\\":1725437154,\\\"task_id\\\":\\\"4f846104-8571-42f1-b04c-f6f034b2fe9e\\\",\\\"id\\\":\\\"91ad550b-1109-4062-88f8-07be18238e0e\\\",\\\"answer\\\":\\\"The\\\"}\\n\"\n        val jsonPath = \"invalidKey\"\n\n        // when\n        val result = JsonPathProcessor.parseSSEResult(sseInput, jsonPath)\n\n        // then\n        assertEquals(\"\", result)\n    }\n\n    @Test\n    fun testShouldParseMultipleSseDataLinesWithValidJsonPath() {\n        // given\n        val sseInput =\n            \"data: {\\\"event\\\":\\\"agent_message\\\",\\\"conversation_id\\\":\\\"48929266-a58f-46cc-a5eb-33145e6a96ef\\\",\\\"message_id\\\":\\\"91ad550b-1109-4062-88f8-07be18238e0e\\\",\\\"created_at\\\":1725437154,\\\"task_id\\\":\\\"4f846104-8571-42f1-b04c-f6f034b2fe9e\\\",\\\"id\\\":\\\"91ad550b-1109-4062-88f8-07be18238e0e\\\",\\\"answer\\\":\\\"The\\\"}\\n\" +\n                    \"data: {\\\"event\\\":\\\"message_end\\\",\\\"conversation_id\\\":\\\"48929266-a58f-46cc-a5eb-33145e6a96ef\\\",\\\"message_id\\\":\\\"91ad550b-1109-4062-88f8-07be18238e0e\\\",\\\"created_at\\\":1725437154,\\\"task_id\\\":\\\"4f846104-8571-42f1-b04c-f6f034b2fe9e\\\",\\\"id\\\":\\\"91ad550b-1109-4062-88f8-07be18238e0e\\\"}\\n\"\n        val jsonPath = \"$.event\"\n\n        // when\n        val result = JsonPathProcessor.parseSSEResult(sseInput, jsonPath)\n\n        // then\n        assertEquals(\"agent_messagemessage_end\", result)\n    }\n}\n"
  },
  {
    "path": "shirelang/src/test/kotlin/com/phodal/shirelang/compiler/hobbit/execute/ShireShellRunnerTest.kt",
    "content": "package com.phodal.shirelang.compiler.hobbit.execute\n\nimport com.intellij.testFramework.fixtures.BasePlatformTestCase\nimport com.phodal.shirelang.compiler.execute.processor.shell.ShireShellCommandRunner\nimport org.intellij.lang.annotations.Language\n\nclass ShireShellRunnerTest: BasePlatformTestCase() {\n    fun testFill() {\n        @Language(\"JSON\")\n        val jsonEnv = \"\"\"\n            {\n              \"development\": {\n                \"name\": \"Phodal\"\n              }\n            }\n             \"\"\".trimIndent()\n\n        myFixture.addFileToProject(\"demo.shireEnv.json\", jsonEnv)\n\n        @Language(\"Shell Script\")\n        val content = \"\"\"\n            echo \"Hello ${'$'}{name}, my name is ${'$'}{myName}!\"\n        \"\"\".trimIndent()\n\n        val file = myFixture.addFileToProject(\"demo.seh\", content)\n\n        val fill = ShireShellCommandRunner.fill(\n            project, file.virtualFile, mapOf(\n                \"myName\" to \"Shire\"\n            )\n        )\n\n        assertEquals(\"echo \\\"Hello Phodal, my name is Shire!\\\"\", fill)\n    }\n}\n"
  },
  {
    "path": "shirelang/src/test/kotlin/com/phodal/shirelang/compiler/parser/HobbitHoleParserTest.kt",
    "content": "package com.phodal.shirelang.compiler.parser\n\nimport com.intellij.testFramework.fixtures.BasePlatformTestCase\nimport com.phodal.shirelang.psi.ShireFile\nimport org.intellij.lang.annotations.Language\n\nclass HobbitHoleParserTest : BasePlatformTestCase() {\n    fun testShouldParseFunctions() {\n        @Language(\"Shire\")\n        val code = \"\"\"\n           ---\n           functions:\n             normal: \"defaultOutput.py\"(string)\n             output: \"multipleOutput.py\"(string) -> content, size\n             special: \"accessFunctionIfSupport.py\"::resize(string, number, number) -> image\n           ---\n        \"\"\".trimIndent()\n\n        val file = myFixture.configureByText(\"test.shire\", code)\n        val hobbitHole = HobbitHoleParser.parse(file as ShireFile)!!\n\n        assertEquals(3, hobbitHole.foreignFunctions.size)\n        val firstFunc = hobbitHole.foreignFunctions[\"normal\"]!!\n        assertEquals(\"normal\", firstFunc.funcName)\n        assertEquals(\"defaultOutput.py\", firstFunc.funcPath)\n\n        val secondFunc = hobbitHole.foreignFunctions[\"output\"]!!\n        assertEquals(\"output\", secondFunc.funcName)\n        assertEquals(\"multipleOutput.py\", secondFunc.funcPath)\n        assertEquals(listOf(\"content\", \"size\"), secondFunc.returnVars.keys.toList())\n\n        val thirdFunc = hobbitHole.foreignFunctions[\"special\"]!!\n        assertEquals(\"special\", thirdFunc.funcName)\n        assertEquals(\"accessFunctionIfSupport.py\", thirdFunc.funcPath)\n        assertEquals(listOf(\"image\"), thirdFunc.returnVars.keys.toList())\n        assertEquals(listOf(\"string\", \"number\", \"number\"), thirdFunc.inputTypes)\n    }\n}"
  },
  {
    "path": "shirelang/src/test/kotlin/com/phodal/shirelang/impl/DefaultShireSymbolProvider.kt",
    "content": "package com.phodal.shirelang.impl\n\nimport com.intellij.codeInsight.completion.CompletionParameters\nimport com.intellij.codeInsight.completion.CompletionResultSet\nimport com.intellij.codeInsight.lookup.LookupElement\nimport com.intellij.openapi.fileTypes.PlainTextFileType\nimport com.intellij.openapi.project.Project\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.PsiManager\nimport com.intellij.psi.PsiNamedElement\nimport com.intellij.psi.search.FileTypeIndex\nimport com.intellij.psi.search.ProjectScope\nimport com.phodal.shirecore.provider.shire.ShireSymbolProvider\n\nclass DefaultShireSymbolProvider : ShireSymbolProvider {\n    override val language: String = \"Default\"\n\n    override fun lookupSymbol(\n        project: Project,\n        parameters: CompletionParameters,\n        result: CompletionResultSet,\n    ): List<LookupElement> {\n        return emptyList()\n    }\n\n    override fun lookupElementByName(project: Project, name: String): List<PsiElement>? {\n        val searchScope = ProjectScope.getProjectScope(project)\n        val virtualFiles = FileTypeIndex.getFiles(PlainTextFileType.INSTANCE, searchScope)\n\n        return when (name) {\n            \"String\" -> {\n                emptyList()\n            }\n\n            \"File\" -> {\n                virtualFiles.mapNotNull { PsiManager.getInstance(project).findFile(it) }.toList()\n            }\n\n            else -> {\n                emptyList()\n            }\n        }\n    }\n\n    override fun resolveSymbol(project: Project, symbol: String): List<PsiNamedElement> {\n        return emptyList()\n    }\n}"
  },
  {
    "path": "shirelang/src/test/kotlin/com/phodal/shirelang/regression/ShireCompileTest.kt",
    "content": "package com.phodal.shirelang.regression\n\nimport com.intellij.testFramework.fixtures.BasePlatformTestCase\nimport com.phodal.shirecore.middleware.post.PostProcessorContext\nimport com.phodal.shirelang.compiler.parser.ShireSyntaxAnalyzer\nimport com.phodal.shirelang.compiler.template.ShireVariableTemplateCompiler\nimport com.phodal.shirelang.psi.ShireFile\nimport kotlinx.coroutines.runBlocking\nimport org.intellij.lang.annotations.Language\n\nclass ShireCompileTest : BasePlatformTestCase() {\n    val javaHelloController = \"\"\"\n            package com.phodal.shirelang.controller;\n            \n            import org.springframework.web.bind.annotation.GetMapping;\n            import org.springframework.web.bind.annotation.RestController;\n            \n            @RestController\n            public class HelloController {\n                @GetMapping(\"/hello\")\n                public String hello() {\n                    return \"Hello, World!\";\n                }\n            }\n        \"\"\".trimIndent()\n\n    val javaHelloEntity = \"\"\"\n            package com.phodal.shirelang.entity;\n            \n            public class HelloEntity {\n                private String name;\n            \n                public String getName() {\n                    return name;\n                }\n            \n                public void setName(String name) {\n                    this.name = name;\n                }\n            }\n        \"\"\".trimIndent()\n\n    fun testShouldReturnControllerCodeWithFindCat() {\n        myFixture.addFileToProject(\n            \"src/main/java/com/phodal/shirelang/controller/HelloController.java\",\n            javaHelloController\n        )\n        myFixture.addFileToProject(\"src/main/java/com/phodal/shirelang/entity/HelloEntity.java\", javaHelloEntity)\n\n        @Language(\"Shire\")\n        val code = \"\"\"\n            ---\n            name: \"类图分析\"\n            variables:\n              \"controllers\": /.*.java/ { find(\"Controller\") | grep(\"src/main/java/.*\")  | cat }\n              \"outputFile\": /any/ { print(\"name.adl\") }\n            onStreamingEnd: { parseCode | saveFile(${'$'}outputFile) }\n            ---\n            \n            \n            请将下列信息原样输出，不要添加任何其他描述信息：\n            \n            ${'$'}controllers\n        \"\"\".trimIndent()\n\n        val file = myFixture.configureByText(\"test.shire\", code)\n        val compile = ShireSyntaxAnalyzer(project, file as ShireFile, myFixture.editor).parseAndExecuteLocalCommand()\n        val hole = compile.config!!\n\n        val context = PostProcessorContext(\n            genText = \"User prompt:\\n\\n\",\n        )\n\n        runBlocking {\n            val templateCompiler = ShireVariableTemplateCompiler(project, hole, compile.variableTable, code, myFixture.editor)\n            val compiledVariables =\n                templateCompiler.compileVariable(myFixture.editor, mutableMapOf())\n\n            context.compiledVariables = compiledVariables\n        }\n\n        assertEquals(\n            \"\"\"package com.phodal.shirelang.controller;\n\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\npublic class HelloController {\n    @GetMapping(\"/hello\")\n    public String hello() {\n        return \"Hello, World!\";\n    }\n}\"\"\", context.compiledVariables[\"controllers\"]\n        )\n    }\n\n    fun testShouldReturnControllerCodeWithFindCatWithHead() {\n        myFixture.addFileToProject(\n            \"src/main/java/com/phodal/shirelang/controller/HelloController.java\",\n            javaHelloController\n        )\n        myFixture.addFileToProject(\"src/main/java/com/phodal/shirelang/entity/HelloEntity.java\", javaHelloEntity)\n\n        @Language(\"Shire\")\n        val code = \"\"\"\n            ---\n            name: \"类图分析\"\n            variables:\n              \"output\": \"name.adl\"\n              \"con\": /.*.java/ { print | head(1)}\n              \"controllers\": /.*.java/ { find(\"Controller\") | grep(\"src/main/java/.*\") | head(1)  | cat   }\n              \"outputFile\": /any/ { print(\"name.adl\") }\n            onStreamingEnd: { parseCode | saveFile(${'$'}outputFile) }\n            \n            ---\n            \n            下面是你要执行转换的数据：\n            ${'$'}controllers\n        \"\"\".trimIndent()\n\n        val file = myFixture.configureByText(\"test.shire\", code)\n        val compile = ShireSyntaxAnalyzer(project, file as ShireFile, myFixture.editor).parseAndExecuteLocalCommand()\n        val hole = compile.config!!\n\n        val context = PostProcessorContext(\n            genText = \"User prompt:\\n\\n\",\n        )\n\n        runBlocking {\n            val templateCompiler = ShireVariableTemplateCompiler(project, hole, compile.variableTable, code, myFixture.editor)\n            val compiledVariables =\n                templateCompiler.compileVariable(myFixture.editor, mutableMapOf())\n\n            context.compiledVariables = compiledVariables\n        }\n\n        assertEquals(\n            \"\"\"/src/src/main/java/com/phodal/shirelang/entity/HelloEntity.java\"\"\",\n            context.compiledVariables[\"con\"]\n        )\n        assertEquals(\"package com.phodal.shirelang.controller;\\n\" +\n                \"\\n\" +\n                \"import org.springframework.web.bind.annotation.GetMapping;\\n\" +\n                \"import org.springframework.web.bind.annotation.RestController;\\n\" +\n                \"\\n\" +\n                \"@RestController\\n\" +\n                \"public class HelloController {\\n\" +\n                \"    @GetMapping(\\\"/hello\\\")\\n\" +\n                \"    public String hello() {\\n\" +\n                \"        return \\\"Hello, World!\\\";\\n\" +\n                \"    }\\n\" +\n                \"}\", context.compiledVariables[\"controllers\"])\n    }\n\n//    fun testShouldCompileMarkdownHeader() {\n//\n//        @Language(\"Shire\")\n//        val code = \"\"\"\n//            ## Header\n//\n//            Body\n//        \"\"\".trimIndent()\n//\n//        val p: Properties = Properties()\n//        p.setProperty(\"resource.loader\", \"class\")\n//        p.setProperty(\"class.resource.loader.description\", \"Velocity Classpath Resource Loader\")\n//        p.setProperty(\n//            \"class.resource.loader.class\",\n//            \"org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader\"\n//        )\n//        try {\n//            Velocity.init(p);\n//        } catch (e: Exception) {\n////            e.printStackTrace()\n//        }\n//\n//        val file = myFixture.configureByText(\"test.shire\", code)\n//        val compile = ShireSyntaxAnalyzer(project, file as ShireFile, myFixture.editor).parse()\n//\n//        val result = runBlocking {\n//            val templateCompiler = ShireTemplateCompiler(project, compile.config, compile.variableTable, code)\n//            templateCompiler.compile()\n//        }\n//\n//        assertEquals(\"\", result)\n//    }\n}\n"
  },
  {
    "path": "shirelang/src/test/kotlin/com/phodal/shirelang/regression/ShireTokenizerTest.kt",
    "content": "package com.phodal.shirelang.regression\n\nimport com.intellij.testFramework.fixtures.BasePlatformTestCase\nimport com.phodal.shirecore.middleware.post.PostProcessorContext\nimport com.phodal.shirelang.compiler.parser.ShireSyntaxAnalyzer\nimport com.phodal.shirelang.compiler.template.ShireVariableTemplateCompiler\nimport com.phodal.shirelang.psi.ShireFile\nimport kotlinx.coroutines.runBlocking\nimport org.intellij.lang.annotations.Language\n\nclass ShireTokenizerTest : BasePlatformTestCase() {\n    val javaHelloController = \"\"\"\n            package com.phodal.shirelang.controller;\n            \n            import org.springframework.web.bind.annotation.GetMapping;\n            import org.springframework.web.bind.annotation.RestController;\n            \n            @RestController\n            public class HelloController {\n                @GetMapping(\"/hello\")\n                public String hello() {\n                    return \"Hello, World!\";\n                }\n            }\n        \"\"\".trimIndent()\n\n    fun testShouldReturnControllerCodeWithFindCat() {\n        myFixture.addFileToProject(\n            \"HelloController.java\",\n            javaHelloController\n        )\n\n        @Language(\"Shire\")\n        val code = \"\"\"\n            ---\n            name: \"类图分析\"\n            variables:\n              \"controllers\": /.*.java/ { cat }\n              \"tokens\": /any/ { tokenizer(${'$'}controllers, \"word\") }\n              \"chinese\": /any/ { tokenizer(\"孩子上了幼儿园 安全防拐教育要做好\", \"jieba\") }\n            ---\n            \n            ${'$'}controllers\n        \"\"\".trimIndent()\n\n        val file = myFixture.configureByText(\"test.shire\", code)\n        val compile = ShireSyntaxAnalyzer(project, file as ShireFile, myFixture.editor).parseAndExecuteLocalCommand()\n        val hole = compile.config!!\n\n        val context = PostProcessorContext(\n            genText = \"User prompt:\\n\\n\",\n        )\n\n        runBlocking {\n            val templateCompiler = ShireVariableTemplateCompiler(project, hole, compile.variableTable, code, myFixture.editor)\n            val compiledVariables =\n                templateCompiler.compileVariable(myFixture.editor, mutableMapOf())\n\n            context.compiledVariables = compiledVariables\n        }\n\n        assertEquals(\n            \"\"\"[package, com, phodal, shirelang, controller, import, org, springframework, web, bind, annotation, GetMapping, RestController, public, class, HelloController, hello, String, return, Hello, World]\"\"\",\n            context.compiledVariables[\"tokens\"]\n        )\n        assertEquals(\n            listOf(\"孩子\", \"上\", \"了\", \"幼儿园\",  \"安全\", \"防拐\", \"教育\", \"要\", \"做好\").toString(),\n            context.compiledVariables[\"chinese\"].toString()\n        )\n    }\n}\n"
  },
  {
    "path": "shirelang/src/test/kotlin/com/phodal/shirelang/run/ShireConfigurationTest.kt",
    "content": "package com.phodal.shirelang.run\n\nimport com.phodal.shirelang.run.ShireConfiguration.Companion.mapStringToMap\nimport junit.framework.TestCase.assertEquals\nimport org.junit.Test\n\nclass ShireConfigurationCompanionTest {\n\n    @Test\n    fun should_mapSimpleStringToMap_correctly() {\n        // given\n        val varMapString = \"{key1=value1, key2=value2}\"\n\n        // when\n        val result = mapStringToMap(varMapString)\n\n        // then\n        assertEquals(mapOf(\"key1\" to \"value1\", \"key2\" to \"value2\"), result)\n    }\n\n    @Test\n    fun should_mapEmptyStringToEmptyMap() {\n        // given\n        val varMapString = \"{}\"\n\n        // when\n        val result = mapStringToMap(varMapString)\n\n        // then\n        assertEquals(emptyMap<String, String>(), result)\n    }\n\n    @Test\n    fun should_handleMapWithMultipleValuesPerKey_correctly() {\n        // given\n        val varMapString = \"{key1=value1, key1=value2}\"\n\n        // when\n        val result = mapStringToMap(varMapString)\n\n        // then\n        assertEquals(mapOf(\"key1\" to \"value2\"), result)\n        // Note: As per current implementation, the last value for a key will replace the previous values.\n    }\n\n    @Test\n    fun shouldTransformFromMapAndToString() {\n        // given\n        val varMap = mapOf(\"key1\" to \"value1\", \"key2\" to \"value2\")\n\n        // when\n        val result = mapStringToMap(varMap.toString())\n\n        // then\n        assertEquals(varMap, result)\n    }\n}\n"
  },
  {
    "path": "shirelang/src/test/resources/META-INF/plugin.xml",
    "content": "<idea-plugin package=\"com.phodal\" xmlns:xi=\"http://www.w3.org/2001/XInclude\">\n    <id>com.phodal.shire</id>\n\n    <xi:include href=\"/com.phodal.shirecore.xml\" xpointer=\"xpointer(/idea-plugin/*)\"/>\n    <xi:include href=\"/com.phodal.shire.json.xml\" xpointer=\"xpointer(/idea-plugin/*)\"/>\n\n    <!--suppress PluginXmlValidity -->\n    <content>\n        <module name=\"com.phodal.shirelang\"/>\n    </content>\n\n    <extensions defaultExtensionNs=\"com.phodal\">\n        <shireSymbolProvider implementation=\"com.phodal.shirelang.impl.DefaultShireSymbolProvider\" />\n    </extensions>\n</idea-plugin>\n"
  },
  {
    "path": "shirelang/src/test/testData/parser/AfterStream.shire",
    "content": "---\nafterStreaming: {\n    condition {\n      \"variable-success\" { $selection.length > 1 }\n      \"jsonpath-success\" { jsonpath(\"/bookstore/book[price>35]\") }\n    }\n    case condition {\n      \"variable-sucesss\" { done }\n      \"jsonpath-success\" { task() }\n      default { task() }\n    }\n  }\n---\n"
  },
  {
    "path": "shirelang/src/test/testData/parser/AfterStream.txt",
    "content": "ShireFile\n  ShireFrontMatterHeaderImpl(FRONT_MATTER_HEADER)\n    PsiElement(ShireTokenType.FRONTMATTER_START)('---')\n    PsiElement(ShireTokenType.NEWLINE)('\\n')\n    ShireFrontMatterEntriesImpl(FRONT_MATTER_ENTRIES)\n      ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n        ShireLifecycleIdImpl(LIFECYCLE_ID)\n          PsiElement(ShireTokenType.afterStreaming)('afterStreaming')\n        PsiElement(ShireTokenType.COLON)(':')\n        PsiWhiteSpace(' ')\n        ShireFunctionStatementImpl(FUNCTION_STATEMENT)\n          PsiElement(ShireTokenType.{)('{')\n          PsiElement(ShireTokenType.NEWLINE)('\\n')\n          PsiWhiteSpace('    ')\n          ShireFunctionBodyImpl(FUNCTION_BODY)\n            ShireActionBodyImpl(ACTION_BODY)\n              ShireActionExprImpl(ACTION_EXPR)\n                ShireCaseBodyImpl(CASE_BODY)\n                  ShireConditionFlagImpl(CONDITION_FLAG)\n                    PsiElement(ShireTokenType.condition)('condition')\n                    PsiWhiteSpace(' ')\n                    PsiElement(ShireTokenType.{)('{')\n                    PsiElement(ShireTokenType.NEWLINE)('\\n')\n                    PsiWhiteSpace('      ')\n                    ShireConditionStatementImpl(CONDITION_STATEMENT)\n                      ShireCaseConditionImpl(CASE_CONDITION)\n                        PsiElement(ShireTokenType.QUOTE_STRING)('\"variable-success\"')\n                      PsiWhiteSpace(' ')\n                      PsiElement(ShireTokenType.{)('{')\n                      PsiWhiteSpace(' ')\n                      ShireIneqComparisonExprImpl(INEQ_COMPARISON_EXPR)\n                        ShireRefExprImpl(REF_EXPR)\n                          ShireLiteralExprImpl(LITERAL_EXPR)\n                            PsiElement(VARIABLE_START)('$')\n                            PsiElement(ShireTokenType.IDENTIFIER)('selection')\n                          PsiElement(ShireTokenType..)('.')\n                          PsiElement(ShireTokenType.IDENTIFIER)('length')\n                        PsiWhiteSpace(' ')\n                        ShireIneqComparisonOpImpl(INEQ_COMPARISON_OP)\n                          PsiElement(ShireTokenType.>)('>')\n                        PsiWhiteSpace(' ')\n                        ShireLiteralExprImpl(LITERAL_EXPR)\n                          PsiElement(ShireTokenType.NUMBER)('1')\n                      PsiWhiteSpace(' ')\n                      PsiElement(ShireTokenType.})('}')\n                      PsiElement(ShireTokenType.NEWLINE)('\\n')\n                    PsiWhiteSpace('      ')\n                    ShireConditionStatementImpl(CONDITION_STATEMENT)\n                      ShireCaseConditionImpl(CASE_CONDITION)\n                        PsiElement(ShireTokenType.QUOTE_STRING)('\"jsonpath-success\"')\n                      PsiWhiteSpace(' ')\n                      PsiElement(ShireTokenType.{)('{')\n                      PsiWhiteSpace(' ')\n                      ShireCallExprImpl(CALL_EXPR)\n                        ShireRefExprImpl(REF_EXPR)\n                          PsiElement(ShireTokenType.IDENTIFIER)('jsonpath')\n                        PsiElement(ShireTokenType.()('(')\n                        ShireExpressionListImpl(EXPRESSION_LIST)\n                          ShireLiteralExprImpl(LITERAL_EXPR)\n                            PsiElement(ShireTokenType.QUOTE_STRING)('\"/bookstore/book[price>35]\"')\n                        PsiElement(ShireTokenType.))(')')\n                      PsiWhiteSpace(' ')\n                      PsiElement(ShireTokenType.})('}')\n                      PsiElement(ShireTokenType.NEWLINE)('\\n')\n                    PsiWhiteSpace('    ')\n                    PsiElement(ShireTokenType.})('}')\n                    PsiElement(ShireTokenType.NEWLINE)('\\n')\n                  PsiWhiteSpace('    ')\n                  PsiElement(ShireTokenType.case)('case')\n                  PsiWhiteSpace(' ')\n                  PsiElement(ShireTokenType.condition)('condition')\n                  PsiWhiteSpace(' ')\n                  PsiElement(ShireTokenType.{)('{')\n                  PsiElement(ShireTokenType.NEWLINE)('\\n')\n                  PsiWhiteSpace('      ')\n                  ShireCasePatternActionImpl(CASE_PATTERN_ACTION)\n                    ShireCaseConditionImpl(CASE_CONDITION)\n                      PsiElement(ShireTokenType.QUOTE_STRING)('\"variable-sucesss\"')\n                    PsiWhiteSpace(' ')\n                    PsiElement(ShireTokenType.{)('{')\n                    PsiWhiteSpace(' ')\n                    ShireActionBodyImpl(ACTION_BODY)\n                      ShireActionExprImpl(ACTION_EXPR)\n                        ShireFuncCallImpl(FUNC_CALL)\n                          ShireFuncNameImpl(FUNC_NAME)\n                            PsiElement(ShireTokenType.IDENTIFIER)('done')\n                    PsiWhiteSpace(' ')\n                    PsiElement(ShireTokenType.})('}')\n                    PsiElement(ShireTokenType.NEWLINE)('\\n')\n                  PsiWhiteSpace('      ')\n                  ShireCasePatternActionImpl(CASE_PATTERN_ACTION)\n                    ShireCaseConditionImpl(CASE_CONDITION)\n                      PsiElement(ShireTokenType.QUOTE_STRING)('\"jsonpath-success\"')\n                    PsiWhiteSpace(' ')\n                    PsiElement(ShireTokenType.{)('{')\n                    PsiWhiteSpace(' ')\n                    ShireActionBodyImpl(ACTION_BODY)\n                      ShireActionExprImpl(ACTION_EXPR)\n                        ShireFuncCallImpl(FUNC_CALL)\n                          ShireFuncNameImpl(FUNC_NAME)\n                            PsiElement(ShireTokenType.IDENTIFIER)('task')\n                          PsiElement(ShireTokenType.()('(')\n                          ShirePipelineArgsImpl(PIPELINE_ARGS)\n                            <empty list>\n                          PsiElement(ShireTokenType.))(')')\n                    PsiWhiteSpace(' ')\n                    PsiElement(ShireTokenType.})('}')\n                    PsiElement(ShireTokenType.NEWLINE)('\\n')\n                  PsiWhiteSpace('      ')\n                  ShireCasePatternActionImpl(CASE_PATTERN_ACTION)\n                    ShireCaseConditionImpl(CASE_CONDITION)\n                      PsiElement(ShireTokenType.default)('default')\n                    PsiWhiteSpace(' ')\n                    PsiElement(ShireTokenType.{)('{')\n                    PsiWhiteSpace(' ')\n                    ShireActionBodyImpl(ACTION_BODY)\n                      ShireActionExprImpl(ACTION_EXPR)\n                        ShireFuncCallImpl(FUNC_CALL)\n                          ShireFuncNameImpl(FUNC_NAME)\n                            PsiElement(ShireTokenType.IDENTIFIER)('task')\n                          PsiElement(ShireTokenType.()('(')\n                          ShirePipelineArgsImpl(PIPELINE_ARGS)\n                            <empty list>\n                          PsiElement(ShireTokenType.))(')')\n                    PsiWhiteSpace(' ')\n                    PsiElement(ShireTokenType.})('}')\n                    PsiElement(ShireTokenType.NEWLINE)('\\n')\n                  PsiWhiteSpace('    ')\n                  PsiElement(ShireTokenType.})('}')\n                  PsiElement(ShireTokenType.NEWLINE)('\\n')\n          PsiWhiteSpace('  ')\n          PsiElement(ShireTokenType.})('}')\n          PsiElement(ShireTokenType.NEWLINE)('\\n')\n    PsiElement(ShireTokenType.FRONTMATTER_END)('---')"
  },
  {
    "path": "shirelang/src/test/testData/parser/AutoCommand.shire",
    "content": "/write:Sample.file#L1-L12"
  },
  {
    "path": "shirelang/src/test/testData/parser/AutoCommand.txt",
    "content": "ShireFile\n  ShireUsedImpl(USED)\n    ShireCommandStartImpl(COMMAND_START)\n      PsiElement(COMMAND_START)('/')\n    ShireCommandIdImpl(COMMAND_ID)\n      PsiElement(ShireTokenType.IDENTIFIER)('write')\n    PsiElement(ShireTokenType.COLON)(':')\n    PsiElement(ShireTokenType.COMMAND_PROP)('Sample.file')\n    PsiElement(ShireTokenType.#)('#')\n    PsiElement(ShireTokenType.LINE_INFO)('L1-L12')"
  },
  {
    "path": "shirelang/src/test/testData/parser/AutoRefactor.shire",
    "content": "/refactor:rename com.phodal.shirelang.run.ShireProgramRunner to com.phodal.shirelang.run.ShireProgramRunnerImpl\n/refactor:safeDelete com.phodal.shirelang.run.ShireProgramRunnerImpl\n/refactor:delete com.phodal.shirelang.run.ShireProgramRunnerImpl\n/refactor:move com.phodal.shirelang.ShireProgramRunner to com.phodal.shirelang.run.ShireProgramRunner"
  },
  {
    "path": "shirelang/src/test/testData/parser/AutoRefactor.txt",
    "content": "ShireFile\n  ShireUsedImpl(USED)\n    ShireCommandStartImpl(COMMAND_START)\n      PsiElement(COMMAND_START)('/')\n    ShireCommandIdImpl(COMMAND_ID)\n      PsiElement(ShireTokenType.IDENTIFIER)('refactor')\n    PsiElement(ShireTokenType.COLON)(':')\n    PsiElement(ShireTokenType.COMMAND_PROP)('rename')\n  PsiElement(ShireTokenType.TEXT_SEGMENT)(' com.phodal.shirelang.run.ShireProgramRunner to com.phodal.shirelang.run.ShireProgramRunnerImpl')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  ShireUsedImpl(USED)\n    ShireCommandStartImpl(COMMAND_START)\n      PsiElement(COMMAND_START)('/')\n    ShireCommandIdImpl(COMMAND_ID)\n      PsiElement(ShireTokenType.IDENTIFIER)('refactor')\n    PsiElement(ShireTokenType.COLON)(':')\n    PsiElement(ShireTokenType.COMMAND_PROP)('safeDelete')\n  PsiElement(ShireTokenType.TEXT_SEGMENT)(' com.phodal.shirelang.run.ShireProgramRunnerImpl')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  ShireUsedImpl(USED)\n    ShireCommandStartImpl(COMMAND_START)\n      PsiElement(COMMAND_START)('/')\n    ShireCommandIdImpl(COMMAND_ID)\n      PsiElement(ShireTokenType.IDENTIFIER)('refactor')\n    PsiElement(ShireTokenType.COLON)(':')\n    PsiElement(ShireTokenType.COMMAND_PROP)('delete')\n  PsiElement(ShireTokenType.TEXT_SEGMENT)(' com.phodal.shirelang.run.ShireProgramRunnerImpl')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  ShireUsedImpl(USED)\n    ShireCommandStartImpl(COMMAND_START)\n      PsiElement(COMMAND_START)('/')\n    ShireCommandIdImpl(COMMAND_ID)\n      PsiElement(ShireTokenType.IDENTIFIER)('refactor')\n    PsiElement(ShireTokenType.COLON)(':')\n    PsiElement(ShireTokenType.COMMAND_PROP)('move')\n  PsiElement(ShireTokenType.TEXT_SEGMENT)(' com.phodal.shirelang.ShireProgramRunner to com.phodal.shirelang.run.ShireProgramRunner')"
  },
  {
    "path": "shirelang/src/test/testData/parser/BasicTest.shire",
    "content": "你好 @hello-world sm\n解释一下代码\n$selection 表示选择的内容\n@agent-name 调用特定的 agent\n/file:Sample.file 从文件中读取内容\n/rev:632372da 从版本库中读取内容"
  },
  {
    "path": "shirelang/src/test/testData/parser/BasicTest.txt",
    "content": "ShireFile\n  PsiElement(ShireTokenType.TEXT_SEGMENT)('你好 ')\n  ShireUsedImpl(USED)\n    ShireAgentStartImpl(AGENT_START)\n      PsiElement(AGENT_START)('@')\n    ShireAgentIdImpl(AGENT_ID)\n      PsiElement(ShireTokenType.IDENTIFIER)('hello-world')\n  PsiElement(ShireTokenType.TEXT_SEGMENT)(' sm')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.TEXT_SEGMENT)('解释一下代码')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  ShireUsedImpl(USED)\n    ShireVariableStartImpl(VARIABLE_START)\n      PsiElement(VARIABLE_START)('$')\n    ShireVariableIdImpl(VARIABLE_ID)\n      PsiElement(ShireTokenType.IDENTIFIER)('selection')\n  PsiElement(ShireTokenType.TEXT_SEGMENT)(' 表示选择的内容')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  ShireUsedImpl(USED)\n    ShireAgentStartImpl(AGENT_START)\n      PsiElement(AGENT_START)('@')\n    ShireAgentIdImpl(AGENT_ID)\n      PsiElement(ShireTokenType.IDENTIFIER)('agent-name')\n  PsiElement(ShireTokenType.TEXT_SEGMENT)(' 调用特定的 agent')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  ShireUsedImpl(USED)\n    ShireCommandStartImpl(COMMAND_START)\n      PsiElement(COMMAND_START)('/')\n    ShireCommandIdImpl(COMMAND_ID)\n      PsiElement(ShireTokenType.IDENTIFIER)('file')\n    PsiElement(ShireTokenType.COLON)(':')\n    PsiElement(ShireTokenType.COMMAND_PROP)('Sample.file')\n  PsiElement(ShireTokenType.TEXT_SEGMENT)(' 从文件中读取内容')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  ShireUsedImpl(USED)\n    ShireCommandStartImpl(COMMAND_START)\n      PsiElement(COMMAND_START)('/')\n    ShireCommandIdImpl(COMMAND_ID)\n      PsiElement(ShireTokenType.IDENTIFIER)('rev')\n    PsiElement(ShireTokenType.COLON)(':')\n    PsiElement(ShireTokenType.COMMAND_PROP)('632372da')\n  PsiElement(ShireTokenType.TEXT_SEGMENT)(' 从版本库中读取内容')"
  },
  {
    "path": "shirelang/src/test/testData/parser/BlockStartOnly.shire",
    "content": "```"
  },
  {
    "path": "shirelang/src/test/testData/parser/BlockStartOnly.txt",
    "content": "ShireFile\n  CodeBlockElement(CODE)\n    PsiElement(ShireTokenType.CODE_BLOCK_START)('```')\n    ASTWrapperPsiElement(CODE_CONTENTS)\n      <empty list>"
  },
  {
    "path": "shirelang/src/test/testData/parser/BrowseWeb.shire",
    "content": "/browse:https://ide.unitmesh.cc\n/browse:https://www.example.com/page?param1=value1&param2=value2\n/browse:https://www.example.com/page#section1\n/browse:http://localhost:3000/page\n/browse:https://username:password@www.example.com/page"
  },
  {
    "path": "shirelang/src/test/testData/parser/BrowseWeb.txt",
    "content": "ShireFile\n  ShireUsedImpl(USED)\n    ShireCommandStartImpl(COMMAND_START)\n      PsiElement(COMMAND_START)('/')\n    ShireCommandIdImpl(COMMAND_ID)\n      PsiElement(ShireTokenType.IDENTIFIER)('browse')\n    PsiElement(ShireTokenType.COLON)(':')\n    PsiElement(ShireTokenType.COMMAND_PROP)('https://ide.unitmesh.cc')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  ShireUsedImpl(USED)\n    ShireCommandStartImpl(COMMAND_START)\n      PsiElement(COMMAND_START)('/')\n    ShireCommandIdImpl(COMMAND_ID)\n      PsiElement(ShireTokenType.IDENTIFIER)('browse')\n    PsiElement(ShireTokenType.COLON)(':')\n    PsiElement(ShireTokenType.COMMAND_PROP)('https://www.example.com/page?param1=value1&param2=value2')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  ShireUsedImpl(USED)\n    ShireCommandStartImpl(COMMAND_START)\n      PsiElement(COMMAND_START)('/')\n    ShireCommandIdImpl(COMMAND_ID)\n      PsiElement(ShireTokenType.IDENTIFIER)('browse')\n    PsiElement(ShireTokenType.COLON)(':')\n    PsiElement(ShireTokenType.COMMAND_PROP)('https://www.example.com/page#section1')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  ShireUsedImpl(USED)\n    ShireCommandStartImpl(COMMAND_START)\n      PsiElement(COMMAND_START)('/')\n    ShireCommandIdImpl(COMMAND_ID)\n      PsiElement(ShireTokenType.IDENTIFIER)('browse')\n    PsiElement(ShireTokenType.COLON)(':')\n    PsiElement(ShireTokenType.COMMAND_PROP)('http://localhost:3000/page')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  ShireUsedImpl(USED)\n    ShireCommandStartImpl(COMMAND_START)\n      PsiElement(COMMAND_START)('/')\n    ShireCommandIdImpl(COMMAND_ID)\n      PsiElement(ShireTokenType.IDENTIFIER)('browse')\n    PsiElement(ShireTokenType.COLON)(':')\n    PsiElement(ShireTokenType.COMMAND_PROP)('https://username:password@www.example.com/page')"
  },
  {
    "path": "shirelang/src/test/testData/parser/CommandAndSymbol.shire",
    "content": "/explain /symbol:cc.unitmesh.devti#RevProvider.constructor\n\n/refactor /symbol:cc.unitmesh.devti#RevProvider.completions\n\n/write:presentation/VirtualFilePresentation.java#L1-L12\n"
  },
  {
    "path": "shirelang/src/test/testData/parser/CommandAndSymbol.txt",
    "content": "ShireFile\n  ShireUsedImpl(USED)\n    ShireCommandStartImpl(COMMAND_START)\n      PsiElement(COMMAND_START)('/')\n    ShireCommandIdImpl(COMMAND_ID)\n      PsiElement(ShireTokenType.IDENTIFIER)('explain')\n  PsiElement(ShireTokenType.TEXT_SEGMENT)(' ')\n  ShireUsedImpl(USED)\n    ShireCommandStartImpl(COMMAND_START)\n      PsiElement(COMMAND_START)('/')\n    ShireCommandIdImpl(COMMAND_ID)\n      PsiElement(ShireTokenType.IDENTIFIER)('symbol')\n    PsiElement(ShireTokenType.COLON)(':')\n    PsiElement(ShireTokenType.COMMAND_PROP)('cc.unitmesh.devti#RevProvider.constructor')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  ShireUsedImpl(USED)\n    ShireCommandStartImpl(COMMAND_START)\n      PsiElement(COMMAND_START)('/')\n    ShireCommandIdImpl(COMMAND_ID)\n      PsiElement(ShireTokenType.IDENTIFIER)('refactor')\n  PsiElement(ShireTokenType.TEXT_SEGMENT)(' ')\n  ShireUsedImpl(USED)\n    ShireCommandStartImpl(COMMAND_START)\n      PsiElement(COMMAND_START)('/')\n    ShireCommandIdImpl(COMMAND_ID)\n      PsiElement(ShireTokenType.IDENTIFIER)('symbol')\n    PsiElement(ShireTokenType.COLON)(':')\n    PsiElement(ShireTokenType.COMMAND_PROP)('cc.unitmesh.devti#RevProvider.completions')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  ShireUsedImpl(USED)\n    ShireCommandStartImpl(COMMAND_START)\n      PsiElement(COMMAND_START)('/')\n    ShireCommandIdImpl(COMMAND_ID)\n      PsiElement(ShireTokenType.IDENTIFIER)('write')\n    PsiElement(ShireTokenType.COLON)(':')\n    PsiElement(ShireTokenType.COMMAND_PROP)('presentation/VirtualFilePresentation.java')\n    PsiElement(ShireTokenType.#)('#')\n    PsiElement(ShireTokenType.LINE_INFO)('L1-L12')"
  },
  {
    "path": "shirelang/src/test/testData/parser/ComplexLangId.shire",
    "content": "```typescript jsx\nimport { Button } from '@unitmesh-ui/core';\n```\n"
  },
  {
    "path": "shirelang/src/test/testData/parser/ComplexLangId.txt",
    "content": "ShireFile\n  CodeBlockElement(CODE)\n    PsiElement(ShireTokenType.CODE_BLOCK_START)('```')\n    ShireLanguageIdImpl(LANGUAGE_ID)\n      PsiElement(ShireTokenType.LANGUAGE_IDENTIFIER)('typescript jsx')\n    PsiElement(ShireTokenType.NEWLINE)('\\n')\n    ASTWrapperPsiElement(CODE_CONTENTS)\n      PsiElement(ShireTokenType.CODE_CONTENT)('import { Button } from '@unitmesh-ui/core';')\n      PsiElement(ShireTokenType.NEWLINE)('\\n')\n    PsiElement(ShireTokenType.CODE_BLOCK_END)('```')"
  },
  {
    "path": "shirelang/src/test/testData/parser/CustomFunctions.shire",
    "content": "---\nfunctions:\n  aFunc: \"defaultMain.py\"(string)\n  aFunc: \"multipleOutput.py\"(string, number) -> content\n  cFunc: \"accessFunctionIfSupport.py\"::resize(string, number, number) -> image\n---\n"
  },
  {
    "path": "shirelang/src/test/testData/parser/CustomFunctions.txt",
    "content": "ShireFile\n  ShireFrontMatterHeaderImpl(FRONT_MATTER_HEADER)\n    PsiElement(ShireTokenType.FRONTMATTER_START)('---')\n    PsiElement(ShireTokenType.NEWLINE)('\\n')\n    ShireFrontMatterEntriesImpl(FRONT_MATTER_ENTRIES)\n      ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n        ShireFrontMatterKeyImpl(FRONT_MATTER_KEY)\n          PsiElement(ShireTokenType.functions)('functions')\n        PsiElement(ShireTokenType.COLON)(':')\n        ShireFrontMatterValueImpl(FRONT_MATTER_VALUE)\n          PsiElement(ShireTokenType.NEWLINE)('\\n')\n          ShireObjectKeyValueImpl(OBJECT_KEY_VALUE)\n            PsiElement(ShireTokenType.INDENT)('  ')\n            ShireKeyValueImpl(KEY_VALUE)\n              ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n                ShireFrontMatterKeyImpl(FRONT_MATTER_KEY)\n                  ShireFrontMatterIdImpl(FRONT_MATTER_ID)\n                    PsiElement(ShireTokenType.IDENTIFIER)('aFunc')\n                PsiElement(ShireTokenType.COLON)(':')\n                PsiWhiteSpace(' ')\n                ShireForeignFunctionImpl(FOREIGN_FUNCTION)\n                  ShireForeignPathImpl(FOREIGN_PATH)\n                    PsiElement(ShireTokenType.QUOTE_STRING)('\"defaultMain.py\"')\n                  PsiElement(ShireTokenType.()('(')\n                  ShireForeignTypeImpl(FOREIGN_TYPE)\n                    PsiElement(ShireTokenType.IDENTIFIER)('string')\n                  PsiElement(ShireTokenType.))(')')\n                PsiElement(ShireTokenType.NEWLINE)('\\n')\n            PsiElement(ShireTokenType.INDENT)('  ')\n            ShireKeyValueImpl(KEY_VALUE)\n              ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n                ShireFrontMatterKeyImpl(FRONT_MATTER_KEY)\n                  ShireFrontMatterIdImpl(FRONT_MATTER_ID)\n                    PsiElement(ShireTokenType.IDENTIFIER)('aFunc')\n                PsiElement(ShireTokenType.COLON)(':')\n                PsiWhiteSpace(' ')\n                ShireForeignFunctionImpl(FOREIGN_FUNCTION)\n                  ShireForeignPathImpl(FOREIGN_PATH)\n                    PsiElement(ShireTokenType.QUOTE_STRING)('\"multipleOutput.py\"')\n                  PsiElement(ShireTokenType.()('(')\n                  ShireForeignTypeImpl(FOREIGN_TYPE)\n                    PsiElement(ShireTokenType.IDENTIFIER)('string')\n                  PsiElement(ShireTokenType.,)(',')\n                  PsiWhiteSpace(' ')\n                  ShireForeignTypeImpl(FOREIGN_TYPE)\n                    PsiElement(ShireTokenType.IDENTIFIER)('number')\n                  PsiElement(ShireTokenType.))(')')\n                  PsiWhiteSpace(' ')\n                  PsiElement(ShireTokenType.PROCESS)('->')\n                  PsiWhiteSpace(' ')\n                  ShireForeignOutputImpl(FOREIGN_OUTPUT)\n                    ShireOutputVarImpl(OUTPUT_VAR)\n                      PsiElement(ShireTokenType.IDENTIFIER)('content')\n                PsiElement(ShireTokenType.NEWLINE)('\\n')\n            PsiElement(ShireTokenType.INDENT)('  ')\n            ShireKeyValueImpl(KEY_VALUE)\n              ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n                ShireFrontMatterKeyImpl(FRONT_MATTER_KEY)\n                  ShireFrontMatterIdImpl(FRONT_MATTER_ID)\n                    PsiElement(ShireTokenType.IDENTIFIER)('cFunc')\n                PsiElement(ShireTokenType.COLON)(':')\n                PsiWhiteSpace(' ')\n                ShireForeignFunctionImpl(FOREIGN_FUNCTION)\n                  ShireForeignPathImpl(FOREIGN_PATH)\n                    PsiElement(ShireTokenType.QUOTE_STRING)('\"accessFunctionIfSupport.py\"')\n                  PsiElement(ShireTokenType.::)('::')\n                  ShireForeignFuncNameImpl(FOREIGN_FUNC_NAME)\n                    PsiElement(ShireTokenType.IDENTIFIER)('resize')\n                  PsiElement(ShireTokenType.()('(')\n                  ShireForeignTypeImpl(FOREIGN_TYPE)\n                    PsiElement(ShireTokenType.IDENTIFIER)('string')\n                  PsiElement(ShireTokenType.,)(',')\n                  PsiWhiteSpace(' ')\n                  ShireForeignTypeImpl(FOREIGN_TYPE)\n                    PsiElement(ShireTokenType.IDENTIFIER)('number')\n                  PsiElement(ShireTokenType.,)(',')\n                  PsiWhiteSpace(' ')\n                  ShireForeignTypeImpl(FOREIGN_TYPE)\n                    PsiElement(ShireTokenType.IDENTIFIER)('number')\n                  PsiElement(ShireTokenType.))(')')\n                  PsiWhiteSpace(' ')\n                  PsiElement(ShireTokenType.PROCESS)('->')\n                  PsiWhiteSpace(' ')\n                  ShireForeignOutputImpl(FOREIGN_OUTPUT)\n                    ShireOutputVarImpl(OUTPUT_VAR)\n                      PsiElement(ShireTokenType.IDENTIFIER)('image')\n                PsiElement(ShireTokenType.NEWLINE)('\\n')\n    PsiElement(ShireTokenType.FRONTMATTER_END)('---')"
  },
  {
    "path": "shirelang/src/test/testData/parser/EmptyCodeFence.shire",
    "content": "解释如下的代码：\n\n```\nprint(\"Hello, world!\")\n```\n\n请使用 Markdown 语法返回。"
  },
  {
    "path": "shirelang/src/test/testData/parser/EmptyCodeFence.txt",
    "content": "ShireFile\n  PsiElement(ShireTokenType.TEXT_SEGMENT)('解释如下的代码：')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  CodeBlockElement(CODE)\n    PsiElement(ShireTokenType.CODE_BLOCK_START)('```')\n    PsiElement(ShireTokenType.NEWLINE)('\\n')\n    ASTWrapperPsiElement(CODE_CONTENTS)\n      PsiElement(ShireTokenType.CODE_CONTENT)('print(\"Hello, world!\")')\n      PsiElement(ShireTokenType.NEWLINE)('\\n')\n    PsiElement(ShireTokenType.CODE_BLOCK_END)('```')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.TEXT_SEGMENT)('请使用 Markdown 语法返回。')"
  },
  {
    "path": "shirelang/src/test/testData/parser/FrontMatter.shire",
    "content": "---\ntitle: \"Sample Title\"\ndate: 2022-01-01\nauthor: \"John Doe\"\ntags: [markdown, frontmatter]\n---\n"
  },
  {
    "path": "shirelang/src/test/testData/parser/FrontMatter.txt",
    "content": "ShireFile\n  ShireFrontMatterHeaderImpl(FRONT_MATTER_HEADER)\n    PsiElement(ShireTokenType.FRONTMATTER_START)('---')\n    PsiElement(ShireTokenType.NEWLINE)('\\n')\n    ShireFrontMatterEntriesImpl(FRONT_MATTER_ENTRIES)\n      ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n        ShireFrontMatterKeyImpl(FRONT_MATTER_KEY)\n          ShireFrontMatterIdImpl(FRONT_MATTER_ID)\n            PsiElement(ShireTokenType.IDENTIFIER)('title')\n        PsiElement(ShireTokenType.COLON)(':')\n        PsiWhiteSpace(' ')\n        ShireFrontMatterValueImpl(FRONT_MATTER_VALUE)\n          PsiElement(ShireTokenType.QUOTE_STRING)('\"Sample Title\"')\n        PsiElement(ShireTokenType.NEWLINE)('\\n')\n      ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n        ShireFrontMatterKeyImpl(FRONT_MATTER_KEY)\n          ShireFrontMatterIdImpl(FRONT_MATTER_ID)\n            PsiElement(ShireTokenType.IDENTIFIER)('date')\n        PsiElement(ShireTokenType.COLON)(':')\n        PsiWhiteSpace(' ')\n        ShireFrontMatterValueImpl(FRONT_MATTER_VALUE)\n          PsiElement(ShireTokenType.DATE)('2022-01-01')\n        PsiElement(ShireTokenType.NEWLINE)('\\n')\n      ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n        ShireFrontMatterKeyImpl(FRONT_MATTER_KEY)\n          ShireFrontMatterIdImpl(FRONT_MATTER_ID)\n            PsiElement(ShireTokenType.IDENTIFIER)('author')\n        PsiElement(ShireTokenType.COLON)(':')\n        PsiWhiteSpace(' ')\n        ShireFrontMatterValueImpl(FRONT_MATTER_VALUE)\n          PsiElement(ShireTokenType.QUOTE_STRING)('\"John Doe\"')\n        PsiElement(ShireTokenType.NEWLINE)('\\n')\n      ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n        ShireFrontMatterKeyImpl(FRONT_MATTER_KEY)\n          ShireFrontMatterIdImpl(FRONT_MATTER_ID)\n            PsiElement(ShireTokenType.IDENTIFIER)('tags')\n        PsiElement(ShireTokenType.COLON)(':')\n        PsiWhiteSpace(' ')\n        ShireFrontMatterValueImpl(FRONT_MATTER_VALUE)\n          ShireFrontMatterArrayImpl(FRONT_MATTER_ARRAY)\n            PsiElement(ShireTokenType.[)('[')\n            ShireFrontMatterValueImpl(FRONT_MATTER_VALUE)\n              PsiElement(ShireTokenType.IDENTIFIER)('markdown')\n            PsiElement(ShireTokenType.,)(',')\n            PsiWhiteSpace(' ')\n            ShireFrontMatterValueImpl(FRONT_MATTER_VALUE)\n              PsiElement(ShireTokenType.IDENTIFIER)('frontmatter')\n            PsiElement(ShireTokenType.])(']')\n        PsiElement(ShireTokenType.NEWLINE)('\\n')\n    PsiElement(ShireTokenType.FRONTMATTER_END)('---')"
  },
  {
    "path": "shirelang/src/test/testData/parser/IfExpression.shire",
    "content": "This is an expression that includes an if.\n#if(1 > 0)\n  ${language} if\n#end\n\n\nThis is an expression that includes if and elseif.\n#if($language.length() > 1)\n  ${language} if\n#elseif($language.length() > 5)\n  ${language} elseif\n#end\n\n\nThis is an expression that includes if and else.\n#if($language.length() > 1 )\n  ${language} if\n#else\n  ${language} else\n#end\n\n\nThis is an expression that includes if,elseif and else.\n#if($language.length() > 10 )\n  ${language} if\n#elseif($language.length() > 2 )\n  ${language} elseif\n#else\n  ${language} else\n#end\n\n\nThis is an expression that includes multiple if and elseif.\n#if($language.length() > 1 )\n  ${language} if\n  #if($language.length() > 2 )\n    ${language} if_if\n  #elseif($language.length() > 20 )\n    ${language} if_elseif\n  #end\n#elseif($language.length() > 10 )\n  ${language} elseif\n#end\n\n\nThis is an expression that includes multiple if and else.\n#if($language.length() > 10 )\n  ${language} if\n#else\n  ${language} else\n  #if($language.length() > 2 )\n    ${language} else_if\n  #else\n    ${language} else_else\n  #end\n#end\n\n\nPlease ignore the content and don't reply to me."
  },
  {
    "path": "shirelang/src/test/testData/parser/IfExpression.txt",
    "content": "ShireFile\n  PsiElement(ShireTokenType.TEXT_SEGMENT)('This is an expression that includes an if.')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  ShireVelocityExprImpl(VELOCITY_EXPR)\n    ShireIfExprImpl(IF_EXPR)\n      ShireIfClauseImpl(IF_CLAUSE)\n        PsiElement(ShireTokenType.#)('#')\n        PsiElement(ShireTokenType.if)('if')\n        PsiElement(ShireTokenType.()('(')\n        ShireIneqComparisonExprImpl(INEQ_COMPARISON_EXPR)\n          ShireLiteralExprImpl(LITERAL_EXPR)\n            PsiElement(ShireTokenType.NUMBER)('1')\n          PsiWhiteSpace(' ')\n          ShireIneqComparisonOpImpl(INEQ_COMPARISON_OP)\n            PsiElement(ShireTokenType.>)('>')\n          PsiWhiteSpace(' ')\n          ShireLiteralExprImpl(LITERAL_EXPR)\n            PsiElement(ShireTokenType.NUMBER)('0')\n        PsiElement(ShireTokenType.))(')')\n        ShireVelocityBlockImpl(VELOCITY_BLOCK)\n          PsiElement(ShireTokenType.NEWLINE)('\\n')\n          PsiElement(ShireTokenType.TEXT_SEGMENT)('  ')\n          ShireUsedImpl(USED)\n            ShireVariableStartImpl(VARIABLE_START)\n              PsiElement(VARIABLE_START)('$')\n            ShireVarAccessImpl(VAR_ACCESS)\n              PsiElement(ShireTokenType.{)('{')\n              ShireVariableIdImpl(VARIABLE_ID)\n                PsiElement(ShireTokenType.IDENTIFIER)('language')\n              PsiElement(ShireTokenType.})('}')\n          PsiElement(ShireTokenType.TEXT_SEGMENT)(' if')\n          PsiElement(ShireTokenType.NEWLINE)('\\n')\n      PsiElement(ShireTokenType.#)('#')\n      PsiElement(ShireTokenType.end)('end')\n    PsiElement(ShireTokenType.NEWLINE)('\\n')\n    PsiElement(ShireTokenType.NEWLINE)('\\n')\n    PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.TEXT_SEGMENT)('This is an expression that includes if and elseif.')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  ShireVelocityExprImpl(VELOCITY_EXPR)\n    ShireIfExprImpl(IF_EXPR)\n      ShireIfClauseImpl(IF_CLAUSE)\n        PsiElement(ShireTokenType.#)('#')\n        PsiElement(ShireTokenType.if)('if')\n        PsiElement(ShireTokenType.()('(')\n        ShireIneqComparisonExprImpl(INEQ_COMPARISON_EXPR)\n          ShireCallExprImpl(CALL_EXPR)\n            ShireRefExprImpl(REF_EXPR)\n              ShireLiteralExprImpl(LITERAL_EXPR)\n                PsiElement(VARIABLE_START)('$')\n                PsiElement(ShireTokenType.IDENTIFIER)('language')\n              PsiElement(ShireTokenType..)('.')\n              PsiElement(ShireTokenType.IDENTIFIER)('length')\n            PsiElement(ShireTokenType.()('(')\n            PsiElement(ShireTokenType.))(')')\n          PsiWhiteSpace(' ')\n          ShireIneqComparisonOpImpl(INEQ_COMPARISON_OP)\n            PsiElement(ShireTokenType.>)('>')\n          PsiWhiteSpace(' ')\n          ShireLiteralExprImpl(LITERAL_EXPR)\n            PsiElement(ShireTokenType.NUMBER)('1')\n        PsiElement(ShireTokenType.))(')')\n        ShireVelocityBlockImpl(VELOCITY_BLOCK)\n          PsiElement(ShireTokenType.NEWLINE)('\\n')\n          PsiElement(ShireTokenType.TEXT_SEGMENT)('  ')\n          ShireUsedImpl(USED)\n            ShireVariableStartImpl(VARIABLE_START)\n              PsiElement(VARIABLE_START)('$')\n            ShireVarAccessImpl(VAR_ACCESS)\n              PsiElement(ShireTokenType.{)('{')\n              ShireVariableIdImpl(VARIABLE_ID)\n                PsiElement(ShireTokenType.IDENTIFIER)('language')\n              PsiElement(ShireTokenType.})('}')\n          PsiElement(ShireTokenType.TEXT_SEGMENT)(' if')\n          PsiElement(ShireTokenType.NEWLINE)('\\n')\n      ShireElseifClauseImpl(ELSEIF_CLAUSE)\n        PsiElement(ShireTokenType.#)('#')\n        PsiElement(ShireTokenType.elseif)('elseif')\n        PsiElement(ShireTokenType.()('(')\n        ShireIneqComparisonExprImpl(INEQ_COMPARISON_EXPR)\n          ShireCallExprImpl(CALL_EXPR)\n            ShireRefExprImpl(REF_EXPR)\n              ShireLiteralExprImpl(LITERAL_EXPR)\n                PsiElement(VARIABLE_START)('$')\n                PsiElement(ShireTokenType.IDENTIFIER)('language')\n              PsiElement(ShireTokenType..)('.')\n              PsiElement(ShireTokenType.IDENTIFIER)('length')\n            PsiElement(ShireTokenType.()('(')\n            PsiElement(ShireTokenType.))(')')\n          PsiWhiteSpace(' ')\n          ShireIneqComparisonOpImpl(INEQ_COMPARISON_OP)\n            PsiElement(ShireTokenType.>)('>')\n          PsiWhiteSpace(' ')\n          ShireLiteralExprImpl(LITERAL_EXPR)\n            PsiElement(ShireTokenType.NUMBER)('5')\n        PsiElement(ShireTokenType.))(')')\n        ShireVelocityBlockImpl(VELOCITY_BLOCK)\n          PsiElement(ShireTokenType.NEWLINE)('\\n')\n          PsiElement(ShireTokenType.TEXT_SEGMENT)('  ')\n          ShireUsedImpl(USED)\n            ShireVariableStartImpl(VARIABLE_START)\n              PsiElement(VARIABLE_START)('$')\n            ShireVarAccessImpl(VAR_ACCESS)\n              PsiElement(ShireTokenType.{)('{')\n              ShireVariableIdImpl(VARIABLE_ID)\n                PsiElement(ShireTokenType.IDENTIFIER)('language')\n              PsiElement(ShireTokenType.})('}')\n          PsiElement(ShireTokenType.TEXT_SEGMENT)(' elseif')\n          PsiElement(ShireTokenType.NEWLINE)('\\n')\n      PsiElement(ShireTokenType.#)('#')\n      PsiElement(ShireTokenType.end)('end')\n    PsiElement(ShireTokenType.NEWLINE)('\\n')\n    PsiElement(ShireTokenType.NEWLINE)('\\n')\n    PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.TEXT_SEGMENT)('This is an expression that includes if and else.')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  ShireVelocityExprImpl(VELOCITY_EXPR)\n    ShireIfExprImpl(IF_EXPR)\n      ShireIfClauseImpl(IF_CLAUSE)\n        PsiElement(ShireTokenType.#)('#')\n        PsiElement(ShireTokenType.if)('if')\n        PsiElement(ShireTokenType.()('(')\n        ShireIneqComparisonExprImpl(INEQ_COMPARISON_EXPR)\n          ShireCallExprImpl(CALL_EXPR)\n            ShireRefExprImpl(REF_EXPR)\n              ShireLiteralExprImpl(LITERAL_EXPR)\n                PsiElement(VARIABLE_START)('$')\n                PsiElement(ShireTokenType.IDENTIFIER)('language')\n              PsiElement(ShireTokenType..)('.')\n              PsiElement(ShireTokenType.IDENTIFIER)('length')\n            PsiElement(ShireTokenType.()('(')\n            PsiElement(ShireTokenType.))(')')\n          PsiWhiteSpace(' ')\n          ShireIneqComparisonOpImpl(INEQ_COMPARISON_OP)\n            PsiElement(ShireTokenType.>)('>')\n          PsiWhiteSpace(' ')\n          ShireLiteralExprImpl(LITERAL_EXPR)\n            PsiElement(ShireTokenType.NUMBER)('1')\n        PsiWhiteSpace(' ')\n        PsiElement(ShireTokenType.))(')')\n        ShireVelocityBlockImpl(VELOCITY_BLOCK)\n          PsiElement(ShireTokenType.NEWLINE)('\\n')\n          PsiElement(ShireTokenType.TEXT_SEGMENT)('  ')\n          ShireUsedImpl(USED)\n            ShireVariableStartImpl(VARIABLE_START)\n              PsiElement(VARIABLE_START)('$')\n            ShireVarAccessImpl(VAR_ACCESS)\n              PsiElement(ShireTokenType.{)('{')\n              ShireVariableIdImpl(VARIABLE_ID)\n                PsiElement(ShireTokenType.IDENTIFIER)('language')\n              PsiElement(ShireTokenType.})('}')\n          PsiElement(ShireTokenType.TEXT_SEGMENT)(' if')\n          PsiElement(ShireTokenType.NEWLINE)('\\n')\n      ShireElseClauseImpl(ELSE_CLAUSE)\n        PsiElement(ShireTokenType.#)('#')\n        PsiElement(ShireTokenType.else)('else')\n        ShireVelocityBlockImpl(VELOCITY_BLOCK)\n          PsiElement(ShireTokenType.NEWLINE)('\\n')\n          PsiElement(ShireTokenType.TEXT_SEGMENT)('  ')\n          ShireUsedImpl(USED)\n            ShireVariableStartImpl(VARIABLE_START)\n              PsiElement(VARIABLE_START)('$')\n            ShireVarAccessImpl(VAR_ACCESS)\n              PsiElement(ShireTokenType.{)('{')\n              ShireVariableIdImpl(VARIABLE_ID)\n                PsiElement(ShireTokenType.IDENTIFIER)('language')\n              PsiElement(ShireTokenType.})('}')\n          PsiElement(ShireTokenType.TEXT_SEGMENT)(' else')\n          PsiElement(ShireTokenType.NEWLINE)('\\n')\n      PsiElement(ShireTokenType.#)('#')\n      PsiElement(ShireTokenType.end)('end')\n    PsiElement(ShireTokenType.NEWLINE)('\\n')\n    PsiElement(ShireTokenType.NEWLINE)('\\n')\n    PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.TEXT_SEGMENT)('This is an expression that includes if,elseif and else.')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  ShireVelocityExprImpl(VELOCITY_EXPR)\n    ShireIfExprImpl(IF_EXPR)\n      ShireIfClauseImpl(IF_CLAUSE)\n        PsiElement(ShireTokenType.#)('#')\n        PsiElement(ShireTokenType.if)('if')\n        PsiElement(ShireTokenType.()('(')\n        ShireIneqComparisonExprImpl(INEQ_COMPARISON_EXPR)\n          ShireCallExprImpl(CALL_EXPR)\n            ShireRefExprImpl(REF_EXPR)\n              ShireLiteralExprImpl(LITERAL_EXPR)\n                PsiElement(VARIABLE_START)('$')\n                PsiElement(ShireTokenType.IDENTIFIER)('language')\n              PsiElement(ShireTokenType..)('.')\n              PsiElement(ShireTokenType.IDENTIFIER)('length')\n            PsiElement(ShireTokenType.()('(')\n            PsiElement(ShireTokenType.))(')')\n          PsiWhiteSpace(' ')\n          ShireIneqComparisonOpImpl(INEQ_COMPARISON_OP)\n            PsiElement(ShireTokenType.>)('>')\n          PsiWhiteSpace(' ')\n          ShireLiteralExprImpl(LITERAL_EXPR)\n            PsiElement(ShireTokenType.NUMBER)('10')\n        PsiWhiteSpace(' ')\n        PsiElement(ShireTokenType.))(')')\n        ShireVelocityBlockImpl(VELOCITY_BLOCK)\n          PsiElement(ShireTokenType.NEWLINE)('\\n')\n          PsiElement(ShireTokenType.TEXT_SEGMENT)('  ')\n          ShireUsedImpl(USED)\n            ShireVariableStartImpl(VARIABLE_START)\n              PsiElement(VARIABLE_START)('$')\n            ShireVarAccessImpl(VAR_ACCESS)\n              PsiElement(ShireTokenType.{)('{')\n              ShireVariableIdImpl(VARIABLE_ID)\n                PsiElement(ShireTokenType.IDENTIFIER)('language')\n              PsiElement(ShireTokenType.})('}')\n          PsiElement(ShireTokenType.TEXT_SEGMENT)(' if')\n          PsiElement(ShireTokenType.NEWLINE)('\\n')\n      ShireElseifClauseImpl(ELSEIF_CLAUSE)\n        PsiElement(ShireTokenType.#)('#')\n        PsiElement(ShireTokenType.elseif)('elseif')\n        PsiElement(ShireTokenType.()('(')\n        ShireIneqComparisonExprImpl(INEQ_COMPARISON_EXPR)\n          ShireCallExprImpl(CALL_EXPR)\n            ShireRefExprImpl(REF_EXPR)\n              ShireLiteralExprImpl(LITERAL_EXPR)\n                PsiElement(VARIABLE_START)('$')\n                PsiElement(ShireTokenType.IDENTIFIER)('language')\n              PsiElement(ShireTokenType..)('.')\n              PsiElement(ShireTokenType.IDENTIFIER)('length')\n            PsiElement(ShireTokenType.()('(')\n            PsiElement(ShireTokenType.))(')')\n          PsiWhiteSpace(' ')\n          ShireIneqComparisonOpImpl(INEQ_COMPARISON_OP)\n            PsiElement(ShireTokenType.>)('>')\n          PsiWhiteSpace(' ')\n          ShireLiteralExprImpl(LITERAL_EXPR)\n            PsiElement(ShireTokenType.NUMBER)('2')\n        PsiWhiteSpace(' ')\n        PsiElement(ShireTokenType.))(')')\n        ShireVelocityBlockImpl(VELOCITY_BLOCK)\n          PsiElement(ShireTokenType.NEWLINE)('\\n')\n          PsiElement(ShireTokenType.TEXT_SEGMENT)('  ')\n          ShireUsedImpl(USED)\n            ShireVariableStartImpl(VARIABLE_START)\n              PsiElement(VARIABLE_START)('$')\n            ShireVarAccessImpl(VAR_ACCESS)\n              PsiElement(ShireTokenType.{)('{')\n              ShireVariableIdImpl(VARIABLE_ID)\n                PsiElement(ShireTokenType.IDENTIFIER)('language')\n              PsiElement(ShireTokenType.})('}')\n          PsiElement(ShireTokenType.TEXT_SEGMENT)(' elseif')\n          PsiElement(ShireTokenType.NEWLINE)('\\n')\n      ShireElseClauseImpl(ELSE_CLAUSE)\n        PsiElement(ShireTokenType.#)('#')\n        PsiElement(ShireTokenType.else)('else')\n        ShireVelocityBlockImpl(VELOCITY_BLOCK)\n          PsiElement(ShireTokenType.NEWLINE)('\\n')\n          PsiElement(ShireTokenType.TEXT_SEGMENT)('  ')\n          ShireUsedImpl(USED)\n            ShireVariableStartImpl(VARIABLE_START)\n              PsiElement(VARIABLE_START)('$')\n            ShireVarAccessImpl(VAR_ACCESS)\n              PsiElement(ShireTokenType.{)('{')\n              ShireVariableIdImpl(VARIABLE_ID)\n                PsiElement(ShireTokenType.IDENTIFIER)('language')\n              PsiElement(ShireTokenType.})('}')\n          PsiElement(ShireTokenType.TEXT_SEGMENT)(' else')\n          PsiElement(ShireTokenType.NEWLINE)('\\n')\n      PsiElement(ShireTokenType.#)('#')\n      PsiElement(ShireTokenType.end)('end')\n    PsiElement(ShireTokenType.NEWLINE)('\\n')\n    PsiElement(ShireTokenType.NEWLINE)('\\n')\n    PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.TEXT_SEGMENT)('This is an expression that includes multiple if and elseif.')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  ShireVelocityExprImpl(VELOCITY_EXPR)\n    ShireIfExprImpl(IF_EXPR)\n      ShireIfClauseImpl(IF_CLAUSE)\n        PsiElement(ShireTokenType.#)('#')\n        PsiElement(ShireTokenType.if)('if')\n        PsiElement(ShireTokenType.()('(')\n        ShireIneqComparisonExprImpl(INEQ_COMPARISON_EXPR)\n          ShireCallExprImpl(CALL_EXPR)\n            ShireRefExprImpl(REF_EXPR)\n              ShireLiteralExprImpl(LITERAL_EXPR)\n                PsiElement(VARIABLE_START)('$')\n                PsiElement(ShireTokenType.IDENTIFIER)('language')\n              PsiElement(ShireTokenType..)('.')\n              PsiElement(ShireTokenType.IDENTIFIER)('length')\n            PsiElement(ShireTokenType.()('(')\n            PsiElement(ShireTokenType.))(')')\n          PsiWhiteSpace(' ')\n          ShireIneqComparisonOpImpl(INEQ_COMPARISON_OP)\n            PsiElement(ShireTokenType.>)('>')\n          PsiWhiteSpace(' ')\n          ShireLiteralExprImpl(LITERAL_EXPR)\n            PsiElement(ShireTokenType.NUMBER)('1')\n        PsiWhiteSpace(' ')\n        PsiElement(ShireTokenType.))(')')\n        ShireVelocityBlockImpl(VELOCITY_BLOCK)\n          PsiElement(ShireTokenType.NEWLINE)('\\n')\n          PsiElement(ShireTokenType.TEXT_SEGMENT)('  ')\n          ShireUsedImpl(USED)\n            ShireVariableStartImpl(VARIABLE_START)\n              PsiElement(VARIABLE_START)('$')\n            ShireVarAccessImpl(VAR_ACCESS)\n              PsiElement(ShireTokenType.{)('{')\n              ShireVariableIdImpl(VARIABLE_ID)\n                PsiElement(ShireTokenType.IDENTIFIER)('language')\n              PsiElement(ShireTokenType.})('}')\n          PsiElement(ShireTokenType.TEXT_SEGMENT)(' if')\n          PsiElement(ShireTokenType.NEWLINE)('\\n')\n          PsiElement(ShireTokenType.TEXT_SEGMENT)('  ')\n          ShireVelocityExprImpl(VELOCITY_EXPR)\n            ShireIfExprImpl(IF_EXPR)\n              ShireIfClauseImpl(IF_CLAUSE)\n                PsiElement(ShireTokenType.#)('#')\n                PsiElement(ShireTokenType.if)('if')\n                PsiElement(ShireTokenType.()('(')\n                ShireIneqComparisonExprImpl(INEQ_COMPARISON_EXPR)\n                  ShireCallExprImpl(CALL_EXPR)\n                    ShireRefExprImpl(REF_EXPR)\n                      ShireLiteralExprImpl(LITERAL_EXPR)\n                        PsiElement(VARIABLE_START)('$')\n                        PsiElement(ShireTokenType.IDENTIFIER)('language')\n                      PsiElement(ShireTokenType..)('.')\n                      PsiElement(ShireTokenType.IDENTIFIER)('length')\n                    PsiElement(ShireTokenType.()('(')\n                    PsiElement(ShireTokenType.))(')')\n                  PsiWhiteSpace(' ')\n                  ShireIneqComparisonOpImpl(INEQ_COMPARISON_OP)\n                    PsiElement(ShireTokenType.>)('>')\n                  PsiWhiteSpace(' ')\n                  ShireLiteralExprImpl(LITERAL_EXPR)\n                    PsiElement(ShireTokenType.NUMBER)('2')\n                PsiWhiteSpace(' ')\n                PsiElement(ShireTokenType.))(')')\n                ShireVelocityBlockImpl(VELOCITY_BLOCK)\n                  PsiElement(ShireTokenType.NEWLINE)('\\n')\n                  PsiElement(ShireTokenType.TEXT_SEGMENT)('    ')\n                  ShireUsedImpl(USED)\n                    ShireVariableStartImpl(VARIABLE_START)\n                      PsiElement(VARIABLE_START)('$')\n                    ShireVarAccessImpl(VAR_ACCESS)\n                      PsiElement(ShireTokenType.{)('{')\n                      ShireVariableIdImpl(VARIABLE_ID)\n                        PsiElement(ShireTokenType.IDENTIFIER)('language')\n                      PsiElement(ShireTokenType.})('}')\n                  PsiElement(ShireTokenType.TEXT_SEGMENT)(' if_if')\n                  PsiElement(ShireTokenType.NEWLINE)('\\n')\n                  PsiElement(ShireTokenType.TEXT_SEGMENT)('  ')\n              ShireElseifClauseImpl(ELSEIF_CLAUSE)\n                PsiElement(ShireTokenType.#)('#')\n                PsiElement(ShireTokenType.elseif)('elseif')\n                PsiElement(ShireTokenType.()('(')\n                ShireIneqComparisonExprImpl(INEQ_COMPARISON_EXPR)\n                  ShireCallExprImpl(CALL_EXPR)\n                    ShireRefExprImpl(REF_EXPR)\n                      ShireLiteralExprImpl(LITERAL_EXPR)\n                        PsiElement(VARIABLE_START)('$')\n                        PsiElement(ShireTokenType.IDENTIFIER)('language')\n                      PsiElement(ShireTokenType..)('.')\n                      PsiElement(ShireTokenType.IDENTIFIER)('length')\n                    PsiElement(ShireTokenType.()('(')\n                    PsiElement(ShireTokenType.))(')')\n                  PsiWhiteSpace(' ')\n                  ShireIneqComparisonOpImpl(INEQ_COMPARISON_OP)\n                    PsiElement(ShireTokenType.>)('>')\n                  PsiWhiteSpace(' ')\n                  ShireLiteralExprImpl(LITERAL_EXPR)\n                    PsiElement(ShireTokenType.NUMBER)('20')\n                PsiWhiteSpace(' ')\n                PsiElement(ShireTokenType.))(')')\n                ShireVelocityBlockImpl(VELOCITY_BLOCK)\n                  PsiElement(ShireTokenType.NEWLINE)('\\n')\n                  PsiElement(ShireTokenType.TEXT_SEGMENT)('    ')\n                  ShireUsedImpl(USED)\n                    ShireVariableStartImpl(VARIABLE_START)\n                      PsiElement(VARIABLE_START)('$')\n                    ShireVarAccessImpl(VAR_ACCESS)\n                      PsiElement(ShireTokenType.{)('{')\n                      ShireVariableIdImpl(VARIABLE_ID)\n                        PsiElement(ShireTokenType.IDENTIFIER)('language')\n                      PsiElement(ShireTokenType.})('}')\n                  PsiElement(ShireTokenType.TEXT_SEGMENT)(' if_elseif')\n                  PsiElement(ShireTokenType.NEWLINE)('\\n')\n                  PsiElement(ShireTokenType.TEXT_SEGMENT)('  ')\n              PsiElement(ShireTokenType.#)('#')\n              PsiElement(ShireTokenType.end)('end')\n            PsiElement(ShireTokenType.NEWLINE)('\\n')\n      ShireElseifClauseImpl(ELSEIF_CLAUSE)\n        PsiElement(ShireTokenType.#)('#')\n        PsiElement(ShireTokenType.elseif)('elseif')\n        PsiElement(ShireTokenType.()('(')\n        ShireIneqComparisonExprImpl(INEQ_COMPARISON_EXPR)\n          ShireCallExprImpl(CALL_EXPR)\n            ShireRefExprImpl(REF_EXPR)\n              ShireLiteralExprImpl(LITERAL_EXPR)\n                PsiElement(VARIABLE_START)('$')\n                PsiElement(ShireTokenType.IDENTIFIER)('language')\n              PsiElement(ShireTokenType..)('.')\n              PsiElement(ShireTokenType.IDENTIFIER)('length')\n            PsiElement(ShireTokenType.()('(')\n            PsiElement(ShireTokenType.))(')')\n          PsiWhiteSpace(' ')\n          ShireIneqComparisonOpImpl(INEQ_COMPARISON_OP)\n            PsiElement(ShireTokenType.>)('>')\n          PsiWhiteSpace(' ')\n          ShireLiteralExprImpl(LITERAL_EXPR)\n            PsiElement(ShireTokenType.NUMBER)('10')\n        PsiWhiteSpace(' ')\n        PsiElement(ShireTokenType.))(')')\n        ShireVelocityBlockImpl(VELOCITY_BLOCK)\n          PsiElement(ShireTokenType.NEWLINE)('\\n')\n          PsiElement(ShireTokenType.TEXT_SEGMENT)('  ')\n          ShireUsedImpl(USED)\n            ShireVariableStartImpl(VARIABLE_START)\n              PsiElement(VARIABLE_START)('$')\n            ShireVarAccessImpl(VAR_ACCESS)\n              PsiElement(ShireTokenType.{)('{')\n              ShireVariableIdImpl(VARIABLE_ID)\n                PsiElement(ShireTokenType.IDENTIFIER)('language')\n              PsiElement(ShireTokenType.})('}')\n          PsiElement(ShireTokenType.TEXT_SEGMENT)(' elseif')\n          PsiElement(ShireTokenType.NEWLINE)('\\n')\n      PsiElement(ShireTokenType.#)('#')\n      PsiElement(ShireTokenType.end)('end')\n    PsiElement(ShireTokenType.NEWLINE)('\\n')\n    PsiElement(ShireTokenType.NEWLINE)('\\n')\n    PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.TEXT_SEGMENT)('This is an expression that includes multiple if and else.')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  ShireVelocityExprImpl(VELOCITY_EXPR)\n    ShireIfExprImpl(IF_EXPR)\n      ShireIfClauseImpl(IF_CLAUSE)\n        PsiElement(ShireTokenType.#)('#')\n        PsiElement(ShireTokenType.if)('if')\n        PsiElement(ShireTokenType.()('(')\n        ShireIneqComparisonExprImpl(INEQ_COMPARISON_EXPR)\n          ShireCallExprImpl(CALL_EXPR)\n            ShireRefExprImpl(REF_EXPR)\n              ShireLiteralExprImpl(LITERAL_EXPR)\n                PsiElement(VARIABLE_START)('$')\n                PsiElement(ShireTokenType.IDENTIFIER)('language')\n              PsiElement(ShireTokenType..)('.')\n              PsiElement(ShireTokenType.IDENTIFIER)('length')\n            PsiElement(ShireTokenType.()('(')\n            PsiElement(ShireTokenType.))(')')\n          PsiWhiteSpace(' ')\n          ShireIneqComparisonOpImpl(INEQ_COMPARISON_OP)\n            PsiElement(ShireTokenType.>)('>')\n          PsiWhiteSpace(' ')\n          ShireLiteralExprImpl(LITERAL_EXPR)\n            PsiElement(ShireTokenType.NUMBER)('10')\n        PsiWhiteSpace(' ')\n        PsiElement(ShireTokenType.))(')')\n        ShireVelocityBlockImpl(VELOCITY_BLOCK)\n          PsiElement(ShireTokenType.NEWLINE)('\\n')\n          PsiElement(ShireTokenType.TEXT_SEGMENT)('  ')\n          ShireUsedImpl(USED)\n            ShireVariableStartImpl(VARIABLE_START)\n              PsiElement(VARIABLE_START)('$')\n            ShireVarAccessImpl(VAR_ACCESS)\n              PsiElement(ShireTokenType.{)('{')\n              ShireVariableIdImpl(VARIABLE_ID)\n                PsiElement(ShireTokenType.IDENTIFIER)('language')\n              PsiElement(ShireTokenType.})('}')\n          PsiElement(ShireTokenType.TEXT_SEGMENT)(' if')\n          PsiElement(ShireTokenType.NEWLINE)('\\n')\n      ShireElseClauseImpl(ELSE_CLAUSE)\n        PsiElement(ShireTokenType.#)('#')\n        PsiElement(ShireTokenType.else)('else')\n        ShireVelocityBlockImpl(VELOCITY_BLOCK)\n          PsiElement(ShireTokenType.NEWLINE)('\\n')\n          PsiElement(ShireTokenType.TEXT_SEGMENT)('  ')\n          ShireUsedImpl(USED)\n            ShireVariableStartImpl(VARIABLE_START)\n              PsiElement(VARIABLE_START)('$')\n            ShireVarAccessImpl(VAR_ACCESS)\n              PsiElement(ShireTokenType.{)('{')\n              ShireVariableIdImpl(VARIABLE_ID)\n                PsiElement(ShireTokenType.IDENTIFIER)('language')\n              PsiElement(ShireTokenType.})('}')\n          PsiElement(ShireTokenType.TEXT_SEGMENT)(' else')\n          PsiElement(ShireTokenType.NEWLINE)('\\n')\n          PsiElement(ShireTokenType.TEXT_SEGMENT)('  ')\n          ShireVelocityExprImpl(VELOCITY_EXPR)\n            ShireIfExprImpl(IF_EXPR)\n              ShireIfClauseImpl(IF_CLAUSE)\n                PsiElement(ShireTokenType.#)('#')\n                PsiElement(ShireTokenType.if)('if')\n                PsiElement(ShireTokenType.()('(')\n                ShireIneqComparisonExprImpl(INEQ_COMPARISON_EXPR)\n                  ShireCallExprImpl(CALL_EXPR)\n                    ShireRefExprImpl(REF_EXPR)\n                      ShireLiteralExprImpl(LITERAL_EXPR)\n                        PsiElement(VARIABLE_START)('$')\n                        PsiElement(ShireTokenType.IDENTIFIER)('language')\n                      PsiElement(ShireTokenType..)('.')\n                      PsiElement(ShireTokenType.IDENTIFIER)('length')\n                    PsiElement(ShireTokenType.()('(')\n                    PsiElement(ShireTokenType.))(')')\n                  PsiWhiteSpace(' ')\n                  ShireIneqComparisonOpImpl(INEQ_COMPARISON_OP)\n                    PsiElement(ShireTokenType.>)('>')\n                  PsiWhiteSpace(' ')\n                  ShireLiteralExprImpl(LITERAL_EXPR)\n                    PsiElement(ShireTokenType.NUMBER)('2')\n                PsiWhiteSpace(' ')\n                PsiElement(ShireTokenType.))(')')\n                ShireVelocityBlockImpl(VELOCITY_BLOCK)\n                  PsiElement(ShireTokenType.NEWLINE)('\\n')\n                  PsiElement(ShireTokenType.TEXT_SEGMENT)('    ')\n                  ShireUsedImpl(USED)\n                    ShireVariableStartImpl(VARIABLE_START)\n                      PsiElement(VARIABLE_START)('$')\n                    ShireVarAccessImpl(VAR_ACCESS)\n                      PsiElement(ShireTokenType.{)('{')\n                      ShireVariableIdImpl(VARIABLE_ID)\n                        PsiElement(ShireTokenType.IDENTIFIER)('language')\n                      PsiElement(ShireTokenType.})('}')\n                  PsiElement(ShireTokenType.TEXT_SEGMENT)(' else_if')\n                  PsiElement(ShireTokenType.NEWLINE)('\\n')\n                  PsiElement(ShireTokenType.TEXT_SEGMENT)('  ')\n              ShireElseClauseImpl(ELSE_CLAUSE)\n                PsiElement(ShireTokenType.#)('#')\n                PsiElement(ShireTokenType.else)('else')\n                ShireVelocityBlockImpl(VELOCITY_BLOCK)\n                  PsiElement(ShireTokenType.NEWLINE)('\\n')\n                  PsiElement(ShireTokenType.TEXT_SEGMENT)('    ')\n                  ShireUsedImpl(USED)\n                    ShireVariableStartImpl(VARIABLE_START)\n                      PsiElement(VARIABLE_START)('$')\n                    ShireVarAccessImpl(VAR_ACCESS)\n                      PsiElement(ShireTokenType.{)('{')\n                      ShireVariableIdImpl(VARIABLE_ID)\n                        PsiElement(ShireTokenType.IDENTIFIER)('language')\n                      PsiElement(ShireTokenType.})('}')\n                  PsiElement(ShireTokenType.TEXT_SEGMENT)(' else_else')\n                  PsiElement(ShireTokenType.NEWLINE)('\\n')\n                  PsiElement(ShireTokenType.TEXT_SEGMENT)('  ')\n              PsiElement(ShireTokenType.#)('#')\n              PsiElement(ShireTokenType.end)('end')\n            PsiElement(ShireTokenType.NEWLINE)('\\n')\n      PsiElement(ShireTokenType.#)('#')\n      PsiElement(ShireTokenType.end)('end')\n    PsiElement(ShireTokenType.NEWLINE)('\\n')\n    PsiElement(ShireTokenType.NEWLINE)('\\n')\n    PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.TEXT_SEGMENT)('Please ignore the content and don't reply to me.')"
  },
  {
    "path": "shirelang/src/test/testData/parser/JavaAnnotation.shire",
    "content": "```java\n@Target({ElementType.TYPE})\n@Retention(RetentionPolicy.RUNTIME)\npublic @interface ExampleAnnotation {\n    String value() default \"\";\n}\n```"
  },
  {
    "path": "shirelang/src/test/testData/parser/JavaAnnotation.txt",
    "content": "ShireFile\n  CodeBlockElement(CODE)\n    PsiElement(ShireTokenType.CODE_BLOCK_START)('```')\n    ShireLanguageIdImpl(LANGUAGE_ID)\n      PsiElement(ShireTokenType.LANGUAGE_IDENTIFIER)('java')\n    PsiElement(ShireTokenType.NEWLINE)('\\n')\n    ASTWrapperPsiElement(CODE_CONTENTS)\n      PsiElement(ShireTokenType.CODE_CONTENT)('@Target({ElementType.TYPE})')\n      PsiElement(ShireTokenType.NEWLINE)('\\n')\n      PsiElement(ShireTokenType.CODE_CONTENT)('@Retention(RetentionPolicy.RUNTIME)')\n      PsiElement(ShireTokenType.NEWLINE)('\\n')\n      PsiElement(ShireTokenType.CODE_CONTENT)('public @interface ExampleAnnotation {')\n      PsiElement(ShireTokenType.NEWLINE)('\\n')\n      PsiElement(ShireTokenType.CODE_CONTENT)('    String value() default \"\";')\n      PsiElement(ShireTokenType.NEWLINE)('\\n')\n      PsiElement(ShireTokenType.CODE_CONTENT)('}')\n      PsiElement(ShireTokenType.NEWLINE)('\\n')\n    PsiElement(ShireTokenType.CODE_BLOCK_END)('```')"
  },
  {
    "path": "shirelang/src/test/testData/parser/JavaHelloWorld.shire",
    "content": "解释如下的代码：\n\n```java\npublic class Main {\n    public static void main(String[] args) {\n        System.out.println(\"Hello, world!\");\n    }\n}\n```\n\n请使用 Markdown 语法返回。"
  },
  {
    "path": "shirelang/src/test/testData/parser/JavaHelloWorld.txt",
    "content": "ShireFile\n  PsiElement(ShireTokenType.TEXT_SEGMENT)('解释如下的代码：')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  CodeBlockElement(CODE)\n    PsiElement(ShireTokenType.CODE_BLOCK_START)('```')\n    ShireLanguageIdImpl(LANGUAGE_ID)\n      PsiElement(ShireTokenType.LANGUAGE_IDENTIFIER)('java')\n    PsiElement(ShireTokenType.NEWLINE)('\\n')\n    ASTWrapperPsiElement(CODE_CONTENTS)\n      PsiElement(ShireTokenType.CODE_CONTENT)('public class Main {')\n      PsiElement(ShireTokenType.NEWLINE)('\\n')\n      PsiElement(ShireTokenType.CODE_CONTENT)('    public static void main(String[] args) {')\n      PsiElement(ShireTokenType.NEWLINE)('\\n')\n      PsiElement(ShireTokenType.CODE_CONTENT)('        System.out.println(\"Hello, world!\");')\n      PsiElement(ShireTokenType.NEWLINE)('\\n')\n      PsiElement(ShireTokenType.CODE_CONTENT)('    }')\n      PsiElement(ShireTokenType.NEWLINE)('\\n')\n      PsiElement(ShireTokenType.CODE_CONTENT)('}')\n      PsiElement(ShireTokenType.NEWLINE)('\\n')\n    PsiElement(ShireTokenType.CODE_BLOCK_END)('```')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.TEXT_SEGMENT)('请使用 Markdown 语法返回。')"
  },
  {
    "path": "shirelang/src/test/testData/parser/MarkdownCompatible.shire",
    "content": "## Hello\n\n```shire\n---\nname: \"自动  patch\"\nvariables:\n  \"codepath\": /BlogController\\.java/ { print }\n  \"controllerCode\": /BlogController\\.java/ { cat }\n  \"domainLanguage\": /domain-language\\.csv/ { cat }\nonStreamingEnd: { parseCode | patch($codepath, $output) }\n---\n```\n"
  },
  {
    "path": "shirelang/src/test/testData/parser/MarkdownCompatible.txt",
    "content": "ShireFile\n  ShireMarkdownHeaderImpl(MARKDOWN_HEADER)\n    PsiElement(ShireTokenType.#)('#')\n    PsiElement(ShireTokenType.#)('#')\n    PsiElement(ShireTokenType.TEXT_SEGMENT)(' Hello')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  CodeBlockElement(CODE)\n    PsiElement(ShireTokenType.CODE_BLOCK_START)('```')\n    ShireLanguageIdImpl(LANGUAGE_ID)\n      PsiElement(ShireTokenType.LANGUAGE_IDENTIFIER)('shire')\n    PsiElement(ShireTokenType.NEWLINE)('\\n')\n    ASTWrapperPsiElement(CODE_CONTENTS)\n      PsiElement(ShireTokenType.CODE_CONTENT)('---')\n      PsiElement(ShireTokenType.NEWLINE)('\\n')\n      PsiElement(ShireTokenType.CODE_CONTENT)('name: \"自动  patch\"')\n      PsiElement(ShireTokenType.NEWLINE)('\\n')\n      PsiElement(ShireTokenType.CODE_CONTENT)('variables:')\n      PsiElement(ShireTokenType.NEWLINE)('\\n')\n      PsiElement(ShireTokenType.CODE_CONTENT)('  \"codepath\": /BlogController\\.java/ { print }')\n      PsiElement(ShireTokenType.NEWLINE)('\\n')\n      PsiElement(ShireTokenType.CODE_CONTENT)('  \"controllerCode\": /BlogController\\.java/ { cat }')\n      PsiElement(ShireTokenType.NEWLINE)('\\n')\n      PsiElement(ShireTokenType.CODE_CONTENT)('  \"domainLanguage\": /domain-language\\.csv/ { cat }')\n      PsiElement(ShireTokenType.NEWLINE)('\\n')\n      PsiElement(ShireTokenType.CODE_CONTENT)('onStreamingEnd: { parseCode | patch($codepath, $output) }')\n      PsiElement(ShireTokenType.NEWLINE)('\\n')\n      PsiElement(ShireTokenType.CODE_CONTENT)('---')\n      PsiElement(ShireTokenType.NEWLINE)('\\n')\n    PsiElement(ShireTokenType.CODE_BLOCK_END)('```')"
  },
  {
    "path": "shirelang/src/test/testData/parser/MultipleFMVariable.shire",
    "content": "---\nvariables:\n  \"extContext\": /build\\.gradle\\.kts/ { cat | grep(\"org.springframework.boot:spring-boot-starter-jdbc\") | print(\"This project use Spring Framework\")}\n  \"testTemplate\": /\\(.*\\).java/ {\n    case \"$1\" {\n      \"Controller\" { cat(\".shire/templates/ControllerTest.java\") }\n      \"Service\" { cat(\".shire/templates/ServiceTest.java\") }\n      default  { cat(\".shire/templates/DefaultTest.java\") }\n    }\n  }\n  \"allController\": {\n    from {\n        PsiClass clazz /* sample */\n    }\n    where {\n        clazz.getAnAnnotation() == \"org.springframework.web.bind.annotation.RequestMapping\"\n    }\n    select {\n        clazz.id, clazz.name, \"code\"\n    }\n  }\n---\n"
  },
  {
    "path": "shirelang/src/test/testData/parser/MultipleFMVariable.txt",
    "content": "ShireFile\n  ShireFrontMatterHeaderImpl(FRONT_MATTER_HEADER)\n    PsiElement(ShireTokenType.FRONTMATTER_START)('---')\n    PsiElement(ShireTokenType.NEWLINE)('\\n')\n    ShireFrontMatterEntriesImpl(FRONT_MATTER_ENTRIES)\n      ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n        ShireFrontMatterKeyImpl(FRONT_MATTER_KEY)\n          ShireFrontMatterIdImpl(FRONT_MATTER_ID)\n            PsiElement(ShireTokenType.IDENTIFIER)('variables')\n        PsiElement(ShireTokenType.COLON)(':')\n        ShireFrontMatterValueImpl(FRONT_MATTER_VALUE)\n          PsiElement(ShireTokenType.NEWLINE)('\\n')\n          ShireObjectKeyValueImpl(OBJECT_KEY_VALUE)\n            PsiElement(ShireTokenType.INDENT)('  ')\n            ShireKeyValueImpl(KEY_VALUE)\n              ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n                ShireFrontMatterKeyImpl(FRONT_MATTER_KEY)\n                  PsiElement(ShireTokenType.QUOTE_STRING)('\"extContext\"')\n                PsiElement(ShireTokenType.COLON)(':')\n                PsiWhiteSpace(' ')\n                ShirePatternActionImpl(PATTERN_ACTION)\n                  PatternElement(PATTERN)\n                    PsiElement(ShireTokenType.PATTERN_EXPR)('/build\\.gradle\\.kts/')\n                  PsiWhiteSpace(' ')\n                  ShireActionBlockImpl(ACTION_BLOCK)\n                    PsiElement(ShireTokenType.{)('{')\n                    PsiWhiteSpace(' ')\n                    ShireActionBodyImpl(ACTION_BODY)\n                      ShireActionExprImpl(ACTION_EXPR)\n                        ShireFuncCallImpl(FUNC_CALL)\n                          ShireFuncNameImpl(FUNC_NAME)\n                            PsiElement(ShireTokenType.IDENTIFIER)('cat')\n                      PsiWhiteSpace(' ')\n                      PsiElement(ShireTokenType.|)('|')\n                      PsiWhiteSpace(' ')\n                      ShireActionExprImpl(ACTION_EXPR)\n                        ShireGrepFuncCall(FUNC_CALL)\n                          ShireFuncNameImpl(FUNC_NAME)\n                            PsiElement(ShireTokenType.IDENTIFIER)('grep')\n                          PsiElement(ShireTokenType.()('(')\n                          ShirePipelineArgsImpl(PIPELINE_ARGS)\n                            ShirePipelineArgImpl(PIPELINE_ARG)\n                              PsiElement(ShireTokenType.QUOTE_STRING)('\"org.springframework.boot:spring-boot-starter-jdbc\"')\n                          PsiElement(ShireTokenType.))(')')\n                      PsiWhiteSpace(' ')\n                      PsiElement(ShireTokenType.|)('|')\n                      PsiWhiteSpace(' ')\n                      ShireActionExprImpl(ACTION_EXPR)\n                        ShireFuncCallImpl(FUNC_CALL)\n                          ShireFuncNameImpl(FUNC_NAME)\n                            PsiElement(ShireTokenType.IDENTIFIER)('print')\n                          PsiElement(ShireTokenType.()('(')\n                          ShirePipelineArgsImpl(PIPELINE_ARGS)\n                            ShirePipelineArgImpl(PIPELINE_ARG)\n                              PsiElement(ShireTokenType.QUOTE_STRING)('\"This project use Spring Framework\"')\n                          PsiElement(ShireTokenType.))(')')\n                    PsiElement(ShireTokenType.})('}')\n                    PsiElement(ShireTokenType.NEWLINE)('\\n')\n            PsiElement(ShireTokenType.INDENT)('  ')\n            ShireKeyValueImpl(KEY_VALUE)\n              ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n                ShireFrontMatterKeyImpl(FRONT_MATTER_KEY)\n                  PsiElement(ShireTokenType.QUOTE_STRING)('\"testTemplate\"')\n                PsiElement(ShireTokenType.COLON)(':')\n                PsiWhiteSpace(' ')\n                ShirePatternActionImpl(PATTERN_ACTION)\n                  PatternElement(PATTERN)\n                    PsiElement(ShireTokenType.PATTERN_EXPR)('/\\(.*\\).java/')\n                  PsiWhiteSpace(' ')\n                  ShireActionBlockImpl(ACTION_BLOCK)\n                    PsiElement(ShireTokenType.{)('{')\n                    PsiElement(ShireTokenType.NEWLINE)('\\n')\n                    PsiWhiteSpace('    ')\n                    ShireActionBodyImpl(ACTION_BODY)\n                      ShireActionExprImpl(ACTION_EXPR)\n                        ShireCaseBodyImpl(CASE_BODY)\n                          PsiElement(ShireTokenType.case)('case')\n                          PsiWhiteSpace(' ')\n                          PsiElement(ShireTokenType.QUOTE_STRING)('\"$1\"')\n                          PsiWhiteSpace(' ')\n                          PsiElement(ShireTokenType.{)('{')\n                          PsiElement(ShireTokenType.NEWLINE)('\\n')\n                          PsiWhiteSpace('      ')\n                          ShireCasePatternActionImpl(CASE_PATTERN_ACTION)\n                            ShireCaseConditionImpl(CASE_CONDITION)\n                              PsiElement(ShireTokenType.QUOTE_STRING)('\"Controller\"')\n                            PsiWhiteSpace(' ')\n                            PsiElement(ShireTokenType.{)('{')\n                            PsiWhiteSpace(' ')\n                            ShireActionBodyImpl(ACTION_BODY)\n                              ShireActionExprImpl(ACTION_EXPR)\n                                ShireFuncCallImpl(FUNC_CALL)\n                                  ShireFuncNameImpl(FUNC_NAME)\n                                    PsiElement(ShireTokenType.IDENTIFIER)('cat')\n                                  PsiElement(ShireTokenType.()('(')\n                                  ShirePipelineArgsImpl(PIPELINE_ARGS)\n                                    ShirePipelineArgImpl(PIPELINE_ARG)\n                                      PsiElement(ShireTokenType.QUOTE_STRING)('\".shire/templates/ControllerTest.java\"')\n                                  PsiElement(ShireTokenType.))(')')\n                            PsiWhiteSpace(' ')\n                            PsiElement(ShireTokenType.})('}')\n                            PsiElement(ShireTokenType.NEWLINE)('\\n')\n                          PsiWhiteSpace('      ')\n                          ShireCasePatternActionImpl(CASE_PATTERN_ACTION)\n                            ShireCaseConditionImpl(CASE_CONDITION)\n                              PsiElement(ShireTokenType.QUOTE_STRING)('\"Service\"')\n                            PsiWhiteSpace(' ')\n                            PsiElement(ShireTokenType.{)('{')\n                            PsiWhiteSpace(' ')\n                            ShireActionBodyImpl(ACTION_BODY)\n                              ShireActionExprImpl(ACTION_EXPR)\n                                ShireFuncCallImpl(FUNC_CALL)\n                                  ShireFuncNameImpl(FUNC_NAME)\n                                    PsiElement(ShireTokenType.IDENTIFIER)('cat')\n                                  PsiElement(ShireTokenType.()('(')\n                                  ShirePipelineArgsImpl(PIPELINE_ARGS)\n                                    ShirePipelineArgImpl(PIPELINE_ARG)\n                                      PsiElement(ShireTokenType.QUOTE_STRING)('\".shire/templates/ServiceTest.java\"')\n                                  PsiElement(ShireTokenType.))(')')\n                            PsiWhiteSpace(' ')\n                            PsiElement(ShireTokenType.})('}')\n                            PsiElement(ShireTokenType.NEWLINE)('\\n')\n                          PsiWhiteSpace('      ')\n                          ShireCasePatternActionImpl(CASE_PATTERN_ACTION)\n                            ShireCaseConditionImpl(CASE_CONDITION)\n                              PsiElement(ShireTokenType.default)('default')\n                            PsiWhiteSpace('  ')\n                            PsiElement(ShireTokenType.{)('{')\n                            PsiWhiteSpace(' ')\n                            ShireActionBodyImpl(ACTION_BODY)\n                              ShireActionExprImpl(ACTION_EXPR)\n                                ShireFuncCallImpl(FUNC_CALL)\n                                  ShireFuncNameImpl(FUNC_NAME)\n                                    PsiElement(ShireTokenType.IDENTIFIER)('cat')\n                                  PsiElement(ShireTokenType.()('(')\n                                  ShirePipelineArgsImpl(PIPELINE_ARGS)\n                                    ShirePipelineArgImpl(PIPELINE_ARG)\n                                      PsiElement(ShireTokenType.QUOTE_STRING)('\".shire/templates/DefaultTest.java\"')\n                                  PsiElement(ShireTokenType.))(')')\n                            PsiWhiteSpace(' ')\n                            PsiElement(ShireTokenType.})('}')\n                            PsiElement(ShireTokenType.NEWLINE)('\\n')\n                          PsiWhiteSpace('    ')\n                          PsiElement(ShireTokenType.})('}')\n                          PsiElement(ShireTokenType.NEWLINE)('\\n')\n                    PsiWhiteSpace('  ')\n                    PsiElement(ShireTokenType.})('}')\n                    PsiElement(ShireTokenType.NEWLINE)('\\n')\n            PsiElement(ShireTokenType.INDENT)('  ')\n            ShireKeyValueImpl(KEY_VALUE)\n              ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n                ShireFrontMatterKeyImpl(FRONT_MATTER_KEY)\n                  PsiElement(ShireTokenType.QUOTE_STRING)('\"allController\"')\n                PsiElement(ShireTokenType.COLON)(':')\n                PsiWhiteSpace(' ')\n                ShireFunctionStatementImpl(FUNCTION_STATEMENT)\n                  PsiElement(ShireTokenType.{)('{')\n                  PsiElement(ShireTokenType.NEWLINE)('\\n')\n                  PsiWhiteSpace('    ')\n                  ShireFunctionBodyImpl(FUNCTION_BODY)\n                    ShireQueryStatementImpl(QUERY_STATEMENT)\n                      ShireFromClauseImpl(FROM_CLAUSE)\n                        PsiElement(ShireTokenType.from)('from')\n                        PsiWhiteSpace(' ')\n                        PsiElement(ShireTokenType.{)('{')\n                        PsiElement(ShireTokenType.NEWLINE)('\\n')\n                        PsiWhiteSpace('        ')\n                        ShirePsiElementDeclImpl(PSI_ELEMENT_DECL)\n                          ShirePsiVarDeclImpl(PSI_VAR_DECL)\n                            ShirePsiTypeImpl(PSI_TYPE)\n                              PsiElement(ShireTokenType.IDENTIFIER)('PsiClass')\n                            PsiWhiteSpace(' ')\n                            PsiElement(ShireTokenType.IDENTIFIER)('clazz')\n                          PsiWhiteSpace(' ')\n                          PsiComment(ShireTokenType.BLOCK_COMMENT)('/* sample */')\n                          PsiElement(ShireTokenType.NEWLINE)('\\n')\n                        PsiWhiteSpace('    ')\n                        PsiElement(ShireTokenType.})('}')\n                        PsiElement(ShireTokenType.NEWLINE)('\\n')\n                      PsiWhiteSpace('    ')\n                      ShireWhereClauseImpl(WHERE_CLAUSE)\n                        PsiElement(ShireTokenType.where)('where')\n                        PsiWhiteSpace(' ')\n                        PsiElement(ShireTokenType.{)('{')\n                        PsiElement(ShireTokenType.NEWLINE)('\\n')\n                        PsiWhiteSpace('        ')\n                        ShireEqComparisonExprImpl(EQ_COMPARISON_EXPR)\n                          ShireCallExprImpl(CALL_EXPR)\n                            ShireRefExprImpl(REF_EXPR)\n                              ShireRefExprImpl(REF_EXPR)\n                                PsiElement(ShireTokenType.IDENTIFIER)('clazz')\n                              PsiElement(ShireTokenType..)('.')\n                              PsiElement(ShireTokenType.IDENTIFIER)('getAnAnnotation')\n                            PsiElement(ShireTokenType.()('(')\n                            PsiElement(ShireTokenType.))(')')\n                          PsiWhiteSpace(' ')\n                          PsiElement(ShireTokenType.==)('==')\n                          PsiWhiteSpace(' ')\n                          ShireLiteralExprImpl(LITERAL_EXPR)\n                            PsiElement(ShireTokenType.QUOTE_STRING)('\"org.springframework.web.bind.annotation.RequestMapping\"')\n                        PsiElement(ShireTokenType.NEWLINE)('\\n')\n                        PsiWhiteSpace('    ')\n                        PsiElement(ShireTokenType.})('}')\n                        PsiElement(ShireTokenType.NEWLINE)('\\n')\n                      PsiWhiteSpace('    ')\n                      ShireSelectClauseImpl(SELECT_CLAUSE)\n                        PsiElement(ShireTokenType.select)('select')\n                        PsiWhiteSpace(' ')\n                        PsiElement(ShireTokenType.{)('{')\n                        PsiElement(ShireTokenType.NEWLINE)('\\n')\n                        PsiWhiteSpace('        ')\n                        ShireRefExprImpl(REF_EXPR)\n                          ShireRefExprImpl(REF_EXPR)\n                            PsiElement(ShireTokenType.IDENTIFIER)('clazz')\n                          PsiElement(ShireTokenType..)('.')\n                          PsiElement(ShireTokenType.IDENTIFIER)('id')\n                        PsiElement(ShireTokenType.,)(',')\n                        PsiWhiteSpace(' ')\n                        ShireRefExprImpl(REF_EXPR)\n                          ShireRefExprImpl(REF_EXPR)\n                            PsiElement(ShireTokenType.IDENTIFIER)('clazz')\n                          PsiElement(ShireTokenType..)('.')\n                          PsiElement(ShireTokenType.IDENTIFIER)('name')\n                        PsiElement(ShireTokenType.,)(',')\n                        PsiWhiteSpace(' ')\n                        ShireLiteralExprImpl(LITERAL_EXPR)\n                          PsiElement(ShireTokenType.QUOTE_STRING)('\"code\"')\n                        PsiElement(ShireTokenType.NEWLINE)('\\n')\n                        PsiWhiteSpace('    ')\n                        PsiElement(ShireTokenType.})('}')\n                        PsiElement(ShireTokenType.NEWLINE)('\\n')\n                  PsiWhiteSpace('  ')\n                  PsiElement(ShireTokenType.})('}')\n                  PsiElement(ShireTokenType.NEWLINE)('\\n')\n    PsiElement(ShireTokenType.FRONTMATTER_END)('---')"
  },
  {
    "path": "shirelang/src/test/testData/parser/PatternAction.shire",
    "content": "---\nvariables:\n  \"var1\": \"value2\"\n  \"var2\": /.*.java/ { grep(\"error.log\") | sort | xargs(\"rm\")}\n---\n\n$var1\n"
  },
  {
    "path": "shirelang/src/test/testData/parser/PatternAction.txt",
    "content": "ShireFile\n  ShireFrontMatterHeaderImpl(FRONT_MATTER_HEADER)\n    PsiElement(ShireTokenType.FRONTMATTER_START)('---')\n    PsiElement(ShireTokenType.NEWLINE)('\\n')\n    ShireFrontMatterEntriesImpl(FRONT_MATTER_ENTRIES)\n      ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n        ShireFrontMatterKeyImpl(FRONT_MATTER_KEY)\n          ShireFrontMatterIdImpl(FRONT_MATTER_ID)\n            PsiElement(ShireTokenType.IDENTIFIER)('variables')\n        PsiElement(ShireTokenType.COLON)(':')\n        ShireFrontMatterValueImpl(FRONT_MATTER_VALUE)\n          PsiElement(ShireTokenType.NEWLINE)('\\n')\n          ShireObjectKeyValueImpl(OBJECT_KEY_VALUE)\n            PsiElement(ShireTokenType.INDENT)('  ')\n            ShireKeyValueImpl(KEY_VALUE)\n              ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n                ShireFrontMatterKeyImpl(FRONT_MATTER_KEY)\n                  PsiElement(ShireTokenType.QUOTE_STRING)('\"var1\"')\n                PsiElement(ShireTokenType.COLON)(':')\n                PsiWhiteSpace(' ')\n                ShireFrontMatterValueImpl(FRONT_MATTER_VALUE)\n                  PsiElement(ShireTokenType.QUOTE_STRING)('\"value2\"')\n                PsiElement(ShireTokenType.NEWLINE)('\\n')\n            PsiElement(ShireTokenType.INDENT)('  ')\n            ShireKeyValueImpl(KEY_VALUE)\n              ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n                ShireFrontMatterKeyImpl(FRONT_MATTER_KEY)\n                  PsiElement(ShireTokenType.QUOTE_STRING)('\"var2\"')\n                PsiElement(ShireTokenType.COLON)(':')\n                PsiWhiteSpace(' ')\n                ShirePatternActionImpl(PATTERN_ACTION)\n                  PatternElement(PATTERN)\n                    PsiElement(ShireTokenType.PATTERN_EXPR)('/.*.java/')\n                  PsiWhiteSpace(' ')\n                  ShireActionBlockImpl(ACTION_BLOCK)\n                    PsiElement(ShireTokenType.{)('{')\n                    PsiWhiteSpace(' ')\n                    ShireActionBodyImpl(ACTION_BODY)\n                      ShireActionExprImpl(ACTION_EXPR)\n                        ShireGrepFuncCall(FUNC_CALL)\n                          ShireFuncNameImpl(FUNC_NAME)\n                            PsiElement(ShireTokenType.IDENTIFIER)('grep')\n                          PsiElement(ShireTokenType.()('(')\n                          ShirePipelineArgsImpl(PIPELINE_ARGS)\n                            ShirePipelineArgImpl(PIPELINE_ARG)\n                              PsiElement(ShireTokenType.QUOTE_STRING)('\"error.log\"')\n                          PsiElement(ShireTokenType.))(')')\n                      PsiWhiteSpace(' ')\n                      PsiElement(ShireTokenType.|)('|')\n                      PsiWhiteSpace(' ')\n                      ShireActionExprImpl(ACTION_EXPR)\n                        ShireFuncCallImpl(FUNC_CALL)\n                          ShireFuncNameImpl(FUNC_NAME)\n                            PsiElement(ShireTokenType.IDENTIFIER)('sort')\n                      PsiWhiteSpace(' ')\n                      PsiElement(ShireTokenType.|)('|')\n                      PsiWhiteSpace(' ')\n                      ShireActionExprImpl(ACTION_EXPR)\n                        ShireFuncCallImpl(FUNC_CALL)\n                          ShireFuncNameImpl(FUNC_NAME)\n                            PsiElement(ShireTokenType.IDENTIFIER)('xargs')\n                          PsiElement(ShireTokenType.()('(')\n                          ShirePipelineArgsImpl(PIPELINE_ARGS)\n                            ShirePipelineArgImpl(PIPELINE_ARG)\n                              PsiElement(ShireTokenType.QUOTE_STRING)('\"rm\"')\n                          PsiElement(ShireTokenType.))(')')\n                    PsiElement(ShireTokenType.})('}')\n                    PsiElement(ShireTokenType.NEWLINE)('\\n')\n    PsiElement(ShireTokenType.FRONTMATTER_END)('---')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  ShireUsedImpl(USED)\n    ShireVariableStartImpl(VARIABLE_START)\n      PsiElement(VARIABLE_START)('$')\n    ShireVariableIdImpl(VARIABLE_ID)\n      PsiElement(ShireTokenType.IDENTIFIER)('var1')"
  },
  {
    "path": "shirelang/src/test/testData/parser/PatternCaseAction.shire",
    "content": "---\nvariables:\n  \"var1\": \"demo\"\n  \"var2\": /.*.java/ { grep(\"error.log\") | sort | xargs(\"rm\")}\n  \"var3\": /.*.log/ {\n    case \"$0\" {\n      \"error\" { grep(\"ERROR\") | sort | xargs(\"notify_admin\") }\n      \"warn\" { grep(\"WARN\") | sort | xargs(\"notify_admin\") }\n      \"info\" { grep(\"INFO\") | sort | xargs(\"notify_user\") }\n      default  { grep(\"ERROR\") | sort | xargs(\"notify_admin\") }\n    }\n  }\n  \"var4\": 42\n---\n\n$var1\n"
  },
  {
    "path": "shirelang/src/test/testData/parser/PatternCaseAction.txt",
    "content": "ShireFile\n  ShireFrontMatterHeaderImpl(FRONT_MATTER_HEADER)\n    PsiElement(ShireTokenType.FRONTMATTER_START)('---')\n    PsiElement(ShireTokenType.NEWLINE)('\\n')\n    ShireFrontMatterEntriesImpl(FRONT_MATTER_ENTRIES)\n      ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n        ShireFrontMatterKeyImpl(FRONT_MATTER_KEY)\n          ShireFrontMatterIdImpl(FRONT_MATTER_ID)\n            PsiElement(ShireTokenType.IDENTIFIER)('variables')\n        PsiElement(ShireTokenType.COLON)(':')\n        ShireFrontMatterValueImpl(FRONT_MATTER_VALUE)\n          PsiElement(ShireTokenType.NEWLINE)('\\n')\n          ShireObjectKeyValueImpl(OBJECT_KEY_VALUE)\n            PsiElement(ShireTokenType.INDENT)('  ')\n            ShireKeyValueImpl(KEY_VALUE)\n              ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n                ShireFrontMatterKeyImpl(FRONT_MATTER_KEY)\n                  PsiElement(ShireTokenType.QUOTE_STRING)('\"var1\"')\n                PsiElement(ShireTokenType.COLON)(':')\n                PsiWhiteSpace(' ')\n                ShireFrontMatterValueImpl(FRONT_MATTER_VALUE)\n                  PsiElement(ShireTokenType.QUOTE_STRING)('\"demo\"')\n                PsiElement(ShireTokenType.NEWLINE)('\\n')\n            PsiElement(ShireTokenType.INDENT)('  ')\n            ShireKeyValueImpl(KEY_VALUE)\n              ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n                ShireFrontMatterKeyImpl(FRONT_MATTER_KEY)\n                  PsiElement(ShireTokenType.QUOTE_STRING)('\"var2\"')\n                PsiElement(ShireTokenType.COLON)(':')\n                PsiWhiteSpace(' ')\n                ShirePatternActionImpl(PATTERN_ACTION)\n                  PatternElement(PATTERN)\n                    PsiElement(ShireTokenType.PATTERN_EXPR)('/.*.java/')\n                  PsiWhiteSpace(' ')\n                  ShireActionBlockImpl(ACTION_BLOCK)\n                    PsiElement(ShireTokenType.{)('{')\n                    PsiWhiteSpace(' ')\n                    ShireActionBodyImpl(ACTION_BODY)\n                      ShireActionExprImpl(ACTION_EXPR)\n                        ShireGrepFuncCall(FUNC_CALL)\n                          ShireFuncNameImpl(FUNC_NAME)\n                            PsiElement(ShireTokenType.IDENTIFIER)('grep')\n                          PsiElement(ShireTokenType.()('(')\n                          ShirePipelineArgsImpl(PIPELINE_ARGS)\n                            ShirePipelineArgImpl(PIPELINE_ARG)\n                              PsiElement(ShireTokenType.QUOTE_STRING)('\"error.log\"')\n                          PsiElement(ShireTokenType.))(')')\n                      PsiWhiteSpace(' ')\n                      PsiElement(ShireTokenType.|)('|')\n                      PsiWhiteSpace(' ')\n                      ShireActionExprImpl(ACTION_EXPR)\n                        ShireFuncCallImpl(FUNC_CALL)\n                          ShireFuncNameImpl(FUNC_NAME)\n                            PsiElement(ShireTokenType.IDENTIFIER)('sort')\n                      PsiWhiteSpace(' ')\n                      PsiElement(ShireTokenType.|)('|')\n                      PsiWhiteSpace(' ')\n                      ShireActionExprImpl(ACTION_EXPR)\n                        ShireFuncCallImpl(FUNC_CALL)\n                          ShireFuncNameImpl(FUNC_NAME)\n                            PsiElement(ShireTokenType.IDENTIFIER)('xargs')\n                          PsiElement(ShireTokenType.()('(')\n                          ShirePipelineArgsImpl(PIPELINE_ARGS)\n                            ShirePipelineArgImpl(PIPELINE_ARG)\n                              PsiElement(ShireTokenType.QUOTE_STRING)('\"rm\"')\n                          PsiElement(ShireTokenType.))(')')\n                    PsiElement(ShireTokenType.})('}')\n                    PsiElement(ShireTokenType.NEWLINE)('\\n')\n            PsiElement(ShireTokenType.INDENT)('  ')\n            ShireKeyValueImpl(KEY_VALUE)\n              ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n                ShireFrontMatterKeyImpl(FRONT_MATTER_KEY)\n                  PsiElement(ShireTokenType.QUOTE_STRING)('\"var3\"')\n                PsiElement(ShireTokenType.COLON)(':')\n                PsiWhiteSpace(' ')\n                ShirePatternActionImpl(PATTERN_ACTION)\n                  PatternElement(PATTERN)\n                    PsiElement(ShireTokenType.PATTERN_EXPR)('/.*.log/')\n                  PsiWhiteSpace(' ')\n                  ShireActionBlockImpl(ACTION_BLOCK)\n                    PsiElement(ShireTokenType.{)('{')\n                    PsiElement(ShireTokenType.NEWLINE)('\\n')\n                    PsiWhiteSpace('    ')\n                    ShireActionBodyImpl(ACTION_BODY)\n                      ShireActionExprImpl(ACTION_EXPR)\n                        ShireCaseBodyImpl(CASE_BODY)\n                          PsiElement(ShireTokenType.case)('case')\n                          PsiWhiteSpace(' ')\n                          PsiElement(ShireTokenType.QUOTE_STRING)('\"$0\"')\n                          PsiWhiteSpace(' ')\n                          PsiElement(ShireTokenType.{)('{')\n                          PsiElement(ShireTokenType.NEWLINE)('\\n')\n                          PsiWhiteSpace('      ')\n                          ShireCasePatternActionImpl(CASE_PATTERN_ACTION)\n                            ShireCaseConditionImpl(CASE_CONDITION)\n                              PsiElement(ShireTokenType.QUOTE_STRING)('\"error\"')\n                            PsiWhiteSpace(' ')\n                            PsiElement(ShireTokenType.{)('{')\n                            PsiWhiteSpace(' ')\n                            ShireActionBodyImpl(ACTION_BODY)\n                              ShireActionExprImpl(ACTION_EXPR)\n                                ShireGrepFuncCall(FUNC_CALL)\n                                  ShireFuncNameImpl(FUNC_NAME)\n                                    PsiElement(ShireTokenType.IDENTIFIER)('grep')\n                                  PsiElement(ShireTokenType.()('(')\n                                  ShirePipelineArgsImpl(PIPELINE_ARGS)\n                                    ShirePipelineArgImpl(PIPELINE_ARG)\n                                      PsiElement(ShireTokenType.QUOTE_STRING)('\"ERROR\"')\n                                  PsiElement(ShireTokenType.))(')')\n                              PsiWhiteSpace(' ')\n                              PsiElement(ShireTokenType.|)('|')\n                              PsiWhiteSpace(' ')\n                              ShireActionExprImpl(ACTION_EXPR)\n                                ShireFuncCallImpl(FUNC_CALL)\n                                  ShireFuncNameImpl(FUNC_NAME)\n                                    PsiElement(ShireTokenType.IDENTIFIER)('sort')\n                              PsiWhiteSpace(' ')\n                              PsiElement(ShireTokenType.|)('|')\n                              PsiWhiteSpace(' ')\n                              ShireActionExprImpl(ACTION_EXPR)\n                                ShireFuncCallImpl(FUNC_CALL)\n                                  ShireFuncNameImpl(FUNC_NAME)\n                                    PsiElement(ShireTokenType.IDENTIFIER)('xargs')\n                                  PsiElement(ShireTokenType.()('(')\n                                  ShirePipelineArgsImpl(PIPELINE_ARGS)\n                                    ShirePipelineArgImpl(PIPELINE_ARG)\n                                      PsiElement(ShireTokenType.QUOTE_STRING)('\"notify_admin\"')\n                                  PsiElement(ShireTokenType.))(')')\n                            PsiWhiteSpace(' ')\n                            PsiElement(ShireTokenType.})('}')\n                            PsiElement(ShireTokenType.NEWLINE)('\\n')\n                          PsiWhiteSpace('      ')\n                          ShireCasePatternActionImpl(CASE_PATTERN_ACTION)\n                            ShireCaseConditionImpl(CASE_CONDITION)\n                              PsiElement(ShireTokenType.QUOTE_STRING)('\"warn\"')\n                            PsiWhiteSpace(' ')\n                            PsiElement(ShireTokenType.{)('{')\n                            PsiWhiteSpace(' ')\n                            ShireActionBodyImpl(ACTION_BODY)\n                              ShireActionExprImpl(ACTION_EXPR)\n                                ShireGrepFuncCall(FUNC_CALL)\n                                  ShireFuncNameImpl(FUNC_NAME)\n                                    PsiElement(ShireTokenType.IDENTIFIER)('grep')\n                                  PsiElement(ShireTokenType.()('(')\n                                  ShirePipelineArgsImpl(PIPELINE_ARGS)\n                                    ShirePipelineArgImpl(PIPELINE_ARG)\n                                      PsiElement(ShireTokenType.QUOTE_STRING)('\"WARN\"')\n                                  PsiElement(ShireTokenType.))(')')\n                              PsiWhiteSpace(' ')\n                              PsiElement(ShireTokenType.|)('|')\n                              PsiWhiteSpace(' ')\n                              ShireActionExprImpl(ACTION_EXPR)\n                                ShireFuncCallImpl(FUNC_CALL)\n                                  ShireFuncNameImpl(FUNC_NAME)\n                                    PsiElement(ShireTokenType.IDENTIFIER)('sort')\n                              PsiWhiteSpace(' ')\n                              PsiElement(ShireTokenType.|)('|')\n                              PsiWhiteSpace(' ')\n                              ShireActionExprImpl(ACTION_EXPR)\n                                ShireFuncCallImpl(FUNC_CALL)\n                                  ShireFuncNameImpl(FUNC_NAME)\n                                    PsiElement(ShireTokenType.IDENTIFIER)('xargs')\n                                  PsiElement(ShireTokenType.()('(')\n                                  ShirePipelineArgsImpl(PIPELINE_ARGS)\n                                    ShirePipelineArgImpl(PIPELINE_ARG)\n                                      PsiElement(ShireTokenType.QUOTE_STRING)('\"notify_admin\"')\n                                  PsiElement(ShireTokenType.))(')')\n                            PsiWhiteSpace(' ')\n                            PsiElement(ShireTokenType.})('}')\n                            PsiElement(ShireTokenType.NEWLINE)('\\n')\n                          PsiWhiteSpace('      ')\n                          ShireCasePatternActionImpl(CASE_PATTERN_ACTION)\n                            ShireCaseConditionImpl(CASE_CONDITION)\n                              PsiElement(ShireTokenType.QUOTE_STRING)('\"info\"')\n                            PsiWhiteSpace(' ')\n                            PsiElement(ShireTokenType.{)('{')\n                            PsiWhiteSpace(' ')\n                            ShireActionBodyImpl(ACTION_BODY)\n                              ShireActionExprImpl(ACTION_EXPR)\n                                ShireGrepFuncCall(FUNC_CALL)\n                                  ShireFuncNameImpl(FUNC_NAME)\n                                    PsiElement(ShireTokenType.IDENTIFIER)('grep')\n                                  PsiElement(ShireTokenType.()('(')\n                                  ShirePipelineArgsImpl(PIPELINE_ARGS)\n                                    ShirePipelineArgImpl(PIPELINE_ARG)\n                                      PsiElement(ShireTokenType.QUOTE_STRING)('\"INFO\"')\n                                  PsiElement(ShireTokenType.))(')')\n                              PsiWhiteSpace(' ')\n                              PsiElement(ShireTokenType.|)('|')\n                              PsiWhiteSpace(' ')\n                              ShireActionExprImpl(ACTION_EXPR)\n                                ShireFuncCallImpl(FUNC_CALL)\n                                  ShireFuncNameImpl(FUNC_NAME)\n                                    PsiElement(ShireTokenType.IDENTIFIER)('sort')\n                              PsiWhiteSpace(' ')\n                              PsiElement(ShireTokenType.|)('|')\n                              PsiWhiteSpace(' ')\n                              ShireActionExprImpl(ACTION_EXPR)\n                                ShireFuncCallImpl(FUNC_CALL)\n                                  ShireFuncNameImpl(FUNC_NAME)\n                                    PsiElement(ShireTokenType.IDENTIFIER)('xargs')\n                                  PsiElement(ShireTokenType.()('(')\n                                  ShirePipelineArgsImpl(PIPELINE_ARGS)\n                                    ShirePipelineArgImpl(PIPELINE_ARG)\n                                      PsiElement(ShireTokenType.QUOTE_STRING)('\"notify_user\"')\n                                  PsiElement(ShireTokenType.))(')')\n                            PsiWhiteSpace(' ')\n                            PsiElement(ShireTokenType.})('}')\n                            PsiElement(ShireTokenType.NEWLINE)('\\n')\n                          PsiWhiteSpace('      ')\n                          ShireCasePatternActionImpl(CASE_PATTERN_ACTION)\n                            ShireCaseConditionImpl(CASE_CONDITION)\n                              PsiElement(ShireTokenType.default)('default')\n                            PsiWhiteSpace('  ')\n                            PsiElement(ShireTokenType.{)('{')\n                            PsiWhiteSpace(' ')\n                            ShireActionBodyImpl(ACTION_BODY)\n                              ShireActionExprImpl(ACTION_EXPR)\n                                ShireGrepFuncCall(FUNC_CALL)\n                                  ShireFuncNameImpl(FUNC_NAME)\n                                    PsiElement(ShireTokenType.IDENTIFIER)('grep')\n                                  PsiElement(ShireTokenType.()('(')\n                                  ShirePipelineArgsImpl(PIPELINE_ARGS)\n                                    ShirePipelineArgImpl(PIPELINE_ARG)\n                                      PsiElement(ShireTokenType.QUOTE_STRING)('\"ERROR\"')\n                                  PsiElement(ShireTokenType.))(')')\n                              PsiWhiteSpace(' ')\n                              PsiElement(ShireTokenType.|)('|')\n                              PsiWhiteSpace(' ')\n                              ShireActionExprImpl(ACTION_EXPR)\n                                ShireFuncCallImpl(FUNC_CALL)\n                                  ShireFuncNameImpl(FUNC_NAME)\n                                    PsiElement(ShireTokenType.IDENTIFIER)('sort')\n                              PsiWhiteSpace(' ')\n                              PsiElement(ShireTokenType.|)('|')\n                              PsiWhiteSpace(' ')\n                              ShireActionExprImpl(ACTION_EXPR)\n                                ShireFuncCallImpl(FUNC_CALL)\n                                  ShireFuncNameImpl(FUNC_NAME)\n                                    PsiElement(ShireTokenType.IDENTIFIER)('xargs')\n                                  PsiElement(ShireTokenType.()('(')\n                                  ShirePipelineArgsImpl(PIPELINE_ARGS)\n                                    ShirePipelineArgImpl(PIPELINE_ARG)\n                                      PsiElement(ShireTokenType.QUOTE_STRING)('\"notify_admin\"')\n                                  PsiElement(ShireTokenType.))(')')\n                            PsiWhiteSpace(' ')\n                            PsiElement(ShireTokenType.})('}')\n                            PsiElement(ShireTokenType.NEWLINE)('\\n')\n                          PsiWhiteSpace('    ')\n                          PsiElement(ShireTokenType.})('}')\n                          PsiElement(ShireTokenType.NEWLINE)('\\n')\n                    PsiWhiteSpace('  ')\n                    PsiElement(ShireTokenType.})('}')\n                    PsiElement(ShireTokenType.NEWLINE)('\\n')\n            PsiElement(ShireTokenType.INDENT)('  ')\n            ShireKeyValueImpl(KEY_VALUE)\n              ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n                ShireFrontMatterKeyImpl(FRONT_MATTER_KEY)\n                  PsiElement(ShireTokenType.QUOTE_STRING)('\"var4\"')\n                PsiElement(ShireTokenType.COLON)(':')\n                PsiWhiteSpace(' ')\n                ShireFrontMatterValueImpl(FRONT_MATTER_VALUE)\n                  PsiElement(ShireTokenType.NUMBER)('42')\n                PsiElement(ShireTokenType.NEWLINE)('\\n')\n    PsiElement(ShireTokenType.FRONTMATTER_END)('---')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  ShireUsedImpl(USED)\n    ShireVariableStartImpl(VARIABLE_START)\n      PsiElement(VARIABLE_START)('$')\n    ShireVariableIdImpl(VARIABLE_ID)\n      PsiElement(ShireTokenType.IDENTIFIER)('var1')"
  },
  {
    "path": "shirelang/src/test/testData/parser/ShireFmObject.shire",
    "content": "---\nname: \"Java to Kotlin\"\ndescription: \"Convert Java to Kotlin file\"\ninteraction: AppendCursor\nactionLocation: ContextMenu\nenabled: false\nmodel: \"codegeex-4\"\nonStreamingEnd:  { verifyCode | runCode }\n---\n\nConvert follow $language code to Kotlin\n\n$all\n\n"
  },
  {
    "path": "shirelang/src/test/testData/parser/ShireFmObject.txt",
    "content": "ShireFile\n  ShireFrontMatterHeaderImpl(FRONT_MATTER_HEADER)\n    PsiElement(ShireTokenType.FRONTMATTER_START)('---')\n    PsiElement(ShireTokenType.NEWLINE)('\\n')\n    ShireFrontMatterEntriesImpl(FRONT_MATTER_ENTRIES)\n      ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n        ShireFrontMatterKeyImpl(FRONT_MATTER_KEY)\n          ShireFrontMatterIdImpl(FRONT_MATTER_ID)\n            PsiElement(ShireTokenType.IDENTIFIER)('name')\n        PsiElement(ShireTokenType.COLON)(':')\n        PsiWhiteSpace(' ')\n        ShireFrontMatterValueImpl(FRONT_MATTER_VALUE)\n          PsiElement(ShireTokenType.QUOTE_STRING)('\"Java to Kotlin\"')\n        PsiElement(ShireTokenType.NEWLINE)('\\n')\n      ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n        ShireFrontMatterKeyImpl(FRONT_MATTER_KEY)\n          ShireFrontMatterIdImpl(FRONT_MATTER_ID)\n            PsiElement(ShireTokenType.IDENTIFIER)('description')\n        PsiElement(ShireTokenType.COLON)(':')\n        PsiWhiteSpace(' ')\n        ShireFrontMatterValueImpl(FRONT_MATTER_VALUE)\n          PsiElement(ShireTokenType.QUOTE_STRING)('\"Convert Java to Kotlin file\"')\n        PsiElement(ShireTokenType.NEWLINE)('\\n')\n      ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n        ShireFrontMatterKeyImpl(FRONT_MATTER_KEY)\n          ShireFrontMatterIdImpl(FRONT_MATTER_ID)\n            PsiElement(ShireTokenType.IDENTIFIER)('interaction')\n        PsiElement(ShireTokenType.COLON)(':')\n        PsiWhiteSpace(' ')\n        ShireFrontMatterValueImpl(FRONT_MATTER_VALUE)\n          PsiElement(ShireTokenType.IDENTIFIER)('AppendCursor')\n        PsiElement(ShireTokenType.NEWLINE)('\\n')\n      ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n        ShireFrontMatterKeyImpl(FRONT_MATTER_KEY)\n          ShireFrontMatterIdImpl(FRONT_MATTER_ID)\n            PsiElement(ShireTokenType.IDENTIFIER)('actionLocation')\n        PsiElement(ShireTokenType.COLON)(':')\n        PsiWhiteSpace(' ')\n        ShireFrontMatterValueImpl(FRONT_MATTER_VALUE)\n          PsiElement(ShireTokenType.IDENTIFIER)('ContextMenu')\n        PsiElement(ShireTokenType.NEWLINE)('\\n')\n      ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n        ShireFrontMatterKeyImpl(FRONT_MATTER_KEY)\n          ShireFrontMatterIdImpl(FRONT_MATTER_ID)\n            PsiElement(ShireTokenType.IDENTIFIER)('enabled')\n        PsiElement(ShireTokenType.COLON)(':')\n        PsiWhiteSpace(' ')\n        ShireFrontMatterValueImpl(FRONT_MATTER_VALUE)\n          PsiElement(ShireTokenType.BOOLEAN)('false')\n        PsiElement(ShireTokenType.NEWLINE)('\\n')\n      ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n        ShireFrontMatterKeyImpl(FRONT_MATTER_KEY)\n          ShireFrontMatterIdImpl(FRONT_MATTER_ID)\n            PsiElement(ShireTokenType.IDENTIFIER)('model')\n        PsiElement(ShireTokenType.COLON)(':')\n        PsiWhiteSpace(' ')\n        ShireFrontMatterValueImpl(FRONT_MATTER_VALUE)\n          PsiElement(ShireTokenType.QUOTE_STRING)('\"codegeex-4\"')\n        PsiElement(ShireTokenType.NEWLINE)('\\n')\n      ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n        ShireLifecycleIdImpl(LIFECYCLE_ID)\n          PsiElement(ShireTokenType.onStreamingEnd)('onStreamingEnd')\n        PsiElement(ShireTokenType.COLON)(':')\n        PsiWhiteSpace('  ')\n        ShireFunctionStatementImpl(FUNCTION_STATEMENT)\n          PsiElement(ShireTokenType.{)('{')\n          PsiWhiteSpace(' ')\n          ShireFunctionBodyImpl(FUNCTION_BODY)\n            ShireActionBodyImpl(ACTION_BODY)\n              ShireActionExprImpl(ACTION_EXPR)\n                ShireFuncCallImpl(FUNC_CALL)\n                  ShireFuncNameImpl(FUNC_NAME)\n                    PsiElement(ShireTokenType.IDENTIFIER)('verifyCode')\n              PsiWhiteSpace(' ')\n              PsiElement(ShireTokenType.|)('|')\n              PsiWhiteSpace(' ')\n              ShireActionExprImpl(ACTION_EXPR)\n                ShireFuncCallImpl(FUNC_CALL)\n                  ShireFuncNameImpl(FUNC_NAME)\n                    PsiElement(ShireTokenType.IDENTIFIER)('runCode')\n          PsiWhiteSpace(' ')\n          PsiElement(ShireTokenType.})('}')\n          PsiElement(ShireTokenType.NEWLINE)('\\n')\n    PsiElement(ShireTokenType.FRONTMATTER_END)('---')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.TEXT_SEGMENT)('Convert follow ')\n  ShireUsedImpl(USED)\n    ShireVariableStartImpl(VARIABLE_START)\n      PsiElement(VARIABLE_START)('$')\n    ShireVariableIdImpl(VARIABLE_ID)\n      PsiElement(ShireTokenType.IDENTIFIER)('language')\n  PsiElement(ShireTokenType.TEXT_SEGMENT)(' code to Kotlin')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  ShireUsedImpl(USED)\n    ShireVariableStartImpl(VARIABLE_START)\n      PsiElement(VARIABLE_START)('$')\n    ShireVariableIdImpl(VARIABLE_ID)\n      PsiElement(ShireTokenType.IDENTIFIER)('all')"
  },
  {
    "path": "shirelang/src/test/testData/parser/ShirePsiQueryExpression.shire",
    "content": "---\nvariables:\n  \"allController\": {\n    from {\n        PsiClass clazz // the class\n    }\n    where {\n        clazz.extends(\"org.springframework.web.bind.annotation.RestController\") and clazz.getAnAnnotation() == \"org.springframework.web.bind.annotation.RequestMapping\"\n    }\n\n    select {\n        clazz.id, clazz.name, \"code\"\n    }\n  }\n---\n\n$allController\n"
  },
  {
    "path": "shirelang/src/test/testData/parser/ShirePsiQueryExpression.txt",
    "content": "ShireFile\n  ShireFrontMatterHeaderImpl(FRONT_MATTER_HEADER)\n    PsiElement(ShireTokenType.FRONTMATTER_START)('---')\n    PsiElement(ShireTokenType.NEWLINE)('\\n')\n    ShireFrontMatterEntriesImpl(FRONT_MATTER_ENTRIES)\n      ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n        ShireFrontMatterKeyImpl(FRONT_MATTER_KEY)\n          ShireFrontMatterIdImpl(FRONT_MATTER_ID)\n            PsiElement(ShireTokenType.IDENTIFIER)('variables')\n        PsiElement(ShireTokenType.COLON)(':')\n        ShireFrontMatterValueImpl(FRONT_MATTER_VALUE)\n          PsiElement(ShireTokenType.NEWLINE)('\\n')\n          ShireObjectKeyValueImpl(OBJECT_KEY_VALUE)\n            PsiElement(ShireTokenType.INDENT)('  ')\n            ShireKeyValueImpl(KEY_VALUE)\n              ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n                ShireFrontMatterKeyImpl(FRONT_MATTER_KEY)\n                  PsiElement(ShireTokenType.QUOTE_STRING)('\"allController\"')\n                PsiElement(ShireTokenType.COLON)(':')\n                PsiWhiteSpace(' ')\n                ShireFunctionStatementImpl(FUNCTION_STATEMENT)\n                  PsiElement(ShireTokenType.{)('{')\n                  PsiElement(ShireTokenType.NEWLINE)('\\n')\n                  PsiWhiteSpace('    ')\n                  ShireFunctionBodyImpl(FUNCTION_BODY)\n                    ShireQueryStatementImpl(QUERY_STATEMENT)\n                      ShireFromClauseImpl(FROM_CLAUSE)\n                        PsiElement(ShireTokenType.from)('from')\n                        PsiWhiteSpace(' ')\n                        PsiElement(ShireTokenType.{)('{')\n                        PsiElement(ShireTokenType.NEWLINE)('\\n')\n                        PsiWhiteSpace('        ')\n                        ShirePsiElementDeclImpl(PSI_ELEMENT_DECL)\n                          ShirePsiVarDeclImpl(PSI_VAR_DECL)\n                            ShirePsiTypeImpl(PSI_TYPE)\n                              PsiElement(ShireTokenType.IDENTIFIER)('PsiClass')\n                            PsiWhiteSpace(' ')\n                            PsiElement(ShireTokenType.IDENTIFIER)('clazz')\n                          PsiWhiteSpace(' ')\n                          PsiComment(ShireTokenType.COMMENTS)('// the class')\n                          PsiElement(ShireTokenType.NEWLINE)('\\n')\n                        PsiWhiteSpace('    ')\n                        PsiElement(ShireTokenType.})('}')\n                        PsiElement(ShireTokenType.NEWLINE)('\\n')\n                      PsiWhiteSpace('    ')\n                      ShireWhereClauseImpl(WHERE_CLAUSE)\n                        PsiElement(ShireTokenType.where)('where')\n                        PsiWhiteSpace(' ')\n                        PsiElement(ShireTokenType.{)('{')\n                        PsiElement(ShireTokenType.NEWLINE)('\\n')\n                        PsiWhiteSpace('        ')\n                        ShireLogicalAndExprImpl(LOGICAL_AND_EXPR)\n                          ShireCallExprImpl(CALL_EXPR)\n                            ShireRefExprImpl(REF_EXPR)\n                              ShireRefExprImpl(REF_EXPR)\n                                PsiElement(ShireTokenType.IDENTIFIER)('clazz')\n                              PsiElement(ShireTokenType..)('.')\n                              PsiElement(ShireTokenType.IDENTIFIER)('extends')\n                            PsiElement(ShireTokenType.()('(')\n                            ShireExpressionListImpl(EXPRESSION_LIST)\n                              ShireLiteralExprImpl(LITERAL_EXPR)\n                                PsiElement(ShireTokenType.QUOTE_STRING)('\"org.springframework.web.bind.annotation.RestController\"')\n                            PsiElement(ShireTokenType.))(')')\n                          PsiWhiteSpace(' ')\n                          PsiElement(ShireTokenType.and)('and')\n                          PsiWhiteSpace(' ')\n                          ShireEqComparisonExprImpl(EQ_COMPARISON_EXPR)\n                            ShireCallExprImpl(CALL_EXPR)\n                              ShireRefExprImpl(REF_EXPR)\n                                ShireRefExprImpl(REF_EXPR)\n                                  PsiElement(ShireTokenType.IDENTIFIER)('clazz')\n                                PsiElement(ShireTokenType..)('.')\n                                PsiElement(ShireTokenType.IDENTIFIER)('getAnAnnotation')\n                              PsiElement(ShireTokenType.()('(')\n                              PsiElement(ShireTokenType.))(')')\n                            PsiWhiteSpace(' ')\n                            PsiElement(ShireTokenType.==)('==')\n                            PsiWhiteSpace(' ')\n                            ShireLiteralExprImpl(LITERAL_EXPR)\n                              PsiElement(ShireTokenType.QUOTE_STRING)('\"org.springframework.web.bind.annotation.RequestMapping\"')\n                        PsiElement(ShireTokenType.NEWLINE)('\\n')\n                        PsiWhiteSpace('    ')\n                        PsiElement(ShireTokenType.})('}')\n                        PsiElement(ShireTokenType.NEWLINE)('\\n')\n                        PsiElement(ShireTokenType.NEWLINE)('\\n')\n                      PsiWhiteSpace('    ')\n                      ShireSelectClauseImpl(SELECT_CLAUSE)\n                        PsiElement(ShireTokenType.select)('select')\n                        PsiWhiteSpace(' ')\n                        PsiElement(ShireTokenType.{)('{')\n                        PsiElement(ShireTokenType.NEWLINE)('\\n')\n                        PsiWhiteSpace('        ')\n                        ShireRefExprImpl(REF_EXPR)\n                          ShireRefExprImpl(REF_EXPR)\n                            PsiElement(ShireTokenType.IDENTIFIER)('clazz')\n                          PsiElement(ShireTokenType..)('.')\n                          PsiElement(ShireTokenType.IDENTIFIER)('id')\n                        PsiElement(ShireTokenType.,)(',')\n                        PsiWhiteSpace(' ')\n                        ShireRefExprImpl(REF_EXPR)\n                          ShireRefExprImpl(REF_EXPR)\n                            PsiElement(ShireTokenType.IDENTIFIER)('clazz')\n                          PsiElement(ShireTokenType..)('.')\n                          PsiElement(ShireTokenType.IDENTIFIER)('name')\n                        PsiElement(ShireTokenType.,)(',')\n                        PsiWhiteSpace(' ')\n                        ShireLiteralExprImpl(LITERAL_EXPR)\n                          PsiElement(ShireTokenType.QUOTE_STRING)('\"code\"')\n                        PsiElement(ShireTokenType.NEWLINE)('\\n')\n                        PsiWhiteSpace('    ')\n                        PsiElement(ShireTokenType.})('}')\n                        PsiElement(ShireTokenType.NEWLINE)('\\n')\n                  PsiWhiteSpace('  ')\n                  PsiElement(ShireTokenType.})('}')\n                  PsiElement(ShireTokenType.NEWLINE)('\\n')\n    PsiElement(ShireTokenType.FRONTMATTER_END)('---')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  ShireUsedImpl(USED)\n    ShireVariableStartImpl(VARIABLE_START)\n      PsiElement(VARIABLE_START)('$')\n    ShireVariableIdImpl(VARIABLE_ID)\n      PsiElement(ShireTokenType.IDENTIFIER)('allController')"
  },
  {
    "path": "shirelang/src/test/testData/parser/SingleComment.shire",
    "content": "---\nname: \"Gandalf\" // comment 2\nvariables:\n  \"codepath\": /BlogController\\.java/ { print } // comments 3\n---\n\n[flow]:flowable.devin\n[flow](result)\n\n[] is a symbol of comment, follow markdown syntax, and the content in [] is the comment content.\n The comment content is not displayed in the final result. So we can use it to add some notes to the flow.\n\n[ Normal start\n]\n"
  },
  {
    "path": "shirelang/src/test/testData/parser/SingleComment.txt",
    "content": "ShireFile\n  ShireFrontMatterHeaderImpl(FRONT_MATTER_HEADER)\n    PsiElement(ShireTokenType.FRONTMATTER_START)('---')\n    PsiElement(ShireTokenType.NEWLINE)('\\n')\n    ShireFrontMatterEntriesImpl(FRONT_MATTER_ENTRIES)\n      ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n        ShireFrontMatterKeyImpl(FRONT_MATTER_KEY)\n          ShireFrontMatterIdImpl(FRONT_MATTER_ID)\n            PsiElement(ShireTokenType.IDENTIFIER)('name')\n        PsiElement(ShireTokenType.COLON)(':')\n        PsiWhiteSpace(' ')\n        ShireFrontMatterValueImpl(FRONT_MATTER_VALUE)\n          PsiElement(ShireTokenType.QUOTE_STRING)('\"Gandalf\"')\n        PsiWhiteSpace(' ')\n        PsiComment(ShireTokenType.COMMENTS)('// comment 2')\n        PsiElement(ShireTokenType.NEWLINE)('\\n')\n      ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n        ShireFrontMatterKeyImpl(FRONT_MATTER_KEY)\n          ShireFrontMatterIdImpl(FRONT_MATTER_ID)\n            PsiElement(ShireTokenType.IDENTIFIER)('variables')\n        PsiElement(ShireTokenType.COLON)(':')\n        ShireFrontMatterValueImpl(FRONT_MATTER_VALUE)\n          PsiElement(ShireTokenType.NEWLINE)('\\n')\n          ShireObjectKeyValueImpl(OBJECT_KEY_VALUE)\n            PsiElement(ShireTokenType.INDENT)('  ')\n            ShireKeyValueImpl(KEY_VALUE)\n              ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n                ShireFrontMatterKeyImpl(FRONT_MATTER_KEY)\n                  PsiElement(ShireTokenType.QUOTE_STRING)('\"codepath\"')\n                PsiElement(ShireTokenType.COLON)(':')\n                PsiWhiteSpace(' ')\n                ShirePatternActionImpl(PATTERN_ACTION)\n                  PatternElement(PATTERN)\n                    PsiElement(ShireTokenType.PATTERN_EXPR)('/BlogController\\.java/')\n                  PsiWhiteSpace(' ')\n                  ShireActionBlockImpl(ACTION_BLOCK)\n                    PsiElement(ShireTokenType.{)('{')\n                    PsiWhiteSpace(' ')\n                    ShireActionBodyImpl(ACTION_BODY)\n                      ShireActionExprImpl(ACTION_EXPR)\n                        ShireFuncCallImpl(FUNC_CALL)\n                          ShireFuncNameImpl(FUNC_NAME)\n                            PsiElement(ShireTokenType.IDENTIFIER)('print')\n                    PsiWhiteSpace(' ')\n                    PsiElement(ShireTokenType.})('}')\n                    PsiWhiteSpace(' ')\n                    PsiComment(ShireTokenType.COMMENTS)('// comments 3')\n                    PsiElement(ShireTokenType.NEWLINE)('\\n')\n    PsiElement(ShireTokenType.FRONTMATTER_END)('---')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiComment(ShireTokenType.CONTENT_COMMENTS)('[flow]:flowable.devin')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiComment(ShireTokenType.CONTENT_COMMENTS)('[flow](result)')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiComment(ShireTokenType.CONTENT_COMMENTS)('[] is a symbol of comment, follow markdown syntax, and the content in [] is the comment content.')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.TEXT_SEGMENT)(' The comment content is not displayed in the final result. So we can use it to add some notes to the flow.')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.TEXT_SEGMENT)('[ Normal start')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.TEXT_SEGMENT)(']')"
  },
  {
    "path": "shirelang/src/test/testData/parser/VariableAccess.shire",
    "content": "---\nwhen: $selection.length == 1 && $selection.first() == 'file'\n---\n\nWrite unit test for following ${context.lang} code.\n\n${context.frameworkContext}\n\n#if($context.relatedClasses.length() > 0 )\nHere is the relate code maybe you can use\n${context.relatedClasses}\n#end\n\n```$context.lang\n${context.imports}\n${context.sourceCode}\n```\n"
  },
  {
    "path": "shirelang/src/test/testData/parser/VariableAccess.txt",
    "content": "ShireFile\n  ShireFrontMatterHeaderImpl(FRONT_MATTER_HEADER)\n    PsiElement(ShireTokenType.FRONTMATTER_START)('---')\n    PsiElement(ShireTokenType.NEWLINE)('\\n')\n    ShireFrontMatterEntriesImpl(FRONT_MATTER_ENTRIES)\n      ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n        ShireLifecycleIdImpl(LIFECYCLE_ID)\n          PsiElement(ShireTokenType.when)('when')\n        PsiElement(ShireTokenType.COLON)(':')\n        PsiWhiteSpace(' ')\n        ShireLogicalAndExprImpl(LOGICAL_AND_EXPR)\n          ShireEqComparisonExprImpl(EQ_COMPARISON_EXPR)\n            ShireRefExprImpl(REF_EXPR)\n              ShireLiteralExprImpl(LITERAL_EXPR)\n                PsiElement(VARIABLE_START)('$')\n                PsiElement(ShireTokenType.IDENTIFIER)('selection')\n              PsiElement(ShireTokenType..)('.')\n              PsiElement(ShireTokenType.IDENTIFIER)('length')\n            PsiWhiteSpace(' ')\n            PsiElement(ShireTokenType.==)('==')\n            PsiWhiteSpace(' ')\n            ShireLiteralExprImpl(LITERAL_EXPR)\n              PsiElement(ShireTokenType.NUMBER)('1')\n          PsiWhiteSpace(' ')\n          PsiElement(ShireTokenType.&&)('&&')\n          PsiWhiteSpace(' ')\n          ShireEqComparisonExprImpl(EQ_COMPARISON_EXPR)\n            ShireCallExprImpl(CALL_EXPR)\n              ShireRefExprImpl(REF_EXPR)\n                ShireLiteralExprImpl(LITERAL_EXPR)\n                  PsiElement(VARIABLE_START)('$')\n                  PsiElement(ShireTokenType.IDENTIFIER)('selection')\n                PsiElement(ShireTokenType..)('.')\n                PsiElement(ShireTokenType.IDENTIFIER)('first')\n              PsiElement(ShireTokenType.()('(')\n              PsiElement(ShireTokenType.))(')')\n            PsiWhiteSpace(' ')\n            PsiElement(ShireTokenType.==)('==')\n            PsiWhiteSpace(' ')\n            ShireLiteralExprImpl(LITERAL_EXPR)\n              PsiElement(ShireTokenType.QUOTE_STRING)(''file'')\n        PsiElement(ShireTokenType.NEWLINE)('\\n')\n    PsiElement(ShireTokenType.FRONTMATTER_END)('---')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.TEXT_SEGMENT)('Write unit test for following ')\n  ShireUsedImpl(USED)\n    ShireVariableStartImpl(VARIABLE_START)\n      PsiElement(VARIABLE_START)('$')\n    ShireVarAccessImpl(VAR_ACCESS)\n      PsiElement(ShireTokenType.{)('{')\n      ShireVariableIdImpl(VARIABLE_ID)\n        PsiElement(ShireTokenType.IDENTIFIER)('context')\n      PsiElement(ShireTokenType..)('.')\n      ShireVariableIdImpl(VARIABLE_ID)\n        PsiElement(ShireTokenType.IDENTIFIER)('lang')\n      PsiElement(ShireTokenType.})('}')\n  PsiElement(ShireTokenType.TEXT_SEGMENT)(' code.')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  ShireUsedImpl(USED)\n    ShireVariableStartImpl(VARIABLE_START)\n      PsiElement(VARIABLE_START)('$')\n    ShireVarAccessImpl(VAR_ACCESS)\n      PsiElement(ShireTokenType.{)('{')\n      ShireVariableIdImpl(VARIABLE_ID)\n        PsiElement(ShireTokenType.IDENTIFIER)('context')\n      PsiElement(ShireTokenType..)('.')\n      ShireVariableIdImpl(VARIABLE_ID)\n        PsiElement(ShireTokenType.IDENTIFIER)('frameworkContext')\n      PsiElement(ShireTokenType.})('}')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  ShireVelocityExprImpl(VELOCITY_EXPR)\n    ShireIfExprImpl(IF_EXPR)\n      ShireIfClauseImpl(IF_CLAUSE)\n        PsiElement(ShireTokenType.#)('#')\n        PsiElement(ShireTokenType.if)('if')\n        PsiElement(ShireTokenType.()('(')\n        ShireIneqComparisonExprImpl(INEQ_COMPARISON_EXPR)\n          ShireCallExprImpl(CALL_EXPR)\n            ShireRefExprImpl(REF_EXPR)\n              ShireRefExprImpl(REF_EXPR)\n                ShireLiteralExprImpl(LITERAL_EXPR)\n                  PsiElement(VARIABLE_START)('$')\n                  PsiElement(ShireTokenType.IDENTIFIER)('context')\n                PsiElement(ShireTokenType..)('.')\n                PsiElement(ShireTokenType.IDENTIFIER)('relatedClasses')\n              PsiElement(ShireTokenType..)('.')\n              PsiElement(ShireTokenType.IDENTIFIER)('length')\n            PsiElement(ShireTokenType.()('(')\n            PsiElement(ShireTokenType.))(')')\n          PsiWhiteSpace(' ')\n          ShireIneqComparisonOpImpl(INEQ_COMPARISON_OP)\n            PsiElement(ShireTokenType.>)('>')\n          PsiWhiteSpace(' ')\n          ShireLiteralExprImpl(LITERAL_EXPR)\n            PsiElement(ShireTokenType.NUMBER)('0')\n        PsiWhiteSpace(' ')\n        PsiElement(ShireTokenType.))(')')\n        ShireVelocityBlockImpl(VELOCITY_BLOCK)\n          PsiElement(ShireTokenType.NEWLINE)('\\n')\n          PsiElement(ShireTokenType.TEXT_SEGMENT)('Here is the relate code maybe you can use')\n          PsiElement(ShireTokenType.NEWLINE)('\\n')\n          ShireUsedImpl(USED)\n            ShireVariableStartImpl(VARIABLE_START)\n              PsiElement(VARIABLE_START)('$')\n            ShireVarAccessImpl(VAR_ACCESS)\n              PsiElement(ShireTokenType.{)('{')\n              ShireVariableIdImpl(VARIABLE_ID)\n                PsiElement(ShireTokenType.IDENTIFIER)('context')\n              PsiElement(ShireTokenType..)('.')\n              ShireVariableIdImpl(VARIABLE_ID)\n                PsiElement(ShireTokenType.IDENTIFIER)('relatedClasses')\n              PsiElement(ShireTokenType.})('}')\n          PsiElement(ShireTokenType.NEWLINE)('\\n')\n      PsiElement(ShireTokenType.#)('#')\n      PsiElement(ShireTokenType.end)('end')\n    PsiElement(ShireTokenType.NEWLINE)('\\n')\n    PsiElement(ShireTokenType.NEWLINE)('\\n')\n  CodeBlockElement(CODE)\n    PsiElement(ShireTokenType.CODE_BLOCK_START)('```')\n    ShireVariableStartImpl(VARIABLE_START)\n      PsiElement(VARIABLE_START)('$')\n    ShireRefExprImpl(REF_EXPR)\n      ShireRefExprImpl(REF_EXPR)\n        PsiElement(ShireTokenType.IDENTIFIER)('context')\n      PsiElement(ShireTokenType..)('.')\n      PsiElement(ShireTokenType.IDENTIFIER)('lang')\n    PsiElement(ShireTokenType.NEWLINE)('\\n')\n    ASTWrapperPsiElement(CODE_CONTENTS)\n      PsiElement(ShireTokenType.CODE_CONTENT)('${context.imports}')\n      PsiElement(ShireTokenType.NEWLINE)('\\n')\n      PsiElement(ShireTokenType.CODE_CONTENT)('${context.sourceCode}')\n      PsiElement(ShireTokenType.NEWLINE)('\\n')\n    PsiElement(ShireTokenType.CODE_BLOCK_END)('```')"
  },
  {
    "path": "shirelang/src/test/testData/parser/WhenCondition.shire",
    "content": "---\nwhen: { $selection.length == 1 && $selection.first() == 'file' }\n---\n"
  },
  {
    "path": "shirelang/src/test/testData/parser/WhenCondition.txt",
    "content": "ShireFile\n  ShireFrontMatterHeaderImpl(FRONT_MATTER_HEADER)\n    PsiElement(ShireTokenType.FRONTMATTER_START)('---')\n    PsiElement(ShireTokenType.NEWLINE)('\\n')\n    ShireFrontMatterEntriesImpl(FRONT_MATTER_ENTRIES)\n      ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n        ShireLifecycleIdImpl(LIFECYCLE_ID)\n          PsiElement(ShireTokenType.when)('when')\n        PsiElement(ShireTokenType.COLON)(':')\n        PsiWhiteSpace(' ')\n        ShireFunctionStatementImpl(FUNCTION_STATEMENT)\n          PsiElement(ShireTokenType.{)('{')\n          PsiWhiteSpace(' ')\n          ShireFunctionBodyImpl(FUNCTION_BODY)\n            ShireLogicalAndExprImpl(LOGICAL_AND_EXPR)\n              ShireEqComparisonExprImpl(EQ_COMPARISON_EXPR)\n                ShireRefExprImpl(REF_EXPR)\n                  ShireLiteralExprImpl(LITERAL_EXPR)\n                    PsiElement(VARIABLE_START)('$')\n                    PsiElement(ShireTokenType.IDENTIFIER)('selection')\n                  PsiElement(ShireTokenType..)('.')\n                  PsiElement(ShireTokenType.IDENTIFIER)('length')\n                PsiWhiteSpace(' ')\n                PsiElement(ShireTokenType.==)('==')\n                PsiWhiteSpace(' ')\n                ShireLiteralExprImpl(LITERAL_EXPR)\n                  PsiElement(ShireTokenType.NUMBER)('1')\n              PsiWhiteSpace(' ')\n              PsiElement(ShireTokenType.&&)('&&')\n              PsiWhiteSpace(' ')\n              ShireEqComparisonExprImpl(EQ_COMPARISON_EXPR)\n                ShireCallExprImpl(CALL_EXPR)\n                  ShireRefExprImpl(REF_EXPR)\n                    ShireLiteralExprImpl(LITERAL_EXPR)\n                      PsiElement(VARIABLE_START)('$')\n                      PsiElement(ShireTokenType.IDENTIFIER)('selection')\n                    PsiElement(ShireTokenType..)('.')\n                    PsiElement(ShireTokenType.IDENTIFIER)('first')\n                  PsiElement(ShireTokenType.()('(')\n                  PsiElement(ShireTokenType.))(')')\n                PsiWhiteSpace(' ')\n                PsiElement(ShireTokenType.==)('==')\n                PsiWhiteSpace(' ')\n                ShireLiteralExprImpl(LITERAL_EXPR)\n                  PsiElement(ShireTokenType.QUOTE_STRING)(''file'')\n          PsiWhiteSpace(' ')\n          PsiElement(ShireTokenType.})('}')\n          PsiElement(ShireTokenType.NEWLINE)('\\n')\n    PsiElement(ShireTokenType.FRONTMATTER_END)('---')"
  },
  {
    "path": "shirelang/src/test/testData/realworld/AfterStreamingOnly.shire",
    "content": "---\nname: \"Search\"\nvariables:\n  \"testTemplate\": /.*.kt/ { caching(\"disk\") | splitting | embedding }\nafterStreaming: { searching($output) | execute(\"search.shire\") }\n---\n\nYou are a coding assistant who helps the user answer questions about code in their workspace by providing a list of\nrelevant keywords they can search for to answer the question."
  },
  {
    "path": "shirelang/src/test/testData/realworld/AfterStreamingOnly.txt",
    "content": "ShireFile\n  ShireFrontMatterHeaderImpl(FRONT_MATTER_HEADER)\n    PsiElement(ShireTokenType.FRONTMATTER_START)('---')\n    PsiElement(ShireTokenType.NEWLINE)('\\n')\n    ShireFrontMatterEntriesImpl(FRONT_MATTER_ENTRIES)\n      ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n        ShireFrontMatterKeyImpl(FRONT_MATTER_KEY)\n          ShireFrontMatterIdImpl(FRONT_MATTER_ID)\n            PsiElement(ShireTokenType.IDENTIFIER)('name')\n        PsiElement(ShireTokenType.COLON)(':')\n        PsiWhiteSpace(' ')\n        ShireFrontMatterValueImpl(FRONT_MATTER_VALUE)\n          PsiElement(ShireTokenType.QUOTE_STRING)('\"Search\"')\n        PsiElement(ShireTokenType.NEWLINE)('\\n')\n      ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n        ShireFrontMatterKeyImpl(FRONT_MATTER_KEY)\n          ShireFrontMatterIdImpl(FRONT_MATTER_ID)\n            PsiElement(ShireTokenType.IDENTIFIER)('variables')\n        PsiElement(ShireTokenType.COLON)(':')\n        ShireFrontMatterValueImpl(FRONT_MATTER_VALUE)\n          PsiElement(ShireTokenType.NEWLINE)('\\n')\n          ShireObjectKeyValueImpl(OBJECT_KEY_VALUE)\n            PsiElement(ShireTokenType.INDENT)('  ')\n            ShireKeyValueImpl(KEY_VALUE)\n              ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n                ShireFrontMatterKeyImpl(FRONT_MATTER_KEY)\n                  PsiElement(ShireTokenType.QUOTE_STRING)('\"testTemplate\"')\n                PsiElement(ShireTokenType.COLON)(':')\n                PsiWhiteSpace(' ')\n                ShirePatternActionImpl(PATTERN_ACTION)\n                  PatternElement(PATTERN)\n                    PsiElement(ShireTokenType.PATTERN_EXPR)('/.*.kt/')\n                  PsiWhiteSpace(' ')\n                  ShireActionBlockImpl(ACTION_BLOCK)\n                    PsiElement(ShireTokenType.{)('{')\n                    PsiWhiteSpace(' ')\n                    ShireActionBodyImpl(ACTION_BODY)\n                      ShireActionExprImpl(ACTION_EXPR)\n                        ShireFuncCallImpl(FUNC_CALL)\n                          ShireFuncNameImpl(FUNC_NAME)\n                            PsiElement(ShireTokenType.IDENTIFIER)('caching')\n                          PsiElement(ShireTokenType.()('(')\n                          ShirePipelineArgsImpl(PIPELINE_ARGS)\n                            ShirePipelineArgImpl(PIPELINE_ARG)\n                              PsiElement(ShireTokenType.QUOTE_STRING)('\"disk\"')\n                          PsiElement(ShireTokenType.))(')')\n                      PsiWhiteSpace(' ')\n                      PsiElement(ShireTokenType.|)('|')\n                      PsiWhiteSpace(' ')\n                      ShireActionExprImpl(ACTION_EXPR)\n                        ShireFuncCallImpl(FUNC_CALL)\n                          ShireFuncNameImpl(FUNC_NAME)\n                            PsiElement(ShireTokenType.IDENTIFIER)('splitting')\n                      PsiWhiteSpace(' ')\n                      PsiElement(ShireTokenType.|)('|')\n                      PsiWhiteSpace(' ')\n                      ShireActionExprImpl(ACTION_EXPR)\n                        ShireFuncCallImpl(FUNC_CALL)\n                          ShireFuncNameImpl(FUNC_NAME)\n                            PsiElement(ShireTokenType.IDENTIFIER)('embedding')\n                    PsiWhiteSpace(' ')\n                    PsiElement(ShireTokenType.})('}')\n                    PsiElement(ShireTokenType.NEWLINE)('\\n')\n      ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n        ShireLifecycleIdImpl(LIFECYCLE_ID)\n          PsiElement(ShireTokenType.afterStreaming)('afterStreaming')\n        PsiElement(ShireTokenType.COLON)(':')\n        PsiWhiteSpace(' ')\n        ShireFunctionStatementImpl(FUNCTION_STATEMENT)\n          PsiElement(ShireTokenType.{)('{')\n          PsiWhiteSpace(' ')\n          ShireFunctionBodyImpl(FUNCTION_BODY)\n            ShireActionBodyImpl(ACTION_BODY)\n              ShireActionExprImpl(ACTION_EXPR)\n                ShireFuncCallImpl(FUNC_CALL)\n                  ShireFuncNameImpl(FUNC_NAME)\n                    PsiElement(ShireTokenType.IDENTIFIER)('searching')\n                  PsiElement(ShireTokenType.()('(')\n                  ShirePipelineArgsImpl(PIPELINE_ARGS)\n                    ShirePipelineArgImpl(PIPELINE_ARG)\n                      ShireVariableStartImpl(VARIABLE_START)\n                        PsiElement(VARIABLE_START)('$')\n                      ShireVariableIdImpl(VARIABLE_ID)\n                        PsiElement(ShireTokenType.IDENTIFIER)('output')\n                  PsiElement(ShireTokenType.))(')')\n              PsiWhiteSpace(' ')\n              PsiElement(ShireTokenType.|)('|')\n              PsiWhiteSpace(' ')\n              ShireActionExprImpl(ACTION_EXPR)\n                ShireFuncCallImpl(FUNC_CALL)\n                  ShireFuncNameImpl(FUNC_NAME)\n                    PsiElement(ShireTokenType.IDENTIFIER)('execute')\n                  PsiElement(ShireTokenType.()('(')\n                  ShirePipelineArgsImpl(PIPELINE_ARGS)\n                    ShirePipelineArgImpl(PIPELINE_ARG)\n                      PsiElement(ShireTokenType.QUOTE_STRING)('\"search.shire\"')\n                  PsiElement(ShireTokenType.))(')')\n          PsiWhiteSpace(' ')\n          PsiElement(ShireTokenType.})('}')\n          PsiElement(ShireTokenType.NEWLINE)('\\n')\n    PsiElement(ShireTokenType.FRONTMATTER_END)('---')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.TEXT_SEGMENT)('You are a coding assistant who helps the user answer questions about code in their workspace by providing a list of')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.TEXT_SEGMENT)('relevant keywords they can search for to answer the question.')"
  },
  {
    "path": "shirelang/src/test/testData/realworld/Autotest.shire",
    "content": "---\nname: \"AutoTest\"\ndescription: \"AutoTest\"\ninteraction: AppendCursor\nactionLocation: ContextMenu\nwhen: $fileName.contains(\".java\") && $filePath.contains(\"src/main/java\")\nfileName-rules:\n  /.*Controller.java/: \"When testing controller, you MUST use MockMvc and test API only.\"\nvariables:\n  \"extContext\": /build\\.gradle\\.kts/ { cat | grep(\"org.springframework.boot:spring-boot-starter-jdbc\") | print(\"This project use Spring Framework\")}\n  \"testTemplate\": /\\(.*\\).java/ {\n    case \"$1\" {\n      \"Controller\" { cat(\".shire/templates/ControllerTest.java\") }\n      \"Service\" { cat(\".shire/templates/ServiceTest.java\") }\n      default  { cat(\".shire/templates/DefaultTest.java\") }\n    }\n  }\n  \"allController\": {\n    from {\n        PsiClass clazz /* sample */\n    }\n    where {\n        clazz.getMethods().length() > 0\n    }\n    select {\n        clazz.getMethods()\n    }\n  }\n---\nWrite unit test for following ${context.language} code.\n\n${context.frameworkContext}\n\n#if($context.relatedClasses.length() > 0 )\nHere is the relate code maybe you can use\n${context.relatedClasses}\n#end\n\n#if($context.currentClassName.length() > 0 )\nThis is the class where the source code resides:\n${context.currentClassCode}\n#end\n\n${context.extContext}\n\nHere is the imports to help you understand:\n\n${context.imports}\n\nHere is the source code to be tested:\n```${context.language}\n${context.selection}\n```\n\n#if($context.isNewFile)\nShould include package and imports. Start method test code with Markdown code block here:\n#else\nShould include package and imports. Start ${context.targetTestFileName} test code with Markdown code block here:\n#end\n\n$allController\n"
  },
  {
    "path": "shirelang/src/test/testData/realworld/Autotest.txt",
    "content": "ShireFile\n  ShireFrontMatterHeaderImpl(FRONT_MATTER_HEADER)\n    PsiElement(ShireTokenType.FRONTMATTER_START)('---')\n    PsiElement(ShireTokenType.NEWLINE)('\\n')\n    ShireFrontMatterEntriesImpl(FRONT_MATTER_ENTRIES)\n      ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n        ShireFrontMatterKeyImpl(FRONT_MATTER_KEY)\n          ShireFrontMatterIdImpl(FRONT_MATTER_ID)\n            PsiElement(ShireTokenType.IDENTIFIER)('name')\n        PsiElement(ShireTokenType.COLON)(':')\n        PsiWhiteSpace(' ')\n        ShireFrontMatterValueImpl(FRONT_MATTER_VALUE)\n          PsiElement(ShireTokenType.QUOTE_STRING)('\"AutoTest\"')\n        PsiElement(ShireTokenType.NEWLINE)('\\n')\n      ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n        ShireFrontMatterKeyImpl(FRONT_MATTER_KEY)\n          ShireFrontMatterIdImpl(FRONT_MATTER_ID)\n            PsiElement(ShireTokenType.IDENTIFIER)('description')\n        PsiElement(ShireTokenType.COLON)(':')\n        PsiWhiteSpace(' ')\n        ShireFrontMatterValueImpl(FRONT_MATTER_VALUE)\n          PsiElement(ShireTokenType.QUOTE_STRING)('\"AutoTest\"')\n        PsiElement(ShireTokenType.NEWLINE)('\\n')\n      ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n        ShireFrontMatterKeyImpl(FRONT_MATTER_KEY)\n          ShireFrontMatterIdImpl(FRONT_MATTER_ID)\n            PsiElement(ShireTokenType.IDENTIFIER)('interaction')\n        PsiElement(ShireTokenType.COLON)(':')\n        PsiWhiteSpace(' ')\n        ShireFrontMatterValueImpl(FRONT_MATTER_VALUE)\n          PsiElement(ShireTokenType.IDENTIFIER)('AppendCursor')\n        PsiElement(ShireTokenType.NEWLINE)('\\n')\n      ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n        ShireFrontMatterKeyImpl(FRONT_MATTER_KEY)\n          ShireFrontMatterIdImpl(FRONT_MATTER_ID)\n            PsiElement(ShireTokenType.IDENTIFIER)('actionLocation')\n        PsiElement(ShireTokenType.COLON)(':')\n        PsiWhiteSpace(' ')\n        ShireFrontMatterValueImpl(FRONT_MATTER_VALUE)\n          PsiElement(ShireTokenType.IDENTIFIER)('ContextMenu')\n        PsiElement(ShireTokenType.NEWLINE)('\\n')\n      ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n        ShireLifecycleIdImpl(LIFECYCLE_ID)\n          PsiElement(ShireTokenType.when)('when')\n        PsiElement(ShireTokenType.COLON)(':')\n        PsiWhiteSpace(' ')\n        ShireLogicalAndExprImpl(LOGICAL_AND_EXPR)\n          ShireCallExprImpl(CALL_EXPR)\n            ShireRefExprImpl(REF_EXPR)\n              ShireLiteralExprImpl(LITERAL_EXPR)\n                PsiElement(VARIABLE_START)('$')\n                PsiElement(ShireTokenType.IDENTIFIER)('fileName')\n              PsiElement(ShireTokenType..)('.')\n              PsiElement(ShireTokenType.IDENTIFIER)('contains')\n            PsiElement(ShireTokenType.()('(')\n            ShireExpressionListImpl(EXPRESSION_LIST)\n              ShireLiteralExprImpl(LITERAL_EXPR)\n                PsiElement(ShireTokenType.QUOTE_STRING)('\".java\"')\n            PsiElement(ShireTokenType.))(')')\n          PsiWhiteSpace(' ')\n          PsiElement(ShireTokenType.&&)('&&')\n          PsiWhiteSpace(' ')\n          ShireCallExprImpl(CALL_EXPR)\n            ShireRefExprImpl(REF_EXPR)\n              ShireLiteralExprImpl(LITERAL_EXPR)\n                PsiElement(VARIABLE_START)('$')\n                PsiElement(ShireTokenType.IDENTIFIER)('filePath')\n              PsiElement(ShireTokenType..)('.')\n              PsiElement(ShireTokenType.IDENTIFIER)('contains')\n            PsiElement(ShireTokenType.()('(')\n            ShireExpressionListImpl(EXPRESSION_LIST)\n              ShireLiteralExprImpl(LITERAL_EXPR)\n                PsiElement(ShireTokenType.QUOTE_STRING)('\"src/main/java\"')\n            PsiElement(ShireTokenType.))(')')\n        PsiElement(ShireTokenType.NEWLINE)('\\n')\n      ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n        ShireFrontMatterKeyImpl(FRONT_MATTER_KEY)\n          ShireFrontMatterIdImpl(FRONT_MATTER_ID)\n            PsiElement(ShireTokenType.IDENTIFIER)('fileName-rules')\n        PsiElement(ShireTokenType.COLON)(':')\n        ShireFrontMatterValueImpl(FRONT_MATTER_VALUE)\n          PsiElement(ShireTokenType.NEWLINE)('\\n')\n          ShireObjectKeyValueImpl(OBJECT_KEY_VALUE)\n            PsiElement(ShireTokenType.INDENT)('  ')\n            ShireKeyValueImpl(KEY_VALUE)\n              ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n                ShireFrontMatterKeyImpl(FRONT_MATTER_KEY)\n                  PatternElement(PATTERN)\n                    PsiElement(ShireTokenType.PATTERN_EXPR)('/.*Controller.java/')\n                PsiElement(ShireTokenType.COLON)(':')\n                PsiWhiteSpace(' ')\n                ShireFrontMatterValueImpl(FRONT_MATTER_VALUE)\n                  PsiElement(ShireTokenType.QUOTE_STRING)('\"When testing controller, you MUST use MockMvc and test API only.\"')\n                PsiElement(ShireTokenType.NEWLINE)('\\n')\n      ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n        ShireFrontMatterKeyImpl(FRONT_MATTER_KEY)\n          ShireFrontMatterIdImpl(FRONT_MATTER_ID)\n            PsiElement(ShireTokenType.IDENTIFIER)('variables')\n        PsiElement(ShireTokenType.COLON)(':')\n        ShireFrontMatterValueImpl(FRONT_MATTER_VALUE)\n          PsiElement(ShireTokenType.NEWLINE)('\\n')\n          ShireObjectKeyValueImpl(OBJECT_KEY_VALUE)\n            PsiElement(ShireTokenType.INDENT)('  ')\n            ShireKeyValueImpl(KEY_VALUE)\n              ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n                ShireFrontMatterKeyImpl(FRONT_MATTER_KEY)\n                  PsiElement(ShireTokenType.QUOTE_STRING)('\"extContext\"')\n                PsiElement(ShireTokenType.COLON)(':')\n                PsiWhiteSpace(' ')\n                ShirePatternActionImpl(PATTERN_ACTION)\n                  PatternElement(PATTERN)\n                    PsiElement(ShireTokenType.PATTERN_EXPR)('/build\\.gradle\\.kts/')\n                  PsiWhiteSpace(' ')\n                  ShireActionBlockImpl(ACTION_BLOCK)\n                    PsiElement(ShireTokenType.{)('{')\n                    PsiWhiteSpace(' ')\n                    ShireActionBodyImpl(ACTION_BODY)\n                      ShireActionExprImpl(ACTION_EXPR)\n                        ShireFuncCallImpl(FUNC_CALL)\n                          ShireFuncNameImpl(FUNC_NAME)\n                            PsiElement(ShireTokenType.IDENTIFIER)('cat')\n                      PsiWhiteSpace(' ')\n                      PsiElement(ShireTokenType.|)('|')\n                      PsiWhiteSpace(' ')\n                      ShireActionExprImpl(ACTION_EXPR)\n                        ShireGrepFuncCall(FUNC_CALL)\n                          ShireFuncNameImpl(FUNC_NAME)\n                            PsiElement(ShireTokenType.IDENTIFIER)('grep')\n                          PsiElement(ShireTokenType.()('(')\n                          ShirePipelineArgsImpl(PIPELINE_ARGS)\n                            ShirePipelineArgImpl(PIPELINE_ARG)\n                              PsiElement(ShireTokenType.QUOTE_STRING)('\"org.springframework.boot:spring-boot-starter-jdbc\"')\n                          PsiElement(ShireTokenType.))(')')\n                      PsiWhiteSpace(' ')\n                      PsiElement(ShireTokenType.|)('|')\n                      PsiWhiteSpace(' ')\n                      ShireActionExprImpl(ACTION_EXPR)\n                        ShireFuncCallImpl(FUNC_CALL)\n                          ShireFuncNameImpl(FUNC_NAME)\n                            PsiElement(ShireTokenType.IDENTIFIER)('print')\n                          PsiElement(ShireTokenType.()('(')\n                          ShirePipelineArgsImpl(PIPELINE_ARGS)\n                            ShirePipelineArgImpl(PIPELINE_ARG)\n                              PsiElement(ShireTokenType.QUOTE_STRING)('\"This project use Spring Framework\"')\n                          PsiElement(ShireTokenType.))(')')\n                    PsiElement(ShireTokenType.})('}')\n                    PsiElement(ShireTokenType.NEWLINE)('\\n')\n            PsiElement(ShireTokenType.INDENT)('  ')\n            ShireKeyValueImpl(KEY_VALUE)\n              ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n                ShireFrontMatterKeyImpl(FRONT_MATTER_KEY)\n                  PsiElement(ShireTokenType.QUOTE_STRING)('\"testTemplate\"')\n                PsiElement(ShireTokenType.COLON)(':')\n                PsiWhiteSpace(' ')\n                ShirePatternActionImpl(PATTERN_ACTION)\n                  PatternElement(PATTERN)\n                    PsiElement(ShireTokenType.PATTERN_EXPR)('/\\(.*\\).java/')\n                  PsiWhiteSpace(' ')\n                  ShireActionBlockImpl(ACTION_BLOCK)\n                    PsiElement(ShireTokenType.{)('{')\n                    PsiElement(ShireTokenType.NEWLINE)('\\n')\n                    PsiWhiteSpace('    ')\n                    ShireActionBodyImpl(ACTION_BODY)\n                      ShireActionExprImpl(ACTION_EXPR)\n                        ShireCaseBodyImpl(CASE_BODY)\n                          PsiElement(ShireTokenType.case)('case')\n                          PsiWhiteSpace(' ')\n                          PsiElement(ShireTokenType.QUOTE_STRING)('\"$1\"')\n                          PsiWhiteSpace(' ')\n                          PsiElement(ShireTokenType.{)('{')\n                          PsiElement(ShireTokenType.NEWLINE)('\\n')\n                          PsiWhiteSpace('      ')\n                          ShireCasePatternActionImpl(CASE_PATTERN_ACTION)\n                            ShireCaseConditionImpl(CASE_CONDITION)\n                              PsiElement(ShireTokenType.QUOTE_STRING)('\"Controller\"')\n                            PsiWhiteSpace(' ')\n                            PsiElement(ShireTokenType.{)('{')\n                            PsiWhiteSpace(' ')\n                            ShireActionBodyImpl(ACTION_BODY)\n                              ShireActionExprImpl(ACTION_EXPR)\n                                ShireFuncCallImpl(FUNC_CALL)\n                                  ShireFuncNameImpl(FUNC_NAME)\n                                    PsiElement(ShireTokenType.IDENTIFIER)('cat')\n                                  PsiElement(ShireTokenType.()('(')\n                                  ShirePipelineArgsImpl(PIPELINE_ARGS)\n                                    ShirePipelineArgImpl(PIPELINE_ARG)\n                                      PsiElement(ShireTokenType.QUOTE_STRING)('\".shire/templates/ControllerTest.java\"')\n                                  PsiElement(ShireTokenType.))(')')\n                            PsiWhiteSpace(' ')\n                            PsiElement(ShireTokenType.})('}')\n                            PsiElement(ShireTokenType.NEWLINE)('\\n')\n                          PsiWhiteSpace('      ')\n                          ShireCasePatternActionImpl(CASE_PATTERN_ACTION)\n                            ShireCaseConditionImpl(CASE_CONDITION)\n                              PsiElement(ShireTokenType.QUOTE_STRING)('\"Service\"')\n                            PsiWhiteSpace(' ')\n                            PsiElement(ShireTokenType.{)('{')\n                            PsiWhiteSpace(' ')\n                            ShireActionBodyImpl(ACTION_BODY)\n                              ShireActionExprImpl(ACTION_EXPR)\n                                ShireFuncCallImpl(FUNC_CALL)\n                                  ShireFuncNameImpl(FUNC_NAME)\n                                    PsiElement(ShireTokenType.IDENTIFIER)('cat')\n                                  PsiElement(ShireTokenType.()('(')\n                                  ShirePipelineArgsImpl(PIPELINE_ARGS)\n                                    ShirePipelineArgImpl(PIPELINE_ARG)\n                                      PsiElement(ShireTokenType.QUOTE_STRING)('\".shire/templates/ServiceTest.java\"')\n                                  PsiElement(ShireTokenType.))(')')\n                            PsiWhiteSpace(' ')\n                            PsiElement(ShireTokenType.})('}')\n                            PsiElement(ShireTokenType.NEWLINE)('\\n')\n                          PsiWhiteSpace('      ')\n                          ShireCasePatternActionImpl(CASE_PATTERN_ACTION)\n                            ShireCaseConditionImpl(CASE_CONDITION)\n                              PsiElement(ShireTokenType.default)('default')\n                            PsiWhiteSpace('  ')\n                            PsiElement(ShireTokenType.{)('{')\n                            PsiWhiteSpace(' ')\n                            ShireActionBodyImpl(ACTION_BODY)\n                              ShireActionExprImpl(ACTION_EXPR)\n                                ShireFuncCallImpl(FUNC_CALL)\n                                  ShireFuncNameImpl(FUNC_NAME)\n                                    PsiElement(ShireTokenType.IDENTIFIER)('cat')\n                                  PsiElement(ShireTokenType.()('(')\n                                  ShirePipelineArgsImpl(PIPELINE_ARGS)\n                                    ShirePipelineArgImpl(PIPELINE_ARG)\n                                      PsiElement(ShireTokenType.QUOTE_STRING)('\".shire/templates/DefaultTest.java\"')\n                                  PsiElement(ShireTokenType.))(')')\n                            PsiWhiteSpace(' ')\n                            PsiElement(ShireTokenType.})('}')\n                            PsiElement(ShireTokenType.NEWLINE)('\\n')\n                          PsiWhiteSpace('    ')\n                          PsiElement(ShireTokenType.})('}')\n                          PsiElement(ShireTokenType.NEWLINE)('\\n')\n                    PsiWhiteSpace('  ')\n                    PsiElement(ShireTokenType.})('}')\n                    PsiElement(ShireTokenType.NEWLINE)('\\n')\n            PsiElement(ShireTokenType.INDENT)('  ')\n            ShireKeyValueImpl(KEY_VALUE)\n              ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n                ShireFrontMatterKeyImpl(FRONT_MATTER_KEY)\n                  PsiElement(ShireTokenType.QUOTE_STRING)('\"allController\"')\n                PsiElement(ShireTokenType.COLON)(':')\n                PsiWhiteSpace(' ')\n                ShireFunctionStatementImpl(FUNCTION_STATEMENT)\n                  PsiElement(ShireTokenType.{)('{')\n                  PsiElement(ShireTokenType.NEWLINE)('\\n')\n                  PsiWhiteSpace('    ')\n                  ShireFunctionBodyImpl(FUNCTION_BODY)\n                    ShireQueryStatementImpl(QUERY_STATEMENT)\n                      ShireFromClauseImpl(FROM_CLAUSE)\n                        PsiElement(ShireTokenType.from)('from')\n                        PsiWhiteSpace(' ')\n                        PsiElement(ShireTokenType.{)('{')\n                        PsiElement(ShireTokenType.NEWLINE)('\\n')\n                        PsiWhiteSpace('        ')\n                        ShirePsiElementDeclImpl(PSI_ELEMENT_DECL)\n                          ShirePsiVarDeclImpl(PSI_VAR_DECL)\n                            ShirePsiTypeImpl(PSI_TYPE)\n                              PsiElement(ShireTokenType.IDENTIFIER)('PsiClass')\n                            PsiWhiteSpace(' ')\n                            PsiElement(ShireTokenType.IDENTIFIER)('clazz')\n                          PsiWhiteSpace(' ')\n                          PsiComment(ShireTokenType.BLOCK_COMMENT)('/* sample */')\n                          PsiElement(ShireTokenType.NEWLINE)('\\n')\n                        PsiWhiteSpace('    ')\n                        PsiElement(ShireTokenType.})('}')\n                        PsiElement(ShireTokenType.NEWLINE)('\\n')\n                      PsiWhiteSpace('    ')\n                      ShireWhereClauseImpl(WHERE_CLAUSE)\n                        PsiElement(ShireTokenType.where)('where')\n                        PsiWhiteSpace(' ')\n                        PsiElement(ShireTokenType.{)('{')\n                        PsiElement(ShireTokenType.NEWLINE)('\\n')\n                        PsiWhiteSpace('        ')\n                        ShireIneqComparisonExprImpl(INEQ_COMPARISON_EXPR)\n                          ShireCallExprImpl(CALL_EXPR)\n                            ShireRefExprImpl(REF_EXPR)\n                              ShireCallExprImpl(CALL_EXPR)\n                                ShireRefExprImpl(REF_EXPR)\n                                  ShireRefExprImpl(REF_EXPR)\n                                    PsiElement(ShireTokenType.IDENTIFIER)('clazz')\n                                  PsiElement(ShireTokenType..)('.')\n                                  PsiElement(ShireTokenType.IDENTIFIER)('getMethods')\n                                PsiElement(ShireTokenType.()('(')\n                                PsiElement(ShireTokenType.))(')')\n                              PsiElement(ShireTokenType..)('.')\n                              PsiElement(ShireTokenType.IDENTIFIER)('length')\n                            PsiElement(ShireTokenType.()('(')\n                            PsiElement(ShireTokenType.))(')')\n                          PsiWhiteSpace(' ')\n                          ShireIneqComparisonOpImpl(INEQ_COMPARISON_OP)\n                            PsiElement(ShireTokenType.>)('>')\n                          PsiWhiteSpace(' ')\n                          ShireLiteralExprImpl(LITERAL_EXPR)\n                            PsiElement(ShireTokenType.NUMBER)('0')\n                        PsiElement(ShireTokenType.NEWLINE)('\\n')\n                        PsiWhiteSpace('    ')\n                        PsiElement(ShireTokenType.})('}')\n                        PsiElement(ShireTokenType.NEWLINE)('\\n')\n                      PsiWhiteSpace('    ')\n                      ShireSelectClauseImpl(SELECT_CLAUSE)\n                        PsiElement(ShireTokenType.select)('select')\n                        PsiWhiteSpace(' ')\n                        PsiElement(ShireTokenType.{)('{')\n                        PsiElement(ShireTokenType.NEWLINE)('\\n')\n                        PsiWhiteSpace('        ')\n                        ShireCallExprImpl(CALL_EXPR)\n                          ShireRefExprImpl(REF_EXPR)\n                            ShireRefExprImpl(REF_EXPR)\n                              PsiElement(ShireTokenType.IDENTIFIER)('clazz')\n                            PsiElement(ShireTokenType..)('.')\n                            PsiElement(ShireTokenType.IDENTIFIER)('getMethods')\n                          PsiElement(ShireTokenType.()('(')\n                          PsiElement(ShireTokenType.))(')')\n                        PsiElement(ShireTokenType.NEWLINE)('\\n')\n                        PsiWhiteSpace('    ')\n                        PsiElement(ShireTokenType.})('}')\n                        PsiElement(ShireTokenType.NEWLINE)('\\n')\n                  PsiWhiteSpace('  ')\n                  PsiElement(ShireTokenType.})('}')\n                  PsiElement(ShireTokenType.NEWLINE)('\\n')\n    PsiElement(ShireTokenType.FRONTMATTER_END)('---')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.TEXT_SEGMENT)('Write unit test for following ')\n  ShireUsedImpl(USED)\n    ShireVariableStartImpl(VARIABLE_START)\n      PsiElement(VARIABLE_START)('$')\n    ShireVarAccessImpl(VAR_ACCESS)\n      PsiElement(ShireTokenType.{)('{')\n      ShireVariableIdImpl(VARIABLE_ID)\n        PsiElement(ShireTokenType.IDENTIFIER)('context')\n      PsiElement(ShireTokenType..)('.')\n      ShireVariableIdImpl(VARIABLE_ID)\n        PsiElement(ShireTokenType.IDENTIFIER)('language')\n      PsiElement(ShireTokenType.})('}')\n  PsiElement(ShireTokenType.TEXT_SEGMENT)(' code.')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  ShireUsedImpl(USED)\n    ShireVariableStartImpl(VARIABLE_START)\n      PsiElement(VARIABLE_START)('$')\n    ShireVarAccessImpl(VAR_ACCESS)\n      PsiElement(ShireTokenType.{)('{')\n      ShireVariableIdImpl(VARIABLE_ID)\n        PsiElement(ShireTokenType.IDENTIFIER)('context')\n      PsiElement(ShireTokenType..)('.')\n      ShireVariableIdImpl(VARIABLE_ID)\n        PsiElement(ShireTokenType.IDENTIFIER)('frameworkContext')\n      PsiElement(ShireTokenType.})('}')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  ShireVelocityExprImpl(VELOCITY_EXPR)\n    ShireIfExprImpl(IF_EXPR)\n      ShireIfClauseImpl(IF_CLAUSE)\n        PsiElement(ShireTokenType.#)('#')\n        PsiElement(ShireTokenType.if)('if')\n        PsiElement(ShireTokenType.()('(')\n        ShireIneqComparisonExprImpl(INEQ_COMPARISON_EXPR)\n          ShireCallExprImpl(CALL_EXPR)\n            ShireRefExprImpl(REF_EXPR)\n              ShireRefExprImpl(REF_EXPR)\n                ShireLiteralExprImpl(LITERAL_EXPR)\n                  PsiElement(VARIABLE_START)('$')\n                  PsiElement(ShireTokenType.IDENTIFIER)('context')\n                PsiElement(ShireTokenType..)('.')\n                PsiElement(ShireTokenType.IDENTIFIER)('relatedClasses')\n              PsiElement(ShireTokenType..)('.')\n              PsiElement(ShireTokenType.IDENTIFIER)('length')\n            PsiElement(ShireTokenType.()('(')\n            PsiElement(ShireTokenType.))(')')\n          PsiWhiteSpace(' ')\n          ShireIneqComparisonOpImpl(INEQ_COMPARISON_OP)\n            PsiElement(ShireTokenType.>)('>')\n          PsiWhiteSpace(' ')\n          ShireLiteralExprImpl(LITERAL_EXPR)\n            PsiElement(ShireTokenType.NUMBER)('0')\n        PsiWhiteSpace(' ')\n        PsiElement(ShireTokenType.))(')')\n        ShireVelocityBlockImpl(VELOCITY_BLOCK)\n          PsiElement(ShireTokenType.NEWLINE)('\\n')\n          PsiElement(ShireTokenType.TEXT_SEGMENT)('Here is the relate code maybe you can use')\n          PsiElement(ShireTokenType.NEWLINE)('\\n')\n          ShireUsedImpl(USED)\n            ShireVariableStartImpl(VARIABLE_START)\n              PsiElement(VARIABLE_START)('$')\n            ShireVarAccessImpl(VAR_ACCESS)\n              PsiElement(ShireTokenType.{)('{')\n              ShireVariableIdImpl(VARIABLE_ID)\n                PsiElement(ShireTokenType.IDENTIFIER)('context')\n              PsiElement(ShireTokenType..)('.')\n              ShireVariableIdImpl(VARIABLE_ID)\n                PsiElement(ShireTokenType.IDENTIFIER)('relatedClasses')\n              PsiElement(ShireTokenType.})('}')\n          PsiElement(ShireTokenType.NEWLINE)('\\n')\n      PsiElement(ShireTokenType.#)('#')\n      PsiElement(ShireTokenType.end)('end')\n    PsiElement(ShireTokenType.NEWLINE)('\\n')\n    PsiElement(ShireTokenType.NEWLINE)('\\n')\n  ShireVelocityExprImpl(VELOCITY_EXPR)\n    ShireIfExprImpl(IF_EXPR)\n      ShireIfClauseImpl(IF_CLAUSE)\n        PsiElement(ShireTokenType.#)('#')\n        PsiElement(ShireTokenType.if)('if')\n        PsiElement(ShireTokenType.()('(')\n        ShireIneqComparisonExprImpl(INEQ_COMPARISON_EXPR)\n          ShireCallExprImpl(CALL_EXPR)\n            ShireRefExprImpl(REF_EXPR)\n              ShireRefExprImpl(REF_EXPR)\n                ShireLiteralExprImpl(LITERAL_EXPR)\n                  PsiElement(VARIABLE_START)('$')\n                  PsiElement(ShireTokenType.IDENTIFIER)('context')\n                PsiElement(ShireTokenType..)('.')\n                PsiElement(ShireTokenType.IDENTIFIER)('currentClassName')\n              PsiElement(ShireTokenType..)('.')\n              PsiElement(ShireTokenType.IDENTIFIER)('length')\n            PsiElement(ShireTokenType.()('(')\n            PsiElement(ShireTokenType.))(')')\n          PsiWhiteSpace(' ')\n          ShireIneqComparisonOpImpl(INEQ_COMPARISON_OP)\n            PsiElement(ShireTokenType.>)('>')\n          PsiWhiteSpace(' ')\n          ShireLiteralExprImpl(LITERAL_EXPR)\n            PsiElement(ShireTokenType.NUMBER)('0')\n        PsiWhiteSpace(' ')\n        PsiElement(ShireTokenType.))(')')\n        ShireVelocityBlockImpl(VELOCITY_BLOCK)\n          PsiElement(ShireTokenType.NEWLINE)('\\n')\n          PsiElement(ShireTokenType.TEXT_SEGMENT)('This is the class where the source code resides:')\n          PsiElement(ShireTokenType.NEWLINE)('\\n')\n          ShireUsedImpl(USED)\n            ShireVariableStartImpl(VARIABLE_START)\n              PsiElement(VARIABLE_START)('$')\n            ShireVarAccessImpl(VAR_ACCESS)\n              PsiElement(ShireTokenType.{)('{')\n              ShireVariableIdImpl(VARIABLE_ID)\n                PsiElement(ShireTokenType.IDENTIFIER)('context')\n              PsiElement(ShireTokenType..)('.')\n              ShireVariableIdImpl(VARIABLE_ID)\n                PsiElement(ShireTokenType.IDENTIFIER)('currentClassCode')\n              PsiElement(ShireTokenType.})('}')\n          PsiElement(ShireTokenType.NEWLINE)('\\n')\n      PsiElement(ShireTokenType.#)('#')\n      PsiElement(ShireTokenType.end)('end')\n    PsiElement(ShireTokenType.NEWLINE)('\\n')\n    PsiElement(ShireTokenType.NEWLINE)('\\n')\n  ShireUsedImpl(USED)\n    ShireVariableStartImpl(VARIABLE_START)\n      PsiElement(VARIABLE_START)('$')\n    ShireVarAccessImpl(VAR_ACCESS)\n      PsiElement(ShireTokenType.{)('{')\n      ShireVariableIdImpl(VARIABLE_ID)\n        PsiElement(ShireTokenType.IDENTIFIER)('context')\n      PsiElement(ShireTokenType..)('.')\n      ShireVariableIdImpl(VARIABLE_ID)\n        PsiElement(ShireTokenType.IDENTIFIER)('extContext')\n      PsiElement(ShireTokenType.})('}')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.TEXT_SEGMENT)('Here is the imports to help you understand:')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  ShireUsedImpl(USED)\n    ShireVariableStartImpl(VARIABLE_START)\n      PsiElement(VARIABLE_START)('$')\n    ShireVarAccessImpl(VAR_ACCESS)\n      PsiElement(ShireTokenType.{)('{')\n      ShireVariableIdImpl(VARIABLE_ID)\n        PsiElement(ShireTokenType.IDENTIFIER)('context')\n      PsiElement(ShireTokenType..)('.')\n      ShireVariableIdImpl(VARIABLE_ID)\n        PsiElement(ShireTokenType.IDENTIFIER)('imports')\n      PsiElement(ShireTokenType.})('}')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.TEXT_SEGMENT)('Here is the source code to be tested:')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  CodeBlockElement(CODE)\n    PsiElement(ShireTokenType.CODE_BLOCK_START)('```')\n    ShireVariableStartImpl(VARIABLE_START)\n      PsiElement(VARIABLE_START)('$')\n    ShireVariableExprImpl(VARIABLE_EXPR)\n      PsiElement(ShireTokenType.{)('{')\n      ShireRefExprImpl(REF_EXPR)\n        ShireRefExprImpl(REF_EXPR)\n          PsiElement(ShireTokenType.IDENTIFIER)('context')\n        PsiElement(ShireTokenType..)('.')\n        PsiElement(ShireTokenType.IDENTIFIER)('language')\n      PsiElement(ShireTokenType.})('}')\n    PsiElement(ShireTokenType.NEWLINE)('\\n')\n    ASTWrapperPsiElement(CODE_CONTENTS)\n      PsiElement(ShireTokenType.CODE_CONTENT)('${context.selection}')\n      PsiElement(ShireTokenType.NEWLINE)('\\n')\n    PsiElement(ShireTokenType.CODE_BLOCK_END)('```')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  ShireVelocityExprImpl(VELOCITY_EXPR)\n    ShireIfExprImpl(IF_EXPR)\n      ShireIfClauseImpl(IF_CLAUSE)\n        PsiElement(ShireTokenType.#)('#')\n        PsiElement(ShireTokenType.if)('if')\n        PsiElement(ShireTokenType.()('(')\n        ShireRefExprImpl(REF_EXPR)\n          ShireLiteralExprImpl(LITERAL_EXPR)\n            PsiElement(VARIABLE_START)('$')\n            PsiElement(ShireTokenType.IDENTIFIER)('context')\n          PsiElement(ShireTokenType..)('.')\n          PsiElement(ShireTokenType.IDENTIFIER)('isNewFile')\n        PsiElement(ShireTokenType.))(')')\n        ShireVelocityBlockImpl(VELOCITY_BLOCK)\n          PsiElement(ShireTokenType.NEWLINE)('\\n')\n          PsiElement(ShireTokenType.TEXT_SEGMENT)('Should include package and imports. Start method test code with Markdown code block here:')\n          PsiElement(ShireTokenType.NEWLINE)('\\n')\n      ShireElseClauseImpl(ELSE_CLAUSE)\n        PsiElement(ShireTokenType.#)('#')\n        PsiElement(ShireTokenType.else)('else')\n        ShireVelocityBlockImpl(VELOCITY_BLOCK)\n          PsiElement(ShireTokenType.NEWLINE)('\\n')\n          PsiElement(ShireTokenType.TEXT_SEGMENT)('Should include package and imports. Start ')\n          ShireUsedImpl(USED)\n            ShireVariableStartImpl(VARIABLE_START)\n              PsiElement(VARIABLE_START)('$')\n            ShireVarAccessImpl(VAR_ACCESS)\n              PsiElement(ShireTokenType.{)('{')\n              ShireVariableIdImpl(VARIABLE_ID)\n                PsiElement(ShireTokenType.IDENTIFIER)('context')\n              PsiElement(ShireTokenType..)('.')\n              ShireVariableIdImpl(VARIABLE_ID)\n                PsiElement(ShireTokenType.IDENTIFIER)('targetTestFileName')\n              PsiElement(ShireTokenType.})('}')\n          PsiElement(ShireTokenType.TEXT_SEGMENT)(' test code with Markdown code block here:')\n          PsiElement(ShireTokenType.NEWLINE)('\\n')\n      PsiElement(ShireTokenType.#)('#')\n      PsiElement(ShireTokenType.end)('end')\n    PsiElement(ShireTokenType.NEWLINE)('\\n')\n    PsiElement(ShireTokenType.NEWLINE)('\\n')\n  ShireUsedImpl(USED)\n    ShireVariableStartImpl(VARIABLE_START)\n      PsiElement(VARIABLE_START)('$')\n    ShireVariableIdImpl(VARIABLE_ID)\n      PsiElement(ShireTokenType.IDENTIFIER)('allController')"
  },
  {
    "path": "shirelang/src/test/testData/realworld/ContentTee.shire",
    "content": "---\nname: \"Context Variable\"\ndescription: \"Here is a description of the action.\"\ninteraction:  RunPanel\nvariables:\n  \"currentCode\": /HobbitHole\\.kt/ { cat }\n  \"testCode\": /ShireCompileTest\\.kt/ { cat }\n  \"actionLocation\": /ShireActionLocation\\.kt/ { cat }\nonStreamingEnd: { append($actionLocation) | saveFile(\"docs/shire/shire-hobbit-hole.md\") }\n---\n\n我有一份用户手册写得不好，需要你从用户容易阅读的角度，重新写一份。\n\n根据如下的代码用例、文档，编写对应的 HobbitHole 相关信息的 markdown 文档。\n\n现有代码：\n\n$currentCode\n\n代码用例如下：\n\n$testCode\n\n要求：\n\n1. 尽详细介绍 HobbitHole 的相关信息和示例。\n2. 请按现有的文档 Heading 方式编写，并去除非必要的代码。\n"
  },
  {
    "path": "shirelang/src/test/testData/realworld/ContentTee.txt",
    "content": "ShireFile\n  ShireFrontMatterHeaderImpl(FRONT_MATTER_HEADER)\n    PsiElement(ShireTokenType.FRONTMATTER_START)('---')\n    PsiElement(ShireTokenType.NEWLINE)('\\n')\n    ShireFrontMatterEntriesImpl(FRONT_MATTER_ENTRIES)\n      ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n        ShireFrontMatterKeyImpl(FRONT_MATTER_KEY)\n          ShireFrontMatterIdImpl(FRONT_MATTER_ID)\n            PsiElement(ShireTokenType.IDENTIFIER)('name')\n        PsiElement(ShireTokenType.COLON)(':')\n        PsiWhiteSpace(' ')\n        ShireFrontMatterValueImpl(FRONT_MATTER_VALUE)\n          PsiElement(ShireTokenType.QUOTE_STRING)('\"Context Variable\"')\n        PsiElement(ShireTokenType.NEWLINE)('\\n')\n      ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n        ShireFrontMatterKeyImpl(FRONT_MATTER_KEY)\n          ShireFrontMatterIdImpl(FRONT_MATTER_ID)\n            PsiElement(ShireTokenType.IDENTIFIER)('description')\n        PsiElement(ShireTokenType.COLON)(':')\n        PsiWhiteSpace(' ')\n        ShireFrontMatterValueImpl(FRONT_MATTER_VALUE)\n          PsiElement(ShireTokenType.QUOTE_STRING)('\"Here is a description of the action.\"')\n        PsiElement(ShireTokenType.NEWLINE)('\\n')\n      ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n        ShireFrontMatterKeyImpl(FRONT_MATTER_KEY)\n          ShireFrontMatterIdImpl(FRONT_MATTER_ID)\n            PsiElement(ShireTokenType.IDENTIFIER)('interaction')\n        PsiElement(ShireTokenType.COLON)(':')\n        PsiWhiteSpace('  ')\n        ShireFrontMatterValueImpl(FRONT_MATTER_VALUE)\n          PsiElement(ShireTokenType.IDENTIFIER)('RunPanel')\n        PsiElement(ShireTokenType.NEWLINE)('\\n')\n      ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n        ShireFrontMatterKeyImpl(FRONT_MATTER_KEY)\n          ShireFrontMatterIdImpl(FRONT_MATTER_ID)\n            PsiElement(ShireTokenType.IDENTIFIER)('variables')\n        PsiElement(ShireTokenType.COLON)(':')\n        ShireFrontMatterValueImpl(FRONT_MATTER_VALUE)\n          PsiElement(ShireTokenType.NEWLINE)('\\n')\n          ShireObjectKeyValueImpl(OBJECT_KEY_VALUE)\n            PsiElement(ShireTokenType.INDENT)('  ')\n            ShireKeyValueImpl(KEY_VALUE)\n              ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n                ShireFrontMatterKeyImpl(FRONT_MATTER_KEY)\n                  PsiElement(ShireTokenType.QUOTE_STRING)('\"currentCode\"')\n                PsiElement(ShireTokenType.COLON)(':')\n                PsiWhiteSpace(' ')\n                ShirePatternActionImpl(PATTERN_ACTION)\n                  PatternElement(PATTERN)\n                    PsiElement(ShireTokenType.PATTERN_EXPR)('/HobbitHole\\.kt/')\n                  PsiWhiteSpace(' ')\n                  ShireActionBlockImpl(ACTION_BLOCK)\n                    PsiElement(ShireTokenType.{)('{')\n                    PsiWhiteSpace(' ')\n                    ShireActionBodyImpl(ACTION_BODY)\n                      ShireActionExprImpl(ACTION_EXPR)\n                        ShireFuncCallImpl(FUNC_CALL)\n                          ShireFuncNameImpl(FUNC_NAME)\n                            PsiElement(ShireTokenType.IDENTIFIER)('cat')\n                    PsiWhiteSpace(' ')\n                    PsiElement(ShireTokenType.})('}')\n                    PsiElement(ShireTokenType.NEWLINE)('\\n')\n            PsiElement(ShireTokenType.INDENT)('  ')\n            ShireKeyValueImpl(KEY_VALUE)\n              ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n                ShireFrontMatterKeyImpl(FRONT_MATTER_KEY)\n                  PsiElement(ShireTokenType.QUOTE_STRING)('\"testCode\"')\n                PsiElement(ShireTokenType.COLON)(':')\n                PsiWhiteSpace(' ')\n                ShirePatternActionImpl(PATTERN_ACTION)\n                  PatternElement(PATTERN)\n                    PsiElement(ShireTokenType.PATTERN_EXPR)('/ShireCompileTest\\.kt/')\n                  PsiWhiteSpace(' ')\n                  ShireActionBlockImpl(ACTION_BLOCK)\n                    PsiElement(ShireTokenType.{)('{')\n                    PsiWhiteSpace(' ')\n                    ShireActionBodyImpl(ACTION_BODY)\n                      ShireActionExprImpl(ACTION_EXPR)\n                        ShireFuncCallImpl(FUNC_CALL)\n                          ShireFuncNameImpl(FUNC_NAME)\n                            PsiElement(ShireTokenType.IDENTIFIER)('cat')\n                    PsiWhiteSpace(' ')\n                    PsiElement(ShireTokenType.})('}')\n                    PsiElement(ShireTokenType.NEWLINE)('\\n')\n            PsiElement(ShireTokenType.INDENT)('  ')\n            ShireKeyValueImpl(KEY_VALUE)\n              ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n                ShireFrontMatterKeyImpl(FRONT_MATTER_KEY)\n                  PsiElement(ShireTokenType.QUOTE_STRING)('\"actionLocation\"')\n                PsiElement(ShireTokenType.COLON)(':')\n                PsiWhiteSpace(' ')\n                ShirePatternActionImpl(PATTERN_ACTION)\n                  PatternElement(PATTERN)\n                    PsiElement(ShireTokenType.PATTERN_EXPR)('/ShireActionLocation\\.kt/')\n                  PsiWhiteSpace(' ')\n                  ShireActionBlockImpl(ACTION_BLOCK)\n                    PsiElement(ShireTokenType.{)('{')\n                    PsiWhiteSpace(' ')\n                    ShireActionBodyImpl(ACTION_BODY)\n                      ShireActionExprImpl(ACTION_EXPR)\n                        ShireFuncCallImpl(FUNC_CALL)\n                          ShireFuncNameImpl(FUNC_NAME)\n                            PsiElement(ShireTokenType.IDENTIFIER)('cat')\n                    PsiWhiteSpace(' ')\n                    PsiElement(ShireTokenType.})('}')\n                    PsiElement(ShireTokenType.NEWLINE)('\\n')\n      ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n        ShireLifecycleIdImpl(LIFECYCLE_ID)\n          PsiElement(ShireTokenType.onStreamingEnd)('onStreamingEnd')\n        PsiElement(ShireTokenType.COLON)(':')\n        PsiWhiteSpace(' ')\n        ShireFunctionStatementImpl(FUNCTION_STATEMENT)\n          PsiElement(ShireTokenType.{)('{')\n          PsiWhiteSpace(' ')\n          ShireFunctionBodyImpl(FUNCTION_BODY)\n            ShireActionBodyImpl(ACTION_BODY)\n              ShireActionExprImpl(ACTION_EXPR)\n                ShireFuncCallImpl(FUNC_CALL)\n                  ShireFuncNameImpl(FUNC_NAME)\n                    PsiElement(ShireTokenType.IDENTIFIER)('append')\n                  PsiElement(ShireTokenType.()('(')\n                  ShirePipelineArgsImpl(PIPELINE_ARGS)\n                    ShirePipelineArgImpl(PIPELINE_ARG)\n                      ShireVariableStartImpl(VARIABLE_START)\n                        PsiElement(VARIABLE_START)('$')\n                      ShireVariableIdImpl(VARIABLE_ID)\n                        PsiElement(ShireTokenType.IDENTIFIER)('actionLocation')\n                  PsiElement(ShireTokenType.))(')')\n              PsiWhiteSpace(' ')\n              PsiElement(ShireTokenType.|)('|')\n              PsiWhiteSpace(' ')\n              ShireActionExprImpl(ACTION_EXPR)\n                ShireFuncCallImpl(FUNC_CALL)\n                  ShireFuncNameImpl(FUNC_NAME)\n                    PsiElement(ShireTokenType.IDENTIFIER)('saveFile')\n                  PsiElement(ShireTokenType.()('(')\n                  ShirePipelineArgsImpl(PIPELINE_ARGS)\n                    ShirePipelineArgImpl(PIPELINE_ARG)\n                      PsiElement(ShireTokenType.QUOTE_STRING)('\"docs/shire/shire-hobbit-hole.md\"')\n                  PsiElement(ShireTokenType.))(')')\n          PsiWhiteSpace(' ')\n          PsiElement(ShireTokenType.})('}')\n          PsiElement(ShireTokenType.NEWLINE)('\\n')\n    PsiElement(ShireTokenType.FRONTMATTER_END)('---')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.TEXT_SEGMENT)('我有一份用户手册写得不好，需要你从用户容易阅读的角度，重新写一份。')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.TEXT_SEGMENT)('根据如下的代码用例、文档，编写对应的 HobbitHole 相关信息的 markdown 文档。')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.TEXT_SEGMENT)('现有代码：')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  ShireUsedImpl(USED)\n    ShireVariableStartImpl(VARIABLE_START)\n      PsiElement(VARIABLE_START)('$')\n    ShireVariableIdImpl(VARIABLE_ID)\n      PsiElement(ShireTokenType.IDENTIFIER)('currentCode')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.TEXT_SEGMENT)('代码用例如下：')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  ShireUsedImpl(USED)\n    ShireVariableStartImpl(VARIABLE_START)\n      PsiElement(VARIABLE_START)('$')\n    ShireVariableIdImpl(VARIABLE_ID)\n      PsiElement(ShireTokenType.IDENTIFIER)('testCode')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.TEXT_SEGMENT)('要求：')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.TEXT_SEGMENT)('1. 尽详细介绍 HobbitHole 的相关信息和示例。')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.TEXT_SEGMENT)('2. 请按现有的文档 Heading 方式编写，并去除非必要的代码。')"
  },
  {
    "path": "shirelang/src/test/testData/realworld/LifeCycle.shire",
    "content": "---\nwhen: \"xxx\"\nvariables:\n  \"testTemplate\": /\\(.*\\).java/ {\n    case \"$1\" {\n      \"Controller\" { cat(\".shire/templates/ControllerTest.java\") }\n      \"Service\" { cat(\".shire/templates/ServiceTest.java\") }\n      default  { cat(\".shire/templates/DefaultTest.java\") }\n    }\n  }\nonStreaming: { /* functions */ }\nonStreamingEnd: { parseCode(\"json\") | verifyCode(\"json\") | runCode(\"json\") }\nafterStreaming: {\n    condition {\n      \"variable-success\" { $selection.length > 1 }\n      \"jsonpath-success\" { jsonpath(\"/bookstore/book[price>35]\") }\n    }\n    case condition {\n      \"variable-sucesss\" { done }\n      \"jsonpath-success\" { task() }\n      default { task() }\n    }\n  }\n---\n\nonStreamingDone:\n\n- Array of processor\n- Object flow\n\n```markdown\n- //bookstore/book[price>35]\n- $.phoneNumbers[:1].type\n- Regex\n```\n"
  },
  {
    "path": "shirelang/src/test/testData/realworld/LifeCycle.txt",
    "content": "ShireFile\n  ShireFrontMatterHeaderImpl(FRONT_MATTER_HEADER)\n    PsiElement(ShireTokenType.FRONTMATTER_START)('---')\n    PsiElement(ShireTokenType.NEWLINE)('\\n')\n    ShireFrontMatterEntriesImpl(FRONT_MATTER_ENTRIES)\n      ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n        ShireLifecycleIdImpl(LIFECYCLE_ID)\n          PsiElement(ShireTokenType.when)('when')\n        PsiElement(ShireTokenType.COLON)(':')\n        PsiWhiteSpace(' ')\n        ShireLiteralExprImpl(LITERAL_EXPR)\n          PsiElement(ShireTokenType.QUOTE_STRING)('\"xxx\"')\n        PsiElement(ShireTokenType.NEWLINE)('\\n')\n      ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n        ShireFrontMatterKeyImpl(FRONT_MATTER_KEY)\n          ShireFrontMatterIdImpl(FRONT_MATTER_ID)\n            PsiElement(ShireTokenType.IDENTIFIER)('variables')\n        PsiElement(ShireTokenType.COLON)(':')\n        ShireFrontMatterValueImpl(FRONT_MATTER_VALUE)\n          PsiElement(ShireTokenType.NEWLINE)('\\n')\n          ShireObjectKeyValueImpl(OBJECT_KEY_VALUE)\n            PsiElement(ShireTokenType.INDENT)('  ')\n            ShireKeyValueImpl(KEY_VALUE)\n              ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n                ShireFrontMatterKeyImpl(FRONT_MATTER_KEY)\n                  PsiElement(ShireTokenType.QUOTE_STRING)('\"testTemplate\"')\n                PsiElement(ShireTokenType.COLON)(':')\n                PsiWhiteSpace(' ')\n                ShirePatternActionImpl(PATTERN_ACTION)\n                  PatternElement(PATTERN)\n                    PsiElement(ShireTokenType.PATTERN_EXPR)('/\\(.*\\).java/')\n                  PsiWhiteSpace(' ')\n                  ShireActionBlockImpl(ACTION_BLOCK)\n                    PsiElement(ShireTokenType.{)('{')\n                    PsiElement(ShireTokenType.NEWLINE)('\\n')\n                    PsiWhiteSpace('    ')\n                    ShireActionBodyImpl(ACTION_BODY)\n                      ShireActionExprImpl(ACTION_EXPR)\n                        ShireCaseBodyImpl(CASE_BODY)\n                          PsiElement(ShireTokenType.case)('case')\n                          PsiWhiteSpace(' ')\n                          PsiElement(ShireTokenType.QUOTE_STRING)('\"$1\"')\n                          PsiWhiteSpace(' ')\n                          PsiElement(ShireTokenType.{)('{')\n                          PsiElement(ShireTokenType.NEWLINE)('\\n')\n                          PsiWhiteSpace('      ')\n                          ShireCasePatternActionImpl(CASE_PATTERN_ACTION)\n                            ShireCaseConditionImpl(CASE_CONDITION)\n                              PsiElement(ShireTokenType.QUOTE_STRING)('\"Controller\"')\n                            PsiWhiteSpace(' ')\n                            PsiElement(ShireTokenType.{)('{')\n                            PsiWhiteSpace(' ')\n                            ShireActionBodyImpl(ACTION_BODY)\n                              ShireActionExprImpl(ACTION_EXPR)\n                                ShireFuncCallImpl(FUNC_CALL)\n                                  ShireFuncNameImpl(FUNC_NAME)\n                                    PsiElement(ShireTokenType.IDENTIFIER)('cat')\n                                  PsiElement(ShireTokenType.()('(')\n                                  ShirePipelineArgsImpl(PIPELINE_ARGS)\n                                    ShirePipelineArgImpl(PIPELINE_ARG)\n                                      PsiElement(ShireTokenType.QUOTE_STRING)('\".shire/templates/ControllerTest.java\"')\n                                  PsiElement(ShireTokenType.))(')')\n                            PsiWhiteSpace(' ')\n                            PsiElement(ShireTokenType.})('}')\n                            PsiElement(ShireTokenType.NEWLINE)('\\n')\n                          PsiWhiteSpace('      ')\n                          ShireCasePatternActionImpl(CASE_PATTERN_ACTION)\n                            ShireCaseConditionImpl(CASE_CONDITION)\n                              PsiElement(ShireTokenType.QUOTE_STRING)('\"Service\"')\n                            PsiWhiteSpace(' ')\n                            PsiElement(ShireTokenType.{)('{')\n                            PsiWhiteSpace(' ')\n                            ShireActionBodyImpl(ACTION_BODY)\n                              ShireActionExprImpl(ACTION_EXPR)\n                                ShireFuncCallImpl(FUNC_CALL)\n                                  ShireFuncNameImpl(FUNC_NAME)\n                                    PsiElement(ShireTokenType.IDENTIFIER)('cat')\n                                  PsiElement(ShireTokenType.()('(')\n                                  ShirePipelineArgsImpl(PIPELINE_ARGS)\n                                    ShirePipelineArgImpl(PIPELINE_ARG)\n                                      PsiElement(ShireTokenType.QUOTE_STRING)('\".shire/templates/ServiceTest.java\"')\n                                  PsiElement(ShireTokenType.))(')')\n                            PsiWhiteSpace(' ')\n                            PsiElement(ShireTokenType.})('}')\n                            PsiElement(ShireTokenType.NEWLINE)('\\n')\n                          PsiWhiteSpace('      ')\n                          ShireCasePatternActionImpl(CASE_PATTERN_ACTION)\n                            ShireCaseConditionImpl(CASE_CONDITION)\n                              PsiElement(ShireTokenType.default)('default')\n                            PsiWhiteSpace('  ')\n                            PsiElement(ShireTokenType.{)('{')\n                            PsiWhiteSpace(' ')\n                            ShireActionBodyImpl(ACTION_BODY)\n                              ShireActionExprImpl(ACTION_EXPR)\n                                ShireFuncCallImpl(FUNC_CALL)\n                                  ShireFuncNameImpl(FUNC_NAME)\n                                    PsiElement(ShireTokenType.IDENTIFIER)('cat')\n                                  PsiElement(ShireTokenType.()('(')\n                                  ShirePipelineArgsImpl(PIPELINE_ARGS)\n                                    ShirePipelineArgImpl(PIPELINE_ARG)\n                                      PsiElement(ShireTokenType.QUOTE_STRING)('\".shire/templates/DefaultTest.java\"')\n                                  PsiElement(ShireTokenType.))(')')\n                            PsiWhiteSpace(' ')\n                            PsiElement(ShireTokenType.})('}')\n                            PsiElement(ShireTokenType.NEWLINE)('\\n')\n                          PsiWhiteSpace('    ')\n                          PsiElement(ShireTokenType.})('}')\n                          PsiElement(ShireTokenType.NEWLINE)('\\n')\n                    PsiWhiteSpace('  ')\n                    PsiElement(ShireTokenType.})('}')\n                    PsiElement(ShireTokenType.NEWLINE)('\\n')\n      ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n        ShireLifecycleIdImpl(LIFECYCLE_ID)\n          PsiElement(ShireTokenType.onStreaming)('onStreaming')\n        PsiElement(ShireTokenType.COLON)(':')\n        PsiWhiteSpace(' ')\n        ShireFunctionStatementImpl(FUNCTION_STATEMENT)\n          PsiElement(ShireTokenType.{)('{')\n          PsiWhiteSpace(' ')\n          PsiComment(ShireTokenType.BLOCK_COMMENT)('/* functions */')\n          PsiWhiteSpace(' ')\n          PsiElement(ShireTokenType.})('}')\n          PsiElement(ShireTokenType.NEWLINE)('\\n')\n      ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n        ShireLifecycleIdImpl(LIFECYCLE_ID)\n          PsiElement(ShireTokenType.onStreamingEnd)('onStreamingEnd')\n        PsiElement(ShireTokenType.COLON)(':')\n        PsiWhiteSpace(' ')\n        ShireFunctionStatementImpl(FUNCTION_STATEMENT)\n          PsiElement(ShireTokenType.{)('{')\n          PsiWhiteSpace(' ')\n          ShireFunctionBodyImpl(FUNCTION_BODY)\n            ShireActionBodyImpl(ACTION_BODY)\n              ShireActionExprImpl(ACTION_EXPR)\n                ShireFuncCallImpl(FUNC_CALL)\n                  ShireFuncNameImpl(FUNC_NAME)\n                    PsiElement(ShireTokenType.IDENTIFIER)('parseCode')\n                  PsiElement(ShireTokenType.()('(')\n                  ShirePipelineArgsImpl(PIPELINE_ARGS)\n                    ShirePipelineArgImpl(PIPELINE_ARG)\n                      PsiElement(ShireTokenType.QUOTE_STRING)('\"json\"')\n                  PsiElement(ShireTokenType.))(')')\n              PsiWhiteSpace(' ')\n              PsiElement(ShireTokenType.|)('|')\n              PsiWhiteSpace(' ')\n              ShireActionExprImpl(ACTION_EXPR)\n                ShireFuncCallImpl(FUNC_CALL)\n                  ShireFuncNameImpl(FUNC_NAME)\n                    PsiElement(ShireTokenType.IDENTIFIER)('verifyCode')\n                  PsiElement(ShireTokenType.()('(')\n                  ShirePipelineArgsImpl(PIPELINE_ARGS)\n                    ShirePipelineArgImpl(PIPELINE_ARG)\n                      PsiElement(ShireTokenType.QUOTE_STRING)('\"json\"')\n                  PsiElement(ShireTokenType.))(')')\n              PsiWhiteSpace(' ')\n              PsiElement(ShireTokenType.|)('|')\n              PsiWhiteSpace(' ')\n              ShireActionExprImpl(ACTION_EXPR)\n                ShireFuncCallImpl(FUNC_CALL)\n                  ShireFuncNameImpl(FUNC_NAME)\n                    PsiElement(ShireTokenType.IDENTIFIER)('runCode')\n                  PsiElement(ShireTokenType.()('(')\n                  ShirePipelineArgsImpl(PIPELINE_ARGS)\n                    ShirePipelineArgImpl(PIPELINE_ARG)\n                      PsiElement(ShireTokenType.QUOTE_STRING)('\"json\"')\n                  PsiElement(ShireTokenType.))(')')\n          PsiWhiteSpace(' ')\n          PsiElement(ShireTokenType.})('}')\n          PsiElement(ShireTokenType.NEWLINE)('\\n')\n      ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n        ShireLifecycleIdImpl(LIFECYCLE_ID)\n          PsiElement(ShireTokenType.afterStreaming)('afterStreaming')\n        PsiElement(ShireTokenType.COLON)(':')\n        PsiWhiteSpace(' ')\n        ShireFunctionStatementImpl(FUNCTION_STATEMENT)\n          PsiElement(ShireTokenType.{)('{')\n          PsiElement(ShireTokenType.NEWLINE)('\\n')\n          PsiWhiteSpace('    ')\n          ShireFunctionBodyImpl(FUNCTION_BODY)\n            ShireActionBodyImpl(ACTION_BODY)\n              ShireActionExprImpl(ACTION_EXPR)\n                ShireCaseBodyImpl(CASE_BODY)\n                  ShireConditionFlagImpl(CONDITION_FLAG)\n                    PsiElement(ShireTokenType.condition)('condition')\n                    PsiWhiteSpace(' ')\n                    PsiElement(ShireTokenType.{)('{')\n                    PsiElement(ShireTokenType.NEWLINE)('\\n')\n                    PsiWhiteSpace('      ')\n                    ShireConditionStatementImpl(CONDITION_STATEMENT)\n                      ShireCaseConditionImpl(CASE_CONDITION)\n                        PsiElement(ShireTokenType.QUOTE_STRING)('\"variable-success\"')\n                      PsiWhiteSpace(' ')\n                      PsiElement(ShireTokenType.{)('{')\n                      PsiWhiteSpace(' ')\n                      ShireIneqComparisonExprImpl(INEQ_COMPARISON_EXPR)\n                        ShireRefExprImpl(REF_EXPR)\n                          ShireLiteralExprImpl(LITERAL_EXPR)\n                            PsiElement(VARIABLE_START)('$')\n                            PsiElement(ShireTokenType.IDENTIFIER)('selection')\n                          PsiElement(ShireTokenType..)('.')\n                          PsiElement(ShireTokenType.IDENTIFIER)('length')\n                        PsiWhiteSpace(' ')\n                        ShireIneqComparisonOpImpl(INEQ_COMPARISON_OP)\n                          PsiElement(ShireTokenType.>)('>')\n                        PsiWhiteSpace(' ')\n                        ShireLiteralExprImpl(LITERAL_EXPR)\n                          PsiElement(ShireTokenType.NUMBER)('1')\n                      PsiWhiteSpace(' ')\n                      PsiElement(ShireTokenType.})('}')\n                      PsiElement(ShireTokenType.NEWLINE)('\\n')\n                    PsiWhiteSpace('      ')\n                    ShireConditionStatementImpl(CONDITION_STATEMENT)\n                      ShireCaseConditionImpl(CASE_CONDITION)\n                        PsiElement(ShireTokenType.QUOTE_STRING)('\"jsonpath-success\"')\n                      PsiWhiteSpace(' ')\n                      PsiElement(ShireTokenType.{)('{')\n                      PsiWhiteSpace(' ')\n                      ShireCallExprImpl(CALL_EXPR)\n                        ShireRefExprImpl(REF_EXPR)\n                          PsiElement(ShireTokenType.IDENTIFIER)('jsonpath')\n                        PsiElement(ShireTokenType.()('(')\n                        ShireExpressionListImpl(EXPRESSION_LIST)\n                          ShireLiteralExprImpl(LITERAL_EXPR)\n                            PsiElement(ShireTokenType.QUOTE_STRING)('\"/bookstore/book[price>35]\"')\n                        PsiElement(ShireTokenType.))(')')\n                      PsiWhiteSpace(' ')\n                      PsiElement(ShireTokenType.})('}')\n                      PsiElement(ShireTokenType.NEWLINE)('\\n')\n                    PsiWhiteSpace('    ')\n                    PsiElement(ShireTokenType.})('}')\n                    PsiElement(ShireTokenType.NEWLINE)('\\n')\n                  PsiWhiteSpace('    ')\n                  PsiElement(ShireTokenType.case)('case')\n                  PsiWhiteSpace(' ')\n                  PsiElement(ShireTokenType.condition)('condition')\n                  PsiWhiteSpace(' ')\n                  PsiElement(ShireTokenType.{)('{')\n                  PsiElement(ShireTokenType.NEWLINE)('\\n')\n                  PsiWhiteSpace('      ')\n                  ShireCasePatternActionImpl(CASE_PATTERN_ACTION)\n                    ShireCaseConditionImpl(CASE_CONDITION)\n                      PsiElement(ShireTokenType.QUOTE_STRING)('\"variable-sucesss\"')\n                    PsiWhiteSpace(' ')\n                    PsiElement(ShireTokenType.{)('{')\n                    PsiWhiteSpace(' ')\n                    ShireActionBodyImpl(ACTION_BODY)\n                      ShireActionExprImpl(ACTION_EXPR)\n                        ShireFuncCallImpl(FUNC_CALL)\n                          ShireFuncNameImpl(FUNC_NAME)\n                            PsiElement(ShireTokenType.IDENTIFIER)('done')\n                    PsiWhiteSpace(' ')\n                    PsiElement(ShireTokenType.})('}')\n                    PsiElement(ShireTokenType.NEWLINE)('\\n')\n                  PsiWhiteSpace('      ')\n                  ShireCasePatternActionImpl(CASE_PATTERN_ACTION)\n                    ShireCaseConditionImpl(CASE_CONDITION)\n                      PsiElement(ShireTokenType.QUOTE_STRING)('\"jsonpath-success\"')\n                    PsiWhiteSpace(' ')\n                    PsiElement(ShireTokenType.{)('{')\n                    PsiWhiteSpace(' ')\n                    ShireActionBodyImpl(ACTION_BODY)\n                      ShireActionExprImpl(ACTION_EXPR)\n                        ShireFuncCallImpl(FUNC_CALL)\n                          ShireFuncNameImpl(FUNC_NAME)\n                            PsiElement(ShireTokenType.IDENTIFIER)('task')\n                          PsiElement(ShireTokenType.()('(')\n                          ShirePipelineArgsImpl(PIPELINE_ARGS)\n                            <empty list>\n                          PsiElement(ShireTokenType.))(')')\n                    PsiWhiteSpace(' ')\n                    PsiElement(ShireTokenType.})('}')\n                    PsiElement(ShireTokenType.NEWLINE)('\\n')\n                  PsiWhiteSpace('      ')\n                  ShireCasePatternActionImpl(CASE_PATTERN_ACTION)\n                    ShireCaseConditionImpl(CASE_CONDITION)\n                      PsiElement(ShireTokenType.default)('default')\n                    PsiWhiteSpace(' ')\n                    PsiElement(ShireTokenType.{)('{')\n                    PsiWhiteSpace(' ')\n                    ShireActionBodyImpl(ACTION_BODY)\n                      ShireActionExprImpl(ACTION_EXPR)\n                        ShireFuncCallImpl(FUNC_CALL)\n                          ShireFuncNameImpl(FUNC_NAME)\n                            PsiElement(ShireTokenType.IDENTIFIER)('task')\n                          PsiElement(ShireTokenType.()('(')\n                          ShirePipelineArgsImpl(PIPELINE_ARGS)\n                            <empty list>\n                          PsiElement(ShireTokenType.))(')')\n                    PsiWhiteSpace(' ')\n                    PsiElement(ShireTokenType.})('}')\n                    PsiElement(ShireTokenType.NEWLINE)('\\n')\n                  PsiWhiteSpace('    ')\n                  PsiElement(ShireTokenType.})('}')\n                  PsiElement(ShireTokenType.NEWLINE)('\\n')\n          PsiWhiteSpace('  ')\n          PsiElement(ShireTokenType.})('}')\n          PsiElement(ShireTokenType.NEWLINE)('\\n')\n    PsiElement(ShireTokenType.FRONTMATTER_END)('---')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.TEXT_SEGMENT)('onStreamingDone:')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.TEXT_SEGMENT)('- Array of processor')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.TEXT_SEGMENT)('- Object flow')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  CodeBlockElement(CODE)\n    PsiElement(ShireTokenType.CODE_BLOCK_START)('```')\n    ShireLanguageIdImpl(LANGUAGE_ID)\n      PsiElement(ShireTokenType.LANGUAGE_IDENTIFIER)('markdown')\n    PsiElement(ShireTokenType.NEWLINE)('\\n')\n    ASTWrapperPsiElement(CODE_CONTENTS)\n      PsiElement(ShireTokenType.CODE_CONTENT)('- //bookstore/book[price>35]')\n      PsiElement(ShireTokenType.NEWLINE)('\\n')\n      PsiElement(ShireTokenType.CODE_CONTENT)('- $.phoneNumbers[:1].type')\n      PsiElement(ShireTokenType.NEWLINE)('\\n')\n      PsiElement(ShireTokenType.CODE_CONTENT)('- Regex')\n      PsiElement(ShireTokenType.NEWLINE)('\\n')\n    PsiElement(ShireTokenType.CODE_BLOCK_END)('```')"
  },
  {
    "path": "shirelang/src/test/testData/realworld/LoginCommit.shire",
    "content": "---\nname: \"Commit message\"\nenabled: false\ndescription: \"生成提交信息\"\ninteraction: AppendCursor\nactionLocation: CommitMenu\nvariables:\n  \"story\": /any/ { thread(\".shire/fetch-jira.sh\") | jsonpath(\"$.data[*].Story\") }\n---\n\n请遵循常规提交规范，例如：\n\n- fix(authentication): 修复密码正则表达式模式问题 #$story\n"
  },
  {
    "path": "shirelang/src/test/testData/realworld/LoginCommit.txt",
    "content": "ShireFile\n  ShireFrontMatterHeaderImpl(FRONT_MATTER_HEADER)\n    PsiElement(ShireTokenType.FRONTMATTER_START)('---')\n    PsiElement(ShireTokenType.NEWLINE)('\\n')\n    ShireFrontMatterEntriesImpl(FRONT_MATTER_ENTRIES)\n      ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n        ShireFrontMatterKeyImpl(FRONT_MATTER_KEY)\n          ShireFrontMatterIdImpl(FRONT_MATTER_ID)\n            PsiElement(ShireTokenType.IDENTIFIER)('name')\n        PsiElement(ShireTokenType.COLON)(':')\n        PsiWhiteSpace(' ')\n        ShireFrontMatterValueImpl(FRONT_MATTER_VALUE)\n          PsiElement(ShireTokenType.QUOTE_STRING)('\"Commit message\"')\n        PsiElement(ShireTokenType.NEWLINE)('\\n')\n      ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n        ShireFrontMatterKeyImpl(FRONT_MATTER_KEY)\n          ShireFrontMatterIdImpl(FRONT_MATTER_ID)\n            PsiElement(ShireTokenType.IDENTIFIER)('enabled')\n        PsiElement(ShireTokenType.COLON)(':')\n        PsiWhiteSpace(' ')\n        ShireFrontMatterValueImpl(FRONT_MATTER_VALUE)\n          PsiElement(ShireTokenType.BOOLEAN)('false')\n        PsiElement(ShireTokenType.NEWLINE)('\\n')\n      ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n        ShireFrontMatterKeyImpl(FRONT_MATTER_KEY)\n          ShireFrontMatterIdImpl(FRONT_MATTER_ID)\n            PsiElement(ShireTokenType.IDENTIFIER)('description')\n        PsiElement(ShireTokenType.COLON)(':')\n        PsiWhiteSpace(' ')\n        ShireFrontMatterValueImpl(FRONT_MATTER_VALUE)\n          PsiElement(ShireTokenType.QUOTE_STRING)('\"生成提交信息\"')\n        PsiElement(ShireTokenType.NEWLINE)('\\n')\n      ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n        ShireFrontMatterKeyImpl(FRONT_MATTER_KEY)\n          ShireFrontMatterIdImpl(FRONT_MATTER_ID)\n            PsiElement(ShireTokenType.IDENTIFIER)('interaction')\n        PsiElement(ShireTokenType.COLON)(':')\n        PsiWhiteSpace(' ')\n        ShireFrontMatterValueImpl(FRONT_MATTER_VALUE)\n          PsiElement(ShireTokenType.IDENTIFIER)('AppendCursor')\n        PsiElement(ShireTokenType.NEWLINE)('\\n')\n      ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n        ShireFrontMatterKeyImpl(FRONT_MATTER_KEY)\n          ShireFrontMatterIdImpl(FRONT_MATTER_ID)\n            PsiElement(ShireTokenType.IDENTIFIER)('actionLocation')\n        PsiElement(ShireTokenType.COLON)(':')\n        PsiWhiteSpace(' ')\n        ShireFrontMatterValueImpl(FRONT_MATTER_VALUE)\n          PsiElement(ShireTokenType.IDENTIFIER)('CommitMenu')\n        PsiElement(ShireTokenType.NEWLINE)('\\n')\n      ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n        ShireFrontMatterKeyImpl(FRONT_MATTER_KEY)\n          ShireFrontMatterIdImpl(FRONT_MATTER_ID)\n            PsiElement(ShireTokenType.IDENTIFIER)('variables')\n        PsiElement(ShireTokenType.COLON)(':')\n        ShireFrontMatterValueImpl(FRONT_MATTER_VALUE)\n          PsiElement(ShireTokenType.NEWLINE)('\\n')\n          ShireObjectKeyValueImpl(OBJECT_KEY_VALUE)\n            PsiElement(ShireTokenType.INDENT)('  ')\n            ShireKeyValueImpl(KEY_VALUE)\n              ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n                ShireFrontMatterKeyImpl(FRONT_MATTER_KEY)\n                  PsiElement(ShireTokenType.QUOTE_STRING)('\"story\"')\n                PsiElement(ShireTokenType.COLON)(':')\n                PsiWhiteSpace(' ')\n                ShirePatternActionImpl(PATTERN_ACTION)\n                  PatternElement(PATTERN)\n                    PsiElement(ShireTokenType.PATTERN_EXPR)('/any/')\n                  PsiWhiteSpace(' ')\n                  ShireActionBlockImpl(ACTION_BLOCK)\n                    PsiElement(ShireTokenType.{)('{')\n                    PsiWhiteSpace(' ')\n                    ShireActionBodyImpl(ACTION_BODY)\n                      ShireActionExprImpl(ACTION_EXPR)\n                        ShireFuncCallImpl(FUNC_CALL)\n                          ShireFuncNameImpl(FUNC_NAME)\n                            PsiElement(ShireTokenType.IDENTIFIER)('thread')\n                          PsiElement(ShireTokenType.()('(')\n                          ShirePipelineArgsImpl(PIPELINE_ARGS)\n                            ShirePipelineArgImpl(PIPELINE_ARG)\n                              PsiElement(ShireTokenType.QUOTE_STRING)('\".shire/fetch-jira.sh\"')\n                          PsiElement(ShireTokenType.))(')')\n                      PsiWhiteSpace(' ')\n                      PsiElement(ShireTokenType.|)('|')\n                      PsiWhiteSpace(' ')\n                      ShireActionExprImpl(ACTION_EXPR)\n                        ShireFuncCallImpl(FUNC_CALL)\n                          ShireFuncNameImpl(FUNC_NAME)\n                            PsiElement(ShireTokenType.IDENTIFIER)('jsonpath')\n                          PsiElement(ShireTokenType.()('(')\n                          ShirePipelineArgsImpl(PIPELINE_ARGS)\n                            ShirePipelineArgImpl(PIPELINE_ARG)\n                              PsiElement(ShireTokenType.QUOTE_STRING)('\"$.data[*].Story\"')\n                          PsiElement(ShireTokenType.))(')')\n                    PsiWhiteSpace(' ')\n                    PsiElement(ShireTokenType.})('}')\n                    PsiElement(ShireTokenType.NEWLINE)('\\n')\n    PsiElement(ShireTokenType.FRONTMATTER_END)('---')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.TEXT_SEGMENT)('请遵循常规提交规范，例如：')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.TEXT_SEGMENT)('- fix(authentication): 修复密码正则表达式模式问题 ')\n  ShireUsedImpl(USED)\n    PsiElement(ShireTokenType.#)('#')\n    ShireVariableStartImpl(VARIABLE_START)\n      PsiElement(VARIABLE_START)('$')\n    ShireRefExprImpl(REF_EXPR)\n      PsiElement(ShireTokenType.IDENTIFIER)('story')"
  },
  {
    "path": "shirelang/src/test/testData/realworld/OnPaste.shire",
    "content": "---\nname: \"PasteMaster\"\nactionLocation: ContextMenu\nenabled: false\n---\n\n[interaction: OnPaste]\n\n代码生成。根据当前的代码上下文（光标前后），对用户粘贴的代码，生成新的代码。\n\n光标前的代码：\n\n$beforeCursor\n\n光标后的代码：\n\n$afterCursor\n\n用户粘贴的代码：\n\n"
  },
  {
    "path": "shirelang/src/test/testData/realworld/OnPaste.txt",
    "content": "ShireFile\n  ShireFrontMatterHeaderImpl(FRONT_MATTER_HEADER)\n    PsiElement(ShireTokenType.FRONTMATTER_START)('---')\n    PsiElement(ShireTokenType.NEWLINE)('\\n')\n    ShireFrontMatterEntriesImpl(FRONT_MATTER_ENTRIES)\n      ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n        ShireFrontMatterKeyImpl(FRONT_MATTER_KEY)\n          ShireFrontMatterIdImpl(FRONT_MATTER_ID)\n            PsiElement(ShireTokenType.IDENTIFIER)('name')\n        PsiElement(ShireTokenType.COLON)(':')\n        PsiWhiteSpace(' ')\n        ShireFrontMatterValueImpl(FRONT_MATTER_VALUE)\n          PsiElement(ShireTokenType.QUOTE_STRING)('\"PasteMaster\"')\n        PsiElement(ShireTokenType.NEWLINE)('\\n')\n      ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n        ShireFrontMatterKeyImpl(FRONT_MATTER_KEY)\n          ShireFrontMatterIdImpl(FRONT_MATTER_ID)\n            PsiElement(ShireTokenType.IDENTIFIER)('actionLocation')\n        PsiElement(ShireTokenType.COLON)(':')\n        PsiWhiteSpace(' ')\n        ShireFrontMatterValueImpl(FRONT_MATTER_VALUE)\n          PsiElement(ShireTokenType.IDENTIFIER)('ContextMenu')\n        PsiElement(ShireTokenType.NEWLINE)('\\n')\n      ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n        ShireFrontMatterKeyImpl(FRONT_MATTER_KEY)\n          ShireFrontMatterIdImpl(FRONT_MATTER_ID)\n            PsiElement(ShireTokenType.IDENTIFIER)('enabled')\n        PsiElement(ShireTokenType.COLON)(':')\n        PsiWhiteSpace(' ')\n        ShireFrontMatterValueImpl(FRONT_MATTER_VALUE)\n          PsiElement(ShireTokenType.BOOLEAN)('false')\n        PsiElement(ShireTokenType.NEWLINE)('\\n')\n    PsiElement(ShireTokenType.FRONTMATTER_END)('---')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiComment(ShireTokenType.CONTENT_COMMENTS)('[interaction: OnPaste]')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.TEXT_SEGMENT)('代码生成。根据当前的代码上下文（光标前后），对用户粘贴的代码，生成新的代码。')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.TEXT_SEGMENT)('光标前的代码：')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  ShireUsedImpl(USED)\n    ShireVariableStartImpl(VARIABLE_START)\n      PsiElement(VARIABLE_START)('$')\n    ShireVariableIdImpl(VARIABLE_ID)\n      PsiElement(ShireTokenType.IDENTIFIER)('beforeCursor')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.TEXT_SEGMENT)('光标后的代码：')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  ShireUsedImpl(USED)\n    ShireVariableStartImpl(VARIABLE_START)\n      PsiElement(VARIABLE_START)('$')\n    ShireVariableIdImpl(VARIABLE_ID)\n      PsiElement(ShireTokenType.IDENTIFIER)('afterCursor')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.TEXT_SEGMENT)('用户粘贴的代码：')"
  },
  {
    "path": "shirelang/src/test/testData/realworld/OutputInVariable.shire",
    "content": "---\nname: \"代码修改\"\nvariables:\n  \"controllerCode\": /any/ { cat($output) }\nonStreamingEnd: { parseCode | saveFile($output) }\n---\n\n$output\n"
  },
  {
    "path": "shirelang/src/test/testData/realworld/OutputInVariable.txt",
    "content": "ShireFile\n  ShireFrontMatterHeaderImpl(FRONT_MATTER_HEADER)\n    PsiElement(ShireTokenType.FRONTMATTER_START)('---')\n    PsiElement(ShireTokenType.NEWLINE)('\\n')\n    ShireFrontMatterEntriesImpl(FRONT_MATTER_ENTRIES)\n      ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n        ShireFrontMatterKeyImpl(FRONT_MATTER_KEY)\n          ShireFrontMatterIdImpl(FRONT_MATTER_ID)\n            PsiElement(ShireTokenType.IDENTIFIER)('name')\n        PsiElement(ShireTokenType.COLON)(':')\n        PsiWhiteSpace(' ')\n        ShireFrontMatterValueImpl(FRONT_MATTER_VALUE)\n          PsiElement(ShireTokenType.QUOTE_STRING)('\"代码修改\"')\n        PsiElement(ShireTokenType.NEWLINE)('\\n')\n      ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n        ShireFrontMatterKeyImpl(FRONT_MATTER_KEY)\n          ShireFrontMatterIdImpl(FRONT_MATTER_ID)\n            PsiElement(ShireTokenType.IDENTIFIER)('variables')\n        PsiElement(ShireTokenType.COLON)(':')\n        ShireFrontMatterValueImpl(FRONT_MATTER_VALUE)\n          PsiElement(ShireTokenType.NEWLINE)('\\n')\n          ShireObjectKeyValueImpl(OBJECT_KEY_VALUE)\n            PsiElement(ShireTokenType.INDENT)('  ')\n            ShireKeyValueImpl(KEY_VALUE)\n              ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n                ShireFrontMatterKeyImpl(FRONT_MATTER_KEY)\n                  PsiElement(ShireTokenType.QUOTE_STRING)('\"controllerCode\"')\n                PsiElement(ShireTokenType.COLON)(':')\n                PsiWhiteSpace(' ')\n                ShirePatternActionImpl(PATTERN_ACTION)\n                  PatternElement(PATTERN)\n                    PsiElement(ShireTokenType.PATTERN_EXPR)('/any/')\n                  PsiWhiteSpace(' ')\n                  ShireActionBlockImpl(ACTION_BLOCK)\n                    PsiElement(ShireTokenType.{)('{')\n                    PsiWhiteSpace(' ')\n                    ShireActionBodyImpl(ACTION_BODY)\n                      ShireActionExprImpl(ACTION_EXPR)\n                        ShireFuncCallImpl(FUNC_CALL)\n                          ShireFuncNameImpl(FUNC_NAME)\n                            PsiElement(ShireTokenType.IDENTIFIER)('cat')\n                          PsiElement(ShireTokenType.()('(')\n                          ShirePipelineArgsImpl(PIPELINE_ARGS)\n                            ShirePipelineArgImpl(PIPELINE_ARG)\n                              ShireVariableStartImpl(VARIABLE_START)\n                                PsiElement(VARIABLE_START)('$')\n                              ShireVariableIdImpl(VARIABLE_ID)\n                                PsiElement(ShireTokenType.IDENTIFIER)('output')\n                          PsiElement(ShireTokenType.))(')')\n                    PsiWhiteSpace(' ')\n                    PsiElement(ShireTokenType.})('}')\n                    PsiElement(ShireTokenType.NEWLINE)('\\n')\n      ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n        ShireLifecycleIdImpl(LIFECYCLE_ID)\n          PsiElement(ShireTokenType.onStreamingEnd)('onStreamingEnd')\n        PsiElement(ShireTokenType.COLON)(':')\n        PsiWhiteSpace(' ')\n        ShireFunctionStatementImpl(FUNCTION_STATEMENT)\n          PsiElement(ShireTokenType.{)('{')\n          PsiWhiteSpace(' ')\n          ShireFunctionBodyImpl(FUNCTION_BODY)\n            ShireActionBodyImpl(ACTION_BODY)\n              ShireActionExprImpl(ACTION_EXPR)\n                ShireFuncCallImpl(FUNC_CALL)\n                  ShireFuncNameImpl(FUNC_NAME)\n                    PsiElement(ShireTokenType.IDENTIFIER)('parseCode')\n              PsiWhiteSpace(' ')\n              PsiElement(ShireTokenType.|)('|')\n              PsiWhiteSpace(' ')\n              ShireActionExprImpl(ACTION_EXPR)\n                ShireFuncCallImpl(FUNC_CALL)\n                  ShireFuncNameImpl(FUNC_NAME)\n                    PsiElement(ShireTokenType.IDENTIFIER)('saveFile')\n                  PsiElement(ShireTokenType.()('(')\n                  ShirePipelineArgsImpl(PIPELINE_ARGS)\n                    ShirePipelineArgImpl(PIPELINE_ARG)\n                      ShireVariableStartImpl(VARIABLE_START)\n                        PsiElement(VARIABLE_START)('$')\n                      ShireVariableIdImpl(VARIABLE_ID)\n                        PsiElement(ShireTokenType.IDENTIFIER)('output')\n                  PsiElement(ShireTokenType.))(')')\n          PsiWhiteSpace(' ')\n          PsiElement(ShireTokenType.})('}')\n          PsiElement(ShireTokenType.NEWLINE)('\\n')\n    PsiElement(ShireTokenType.FRONTMATTER_END)('---')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  ShireUsedImpl(USED)\n    ShireVariableStartImpl(VARIABLE_START)\n      PsiElement(VARIABLE_START)('$')\n    ShireVariableIdImpl(VARIABLE_ID)\n      PsiElement(ShireTokenType.IDENTIFIER)('output')"
  },
  {
    "path": "shirelang/src/test/testData/realworld/WhenAfterStreaming.shire",
    "content": "---\ninteraction: AppendCursor\nonStreamingEnd: { parseCode(\"json\") | verifyCode(\"json\") | runCode(\"json\") }\nwhen: \"$selection.length == 1 && $selection.first() == 'file'\"\n---\n\nhi\n\nHello! How can I assist you today?\n\n"
  },
  {
    "path": "shirelang/src/test/testData/realworld/WhenAfterStreaming.txt",
    "content": "ShireFile\n  ShireFrontMatterHeaderImpl(FRONT_MATTER_HEADER)\n    PsiElement(ShireTokenType.FRONTMATTER_START)('---')\n    PsiElement(ShireTokenType.NEWLINE)('\\n')\n    ShireFrontMatterEntriesImpl(FRONT_MATTER_ENTRIES)\n      ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n        ShireFrontMatterKeyImpl(FRONT_MATTER_KEY)\n          ShireFrontMatterIdImpl(FRONT_MATTER_ID)\n            PsiElement(ShireTokenType.IDENTIFIER)('interaction')\n        PsiElement(ShireTokenType.COLON)(':')\n        PsiWhiteSpace(' ')\n        ShireFrontMatterValueImpl(FRONT_MATTER_VALUE)\n          PsiElement(ShireTokenType.IDENTIFIER)('AppendCursor')\n        PsiElement(ShireTokenType.NEWLINE)('\\n')\n      ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n        ShireLifecycleIdImpl(LIFECYCLE_ID)\n          PsiElement(ShireTokenType.onStreamingEnd)('onStreamingEnd')\n        PsiElement(ShireTokenType.COLON)(':')\n        PsiWhiteSpace(' ')\n        ShireFunctionStatementImpl(FUNCTION_STATEMENT)\n          PsiElement(ShireTokenType.{)('{')\n          PsiWhiteSpace(' ')\n          ShireFunctionBodyImpl(FUNCTION_BODY)\n            ShireActionBodyImpl(ACTION_BODY)\n              ShireActionExprImpl(ACTION_EXPR)\n                ShireFuncCallImpl(FUNC_CALL)\n                  ShireFuncNameImpl(FUNC_NAME)\n                    PsiElement(ShireTokenType.IDENTIFIER)('parseCode')\n                  PsiElement(ShireTokenType.()('(')\n                  ShirePipelineArgsImpl(PIPELINE_ARGS)\n                    ShirePipelineArgImpl(PIPELINE_ARG)\n                      PsiElement(ShireTokenType.QUOTE_STRING)('\"json\"')\n                  PsiElement(ShireTokenType.))(')')\n              PsiWhiteSpace(' ')\n              PsiElement(ShireTokenType.|)('|')\n              PsiWhiteSpace(' ')\n              ShireActionExprImpl(ACTION_EXPR)\n                ShireFuncCallImpl(FUNC_CALL)\n                  ShireFuncNameImpl(FUNC_NAME)\n                    PsiElement(ShireTokenType.IDENTIFIER)('verifyCode')\n                  PsiElement(ShireTokenType.()('(')\n                  ShirePipelineArgsImpl(PIPELINE_ARGS)\n                    ShirePipelineArgImpl(PIPELINE_ARG)\n                      PsiElement(ShireTokenType.QUOTE_STRING)('\"json\"')\n                  PsiElement(ShireTokenType.))(')')\n              PsiWhiteSpace(' ')\n              PsiElement(ShireTokenType.|)('|')\n              PsiWhiteSpace(' ')\n              ShireActionExprImpl(ACTION_EXPR)\n                ShireFuncCallImpl(FUNC_CALL)\n                  ShireFuncNameImpl(FUNC_NAME)\n                    PsiElement(ShireTokenType.IDENTIFIER)('runCode')\n                  PsiElement(ShireTokenType.()('(')\n                  ShirePipelineArgsImpl(PIPELINE_ARGS)\n                    ShirePipelineArgImpl(PIPELINE_ARG)\n                      PsiElement(ShireTokenType.QUOTE_STRING)('\"json\"')\n                  PsiElement(ShireTokenType.))(')')\n          PsiWhiteSpace(' ')\n          PsiElement(ShireTokenType.})('}')\n          PsiElement(ShireTokenType.NEWLINE)('\\n')\n      ShireFrontMatterEntryImpl(FRONT_MATTER_ENTRY)\n        ShireLifecycleIdImpl(LIFECYCLE_ID)\n          PsiElement(ShireTokenType.when)('when')\n        PsiElement(ShireTokenType.COLON)(':')\n        PsiWhiteSpace(' ')\n        ShireLiteralExprImpl(LITERAL_EXPR)\n          PsiElement(ShireTokenType.QUOTE_STRING)('\"$selection.length == 1 && $selection.first() == 'file'\"')\n        PsiElement(ShireTokenType.NEWLINE)('\\n')\n    PsiElement(ShireTokenType.FRONTMATTER_END)('---')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.TEXT_SEGMENT)('hi')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.NEWLINE)('\\n')\n  PsiElement(ShireTokenType.TEXT_SEGMENT)('Hello! How can I assist you today?')"
  },
  {
    "path": "src/description.html",
    "content": "<h1>Shire - AI Coding Agent Language</h1>\n<blockquote>Shire offers a straightforward AI Coding &amp; Agents Language that enables communication between an LLM\nand control IDE for automated programming.\n</blockquote>\n\n<h2>Shire syntax Overview</h2>\n\n<img src=\"https://shire.phodal.com/images/shire-sheet.svg\" alt=\"Shire Cheatsheet\" />\n\n<h2>Shire Data Architecture</h2>\n\n<img src=\"https://shire.phodal.com/images/shire-data-flow.svg\" alt=\"Shire Data Architecture\" />\n\n<h2>Shire code example</h2>\n\n<pre>\n<code lang=\"shire\">\n---\nname: \"AutoTest\"\ndescription: \"Auto generate test in ContextMenu with use selection code\"\nactionLocation: ContextMenu\ninteraction: AppendCursor\nwhen: $fileName.contains(\".java\") && $filePath.contains(\"src/main/java\")\n---\n\n@ext-context.autotest\n\nWrite unit test for following ${context.language} code.\n\n${context.frameworkContext}\n\nHere is the source code to be tested:\n\n/file:src/main/kotlin/com/phodal/blog/controller/UserController.kt\n</code>\n</pre>"
  },
  {
    "path": "src/main/kotlin/com/phodal/shire/ShireIdeaIcons.kt",
    "content": "package com.phodal.shire\n\nimport com.intellij.openapi.util.IconLoader\nimport javax.swing.Icon\n\nobject ShireIdeaIcons {\n    @JvmField\n    val Default: Icon = IconLoader.getIcon(\"/icons/shire-mkt.svg\", ShireIdeaIcons::class.java)\n    @JvmField\n    val Download: Icon = IconLoader.getIcon(\"/icons/download.svg\", ShireIdeaIcons::class.java)\n}\n"
  },
  {
    "path": "src/main/kotlin/com/phodal/shire/ShireMainBundle.kt",
    "content": "package com.phodal.shire\n\nimport com.intellij.DynamicBundle\nimport org.jetbrains.annotations.NonNls\nimport org.jetbrains.annotations.PropertyKey\n\n@NonNls\nprivate const val BUNDLE = \"messages.ShireMainBundle\"\n\nobject ShireMainBundle : DynamicBundle(BUNDLE) {\n    @JvmStatic\n    fun message(@PropertyKey(resourceBundle = BUNDLE) key: String, vararg params: Any) = getMessage(key, *params)\n\n    @Suppress(\"unused\")\n    @JvmStatic\n    fun messagePointer(@PropertyKey(resourceBundle = BUNDLE) key: String, vararg params: Any) =\n        getLazyMessage(key, *params)\n}\n"
  },
  {
    "path": "src/main/kotlin/com/phodal/shire/inline/ShireGutterIconRenderer.kt",
    "content": "package com.phodal.shire.inline\n\nimport com.intellij.openapi.actionSystem.AnAction\nimport com.intellij.openapi.actionSystem.AnActionEvent\nimport com.intellij.openapi.editor.markup.GutterIconRenderer\nimport com.phodal.shire.ShireIdeaIcons\nimport javax.swing.Icon\n\nclass ShireGutterIconRenderer(\n    val line: Int, val onClick: () -> Unit,\n) : GutterIconRenderer() {\n    override fun getClickAction(): AnAction {\n        return object : AnAction() {\n            override fun actionPerformed(e: AnActionEvent) {\n                onClick()\n            }\n        }\n    }\n\n    override fun getIcon(): Icon = ShireIdeaIcons.Default\n    override fun equals(other: Any?): Boolean {\n        if (this === other) return true\n        if (javaClass != other?.javaClass) return false\n\n        other as ShireGutterIconRenderer\n\n        if (line != other.line) return false\n        if (onClick != other.onClick) return false\n\n        return true\n    }\n\n    override fun hashCode(): Int {\n        var result = line\n        result = 31 * result + onClick.hashCode()\n        return result\n    }\n\n}"
  },
  {
    "path": "src/main/kotlin/com/phodal/shire/inline/ShireInlineChatPanel.kt",
    "content": "package com.phodal.shire.inline\n\nimport com.intellij.icons.AllIcons\nimport com.intellij.ide.KeyboardAwareFocusOwner\nimport com.intellij.openapi.Disposable\nimport com.intellij.openapi.actionSystem.Presentation\nimport com.intellij.openapi.actionSystem.impl.ActionButton\nimport com.intellij.openapi.application.ApplicationManager\nimport com.intellij.openapi.application.invokeLater\nimport com.intellij.openapi.editor.Editor\nimport com.intellij.openapi.editor.EditorCustomElementRenderer\nimport com.intellij.openapi.editor.Inlay\nimport com.intellij.openapi.editor.markup.TextAttributes\nimport com.intellij.openapi.project.DumbAwareAction\nimport com.intellij.openapi.wm.IdeFocusManager\nimport com.intellij.ui.JBColor\nimport com.intellij.ui.RoundedLineBorder\nimport com.intellij.ui.components.JBTextArea\nimport com.intellij.util.ui.JBUI.CurrentTheme\nimport com.phodal.shirecore.ShireCoroutineScope\nimport com.phodal.shirecore.llm.LlmProvider\nimport com.phodal.shirecore.provider.streaming.OnStreamingService\nimport com.phodal.shirecore.runner.console.cancelHandler\nimport com.phodal.shirecore.ui.ShirePanelView\nimport com.phodal.shirecore.ui.input.ShireLineBorder\nimport kotlinx.coroutines.flow.Flow\nimport kotlinx.coroutines.flow.cancellable\nimport kotlinx.coroutines.launch\nimport java.awt.*\nimport java.awt.event.*\nimport java.awt.geom.Rectangle2D\nimport javax.swing.*\n\nclass ShireInlineChatPanel(val editor: Editor) : JPanel(GridBagLayout()), EditorCustomElementRenderer,\n    Disposable {\n    var inlay: Inlay<*>? = null\n    val inputPanel = ShireInlineChatInputPanel(this, onSubmit = { input ->\n        this.centerPanel.isVisible = true\n        val project = editor.project!!\n\n        val prompt = ShireInlineChatService.getInstance().prompt(project, input)\n        val flow: Flow<String>? = LlmProvider.provider(project)?.stream(prompt, \"\", false)\n\n        val panelView = ShirePanelView(project, showInput = false)\n        panelView.minimumSize = Dimension(800, 40)\n        setContent(panelView)\n\n        ShireCoroutineScope.scope(project).launch {\n            val suggestion = StringBuilder()\n            panelView.onStart()\n\n            flow?.cancelHandler { panelView.handleCancel = it }?.cancellable()?.collect { char ->\n                suggestion.append(char)\n\n                invokeLater {\n                    panelView.onUpdate(suggestion.toString())\n                    panelView.resize()\n                }\n            }\n\n            panelView.resize()\n            panelView.onFinish(suggestion.toString())\n        }\n    })\n    private var centerPanel: JPanel = JPanel(BorderLayout())\n    private var container: Container? = null\n\n    init {\n        border = BorderFactory.createCompoundBorder(\n            BorderFactory.createCompoundBorder(\n                BorderFactory.createEmptyBorder(0, 0, 0, 0),\n                RoundedLineBorder(JBColor.LIGHT_GRAY, 12, 1)\n            ),\n            BorderFactory.createCompoundBorder(\n                ShireLineBorder(JBColor.border(), 1, true, 8),\n                BorderFactory.createMatteBorder(6, 8, 6, 8, JBColor.border())\n            )\n        )\n\n        isOpaque = false\n\n        val c = GridBagConstraints()\n        c.gridx = 0\n        c.gridy = 0\n        c.weightx = 1.0\n        c.fill = GridBagConstraints.HORIZONTAL\n        add(this.inputPanel, c)\n\n        this.centerPanel = JPanel(BorderLayout()).apply {\n            isOpaque = false\n            this.addMouseListener(object : MouseAdapter() {\n                override fun mouseClicked(e: MouseEvent) {\n                    IdeFocusManager.getInstance(editor.project).requestFocus(inputPanel.getInputComponent(), true)\n                }\n            })\n        }\n\n        c.gridx = 0\n        c.gridy = 1\n        c.fill = GridBagConstraints.BOTH\n        add(this.centerPanel, c)\n\n        this.inAllChildren { child ->\n            child.addComponentListener(object : ComponentAdapter() {\n                override fun componentResized(e: ComponentEvent) {\n                    this@ShireInlineChatPanel.redraw()\n                }\n            })\n        }\n    }\n\n    override fun calcWidthInPixels(inlay: Inlay<*>): Int = size.width\n\n    override fun calcHeightInPixels(inlay: Inlay<*>): Int = size.height\n\n    private fun redraw() {\n        ApplicationManager.getApplication().invokeLater {\n            if (this.size.height != this.getMinimumSize().height) {\n                this.size = Dimension(800, this.getMinimumSize().height)\n                this.inlay?.update()\n\n                this.revalidate()\n                this.repaint()\n            }\n        }\n    }\n\n    fun createInlay(offset: Int) {\n        inlay = editor.inlayModel.addBlockElement(offset, false, true, 1, this)\n    }\n\n    fun setInlineContainer(container: Container) {\n        this.container = container\n    }\n\n    override fun paint(inlay: Inlay<*>, g: Graphics2D, targetRegion: Rectangle2D, textAttributes: TextAttributes) {\n        bounds = inlay.bounds ?: return\n        revalidate()\n        repaint()\n    }\n\n    private fun setContent(content: JComponent) {\n        content.isOpaque = true\n        ApplicationManager.getApplication().invokeLater {\n            if (!this.centerPanel.isVisible) {\n                this.centerPanel.isVisible = true\n            }\n\n            this.centerPanel.removeAll()\n            this.centerPanel.add(content, BorderLayout.CENTER)\n\n            this@ShireInlineChatPanel.redraw()\n        }\n    }\n\n    fun JComponent.inAllChildren(callback: (JComponent) -> Unit) {\n        callback(this)\n        components.forEach { component ->\n            if (component is JComponent) {\n                component.inAllChildren(callback)\n            }\n        }\n    }\n\n    override fun dispose() {\n        inputPanel.dispose()\n        inlay?.dispose()\n        inlay = null\n    }\n}\n\nclass ShireInlineChatInputPanel(\n    val shireInlineChatPanel: ShireInlineChatPanel,\n    val onSubmit: (String) -> Unit,\n) : JPanel(GridBagLayout()), Disposable {\n    private val textArea: JBTextArea\n\n    init {\n        layout = BorderLayout()\n        textArea = object: JBTextArea(), KeyboardAwareFocusOwner {\n            override fun skipKeyEventDispatcher(event: KeyEvent): Boolean = true\n\n            init {\n                isOpaque = false\n                isFocusable = true\n                lineWrap = true\n                wrapStyleWord = true\n                border = BorderFactory.createEmptyBorder(8, 6, 8, 6)\n            }\n        }\n\n        border = ShireLineBorder(CurrentTheme.Focus.focusColor(), 1, true, 8)\n\n        // escape to close\n        textArea.actionMap.put(\"escapeAction\", object : AbstractAction() {\n            override fun actionPerformed(e: ActionEvent) {\n                ShireInlineChatService.getInstance().closeInlineChat(shireInlineChatPanel.editor)\n            }\n        })\n        textArea.inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), \"escapeAction\")\n        // submit with enter\n        textArea.actionMap.put(\"enterAction\", object : AbstractAction() {\n            override fun actionPerformed(e: ActionEvent) {\n                submit()\n            }\n        })\n        textArea.inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), \"enterAction\")\n        // newLine with shift + enter\n        textArea.actionMap.put(\"newlineAction\", object : AbstractAction() {\n            override fun actionPerformed(e: ActionEvent) {\n                textArea.append(\"\\n\")\n            }\n        })\n        textArea.inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, KeyEvent.SHIFT_DOWN_MASK), \"newlineAction\")\n        add(textArea)\n\n        val submitPresentation = Presentation(\"Submit\")\n        submitPresentation.icon = AllIcons.Actions.Execute\n        val submitButton = ActionButton(\n            DumbAwareAction.create { submit() },\n            submitPresentation, \"\", Dimension(40, 20)\n        )\n\n        add(submitButton, BorderLayout.EAST)\n    }\n\n    private fun submit() {\n        val trimText = textArea.text.trim()\n        textArea.text = \"\"\n        onSubmit(trimText)\n    }\n\n    fun getInputComponent(): Component = textArea\n\n    override fun dispose() {\n\n    }\n}"
  },
  {
    "path": "src/main/kotlin/com/phodal/shire/inline/ShireInlineChatProvider.kt",
    "content": "package com.phodal.shire.inline\n\nimport com.intellij.openapi.Disposable\nimport com.intellij.openapi.components.Service\nimport com.intellij.openapi.editor.Editor\nimport com.intellij.openapi.editor.EditorFactory\nimport com.intellij.openapi.editor.event.EditorFactoryEvent\nimport com.intellij.openapi.editor.event.EditorFactoryListener\nimport com.intellij.openapi.editor.event.SelectionEvent\nimport com.intellij.openapi.editor.event.SelectionListener\nimport com.intellij.openapi.editor.markup.RangeHighlighter\nimport com.intellij.openapi.fileEditor.FileDocumentManager\nimport com.intellij.openapi.fileEditor.FileEditorManager\nimport com.intellij.openapi.fileEditor.TextEditor\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.util.Disposer\nimport com.phodal.shirecore.provider.ide.InlineChatProvider\nimport io.ktor.util.collections.*\n\ndata class GutterIconData(\n    val line: Int,\n    val highlighter: RangeHighlighter,\n)\n\nclass ShireInlineChatProvider: InlineChatProvider {\n    override fun addListener(project: Project) {\n        EditorGutterHandler.getInstance(project).listen()\n    }\n\n    override fun removeListener(project: Project) {\n        EditorGutterHandler.getInstance(project).dispose()\n    }\n}\n\n@Service(Service.Level.PROJECT)\nclass EditorGutterHandler(private val project: Project) : Disposable {\n    val gutterIcons: ConcurrentMap<Editor, GutterIconData?> = ConcurrentMap()\n\n    private var disposable: Disposable? = null\n\n    fun listen() {\n        addEditorFactoryListener()\n    }\n\n    fun addEditorFactoryListener() {\n        if (disposable != null) {\n            return\n        }\n        disposable = Disposer.newDisposable().apply {\n            EditorFactory.getInstance().addEditorFactoryListener(object : EditorFactoryListener {\n                override fun editorCreated(event: EditorFactoryEvent) {\n                    onEditorCreated(event.editor, this@apply)\n                }\n            }, this)\n\n            FileEditorManager.getInstance(project).allEditors.mapNotNull { it as? TextEditor }.forEach {\n                updateGutterIconWithSelection(it.editor)\n                onEditorCreated(it.editor, this@apply)\n            }\n        }\n    }\n\n    fun onEditorCreated(editor: Editor, disposable: Disposable) {\n        editor.selectionModel.addSelectionListener(object : SelectionListener {\n            override fun selectionChanged(e: SelectionEvent) {\n                if (e.editor.project != project) return\n                updateGutterIconWithSelection(editor)\n            }\n        }, disposable)\n    }\n\n    fun updateGutterIconWithSelection(editor: Editor) {\n        if (!editor.selectionModel.hasSelection()) {\n            gutterIcons[editor]?.let {\n                removeGutterIcon(editor, it.highlighter)\n            }\n\n            return\n        }\n\n        val selectionStart = editor.document.getLineNumber(editor.selectionModel.selectionStart)\n        if (selectionStart >= 0 && selectionStart < editor.document.lineCount) {\n            val gutterIconInfo = gutterIcons[editor]\n            if (gutterIconInfo?.line != selectionStart) {\n                addGutterIcon(editor, selectionStart)\n            }\n        }\n    }\n\n    fun addGutterIcon(editor: Editor, line: Int) {\n        val iconData: GutterIconData? = gutterIcons[editor]\n        if (iconData != null) {\n            removeGutterIcon(editor, iconData.highlighter)\n        }\n\n        FileDocumentManager.getInstance().getFile(editor.document) ?: return\n\n        val highlighter = editor.markupModel.addLineHighlighter(null, line, 0)\n        highlighter.gutterIconRenderer = ShireGutterIconRenderer(line, onClick = {\n            ShireInlineChatService.getInstance().showInlineChat(editor)\n        })\n\n        gutterIcons[editor] = GutterIconData(line, highlighter)\n    }\n\n    fun removeGutterIcon(editor: Editor, highlighter: RangeHighlighter? = null) {\n        if (highlighter != null) editor.markupModel.removeHighlighter(highlighter)\n        gutterIcons.remove(editor)\n    }\n\n    override fun dispose() {\n        gutterIcons.forEach {\n            removeGutterIcon(it.key, it.value?.highlighter)\n        }\n        disposable?.let { Disposer.dispose(it) }\n        disposable = null\n    }\n\n    companion object {\n\n        fun getInstance(project: Project): EditorGutterHandler {\n            return project.getService(EditorGutterHandler::class.java)\n        }\n    }\n}\n\n"
  },
  {
    "path": "src/main/kotlin/com/phodal/shire/inline/ShireInlineChatService.kt",
    "content": "package com.phodal.shire.inline\n\nimport com.intellij.openapi.Disposable\nimport com.intellij.openapi.application.ApplicationManager\nimport com.intellij.openapi.components.Service\nimport com.intellij.openapi.editor.Editor\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.wm.IdeFocusManager\nimport com.phodal.shirecore.config.ShireActionLocation\nimport com.phodal.shirecore.provider.ide.ShirePromptBuilder\nimport java.util.concurrent.ConcurrentHashMap\n\n@Service(Service.Level.APP)\nclass ShireInlineChatService : Disposable {\n    private val allChats: ConcurrentHashMap<String, ShireInlineChatPanel> = ConcurrentHashMap()\n\n    fun showInlineChat(editor: Editor) {\n        var canShowInlineChat = true\n        if (allChats.containsKey(editor.virtualFile.url)) {\n            val chatPanel: ShireInlineChatPanel = this.allChats[editor.virtualFile.url]!!\n            canShowInlineChat = chatPanel.inlay?.offset != editor.caretModel.primaryCaret.offset\n            closeInlineChat(editor)\n        }\n\n        if (canShowInlineChat) {\n            if (editor.component is ShireInlineChatPanel) return\n\n            val panel = ShireInlineChatPanel(editor)\n            editor.contentComponent.add(panel)\n            panel.setInlineContainer(editor.contentComponent)\n\n            val offset = if (editor.selectionModel.hasSelection()) {\n                editor.selectionModel.selectionStart\n            } else {\n                editor.caretModel.primaryCaret.offset\n            }\n            panel.createInlay(offset)\n\n            IdeFocusManager.getInstance(editor.project).requestFocus(panel.inputPanel.getInputComponent(), true)\n            allChats[editor.virtualFile.url] = panel\n        }\n    }\n\n    override fun dispose() {\n        allChats.values.forEach {\n            closeInlineChat(it.editor)\n        }\n\n        allChats.clear()\n    }\n\n    fun closeInlineChat(editor: Editor) {\n        val chatPanel = this.allChats[editor.virtualFile.url] ?: return\n\n        chatPanel.dispose()\n\n        editor.contentComponent.remove(chatPanel)\n        editor.contentComponent.revalidate()\n        editor.contentComponent.repaint()\n        allChats.remove(editor.virtualFile.url)\n    }\n\n    fun prompt(project: Project, prompt: String): String {\n        return ShirePromptBuilder.provide()?.build(project, ShireActionLocation.INLINE_CHAT.name, prompt) ?: prompt\n    }\n\n    companion object {\n        fun getInstance(): ShireInlineChatService {\n            return ApplicationManager.getApplication().getService(ShireInlineChatService::class.java)\n        }\n    }\n}\n\n"
  },
  {
    "path": "src/main/kotlin/com/phodal/shire/llm/OpenAILikeProvider.kt",
    "content": "package com.phodal.shire.llm\n\nimport com.intellij.openapi.diagnostic.logger\nimport com.intellij.openapi.project.Project\nimport com.phodal.shirecore.sse.CustomSSEHandler\nimport com.phodal.shirecore.sse.appendCustomHeaders\nimport com.phodal.shirecore.sse.updateCustomFormat\nimport com.phodal.shire.settings.ShireSettingsState\nimport com.phodal.shirecore.llm.*\nimport kotlinx.coroutines.flow.Flow\nimport okhttp3.MediaType.Companion.toMediaTypeOrNull\nimport okhttp3.OkHttpClient\nimport okhttp3.Request\nimport okhttp3.RequestBody.Companion.toRequestBody\nimport java.time.Duration\n\nclass OpenAILikeProvider : CustomSSEHandler(), LlmProvider {\n    private val timeout = Duration.ofSeconds(defaultTimeout)\n    private var modelName: String = ShireSettingsState.getInstance().modelName\n    private var temperature: Float  = ShireSettingsState.getInstance().temperature\n    private var maxTokens: Int? = null\n    private var key: String = ShireSettingsState.getInstance().apiToken\n    private var url: String = ShireSettingsState.getInstance().apiHost.ifEmpty {\n        \"https://api.openai.com/v1/chat/completions\"\n    }\n\n    private val messages: MutableList<ChatMessage> = ArrayList()\n    private var client = OkHttpClient()\n\n    override val requestFormat: String get() = if (maxTokens != null) {\n        \"\"\"{ \"customFields\": {\"model\": \"$modelName\", \"temperature\": $temperature, \"max_tokens\": $maxTokens, \"stream\": true} }\"\"\"\n    } else {\n        \"\"\"{ \"customFields\": {\"model\": \"$modelName\", \"temperature\": $temperature, \"stream\": true} }\"\"\"\n    }\n    override val responseFormat: String get() = \"\\$.choices[0].delta.content\"\n    override var project: Project? = null\n    override fun clearMessage() = messages.clear()\n\n    override fun isApplicable(project: Project, llmConfig: LlmConfig?): Boolean {\n        this.project = project\n        if (llmConfig != null) return llmConfig.checkAvailable()\n        // If the configRunLlm configuration exists, it is also available\n        if (configRunLlm().let { it?.checkAvailable() == true }) return true\n        // dynamic check for the API key and model name\n        return ShireSettingsState.getInstance().apiToken.isNotEmpty()\n                && ShireSettingsState.getInstance().modelName.isNotEmpty()\n    }\n\n    private fun configFromState() {\n        modelName = ShireSettingsState.getInstance().modelName\n        temperature = ShireSettingsState.getInstance().temperature\n        key = ShireSettingsState.getInstance().apiToken\n        url = ShireSettingsState.getInstance().apiHost.ifEmpty {\n            \"https://api.openai.com/v1/chat/completions\"\n        }\n    }\n\n    override fun stream(\n        promptText: String,\n        systemPrompt: String,\n        keepHistory: Boolean,\n        llmConfig: LlmConfig?\n    ): Flow<String> {\n        (llmConfig ?: configRunLlm()).let {\n            if (it != null && it.checkAvailable()) {\n                modelName = it.model\n                temperature = it.temperature.toFloat()\n                key = it.apiKey\n                url = it.apiBase\n                maxTokens = it.maxTokens\n            } else {\n                configFromState()\n            }\n        }\n\n        if (!keepHistory) {\n            clearMessage()\n        }\n\n        messages += ChatMessage(ChatRole.user, promptText)\n\n        val customRequest = CustomRequest(messages)\n        val requestContent = customRequest.updateCustomFormat(requestFormat)\n\n        val body = requestContent.toRequestBody(\"application/json; charset=utf-8\".toMediaTypeOrNull())\n\n        val builder = Request.Builder()\n        if (key.isNotEmpty()) {\n            builder.addHeader(\"Authorization\", \"Bearer $key\")\n            builder.addHeader(\"Content-Type\", \"application/json\")\n        }\n        builder.appendCustomHeaders(requestFormat)\n\n        logger<OpenAILikeProvider>().info(\"Requesting form: $requestContent $body\")\n\n        client = client.newBuilder().readTimeout(timeout).build()\n        val call = client.newCall(builder.url(url).post(body).build())\n\n        val copyMessages = messages.toMutableList()\n        if (!keepHistory) {\n            clearMessage()\n            return streamSSE(call, copyMessages, project!!)\n        } else {\n            return streamSSE(call, messages, project!!)\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/kotlin/com/phodal/shire/marketplace/ShireToolWindowFactory.kt",
    "content": "package com.phodal.shire.marketplace\n\nimport com.intellij.openapi.project.DumbAware\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.wm.ToolWindow\nimport com.intellij.openapi.wm.ToolWindowFactory\nimport com.phodal.shire.marketplace.ui.MarketplaceView\n\nclass ShireToolWindowFactory : ToolWindowFactory, DumbAware {\n    override fun createToolWindowContent(project: Project, toolWindow: ToolWindow) {\n        project.getService(MarketplaceView::class.java).initToolWindow(toolWindow)\n    }\n\n    companion object {\n        const val id = \"ShireToolWindow\"\n    }\n}\n"
  },
  {
    "path": "src/main/kotlin/com/phodal/shire/marketplace/model/ShirePackage.kt",
    "content": "package com.phodal.shire.marketplace.model\n\ndata class ShirePackage(\n    val title: String,\n    val description: String,\n    val link: String,\n    val installCmd: String = \"\",\n    val featured: Boolean = false\n) {\n    // for Jackson, do not remove\n    constructor() : this(\"\", \"\", \"\")\n}"
  },
  {
    "path": "src/main/kotlin/com/phodal/shire/marketplace/ui/IconButtonTableCellEditor.kt",
    "content": "/*\n * Copyright (C) 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.phodal.shire.marketplace.ui\n\nimport com.google.common.annotations.VisibleForTesting\nimport java.awt.Component\nimport javax.swing.AbstractCellEditor\nimport javax.swing.Icon\nimport javax.swing.JTable\nimport javax.swing.event.ChangeEvent\nimport javax.swing.table.TableCellEditor\n\nopen class IconButtonTableCellEditor(value: Any, icon: Icon, tooltipText: String) : AbstractCellEditor(),\n    TableCellEditor {\n    protected var myValue: Any = value\n    protected val myButton: IconButton = IconButton(icon).apply {\n        setOpaque(true)\n        setToolTipText(tooltipText)\n    }\n\n    @get:VisibleForTesting\n    var changeEvent: ChangeEvent?\n        get() = changeEvent\n        set(changeEvent) {\n            super.changeEvent = changeEvent\n        }\n\n    override fun getTableCellEditorComponent(\n        table: JTable,\n        value: Any,\n        selected: Boolean,\n        viewRowIndex: Int,\n        viewColumnIndex: Int,\n    ): Component {\n        // I'd pass selected instead of hard coding true but the selection is changed after the cell is edited. selected is false when I expect\n        // it to be true.\n        return myButton.getTableCellComponent(table, true, true)\n    }\n\n    override fun getCellEditorValue(): Any {\n        checkNotNull(myValue)\n        return myValue as Any\n    }\n}\n"
  },
  {
    "path": "src/main/kotlin/com/phodal/shire/marketplace/ui/IconButtonTableCellRenderer.kt",
    "content": "/*\n * Copyright (C) 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.phodal.shire.marketplace.ui\n\nimport com.google.common.annotations.VisibleForTesting\nimport com.intellij.openapi.util.IconLoader\nimport com.intellij.ui.scale.JBUIScale.scale\nimport com.intellij.util.IconUtil\nimport com.intellij.util.ui.JBDimension\nimport java.awt.Color\nimport java.awt.Component\nimport java.awt.Dimension\nimport java.awt.image.RGBImageFilter\nimport java.util.*\nimport java.util.function.Function\nimport java.util.function.Supplier\nimport java.util.stream.IntStream\nimport javax.swing.Icon\nimport javax.swing.JButton\nimport javax.swing.JTable\nimport javax.swing.UIManager\nimport javax.swing.border.Border\nimport javax.swing.table.TableCellRenderer\nimport javax.swing.table.TableColumn\nimport kotlin.math.max\n\nfun generateColoredIcon(icon: Icon, color: Color): Icon = IconLoader.createLazy(object : Supplier<Icon> {\n    private val cache = mutableMapOf<Int, Icon>()\n\n    override fun get(): Icon =\n        cache.getOrPut(color.rgb) {\n            IconUtil.filterIcon(icon, {\n                object : RGBImageFilter() {\n                    override fun filterRGB(x: Int, y: Int, rgb: Int) = (rgb or 0xffffff) and color.rgb\n                }\n            }, null)\n        }\n})\n\nobject Tables {\n    fun getBackground(table: JTable, selected: Boolean): Color {\n        if (selected) {\n            return table.selectionBackground\n        }\n\n        return table.background\n    }\n\n    fun getBorder(selected: Boolean, focused: Boolean): Border? {\n        return getBorder(\n            selected, focused\n        ) { key: Any -> UIManager.getBorder(key) }\n    }\n\n    @VisibleForTesting\n    fun getBorder(selected: Boolean, focused: Boolean, getBorder: Function<Any, Border?>): Border? {\n        if (!focused) {\n            return getBorder.apply(\"Table.cellNoFocusBorder\")\n        }\n\n        if (selected) {\n            return getBorder.apply(\"Table.focusSelectedCellHighlightBorder\")\n        }\n\n        return getBorder.apply(\"Table.focusCellHighlightBorder\")\n    }\n\n    fun getForeground(table: JTable, selected: Boolean): Color {\n        if (selected) {\n            return table.selectionForeground\n        }\n\n        return table.foreground\n    }\n\n    fun getIcon(table: JTable, selected: Boolean, icon: Icon): Icon {\n        if (selected) {\n            return generateColoredIcon(icon, table.selectionForeground)\n        }\n\n        return icon\n    }\n\n    fun setWidths(column: TableColumn, width: Int) {\n        column.minWidth = width\n        column.maxWidth = width\n        column.preferredWidth = width\n    }\n\n    fun setWidths(column: TableColumn, width: Int, minWidth: Int) {\n        column.minWidth = minWidth\n        column.maxWidth = width\n        column.preferredWidth = width\n    }\n\n    fun getPreferredColumnWidth(table: JTable, viewColumnIndex: Int, minPreferredWidth: Int): Int {\n        val width = IntStream.range(-1, table.rowCount)\n            .map { viewRowIndex: Int ->\n                getPreferredCellWidth(\n                    table,\n                    viewRowIndex,\n                    viewColumnIndex\n                )\n            }\n            .max()\n\n        if (!width.isPresent) {\n            return minPreferredWidth\n        }\n\n        return max(width.asInt.toDouble(), minPreferredWidth.toDouble()).toInt()\n    }\n\n    private fun getPreferredCellWidth(table: JTable, viewRowIndex: Int, viewColumnIndex: Int): Int {\n        val component: Component\n\n        if (viewRowIndex == -1) {\n            val renderer = table.tableHeader.defaultRenderer\n            val value = table.columnModel.getColumn(viewColumnIndex).headerValue\n\n            component = renderer.getTableCellRendererComponent(table, value, false, false, -1, viewColumnIndex)\n        } else {\n            component = table.prepareRenderer(\n                table.getCellRenderer(viewRowIndex, viewColumnIndex),\n                viewRowIndex,\n                viewColumnIndex\n            )\n        }\n\n        return component.preferredSize.width + scale(8)\n    }\n}\n\ninterface IconTableCell {\n    fun getTableCellComponent(table: JTable, selected: Boolean, focused: Boolean): Component {\n        setBackground(Tables.getBackground(table, selected))\n        setBorder(Tables.getBorder(selected, focused))\n\n        val icon = defaultIcon\n            .map { i: Icon -> Tables.getIcon(table, selected, i) }\n            .orElse(null)\n\n        setIcon(icon)\n        return this as Component\n    }\n\n    val defaultIcon: Optional<Icon>\n\n    fun setBackground(background: Color?)\n\n    fun setBorder(border: Border?)\n\n    fun setIcon(icon: Icon?)\n}\n\nclass IconButton(defaultIcon: Icon) : JButton(defaultIcon), IconTableCell {\n    private var myDefaultIcon: Icon\n\n    init {\n        val size: Dimension = JBDimension(22, 22)\n\n        border = null\n        isContentAreaFilled = false\n        maximumSize = size\n        minimumSize = size\n        preferredSize = size\n\n        myDefaultIcon = defaultIcon\n    }\n\n    override var defaultIcon: Optional<Icon>\n        get() = Optional.ofNullable(myDefaultIcon)\n        set(defaultIcon) {\n            myDefaultIcon = defaultIcon.orElse(null)\n        }\n\n    fun setDefaultIcon(defaultIcon: Icon) {\n        myDefaultIcon = defaultIcon\n    }\n}\n\nopen class IconButtonTableCellRenderer(icon: Icon? = null, tooltipText: String? = null) : TableCellRenderer {\n    protected val myButton: IconButton = IconButton(icon!!)\n\n    init {\n        myButton.toolTipText = tooltipText\n    }\n\n    override fun getTableCellRendererComponent(\n        table: JTable,\n        value: Any,\n        selected: Boolean,\n        focused: Boolean,\n        viewRowIndex: Int,\n        viewColumnIndex: Int,\n    ): Component {\n        return myButton.getTableCellComponent(table, selected, focused)\n    }\n\n    companion object {\n        fun getPreferredWidth(table: JTable, c: Class<*>): Int {\n            val renderer = table.getDefaultRenderer(c) as IconButtonTableCellRenderer\n            return renderer.myButton.getTableCellComponent(table, false, false).preferredSize.width + scale(8)\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/com/phodal/shire/marketplace/ui/MarketplaceView.kt",
    "content": "package com.phodal.shire.marketplace.ui\n\nimport com.intellij.openapi.components.State\nimport com.intellij.openapi.components.Storage\nimport com.intellij.openapi.components.StoragePathMacros\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.wm.ToolWindow\nimport com.intellij.ui.content.ContentFactory\nimport com.intellij.ui.dsl.builder.Align\nimport com.intellij.ui.dsl.builder.panel\nimport com.phodal.shirecore.ui.input.ShireChatBoxInput\nimport javax.swing.JPanel\n\n@State(name = \"MarketPlaceView\", storages = [Storage(StoragePathMacros.PRODUCT_WORKSPACE_FILE)])\nclass MarketplaceView(project: Project) {\n    private var myToolWindowPanel: JPanel? = null\n\n    private val shirePackageTableComponent = ShireMarketplaceTableView(project)\n\n    init {\n        val shireInput = ShireChatBoxInput(project)\n        myToolWindowPanel = panel {\n            row {\n                cell(shirePackageTableComponent.mainPanel).align(Align.FILL)\n            }.resizableRow()\n            row {\n                cell(shireInput).align(Align.FILL)\n            }\n        }\n    }\n\n    fun initToolWindow(toolWindow: ToolWindow) {\n        val contentFactory = ContentFactory.getInstance()\n        val content = contentFactory.createContent(myToolWindowPanel, \"Shire Marketplace\", false)\n        toolWindow.contentManager.addContent(content)\n    }\n}"
  },
  {
    "path": "src/main/kotlin/com/phodal/shire/marketplace/ui/ShireMarketplaceTableView.kt",
    "content": "package com.phodal.shire.marketplace.ui\n\nimport com.fasterxml.jackson.databind.ObjectMapper\nimport com.intellij.icons.AllIcons\nimport com.intellij.openapi.application.ApplicationManager\nimport com.intellij.openapi.project.Project\nimport com.intellij.ui.components.JBScrollPane\nimport com.intellij.ui.dsl.builder.Align\nimport com.intellij.ui.dsl.builder.panel\nimport com.intellij.ui.table.TableView\nimport com.intellij.util.ui.ColumnInfo\nimport com.intellij.util.ui.ListTableModel\nimport com.phodal.shire.ShireIdeaIcons\nimport com.phodal.shire.ShireMainBundle\nimport com.phodal.shire.marketplace.model.ShirePackage\nimport com.phodal.shire.marketplace.util.ShireDownloader\nimport com.phodal.shirecore.SHIRE_MKT_HOST\nimport com.phodal.shirecore.ShirelangNotifications\nimport okhttp3.OkHttpClient\nimport okhttp3.Request\nimport okhttp3.Response\nimport java.awt.Component\nimport javax.swing.JButton\nimport javax.swing.JPanel\nimport javax.swing.JTable\nimport javax.swing.table.TableCellEditor\nimport javax.swing.table.TableCellRenderer\n\n\nclass ShireMarketplaceTableView(val project: Project) {\n    private val columns = arrayOf(\n        object : ColumnInfo<ShirePackage, String>(ShireMainBundle.message(\"marketplace.column.name\")) {\n            override fun valueOf(data: ShirePackage): String = data.title\n            override fun getWidth(table: JTable?): Int = 120\n        },\n        object : ColumnInfo<ShirePackage, String>(ShireMainBundle.message(\"marketplace.column.description\")) {\n            override fun valueOf(data: ShirePackage): String = data.description\n        },\n        object : ColumnInfo<ShirePackage, ShirePackage>(\"\") {\n            override fun getWidth(table: JTable?): Int = 40\n            override fun valueOf(item: ShirePackage?): ShirePackage? = item\n            override fun isCellEditable(item: ShirePackage?): Boolean = true\n\n            override fun getEditor(item: ShirePackage): TableCellEditor {\n                return object : IconButtonTableCellEditor(item, ShireIdeaIcons.Download, \"Download\") {\n                    init {\n                        myButton.addActionListener {\n                            ShirelangNotifications.info(project, \"Downloading ${item.title}\")\n                            ShireDownloader(project, item).downloadAndUnzip()\n                            ShirelangNotifications.info(project, \"Success Downloaded ${item.title}\")\n\n                            fireEditingStopped()\n                        }\n                    }\n                }\n            }\n\n            override fun getRenderer(item: ShirePackage?): TableCellRenderer {\n                return object : IconButtonTableCellRenderer(ShireIdeaIcons.Download, \"Download\") {\n                    override fun getTableCellRendererComponent(\n                        table: JTable,\n                        value: Any,\n                        selected: Boolean,\n                        focused: Boolean,\n                        viewRowIndex: Int,\n                        viewColumnIndex: Int,\n                    ): Component {\n                        myButton.isEnabled = true\n\n                        return super.getTableCellRendererComponent(\n                            table,\n                            value,\n                            selected,\n                            focused,\n                            viewRowIndex,\n                            viewColumnIndex\n                        )\n                    }\n                }\n            }\n        }\n    )\n\n    var mainPanel: JPanel\n    private val client = OkHttpClient()\n\n    init {\n        val tableModel = ListTableModel(columns, listOf<ShirePackage>())\n        val tableView = TableView(tableModel)\n        val scrollPane = JBScrollPane(tableView)\n\n        val myReloadButton = JButton(AllIcons.Actions.Refresh)\n\n        mainPanel = panel {\n            row {\n                cell(myReloadButton.apply {\n                    addActionListener {\n                        tableModel.items = makeApiCall()\n                        tableModel.fireTableDataChanged()\n                    }\n                })\n            }\n\n            row {\n                cell(scrollPane).align(Align.FILL)\n            }.resizableRow()\n        }\n\n        ApplicationManager.getApplication().invokeLater {\n            tableModel.items = makeApiCall()\n            tableModel.fireTableDataChanged()\n        }\n    }\n\n    private fun makeApiCall(): List<ShirePackage> {\n        try {\n            val objectMapper = ObjectMapper()\n\n            val request = Request.Builder().url(SHIRE_MKT_HOST).get().build()\n            val responses: Response = client.newCall(request).execute()\n            val jsonData = responses.body?.string()\n            val packages = objectMapper.readValue(jsonData, Array<ShirePackage>::class.java)\n            return packages.toList()\n        } catch (e: Exception) {\n            ShirelangNotifications.error(project, \"Failed to fetch data: $e\")\n            return listOf()\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/com/phodal/shire/marketplace/util/ShireDownloader.kt",
    "content": "// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage com.phodal.shire.marketplace.util\n\nimport com.intellij.openapi.application.ApplicationManager\nimport com.intellij.openapi.application.invokeLater\nimport com.intellij.openapi.diagnostic.logger\nimport com.intellij.openapi.progress.ProgressManager\nimport com.intellij.openapi.progress.impl.BackgroundableProcessIndicator\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.project.guessProjectDir\nimport com.intellij.openapi.util.Computable\nimport com.intellij.openapi.util.io.FileUtil\nimport com.intellij.util.download.DownloadableFileService\nimport com.intellij.util.download.FileDownloader\nimport com.intellij.util.io.ZipUtil\nimport com.phodal.shire.ShireMainBundle\nimport com.phodal.shire.marketplace.model.ShirePackage\nimport com.phodal.shirecore.ShirelangNotifications\nimport java.io.File\nimport java.io.IOException\nimport java.nio.file.Files\nimport java.nio.file.Path\nimport java.util.concurrent.locks.ReentrantReadWriteLock\nimport javax.swing.SwingUtilities\n\nclass ShireDownloader(val project: Project, val item: ShirePackage) {\n    private val downloadLock = ReentrantReadWriteLock()\n\n    fun downloadAndUnzip(): Boolean {\n        val service = DownloadableFileService.getInstance()\n\n        val filename = item.link.substringAfterLast(\"/\")\n        val description = service.createFileDescription(\n            item.link,\n            filename\n        )\n\n        val downloader = service.createDownloader(listOf(description), \"Download Shire package: \" + item.title)\n\n        if (SwingUtilities.isEventDispatchThread()) {\n            ApplicationManager.getApplication()\n                .executeOnPooledThread<Boolean> { downloadWithLock(downloader) }\n\n            return true\n        } else {\n            return downloadWithLock(downloader)\n        }\n    }\n\n    private fun downloadWithLock(downloader: FileDownloader): Boolean {\n        downloadLock.writeLock().lock()\n        try {\n            val pluginDir: File = getPluginDir()\n            return downloadWithProgress {\n                doDownload(pluginDir, downloader).apply {\n                    invokeLater {\n                        project.guessProjectDir()?.refresh(true, true)\n                    }\n                }\n            }\n        } finally {\n            downloadLock.writeLock().unlock()\n        }\n    }\n\n    private fun downloadWithProgress(downloadTask: Computable<Boolean>): Boolean {\n        if (ProgressManager.getInstance().hasProgressIndicator()) {\n            return downloadTask.compute()\n        } else {\n            val indicator =\n                BackgroundableProcessIndicator(\n                    project, ShireMainBundle.message(\"downloading.package\"), null, null, true\n                )\n            return ProgressManager.getInstance().runProcess(downloadTask, indicator)\n        }\n    }\n\n    protected fun doDownload(pluginDir: File?, downloader: FileDownloader): Boolean {\n        var tempDir: Path? = null\n        try {\n            tempDir = Files.createTempDirectory(\".shire-download\")\n            val list = downloader.download(tempDir.toFile())\n            val file = list[0].first.toPath()\n            ZipUtil.extract(file, getTargetDir(pluginDir).toPath(), null)\n            return true\n        } catch (e: IOException) {\n            val message = \"Can't download Shire package: \" + item.title\n            logger<ShireDownloader>().warn(message, e)\n            ShirelangNotifications.error(project, e.message ?: message)\n            return false\n        } finally {\n            if (tempDir != null) {\n                FileUtil.delete(tempDir.toFile())\n            }\n        }\n    }\n\n    private fun getPluginDir(): File {\n        val pluginDir = File(project.basePath, \"\")\n        if (!pluginDir.exists()) {\n            pluginDir.mkdirs()\n\n            return pluginDir\n        }\n\n        return pluginDir\n    }\n\n    private fun getTargetDir(pluginDir: File?): File {\n        return pluginDir?.let { File(it, \"\") } ?: File(project.basePath, \"\")\n    }\n}"
  },
  {
    "path": "src/main/kotlin/com/phodal/shire/settings/ShireLlmSettingsConfigurable.kt",
    "content": "package com.phodal.shire.settings\n\nimport com.intellij.openapi.options.ConfigurableBase\n\nclass ShireLlmSettingsConfigurable @JvmOverloads constructor(private val settings: ShireSettingsState = ShireSettingsState.getInstance()) :\n    ConfigurableBase<ShireSettingUi, ShireSettingsState>(\n        \"com.phodal.shire.settings\",\n        \"Shire Settings\",\n        \"com.phodal.shire.settings\"\n    ) {\n    override fun getSettings(): ShireSettingsState {\n        return ShireSettingsState.getInstance()\n    }\n\n    override fun createUi(): ShireSettingUi {\n        return ShireSettingUi()\n    }\n}\n\n"
  },
  {
    "path": "src/main/kotlin/com/phodal/shire/settings/ShireSettingUi.kt",
    "content": "package com.phodal.shire.settings\n\nimport com.intellij.openapi.options.ConfigurableUi\nimport com.intellij.openapi.project.ProjectManager\nimport com.intellij.ui.components.JBPasswordField\nimport com.intellij.ui.dsl.builder.AlignX\nimport com.intellij.ui.dsl.builder.AlignY\nimport com.intellij.ui.dsl.builder.panel\nimport com.intellij.util.MathUtil\nimport com.intellij.util.ui.JBDimension\nimport com.phodal.shirecore.ShireCoreBundle\nimport com.phodal.shirecore.ShireCoroutineScope\nimport com.phodal.shirecore.llm.LlmConfig\nimport com.phodal.shirecore.llm.LlmProvider\nimport kotlinx.coroutines.CoroutineExceptionHandler\nimport kotlinx.coroutines.Job\nimport kotlinx.coroutines.flow.Flow\nimport kotlinx.coroutines.launch\nimport java.awt.event.KeyAdapter\nimport java.awt.event.KeyEvent\nimport javax.swing.*\n\nclass ShireSettingUi : ConfigurableUi<ShireSettingsState> {\n    private var panel: JPanel? = null\n    private var apiHost: JTextField = JTextField()\n    private var modelName: JTextField = JTextField()\n    private var engineToken: JTextField = JBPasswordField()\n\n    private var temperatureField: JTextField = JTextField(4)\n    private val minTemperature = 0.0f\n    private val maxTemperature = 1.0f\n\n    private val testConnectionButton = JButton(\"Test LLM Connection\")\n    private val testResultField = JTextPane()\n    private var testJob: Job? = null\n\n    init {\n        this.panel = panel {\n            row(\"LLM API Host:\") {\n                cell(apiHost)\n                    .applyToComponent { minimumSize = JBDimension(200, 1) }\n                    .align(AlignX.FILL)\n            }\n            row(\"Model Name:\") {\n                cell(modelName)\n                    .applyToComponent { minimumSize = JBDimension(200, 1) }\n                    .align(AlignX.FILL)\n            }\n\n            row(\"Engine Token:\") {\n                cell(engineToken)\n                    .applyToComponent { minimumSize = JBDimension(200, 1) }\n                    .align(AlignX.FILL)\n            }\n\n\n            row(\"Temperature:\") {\n                cell(temperatureField.apply {\n                    this.text = \"0.1\"\n                    this.addKeyListener(object : KeyAdapter() {\n                        override fun keyPressed(e: KeyEvent) {\n                            if (e.keyCode != KeyEvent.VK_UP && e.keyCode != KeyEvent.VK_DOWN) return\n                            val up = e.keyCode == KeyEvent.VK_UP\n                            try {\n                                var value: Float = temperatureField.getText().toFloat()\n                                value += (if (up) 0.1 else -0.1).toFloat()\n                                value = MathUtil.clamp(value, minTemperature, maxTemperature)\n\n                                if (value < minTemperature) {\n                                    value = minTemperature\n                                } else if (value > maxTemperature) {\n                                    value = maxTemperature\n                                }\n\n                                temperatureField.text = value.toString()\n                            } catch (ignored: NumberFormatException) {\n                            }\n                        }\n                    })\n                })\n            }\n\n\n            row {\n                cell(testConnectionButton.apply {\n                    addActionListener {\n                        onTestConnection()\n                    }\n                })\n                cell(testResultField).align(AlignY.CENTER)\n            }\n            row {\n                text(\"Don't forget to APPLY change after test!\")\n            }\n        }\n    }\n\n    override fun reset(settings: ShireSettingsState) {\n        apiHost.text = settings.apiHost\n        modelName.text = settings.modelName\n        engineToken.text = settings.apiToken\n        temperatureField.text = settings.temperature.toString()\n    }\n\n    override fun isModified(settings: ShireSettingsState): Boolean {\n        return apiHost.text != settings.apiHost ||\n                modelName.text != settings.modelName ||\n                engineToken.text != settings.apiToken ||\n                temperatureField.text != settings.temperature.toString()\n    }\n\n    override fun apply(settings: ShireSettingsState) {\n        settings.apiHost = apiHost.text ?: \"\"\n        settings.modelName = modelName.text ?: \"\"\n        settings.apiToken = engineToken.text ?: \"\"\n        settings.temperature = temperatureField.text.toFloatOrNull() ?: 0.0f\n    }\n\n    override fun getComponent(): JComponent {\n        return panel!!\n    }\n\n\n    private fun onTestConnection() {\n        val project = ProjectManager.getInstance().openProjects.firstOrNull() ?: return\n        // cancel last test job\n        testJob?.cancel()\n        testResultField.text = \"user: hi. robot: \"\n\n        // use CoroutineExceptionHandler to handle exception, it will automatically ignore CancelledException\n        testJob = ShireCoroutineScope.scope(project).launch(CoroutineExceptionHandler { coroutineContext, throwable ->\n            testResultField.text = throwable.message ?: \"Unknown error\"\n        }) {\n            val flowString: Flow<String> =\n                LlmConfig(\n                    model = modelName.text,\n                    apiKey = engineToken.text,\n                    apiBase = apiHost.text,\n                    temperature = temperatureField.text.toDoubleOrNull() ?: 0.0,\n                    title = modelName.text,\n                ).let {\n                    LlmProvider.provider(project, it)\n                        ?.stream(\n                            promptText = \"hi\",\n                            systemPrompt = \"\",\n                            keepHistory = false,\n                            llmConfig = it\n                        )\n                        ?: throw Exception(ShireCoreBundle.message(\"shire.llm.notfound\"))\n                }\n\n            flowString.collect {\n                testResultField.text += it\n            }\n\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/kotlin/com/phodal/shire/settings/ShireSettingsState.kt",
    "content": "package com.phodal.shire.settings\n\nimport com.intellij.openapi.application.ApplicationManager\nimport com.intellij.openapi.components.PersistentStateComponent\nimport com.intellij.openapi.components.Service\nimport com.intellij.openapi.components.State\nimport com.intellij.openapi.components.Storage\nimport com.intellij.util.xmlb.XmlSerializerUtil\n\n@Service(Service.Level.APP)\n@State(name = \"com.phodal.shire.settings.ShireSettingsState\", storages = [Storage(\"ShireSettings.xml\")])\nclass ShireSettingsState : PersistentStateComponent<ShireSettingsState> {\n    var temperature: Float = 0.0f\n    var apiHost = \"\"\n    var modelName = \"\"\n    var apiToken = \"\"\n\n    @Synchronized\n    override fun getState(): ShireSettingsState = this\n\n    @Synchronized\n    override fun loadState(state: ShireSettingsState) = XmlSerializerUtil.copyBean(state, this)\n\n    companion object {\n        fun getInstance(): ShireSettingsState {\n            return ApplicationManager.getApplication().getService(ShireSettingsState::class.java).state\n        }\n    }\n}"
  },
  {
    "path": "src/main/resources/META-INF/docker-contrib.xml",
    "content": "<!-- Defines IDEA IDE-specific contributions and implementations. -->\n<idea-plugin>\n    <extensions defaultExtensionNs=\"com.phodal\">\n        <shireLanguageToolchainProvider language=\"Dockerfile\"\n                                        implementationClass=\"com.phodal.shire.docker.DockerContextProvider\"/>\n    </extensions>\n</idea-plugin>"
  },
  {
    "path": "src/main/resources/META-INF/json-contrib.xml",
    "content": "<!-- Defines IDEA IDE-specific contributions and implementations. -->\n<idea-plugin>\n</idea-plugin>"
  },
  {
    "path": "src/main/resources/META-INF/openrewrite-contrib.xml",
    "content": "<!-- Defines IDEA IDE-specific contributions and implementations. -->\n<idea-plugin>\n</idea-plugin>"
  },
  {
    "path": "src/main/resources/META-INF/plugin.xml",
    "content": "<idea-plugin xmlns:xi=\"http://www.w3.org/2001/XInclude\" allow-bundled-update=\"true\">\n    <id>com.phodal.shire</id>\n    <name>Shire - AI Coding Agent Language</name>\n    <vendor>Phodal Huang</vendor>\n    <depends>com.intellij.modules.platform</depends>\n    <depends optional=\"true\" config-file=\"json-contrib.xml\">com.intellij.modules.json</depends>\n    <depends optional=\"true\" config-file=\"openrewrite-contrib.xml\">com.intellij.openRewrite</depends>\n    <depends optional=\"true\" config-file=\"wiremock-contrib.xml\">com.intellij.wiremock</depends>\n    <depends optional=\"true\" config-file=\"docker-contrib.xml\">Docker</depends>\n\n    <resource-bundle>messages.ShireMainBundle</resource-bundle>\n\n    <xi:include href=\"/com.phodal.shirecore.xml\" xpointer=\"xpointer(/idea-plugin/*)\"/>\n    <xi:include href=\"/com.phodal.shire.json.xml\" xpointer=\"xpointer(/idea-plugin/*)\"/>\n\n    <content>\n        <module name=\"com.phodal.shirelang\"/>\n        <module name=\"com.phodal.shirelang.java\"/>\n        <module name=\"com.phodal.shirelang.javascript\"/>\n        <module name=\"com.phodal.shirelang.python\"/>\n        <module name=\"com.phodal.shirelang.kotlin\"/>\n        <module name=\"com.phodal.shirelang.go\"/>\n        <module name=\"com.phodal.shirelang.markdown\"/>\n        <module name=\"com.phodal.shirelang.proto\"/>\n\n<!--        <module name=\"com.phodal.shire.json\"/>-->\n\n        <module name=\"com.phodal.shire.httpclient\"/>\n        <module name=\"com.phodal.shire.terminal\"/>\n        <module name=\"com.phodal.shire.database\"/>\n        <module name=\"com.phodal.shire.git\"/>\n        <module name=\"com.phodal.shire.mock\"/>\n        <module name=\"com.phodal.shire.sonarqube\"/>\n        <module name=\"com.phodal.shire.openrewrite\"/>\n        <module name=\"com.phodal.shire.mermaid\"/>\n        <module name=\"com.phodal.shire.plantuml\"/>\n        <module name=\"com.phodal.shire.docker\"/>\n    </content>\n\n    <extensions defaultExtensionNs=\"com.intellij\">\n\n        <applicationConfigurable parentId=\"tools\" instance=\"com.phodal.shire.settings.ShireLlmSettingsConfigurable\"\n                                 id=\"shireLlmSettingsConfigurable\"\n                                 displayName=\"Shire\"/>\n\n        <projectService serviceImplementation=\"com.phodal.shire.marketplace.ui.MarketplaceView\" client=\"all\"/>\n        <toolWindow id=\"ShireToolWindow\"\n                    anchor=\"right\" icon=\"com.phodal.shire.ShireIdeaIcons.Default\"\n                    factoryClass=\"com.phodal.shire.marketplace.ShireToolWindowFactory\"/>\n    </extensions>\n\n    <extensions defaultExtensionNs=\"com.phodal\">\n        <shireLlmProvider implementation=\"com.phodal.shire.llm.OpenAILikeProvider\"/>\n\n        <shireInlineChatProvider implementation=\"com.phodal.shire.inline.ShireInlineChatProvider\"/>\n    </extensions>\n</idea-plugin>\n"
  },
  {
    "path": "src/main/resources/META-INF/wiremock-contrib.xml",
    "content": "<!-- Defines IDEA IDE-specific contributions and implementations. -->\n<idea-plugin>\n</idea-plugin>"
  },
  {
    "path": "src/main/resources/intentionDescriptions/ShireIntention/after.txt.template",
    "content": "生成一个 Java hello, world：\n\n```java\npublic class HelloWorld {\n    public static void main(String[] args) {\n        System.out.println(\"Hello, World!\");\n    }\n}\n```"
  },
  {
    "path": "src/main/resources/intentionDescriptions/ShireIntention/before.txt.template",
    "content": "生成一个 Java hello, world："
  },
  {
    "path": "src/main/resources/intentionDescriptions/ShireIntention/description.html",
    "content": "Invoke <b>Shire Hobbit</b> Action on here with AI ability.\n"
  },
  {
    "path": "src/main/resources/intentionDescriptions/ShireIntentionHelper/after.txt.template",
    "content": "生成一个 Java hello, world：\n\n```java\npublic class HelloWorld {\n    public static void main(String[] args) {\n        System.out.println(\"Hello, World!\");\n    }\n}\n```"
  },
  {
    "path": "src/main/resources/intentionDescriptions/ShireIntentionHelper/before.txt.template",
    "content": "生成一个 Java hello, world："
  },
  {
    "path": "src/main/resources/intentionDescriptions/ShireIntentionHelper/description.html",
    "content": "Invoke <b>Shire Hobbit</b> Action on here with AI ability.\n"
  },
  {
    "path": "src/main/resources/messages/ShireMainBundle.properties",
    "content": "intentions.assistant.name=Shire intention action\nintentions.assistant.popup.title=Shire Intention Action\n\nmarketplace.column.name=Name\nmarketplace.column.description=Description\ndownloading.package=Downloading Shire package\nchat.input.title=Create property"
  },
  {
    "path": "src/test/testData/rename/foo.xml",
    "content": "<root>\n    <a<caret>1>Foo</a1>\n</root>\n"
  },
  {
    "path": "src/test/testData/rename/foo_after.xml",
    "content": "<root>\n    <a2>Foo</a2>\n</root>\n"
  },
  {
    "path": "toolsets/README.md",
    "content": ""
  },
  {
    "path": "toolsets/database/src/main/kotlin/com/phodal/shire/database/DatabaseSchemaAssistant.kt",
    "content": "package com.phodal.shire.database\n\nimport com.intellij.database.model.DasTable\nimport com.intellij.database.model.ObjectKind\nimport com.intellij.database.model.RawDataSource\nimport com.intellij.database.psi.DbDataSource\nimport com.intellij.database.psi.DbPsiFacade\nimport com.intellij.database.util.DasUtil\nimport com.intellij.openapi.project.Project\n\nobject DatabaseSchemaAssistant {\n    fun getDataSources(project: Project): List<DbDataSource> = DbPsiFacade.getInstance(project).dataSources.toList()\n\n    fun allRawDatasource(project: Project): List<RawDataSource> {\n        val dbPsiFacade = DbPsiFacade.getInstance(project)\n        return dbPsiFacade.dataSources.map { dataSource ->\n            dbPsiFacade.getDataSourceManager(dataSource).dataSources\n        }.flatten()\n    }\n\n    fun getDatabase(project: Project, dbName: String): RawDataSource? {\n        return allRawDatasource(project).firstOrNull { it.name == dbName }\n    }\n\n    fun getAllTables(project: Project): List<DasTable> {\n        return allRawDatasource(project).map {\n            val schemaName = it.name.substringBeforeLast('@')\n            DasUtil.getTables(it).filter { table ->\n                table.kind == ObjectKind.TABLE && (table.dasParent?.name == schemaName || isSQLiteTable(it, table))\n            }\n        }.flatten()\n    }\n\n    fun getTableByDataSource(dataSource: RawDataSource): List<DasTable> {\n        return DasUtil.getTables(dataSource).toList()\n    }\n\n    fun getTable(dataSource: RawDataSource, tableName: String): List<DasTable> {\n        val dasTables = DasUtil.getTables(dataSource)\n        return dasTables.filter { it.name == tableName }.toList()\n    }\n\n    fun executeSqlQuery(project: Project, sql: String): String {\n        return SQLExecutor.executeSqlQuery(project, sql)\n    }\n\n    private fun isSQLiteTable(\n        rawDataSource: RawDataSource,\n        table: DasTable,\n    ) = (rawDataSource.databaseVersion.name == \"SQLite\" && table.dasParent?.name == \"main\")\n\n    fun getTableColumns(project: Project, tables: List<String> = emptyList()): List<String> {\n        val dasTables = getAllTables(project)\n\n        if (tables.isEmpty()) {\n            return dasTables.map(::displayTable)\n        }\n\n        return dasTables.mapNotNull { table ->\n            if (tables.contains(table.name)) {\n                displayTable(table)\n            } else {\n                null\n            }\n        }\n    }\n\n    fun getTableColumn(table: DasTable): String = displayTable(table)\n\n    private fun displayTable(table: DasTable): String {\n        val dasColumns = DasUtil.getColumns(table)\n        val columns = dasColumns.map { column ->\n            \"${column.name}: ${column.dasType.toDataType()}\"\n        }.joinToString(\", \")\n\n        return \"TableName: ${table.name}, Columns: { $columns }\"\n    }\n}"
  },
  {
    "path": "toolsets/database/src/main/kotlin/com/phodal/shire/database/SQLExecutor.kt",
    "content": "package com.phodal.shire.database\n\nimport com.intellij.database.console.JdbcConsole\nimport com.intellij.database.console.JdbcConsoleProvider\nimport com.intellij.database.console.evaluation.EvaluationRequest\nimport com.intellij.database.console.session.DatabaseSession\nimport com.intellij.database.console.session.DatabaseSessionManager\nimport com.intellij.database.console.session.getSessionTitle\nimport com.intellij.database.datagrid.*\nimport com.intellij.database.model.RawDataSource\nimport com.intellij.database.script.PersistenceConsoleProvider\nimport com.intellij.database.settings.DatabaseSettings\nimport com.intellij.database.util.DbImplUtilCore\nimport com.intellij.database.vfs.DbVFSUtils\nimport com.intellij.openapi.application.ApplicationManager\nimport com.intellij.openapi.application.runInEdt\nimport com.intellij.openapi.application.runReadAction\nimport com.intellij.openapi.editor.ex.EditorEx\nimport com.intellij.openapi.fileEditor.FileEditorManager\nimport com.intellij.openapi.project.Project\nimport com.intellij.psi.PsiFile\nimport com.intellij.psi.PsiManager\nimport com.intellij.sql.psi.SqlPsiFacade\nimport com.intellij.testFramework.LightVirtualFile\nimport com.intellij.util.Consumer\nimport com.phodal.shire.database.DatabaseSchemaAssistant.allRawDatasource\nimport java.util.concurrent.CompletableFuture\n\nobject SQLExecutor {\n    fun executeSqlQuery(project: Project, sql: String): String {\n        val file = LightVirtualFile(\"temp.sql\", sql)\n        val psiFile = runReadAction { PsiManager.getInstance(project).findFile(file) }\n            ?: return \"ShireError[Database]: Can't find PSI file\"\n\n        val dataSource = allRawDatasource(project).firstOrNull()\n            ?: throw IllegalArgumentException(\"ShireError[Database]: No database found\")\n\n        val execOptions = DatabaseSettings.getSettings().execOptions.last()\n        val console: JdbcConsole? = JdbcConsole.getActiveConsoles(project).firstOrNull()\n            ?: JdbcConsoleProvider.getValidConsole(project, file)\n            ?: createConsole(project, file)\n\n        if (console != null) {\n            return executeSqlInConsole(console, sql, dataSource)\n        }\n\n        val editor = FileEditorManager.getInstance(project).selectedTextEditor\n            ?: throw IllegalArgumentException(\"ShireError[Database]: No editor found\")\n\n        val scriptModel = SqlPsiFacade.getInstance(project).createScriptModel(psiFile)\n\n        val info = JdbcConsoleProvider.Info(psiFile, psiFile, editor as EditorEx, scriptModel, execOptions, null)\n        val runners: MutableList<PersistenceConsoleProvider.Runner> = runReadAction {\n            getAttachDataSourceRunnersReflect(info)\n        }\n\n        if (runners.size == 1) {\n            var result = \"\"\n            runInEdt {\n                result = executeSql(project, dataSource, sql) ?: \"Error\"\n            }\n\n            return result\n        } else {\n            try {\n                val chooseAndRunRunnersMethod =\n                    Class.forName(\"com.intellij.database.intentions.RunQueryInConsoleIntentionAction\\$Manager\")\n                        .getDeclaredMethod(\n                            \"chooseAndRunRunners\",\n                            List::class.java,\n                            EditorEx::class.java,\n                            Any::class.java\n                        )\n                chooseAndRunRunnersMethod.invoke(null, runners, info.editor, null)\n            } catch (e: Exception) {\n                println(\"ShireError[Database]: Failed to run query with multiple runners\")\n                throw e\n            }\n            return \"ShireError[Database]: Currently not support multiple runners\"\n        }\n    }\n\n//    private fun getAttachDataSourceRunners(info: JdbcConsoleProvider.Info): MutableList<PersistenceConsoleProvider.Runner> {\n//        val virtualFile = info.editor!!.virtualFile\n//        val project = info.originalFile.project\n//        val title = getSessionTitle(virtualFile)\n//        val consumer: Consumer<in DatabaseSession> =\n//            Consumer<DatabaseSession> { newSession: DatabaseSession? ->\n//                val console = JdbcConsoleProvider.attachConsole(\n//                    project,\n//                    newSession!!, virtualFile\n//                )\n//                if (console != null) {\n//                    val runnable = Runnable { JdbcConsoleProvider.doRunQueryInConsole(console, info) }\n//                    if (DbVFSUtils.isAssociatedWithDataSourceAndSchema(virtualFile)) {\n//                        runnable.run()\n//                    } else {\n//                        DatabaseRunners.chooseSchemaAndRun(info.editor!!, runnable)\n//                    }\n//                }\n//            }\n//\n//        return DatabaseRunners.getAttachDataSourceRunners(info.file, title, consumer)\n//    }\n\n    private fun getAttachDataSourceRunnersReflect(info: JdbcConsoleProvider.Info): MutableList<PersistenceConsoleProvider.Runner> {\n        val virtualFile = info.editor!!.virtualFile\n        val project = info.originalFile.project\n        val title = getSessionTitle(virtualFile)\n        val consumer: Consumer<DatabaseSession> = Consumer<DatabaseSession> { newSession: DatabaseSession? ->\n            val console = JdbcConsoleProvider.attachConsole(project, newSession!!, virtualFile)\n            if (console != null) {\n                val runnable = Runnable { JdbcConsoleProvider.doRunQueryInConsole(console, info) }\n                try {\n                    // 使用反射调用 DbVFSUtils.isAssociatedWithDataSourceAndSchema\n                    val isAssociatedMethod = DbVFSUtils::class.java.getDeclaredMethod(\n                        \"isAssociatedWithDataSourceAndSchema\",\n                        virtualFile::class.java\n                    )\n                    val isAssociated = isAssociatedMethod.invoke(null, virtualFile) as Boolean\n\n                    if (isAssociated) {\n                        runnable.run()\n                    } else {\n                        val chooseSchemaMethod = Class.forName(\"com.intellij.database.console.DatabaseRunners\")\n                            .getDeclaredMethod(\"chooseSchemaAndRun\", EditorEx::class.java, Runnable::class.java)\n                        chooseSchemaMethod.invoke(null, info.editor, runnable)\n                    }\n                } catch (e: Exception) {\n                    println(\"ShireError[Database]: Failed to run query in console\")\n                    throw e\n                }\n            }\n        }\n\n        try {\n            // 使用反射调用 DatabaseRunners.getAttachDataSourceRunners\n            val getRunners = Class.forName(\"com.intellij.database.console.DatabaseRunners\")\n                .getDeclaredMethod(\n                    \"getAttachDataSourceRunners\",\n                    PsiFile::class.java,\n                    String::class.java,\n                    com.intellij.util.Consumer::class.java\n                )\n            @Suppress(\"UNCHECKED_CAST\")\n            return getRunners.invoke(null, info.file, title, consumer) as MutableList<PersistenceConsoleProvider.Runner>\n        } catch (e: Exception) {\n            println(\"ShireError[Database]: Failed to get runners\")\n            throw e\n        }\n    }\n\n    private fun executeSqlInConsole(console: JdbcConsole, sql: String, dataSource: RawDataSource): String {\n        val future: CompletableFuture<String> = CompletableFuture()\n        return ApplicationManager.getApplication().executeOnPooledThread<String> {\n            val messageBus = console.session.messageBus\n            val newConsoleRequest = EvaluationRequest.newRequest(console, sql, dataSource.dbms)\n            messageBus.dataProducer.processRequest(newConsoleRequest)\n            messageBus.addConsumer(object : MyCompatDataConsumer() {\n                var result = mutableListOf<GridRow>()\n                override fun addRows(context: GridDataRequest.Context, rows: MutableList<out GridRow>) {\n                    result += rows\n                    if (rows.size < 100) {\n                        future.complete(result.toString())\n                    }\n                }\n            })\n            return@executeOnPooledThread future.get()\n        }.get()\n    }\n\n    private fun executeSql(project: Project, dataSource: RawDataSource, query: String): String? {\n        val future: CompletableFuture<String> = CompletableFuture()\n        val localDs = DbImplUtilCore.getLocalDataSource(dataSource)\n\n        val session = DatabaseSessionManager.getSession(project, localDs)\n        val messageBus = session.messageBus\n        messageBus.addConsumer(object : MyCompatDataConsumer() {\n            var result = mutableListOf<GridRow>()\n            override fun addRows(context: GridDataRequest.Context, rows: MutableList<out GridRow>) {\n                result += rows\n                if (rows.size < 100) {\n                    future.complete(result.toString())\n                }\n            }\n        })\n\n        val request =\n            object : DataRequest.QueryRequest(session, query, DataRequest.newConstraints(dataSource.dbms), null) {}\n        messageBus.dataProducer.processRequest(request)\n        return future.get()\n    }\n\n    private fun createConsole(project: Project, file: LightVirtualFile): JdbcConsole? {\n        val attached = JdbcConsoleProvider.findOrCreateSession(project, file) ?: return null\n        return JdbcConsoleProvider.attachConsole(project, attached, file)\n    }\n\n    abstract class MyCompatDataConsumer : DataConsumer {\n        override fun setColumns(\n            context: GridDataRequest.Context,\n            subQueryIndex: Int,\n            resultSetIndex: Int,\n            columns: Array<out GridColumn>,\n            firstRowNum: Int,\n        ) {\n            // for Compatibility in IDEA 2023.2.8\n        }\n\n        /// will remove in latest version, so we need to use reflection to call this method in future\n        override fun setColumns(\n            context: GridDataRequest.Context,\n            resultSetIndex: Int,\n            columns: Array<out GridColumn>,\n            firstRowNum: Int,\n        ) {\n            // for Compatibility in IDEA 2023.2.8\n        }\n\n\n        override fun afterLastRowAdded(context: GridDataRequest.Context, total: Int) {\n            // for Compatibility in IDEA 2023.2.8\n        }\n    }\n}"
  },
  {
    "path": "toolsets/database/src/main/kotlin/com/phodal/shire/database/SqlContextBuilder.kt",
    "content": "package com.phodal.shire.database\n\nimport com.intellij.database.console.JdbcConsoleProvider\nimport com.intellij.database.model.ObjectKind\nimport com.intellij.database.model.basic.BasicModel\nimport com.intellij.database.model.basic.BasicSchema\nimport com.intellij.database.model.basic.BasicTable\nimport com.intellij.database.model.basic.BasicTableOrViewColumn\nimport com.intellij.database.psi.DbDataSource\nimport com.intellij.database.util.ObjectPath\nimport com.intellij.database.util.QNameUtil\nimport com.intellij.openapi.project.Project\nimport com.intellij.sql.psi.SqlFile\n\nobject SqlContextBuilder {\n    fun getCurrentNamespace(sqlFile: SqlFile): ObjectPath? {\n        val console = JdbcConsoleProvider.getValidConsole(sqlFile.project, sqlFile.virtualFile)\n        return console?.currentNamespace\n    }\n\n    fun getSchema(ds: DbDataSource?, currentNamespace: ObjectPath?): BasicSchema? {\n        val basicModel = ds?.model as? BasicModel ?: return null\n        val dasObject = QNameUtil.findByPath(basicModel, currentNamespace).firstOrNull() ?: return null\n        return dasObject as? BasicSchema\n    }\n\n    fun formatSchema(schema: BasicSchema): String? {\n        return schema.familyOf(ObjectKind.TABLE)?.jbi()\n            ?.mapNotNull { it as? BasicTable }\n            ?.joinToString(\"\\n\\n\") { describeTable(it) }\n    }\n\n    private fun describeTable(table: BasicTable): String =\n        \"\"\"\n        |create table ${table.name} {\n        |  ${table.columns.joinToString(\",\\n  \") { \"${it.name} ${columnType(it)}\" }}\n        |}\n        \"\"\".trimMargin()\n\n    private fun columnType(it: BasicTableOrViewColumn) = it.dasType.specification\n\n    fun buildDatabaseInfo(project: Project): String {\n        val dataSources = DatabaseSchemaAssistant.allRawDatasource(project)\n        return dataSources.joinToString(\"\\n\") {\n            \"\"\"\n            DatabaseName: ${it.databaseVersion.name}\n            DatabaseVersion: ${it.databaseVersion.version}\n            Database: ${it.name}\n            ConnectionConfig: ${it.connectionConfig?.url}\n            \"\"\".trimIndent()\n        }\n    }\n}\n"
  },
  {
    "path": "toolsets/database/src/main/kotlin/com/phodal/shire/database/provider/DatabaseFunctionProvider.kt",
    "content": "package com.phodal.shire.database.provider\n\nimport com.intellij.database.model.DasTable\nimport com.intellij.database.model.RawDataSource\nimport com.intellij.database.util.DasUtil\nimport com.intellij.database.util.DbUtil\nimport com.intellij.openapi.diagnostic.logger\nimport com.intellij.openapi.project.Project\nimport com.phodal.shire.database.DatabaseSchemaAssistant\nimport com.phodal.shirecore.provider.function.ToolchainFunctionProvider\n\nenum class DatabaseFunction(val funName: String) {\n    Schema(\"schema\"),\n    Table(\"table\"),\n    Column(\"column\"),\n    Query(\"query\")\n    ;\n\n    companion object {\n        fun fromString(value: String): DatabaseFunction? {\n            return entries.firstOrNull { it.funName == value }\n        }\n    }\n}\n\nclass DatabaseFunctionProvider : ToolchainFunctionProvider {\n    override fun isApplicable(project: Project, funcName: String): Boolean {\n        return DatabaseFunction.values().any { it.funName == funcName }\n    }\n\n    override fun execute(\n        project: Project,\n        funcName: String,\n        args: List<Any>,\n        allVariables: Map<String, Any?>,\n    ): Any {\n        val databaseFunction = DatabaseFunction.fromString(funcName)\n            ?: throw IllegalArgumentException(\"Shire[Database]: Invalid Database function name\")\n\n        return when (databaseFunction) {\n            DatabaseFunction.Schema -> listSchemas(args, project)\n            DatabaseFunction.Table -> executeTableFunction(args, project)\n            DatabaseFunction.Column -> executeColumnFunction(args, project)\n            DatabaseFunction.Query -> executeSqlFunction(args, project)\n        }\n    }\n\n    private fun listSchemas(args: List<Any>, project: Project): Any {\n        val dataSources = DbUtil.getDataSources(project)\n        if (dataSources.isEmpty) return \"\"\n\n        return dataSources.mapNotNull {\n            val tableSchema = DasUtil.getTables(it).toList().mapNotNull<DasTable, String> {\n                if (it.dasParent?.name == \"information_schema\") return@mapNotNull null\n                DatabaseSchemaAssistant.getTableColumn(it)\n            }\n\n            if (tableSchema.isEmpty()) return@mapNotNull null\n            val name = it.name.substringBeforeLast('@')\n            \"DATABASE NAME: ${name};\\n${tableSchema.joinToString(\"\\n\")}\"\n        }.joinToString(\"\\n\")\n    }\n\n    private fun executeTableFunction(args: List<Any>, project: Project): Any {\n        if (args.isEmpty()) {\n            val dataSource = DatabaseSchemaAssistant.allRawDatasource(project).firstOrNull()\n                ?: return \"ShireError[Database]: No database found\"\n            return DatabaseSchemaAssistant.getTableByDataSource(dataSource)\n        }\n\n        val dbName = args.first()\n        // for example: [accounts, payment_limits, transactions]\n        var result = mutableListOf<DasTable>()\n        when (dbName) {\n            is String -> {\n                if (dbName.startsWith(\"[\") && dbName.endsWith(\"]\")) {\n                    val tableNames = dbName.substring(1, dbName.length - 1).split(\",\")\n                    result = tableNames.map {\n                        getTable(project, it.trim())\n                    }.flatten().toMutableList()\n                } else {\n                    result = getTable(project, dbName).toMutableList()\n                }\n            }\n\n            is List<*> -> {\n                result = dbName.map {\n                    getTable(project, it as String)\n                }.flatten().toMutableList()\n            }\n\n            else -> {\n\n            }\n        }\n\n        return result\n    }\n\n    private fun executeSqlFunction(args: List<Any>, project: Project): Any {\n        if (args.isEmpty()) {\n            return \"ShireError[DBTool]: SQL function requires a SQL query\"\n        }\n\n        val sqlQuery = args.first()\n        return DatabaseSchemaAssistant.executeSqlQuery(project, sqlQuery as String)\n    }\n\n    private fun executeColumnFunction(args: List<Any>, project: Project): Any {\n        if (args.isEmpty()) {\n            val allTables = DatabaseSchemaAssistant.getAllTables(project)\n            val map = allTables.map {\n                DatabaseSchemaAssistant.getTableColumn(it)\n            }\n            return \"\"\"\n                |```sql\n                |${map.joinToString(\"\\n\")}\n                |```\n            \"\"\".trimMargin()\n        }\n\n        when (val first = args[0]) {\n            is RawDataSource -> {\n                return if (args.size == 1) {\n                    DatabaseSchemaAssistant.getTableByDataSource(first)\n                } else {\n                    DatabaseSchemaAssistant.getTable(first, args[1] as String)\n                }\n            }\n\n            is DasTable -> {\n                return DatabaseSchemaAssistant.getTableColumn(first)\n            }\n\n            is List<*> -> {\n                return when (first.first()) {\n                    is RawDataSource -> {\n                        return first.map {\n                            DatabaseSchemaAssistant.getTableByDataSource(it as RawDataSource)\n                        }\n                    }\n\n                    is DasTable -> {\n                        return first.map {\n                            DatabaseSchemaAssistant.getTableColumn(it as DasTable)\n                        }\n                    }\n\n                    else -> {\n                        \"ShireError[DBTool]: Table function requires a data source or a list of table names\"\n                    }\n                }\n            }\n\n            is String -> {\n                val allTables = DatabaseSchemaAssistant.getAllTables(project)\n                if (first.startsWith(\"[\") && first.endsWith(\"]\")) {\n                    val tableNames = first.substring(1, first.length - 1).split(\",\")\n                    return tableNames.mapNotNull {\n                        val dasTable = allTables.firstOrNull { table ->\n                            table.name == it.trim()\n                        }\n\n                        dasTable?.let {\n                            DatabaseSchemaAssistant.getTableColumn(it)\n                        }\n                    }\n                } else {\n                    val dasTable = allTables.firstOrNull { table ->\n                        table.name == first\n                    }\n\n                    return dasTable?.let {\n                        DatabaseSchemaAssistant.getTableColumn(it)\n                    } ?: \"ShireError[DBTool]: Table not found\"\n                }\n            }\n\n            else -> {\n                logger<DatabaseFunctionProvider>().error(\"ShireError[DBTool] args types: ${first.javaClass}\")\n                return \"ShireError[DBTool]: Table function requires a data source or a list of table names\"\n            }\n        }\n    }\n\n    private fun getTable(project: Project, dbName: String): List<DasTable> {\n        val database = DatabaseSchemaAssistant.getDatabase(project, dbName)\n            ?: return emptyList()\n        return DatabaseSchemaAssistant.getTableByDataSource(database)\n    }\n}\n"
  },
  {
    "path": "toolsets/database/src/main/kotlin/com/phodal/shire/database/provider/DatabaseToolchainProvider.kt",
    "content": "package com.phodal.shire.database.provider\n\nimport com.intellij.database.util.DbUtil\nimport com.intellij.openapi.project.Project\nimport com.phodal.shirecore.provider.context.LanguageToolchainProvider\nimport com.phodal.shirecore.provider.context.ToolchainContextItem\nimport com.phodal.shirecore.provider.context.ToolchainPrepareContext\n\nclass DatabaseToolchainProvider : LanguageToolchainProvider {\n    override fun isApplicable(project: Project, context: ToolchainPrepareContext): Boolean {\n        return return DbUtil.getDataSources(project).isNotEmpty\n    }\n\n    override suspend fun collect(project: Project, context: ToolchainPrepareContext): List<ToolchainContextItem> {\n        val dataSources = DbUtil.getDataSources(project)\n        if (dataSources.isEmpty) return emptyList()\n\n        val infos = dataSources.mapNotNull {\n            val dbNames = it.delegateDataSource?.databaseVersion ?: return@mapNotNull null\n            val nameInfo = dbNames.name + \" \" + dbNames.version\n            val text = \"This project use $nameInfo\"\n            return@mapNotNull ToolchainContextItem(DatabaseToolchainProvider::class, text)\n        }.toList()\n\n        return infos\n    }\n}\n"
  },
  {
    "path": "toolsets/database/src/main/kotlin/com/phodal/shire/database/provider/DatabaseVariableProvider.kt",
    "content": "package com.phodal.shire.database.provider\n\nimport com.intellij.openapi.editor.Editor\nimport com.intellij.openapi.project.Project\nimport com.intellij.psi.PsiElement\nimport com.phodal.shire.database.DatabaseSchemaAssistant\nimport com.phodal.shire.database.SqlContextBuilder\nimport com.phodal.shirecore.provider.variable.ToolchainVariableProvider\nimport com.phodal.shirecore.provider.variable.model.ToolchainVariable\nimport com.phodal.shirecore.provider.variable.model.toolchain.DatabaseToolchainVariable\n\nclass DatabaseVariableProvider : ToolchainVariableProvider {\n    override fun isResolvable(variable: ToolchainVariable, psiElement: PsiElement?, project: Project): Boolean {\n        return variable is DatabaseToolchainVariable\n    }\n\n    override fun resolve(variable: ToolchainVariable, project: Project, editor: Editor, psiElement: PsiElement?): Any {\n        if (variable !is DatabaseToolchainVariable) {\n            return \"\"\n        }\n\n       return when (variable) {\n            DatabaseToolchainVariable.DatabaseInfo -> SqlContextBuilder.buildDatabaseInfo(project)\n            DatabaseToolchainVariable.Databases -> DatabaseSchemaAssistant.getDataSources(project)\n            DatabaseToolchainVariable.Tables -> DatabaseSchemaAssistant.getAllTables(project)\n            DatabaseToolchainVariable.Columns -> DatabaseSchemaAssistant.getTableColumns(project)\n        }\n    }\n}\n\n"
  },
  {
    "path": "toolsets/database/src/main/resources/com.phodal.shire.database.xml",
    "content": "<idea-plugin package=\"com.phodal.shire.database\">\n    <!--suppress PluginXmlValidity -->\n    <dependencies>\n        <plugin id=\"com.intellij.database\"/>\n    </dependencies>\n\n    <extensions defaultExtensionNs=\"com.phodal\">\n        <shireToolchainVariableProvider implementation=\"com.phodal.shire.database.provider.DatabaseVariableProvider\"/>\n\n        <shireLanguageToolchainProvider\n                language=\"SQL\"\n                implementationClass=\"com.phodal.shire.database.provider.DatabaseToolchainProvider\"/>\n\n        <shireToolchainFunctionProvider implementation=\"com.phodal.shire.database.provider.DatabaseFunctionProvider\"/>\n    </extensions>\n</idea-plugin>\n"
  },
  {
    "path": "toolsets/docker/src/main/kotlin/com/phodal/shire/docker/DockerContextProvider.kt",
    "content": "package com.phodal.shire.docker\n\nimport com.intellij.docker.DockerFileSearch\nimport com.intellij.docker.dockerFile.parser.psi.DockerFileFromCommand\nimport com.intellij.openapi.application.runReadAction\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.vfs.VfsUtilCore\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.intellij.psi.PsiManager\nimport com.intellij.psi.impl.source.PsiFileImpl\nimport com.phodal.shirecore.provider.context.LanguageToolchainProvider\nimport com.phodal.shirecore.provider.context.ToolchainContextItem\nimport com.phodal.shirecore.provider.context.ToolchainPrepareContext\n\nclass DockerContextProvider : LanguageToolchainProvider {\n    override fun isApplicable(\n        project: Project,\n        context: ToolchainPrepareContext,\n    ): Boolean = DockerFileSearch.getInstance().getDockerFiles(project).isNotEmpty()\n\n    override suspend fun collect(\n        project: Project,\n        context: ToolchainPrepareContext,\n    ): List<ToolchainContextItem> {\n        val dockerFiles = DockerFileSearch.getInstance().getDockerFiles(project).mapNotNull {\n            runReadAction { PsiManager.getInstance(project).findFile(it) }\n        }\n\n        if (dockerFiles.isEmpty()) return emptyList()\n\n        var context = \"This project use Docker.\"\n\n        val virtualFile = dockerFiles.firstOrNull()?.virtualFile\n            ?: return listOf(ToolchainContextItem(DockerContextProvider::class, context))\n\n        context = \"This project use Docker, path: ${virtualFile.path}\"\n\n        var additionalCtx = \"\"\n        val fromCommands = dockerFiles.map {\n            (it as PsiFileImpl).findChildrenByClass(DockerFileFromCommand::class.java).toList()\n        }.flatten()\n\n        if (fromCommands.isEmpty()) return listOf(ToolchainContextItem(DockerContextProvider::class, context))\n        additionalCtx = fromCommands.joinToString(\"\\n\") {\n            runReadAction { it.text }\n        }\n\n        val text = \"This project use Docker to run in server. Here is related info:\\n$additionalCtx\"\n        return listOf(ToolchainContextItem(DockerContextProvider::class, text))\n    }\n}\n\nfun VirtualFile.readText(): String {\n    return VfsUtilCore.loadText(this)\n}\n"
  },
  {
    "path": "toolsets/docker/src/main/resources/com.phodal.shire.docker.xml",
    "content": "<idea-plugin package=\"com.phodal.shire.docker\">\n    <!--suppress PluginXmlValidity -->\n    <dependencies>\n        <plugin id=\"Docker\"/>\n    </dependencies>\n</idea-plugin>\n"
  },
  {
    "path": "toolsets/git/src/main/kotlin/com/phodal/shire/git/DiffSimplifier.kt",
    "content": "package com.phodal.shire.git\n\nimport com.intellij.openapi.components.Service\nimport com.intellij.openapi.diagnostic.logger\nimport com.intellij.openapi.diff.impl.patch.IdeaTextPatchBuilder\nimport com.intellij.openapi.diff.impl.patch.UnifiedDiffWriter\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.util.io.FileUtilRt\nimport com.intellij.openapi.vcs.changes.*\nimport com.intellij.project.stateStore\nimport org.jetbrains.annotations.NotNull\nimport java.io.StringWriter\nimport java.nio.file.Path\nimport java.nio.file.PathMatcher\nimport kotlin.math.min\n\n@Service(Service.Level.PROJECT)\nclass DiffSimplifier(private val project: Project) {\n    private val logger = logger<DiffSimplifier>()\n\n    /**\n     * Simplifies the given list of changes and returns the resulting diff as a string.\n     *\n     * @param changes The list of changes to be simplified.\n     * @param ignoreFilePatterns The list of file patterns to be ignored during simplification.\n     * @return The simplified diff as a string.\n     * @throws RuntimeException if the project base path is null or if there is an error calculating the diff.\n     */\n    fun simplify(changes: List<Change>, ignoreFilePatterns: List<PathMatcher>): String {\n        var originChanges = \"\"\n\n        try {\n            val writer = StringWriter()\n            val basePath = project.basePath ?: throw RuntimeException(\"Project base path is null.\")\n            val binaryOrTooLargeChanges: List<String> = changes.stream()\n                .filter { change -> isBinaryOrTooLarge(change!!) }\n                .map {\n                    when(it.type) {\n                        Change.Type.NEW -> \"new file ${it.afterRevision?.file?.path}\"\n                        Change.Type.DELETED -> \"delete file ${it.beforeRevision?.file?.path}\"\n                        Change.Type.MODIFICATION -> \"modify file ${it.beforeRevision?.file?.path}\"\n                        Change.Type.MOVED -> \"rename file from ${it.beforeRevision?.file?.path} to ${it.afterRevision?.file?.path}\"\n                    }\n                }\n                .toList()\n\n            val filteredChanges = changes.stream()\n                .filter { change -> !isBinaryOrTooLarge(change!!) }\n                .filter {\n                    val filePath = it.afterRevision?.file\n                    if (filePath != null) {\n                        ignoreFilePatterns.none { pattern ->\n                            pattern.matches(Path.of(it.afterRevision!!.file.path))\n                        }\n                    } else {\n                        true\n                    }\n                }\n                .toList()\n\n            if (filteredChanges.isEmpty()) {\n                return \"\"\n            }\n\n            val patches = IdeaTextPatchBuilder.buildPatch(\n                project,\n                filteredChanges.subList(0, min(filteredChanges.size, 500)),\n                Path.of(basePath),\n                false,\n                true\n            )\n\n\n            UnifiedDiffWriter.write(\n                project,\n                project.stateStore.projectBasePath,\n                patches,\n                writer,\n                \"\\n\",\n                null as CommitContext?,\n                emptyList()\n            )\n\n            originChanges = writer.toString()\n            originChanges += binaryOrTooLargeChanges.joinToString(\"\\n\")\n            return postProcess(originChanges)\n        } catch (e: Exception) {\n            if (originChanges.isNotEmpty()) {\n                logger.info(\"Error calculating diff: $originChanges\", e)\n            }\n\n            throw RuntimeException(\"Error calculating diff: ${e.message}\", e)\n        }\n    }\n\n    companion object {\n        private val revisionRegex = Regex(\"\\\\(revision [^)]+\\\\)\")\n        private const val LINE_TIP = \"\\\\ No newline at end of file\"\n\n        /**\n         * This method is used to process the given diff string and extract relevant information from it.\n         *\n         * @param diffString The diff string to be processed.\n         * @return The processed string containing the extracted information.\n         */\n        @NotNull\n        fun postProcess(@NotNull diffString: String): String {\n            val lines = diffString.lines()\n            val length = lines.size\n            val destination = ArrayList<String>()\n            var index = 0\n            while (true) {\n                if (index >= lines.size) {\n                    break\n                }\n\n                val line = lines[index]\n\n                if (line.startsWith(\"diff --git \") || line.startsWith(\"index:\") || line.startsWith(\"Index:\")) {\n                    index++\n                    continue\n                }\n\n                if (line == \"===================================================================\") {\n                    index++\n                    continue\n                }\n\n                // if a patch includes `\\ No newline at the end of file` remove it\n                if (line.contains(LINE_TIP)) {\n                    index++\n                    continue\n                }\n\n                if (line.startsWith(\"---\\t/dev/null\")) {\n                    index++\n                    continue\n                }\n\n\n                // todo: spike for handle for new file\n                if (line.startsWith(\"@@\") && line.endsWith(\"@@\")) {\n                    index++\n                    continue\n                }\n\n                // handle for new file\n                if (line.startsWith(\"new file mode\")) {\n                    val nextLine = lines[index + 1]\n                    if (nextLine.startsWith(\"--- /dev/null\")) {\n                        val nextNextLine = lines[index + 2]\n                        val withoutHead = nextNextLine.substring(\"+++ b/\".length)\n                        // footer: \t(date 1704768267000)\n                        val withoutFooter = withoutHead.substring(0, withoutHead.indexOf(\"\\t\"))\n                        destination.add(\"new file $withoutFooter\")\n                        index += 3\n                        continue\n                    }\n                }\n\n                // handle for rename\n                if (line.startsWith(\"rename from\")) {\n                    val nextLine = lines[index + 1]\n                    if (nextLine.startsWith(\"rename to\")) {\n                        val from = line.substring(\"rename from \".length)\n                        val to = nextLine.substring(\"rename to \".length)\n                        destination.add(\"rename file from $from to $to\")\n                        // The next value will be \"---\" and the value after that will be \"+++\".\n                        index += 4\n                        continue\n                    }\n                }\n\n                // handle for java and kotlin import change\n                if (line.startsWith(\" import\")) {\n                    val nextLine = lines.getOrNull(index + 1)\n                    if (nextLine?.startsWith(\" import\") == true) {\n                        var oldImportLine = \"\"\n                        var newImportLine = \"\"\n                        // search all import lines until the next line starts with \"Index:\"\n                        val importLines = ArrayList<String>()\n                        importLines.add(line)\n                        importLines.add(nextLine)\n\n                        var tryToFindIndex = index + 2\n                        while (true) {\n                            if (tryToFindIndex >= length) {\n                                break\n                            }\n\n                            val tryLine = lines[tryToFindIndex]\n                            when {\n\n                                tryLine.startsWith(\"Index:\") -> {\n                                    break\n                                }\n\n                                tryLine.startsWith(\" import\") -> {\n                                    importLines.add(tryLine)\n                                }\n\n                                tryLine.startsWith(\"-import \") -> {\n                                    oldImportLine = tryLine.substring(\"-import \".length)\n                                    importLines.add(tryLine)\n                                }\n\n                                tryLine.startsWith(\"+import \") -> {\n                                    newImportLine = tryLine.substring(\"+import \".length)\n                                    importLines.add(tryLine)\n                                }\n                            }\n\n                            tryToFindIndex++\n                        }\n\n                        if (oldImportLine.isNotEmpty() && newImportLine.isNotEmpty()) {\n                            if (importLines.size == tryToFindIndex - index) {\n                                index = tryToFindIndex\n                                destination.add(\"change import from $oldImportLine to $newImportLine\")\n                                continue\n                            }\n                        }\n                    }\n                }\n\n                // handle for delete\n                if (line.startsWith(\"deleted file mode\")) {\n                    val nextLine = lines[index + 1]\n                    if (nextLine.startsWith(\"--- a/\")) {\n                        val withoutHead = nextLine.substring(\"--- a/\".length)\n                        // footer: \t(date 1704768267000)\n                        val withoutFooter = withoutHead.substring(0, withoutHead.indexOf(\"\\t\"))\n                        destination.add(\"delete file $withoutFooter\")\n                        // search for the next line starts with \"Index:\"\n                        while (true) {\n                            if (index + 2 >= length) {\n                                break\n                            }\n\n                            val nextNextLine = lines[index + 2]\n                            if (nextNextLine.startsWith(\"Index:\")) {\n                                index += 3\n                                break\n                            }\n                            index++\n                        }\n\n                        continue\n                    }\n                }\n\n                if (line.startsWith(\"---\") || line.startsWith(\"+++\")) {\n                    // next line\n                    val nextLine = lines[index + 1]\n                    if (nextLine.startsWith(\"+++\")) {\n                        // remove end date\n                        val substringBefore = line.substringBefore(\"(revision\")\n\n                        val startLine = substringBefore\n                            .substring(\"--- a/\".length).trim()\n                        var endIndex = nextLine.indexOf(\"(date\")\n                        if (endIndex == -1) {\n                            endIndex = nextLine.indexOf(\"(revision\")\n                        }\n                        if (endIndex == -1) {\n                            endIndex = nextLine.length\n                        }\n\n                        val withoutEnd = nextLine.substring(\"+++ b/\".length, endIndex).trim()\n\n                        if (startLine == withoutEnd) {\n                            index += 2\n                            destination.add(\"modify file $startLine\")\n                            continue\n                        }\n                    }\n\n                    // remove revision number with regex\n                    val result = revisionRegex.replace(line, \"\").trim()\n                    if (result.isNotEmpty()) {\n                        destination.add(result)\n                    }\n                } else {\n                    if (line.trim().isNotEmpty()) {\n                        destination.add(line)\n                    }\n                }\n\n                index++\n            }\n\n            return destination.joinToString(\"\\n\")\n        }\n\n        private fun isBinaryOrTooLarge(@NotNull change: Change): Boolean {\n            return isBinaryOrTooLarge(change.beforeRevision) || isBinaryOrTooLarge(change.afterRevision)\n        }\n\n        private fun isBinaryOrTooLarge(revision: ContentRevision?): Boolean {\n            val virtualFile = (revision as? CurrentContentRevision)?.virtualFile ?: return false\n            return isBinaryRevision(revision) || FileUtilRt.isTooLarge(virtualFile.length)\n        }\n\n        private fun isBinaryRevision(cr: ContentRevision?): Boolean {\n            if (cr == null) return false\n\n            return when (cr) {\n                is BinaryContentRevision -> true\n                else -> cr.file.fileType.isBinary\n            }\n        }\n    }\n}"
  },
  {
    "path": "toolsets/git/src/main/kotlin/com/phodal/shire/git/VcsPrompting.kt",
    "content": "// MIT License\n//\n//Copyright (c) Jakob Maležič\n//\n//Permission is hereby granted, free of charge, to any person obtaining a copy\n//of this software and associated documentation files (the \"Software\"), to deal\n//in the Software without restriction, including without limitation the rights\n//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n//copies of the Software, and to permit persons to whom the Software is\n//furnished to do so, subject to the following conditions:\n//\n//The above copyright notice and this permission notice shall be included in all\n//copies or substantial portions of the Software.\n//\n//THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n//SOFTWARE.\n\npackage com.phodal.shire.git\n\nimport com.intellij.openapi.components.Service\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.vcs.VcsException\nimport com.intellij.openapi.vcs.changes.*\nimport com.intellij.vcs.log.VcsFullCommitDetails\nimport java.io.IOException\nimport java.io.StringWriter\nimport java.nio.file.FileSystems\nimport java.nio.file.PathMatcher\n\n@Service(Service.Level.PROJECT)\nclass VcsPrompting(private val project: Project) {\n    private val defaultIgnoreFilePatterns: List<PathMatcher> = listOf(\n        \"**/*.json\", \"**/*.jsonl\", \"**/*.txt\", \"**/*.log\", \"**/*.tmp\", \"**/*.temp\", \"**/*.bak\", \"**/*.swp\", \"**/*.svg\",\n    ).map {\n        FileSystems.getDefault().getPathMatcher(\"glob:$it\")\n    }\n\n    fun prepareContext(changes: List<Change>, ignoreFilePatterns: List<PathMatcher> = defaultIgnoreFilePatterns): String {\n        return project.getService(DiffSimplifier::class.java).simplify(changes, ignoreFilePatterns)\n    }\n\n    /**\n     * Builds a diff prompt for a list of VcsFullCommitDetails.\n     *\n     * @param details The list of VcsFullCommitDetails containing commit details.\n     * @param project The Project object representing the current project.\n     * @param ignoreFilePatterns The list of PathMatcher objects representing file patterns to be ignored during diff generation. Default value is an empty list.\n     * @return A Pair object containing a list of commit message summaries and the generated diff prompt as a string. Returns null if the list is empty or no valid changes are found.\n     * @throws VcsException If an error occurs during VCS operations.\n     * @throws IOException If an I/O error occurs.\n     */\n    @Throws(VcsException::class, IOException::class)\n    fun buildDiffPrompt(\n        details: List<VcsFullCommitDetails>,\n        selectList: List<Change>,\n        project: Project,\n        ignoreFilePatterns: List<PathMatcher> = defaultIgnoreFilePatterns,\n    ): String? {\n        val changeText = project.getService(DiffSimplifier::class.java).simplify(selectList, ignoreFilePatterns)\n\n        if (changeText.isEmpty()) {\n            return null\n        }\n\n        val processedText = DiffSimplifier.postProcess(changeText)\n\n        val writer = StringWriter()\n        if (details.isNotEmpty()) {\n            writer.write(\"Commit Message: \")\n            details.forEach { writer.write(it.fullMessage + \"\\n\\n\") }\n        }\n\n        writer.write(\"Changes:\\n\\n```patch\\n$processedText\\n```\")\n\n        return writer.toString()\n    }\n\n    fun getChanges(): List<Change> {\n        val changeListManager = ChangeListManager.getInstance(project)\n        return changeListManager.changeLists.flatMap { it.changes }\n    }\n}\n"
  },
  {
    "path": "toolsets/git/src/main/kotlin/com/phodal/shire/git/provider/GitActionLocationEditor.kt",
    "content": "package com.phodal.shire.git.provider\n\nimport com.intellij.openapi.application.invokeAndWaitIfNeeded\nimport com.intellij.openapi.diagnostic.logger\nimport com.intellij.openapi.editor.Editor\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.vcs.ui.CommitMessage\nimport com.phodal.shirecore.config.ShireActionLocation\nimport com.phodal.shirecore.provider.context.ActionLocationEditor\n\nclass GitActionLocationEditor : ActionLocationEditor {\n    private var commitUi: CommitMessage? = null\n\n    override fun isApplicable(hole: ShireActionLocation): Boolean  {\n        val commitMessage = getCommitUi(hole)\n        if (commitMessage != null) {\n            commitUi = commitMessage\n        }\n\n        return hole == ShireActionLocation.COMMIT_MENU && commitMessage != null\n    }\n\n    override fun resolve(project: Project, hole: ShireActionLocation): Editor? {\n        val commitMessageUi = commitUi ?: getCommitUi(hole) ?: return null\n        val editorField = commitMessageUi.editorField\n\n        @Suppress(\"UnstableApiUsage\")\n        invokeAndWaitIfNeeded { editorField.text = \"\" }\n\n        return editorField.editor\n    }\n\n    private fun getCommitUi(hole: ShireActionLocation): CommitMessage? {\n        if (hole != ShireActionLocation.COMMIT_MENU) return null\n\n        val commitMessageUi = getCommitWorkflowUi()?.commitMessageUi as? CommitMessage\n\n        if (commitMessageUi == null) {\n            logger<GitActionLocationEditor>().error(\"Failed to get commit message UI\")\n            return null\n        }\n\n        return commitMessageUi\n    }\n}\n"
  },
  {
    "path": "toolsets/git/src/main/kotlin/com/phodal/shire/git/provider/GitDataContext.kt",
    "content": "package com.phodal.shire.git.provider\n\nimport com.intellij.ide.DataManager\nimport com.intellij.openapi.vcs.VcsDataKeys\nimport com.intellij.vcs.commit.CommitWorkflowUi\nimport com.phodal.shirecore.variable.template.VariableActionEventDataHolder\n\nfun getCommitWorkflowUi(): CommitWorkflowUi? {\n    VariableActionEventDataHolder.getData()?.dataContext?.let {\n        val commitWorkflowUi = it.getData(VcsDataKeys.COMMIT_WORKFLOW_UI)\n        return commitWorkflowUi as CommitWorkflowUi?\n    }\n\n    val dataContext = DataManager.getInstance().dataContextFromFocus.result\n    val commitWorkflowUi = dataContext?.getData(VcsDataKeys.COMMIT_WORKFLOW_UI)\n    return commitWorkflowUi\n}"
  },
  {
    "path": "toolsets/git/src/main/kotlin/com/phodal/shire/git/provider/GitFunctionProvider.kt",
    "content": "package com.phodal.shire.git.provider\n\nimport com.intellij.dvcs.DvcsUtil\nimport com.intellij.dvcs.push.PushSpec\nimport com.intellij.dvcs.ui.DvcsBundle\nimport com.intellij.openapi.progress.*\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.vcs.AbstractVcs\nimport com.intellij.openapi.vcs.FilePath\nimport com.intellij.openapi.vcs.ProjectLevelVcsManager\nimport com.intellij.openapi.vcs.changes.ChangeListManager\nimport com.intellij.openapi.vcs.changes.CommitContext\nimport com.intellij.openapi.vcs.changes.actions.ScheduleForAdditionActionExtension\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.intellij.util.ObjectUtils\nimport com.intellij.vcsUtil.VcsUtil\nimport com.phodal.shirecore.provider.function.ToolchainFunctionProvider\nimport git4idea.GitRemoteBranch\nimport git4idea.GitVcs\nimport git4idea.i18n.GitBundle\nimport git4idea.push.GitPushOperation\nimport git4idea.push.GitPushSource\nimport git4idea.push.GitPushSupport\nimport git4idea.repo.GitRepository\nimport git4idea.repo.GitRepositoryManager\nimport git4idea.util.GitFileUtils\nimport java.util.concurrent.CompletableFuture\n\n\n/**\n * Use example:\n *\n * ```\n * commit(\"commit message\") | push\n * ```\n *\n */\nenum class GitFunction(val funName: String) {\n    Commit(\"commit\"),\n    Push(\"push\");\n\n    companion object {\n        fun fromString(value: String): GitFunction? {\n            return entries.firstOrNull { it.funName == value }\n        }\n    }\n}\n\nclass GitFunctionProvider : ToolchainFunctionProvider {\n    override fun isApplicable(project: Project, funcName: String): Boolean {\n        return GitFunction.entries.any { it.funName == funcName }\n    }\n\n    override fun execute(project: Project, funcName: String, args: List<Any>, allVariables: Map<String, Any?>): Any {\n        val gitFunc = GitFunction.fromString(funcName)\n            ?: throw IllegalArgumentException(\"Shire[GitTool]: Invalid Git function name\")\n\n        val repositoryManager = GitRepositoryManager.getInstance(project)\n        val repository = repositoryManager.repositories.stream().findFirst().orElse(null)\n            ?: return throw IllegalArgumentException(\"Shire[GitTool]: No git repository found\")\n\n        return when (gitFunc) {\n            GitFunction.Commit -> {\n                commitChanges(project, repository, args.first() as String)\n            }\n\n            GitFunction.Push -> {\n                pushChanges(project, repository).get().toString()\n            }\n        }\n    }\n\n    /**\n     *\n     * How to find code in IDEA: [GitCommand.Commit]\n     */\n    fun commitChanges(project: Project, repository: GitRepository, commitMessage: String) {\n        val root = repository.root\n        val changeListManager = ChangeListManager.getInstance(project)\n        val virtualFiles = changeListManager\n            .allChanges\n            .mapNotNull { it.virtualFile }\n            .asSequence()\n\n        val files = collectPathsFromFiles(\n            project, virtualFiles\n        ).toList()\n\n        GitFileUtils.addPaths(project, root, files, false, false)\n        var commitContext: CommitContext = CommitContext()\n        val option: GitCommitOptions = GitCommitOptions(commitContext)\n\n//        try {\n        GitRepositoryCommitter(repository, option).commitStaged(commitMessage)\n//        } catch (e: Exception) {\n//            throw RuntimeException(\"Shire[GitTool]: Failed to commit changes\")\n//        }\n    }\n\n    private fun collectPathsFromFiles(project: Project, allFiles: Sequence<VirtualFile>): Sequence<FilePath> {\n        val vcsManager = ProjectLevelVcsManager.getInstance(project)\n        val changeListManager = ChangeListManager.getInstance(project)\n\n        return allFiles\n            .filter { file ->\n                val actionExtension = getExtensionFor(project, vcsManager.getVcsFor(file))\n                actionExtension != null &&\n                        changeListManager.getStatus(file).let { status ->\n                            if (file.isDirectory) actionExtension.isStatusForDirectoryAddition(status) else actionExtension.isStatusForAddition(\n                                status\n                            )\n                        }\n            }\n            .map(VcsUtil::getFilePath)\n    }\n\n    private fun getExtensionFor(project: Project, vcs: AbstractVcs?) =\n        if (vcs == null) null\n        else ScheduleForAdditionActionExtension.EP_NAME.findFirstSafe { it.getSupportedVcs(project) == vcs }\n\n    fun pushChanges(project: Project, repository: GitRepository): CompletableFuture<GitRemoteBranch> {\n        val progressIndicator =\n            ObjectUtils.notNull(ProgressManager.getInstance().progressIndicator, EmptyProgressIndicator())\n\n        val future = CompletableFuture<GitRemoteBranch>()\n\n        val branch = repository.currentBranch ?: return CompletableFuture.failedFuture(ProcessCanceledException())\n        val pushTarget =\n            GitPushSupport.getPushTargetIfExist(repository, branch) ?: return CompletableFuture.failedFuture(\n                ProcessCanceledException()\n            )\n\n        val gitPushSupport = DvcsUtil.getPushSupport(GitVcs.getInstance(project)) as? GitPushSupport\n            ?: return CompletableFuture.failedFuture(ProcessCanceledException())\n\n        ProgressManager.getInstance().runProcessWithProgressAsynchronously(\n            object : Task.Backgroundable(repository.project, DvcsBundle.message(\"push.process.pushing\"), true) {\n\n                override fun run(indicator: ProgressIndicator) {\n                    indicator.text = DvcsBundle.message(\"push.process.pushing\")\n                    val pushSpec = PushSpec(GitPushSource.create(branch), pushTarget)\n                    val pushResult = GitPushOperation(\n                        repository.project,\n                        gitPushSupport,\n                        mapOf(repository to pushSpec),\n                        null,\n                        false,\n                        false\n                    )\n                        .execute().results[repository] ?: error(\"Missing push result\")\n                    check(pushResult.error == null) {\n                        GitBundle.message(\"push.failed.error.message\", pushResult.error.orEmpty())\n                    }\n                }\n\n                override fun onSuccess() {\n                    future.complete(pushTarget.branch)\n                }\n\n                override fun onThrowable(error: Throwable) {\n                    future.completeExceptionally(error)\n                }\n\n                override fun onCancel() {\n                    future.completeExceptionally(ProcessCanceledException())\n                }\n            }, progressIndicator\n        )\n\n        return future\n    }\n}"
  },
  {
    "path": "toolsets/git/src/main/kotlin/com/phodal/shire/git/provider/GitQLDataProvider.kt",
    "content": "package com.phodal.shire.git.provider\n\nimport com.intellij.openapi.progress.ProgressIndicator\nimport com.intellij.openapi.progress.ProgressManager\nimport com.intellij.openapi.progress.Task\nimport com.intellij.openapi.progress.impl.BackgroundableProcessIndicator\nimport com.intellij.openapi.project.Project\nimport com.phodal.shirecore.ShireCoreBundle\nimport com.phodal.shirecore.provider.shire.ShireQLDataProvider\nimport com.phodal.shirecore.provider.shire.ShireQLDataType\nimport com.phodal.shirecore.variable.vcs.GitEntity\nimport com.phodal.shirecore.variable.vcs.ShireGitCommit\nimport git4idea.GitCommit\nimport git4idea.history.GitHistoryUtils\nimport git4idea.repo.GitRepositoryManager\nimport kotlinx.coroutines.future.await\nimport kotlinx.coroutines.runBlocking\nimport java.util.concurrent.CompletableFuture\n\nclass GitQLDataProvider : ShireQLDataProvider {\n    override fun lookupGitData(myProject: Project, dataTypes: List<ShireQLDataType>): Map<ShireQLDataType, List<GitEntity>?> {\n        val result = mutableMapOf<ShireQLDataType, List<GitEntity>?>()\n        dataTypes.forEach {\n            when (it) {\n                ShireQLDataType.GIT_COMMIT -> {\n                    val commits = buildCommits(myProject)\n                    result[it] = commits\n                }\n                ShireQLDataType.GIT_BRANCH -> TODO()\n                ShireQLDataType.GIT_FILE_COMMIT -> TODO()\n                ShireQLDataType.GIT_FILE_BRANCH -> TODO()\n                else -> {\n                    result[it] = null\n                }\n            }\n        }\n\n        return result\n    }\n\n    private fun buildCommits(myProject: Project): List<GitEntity> {\n        val repository = GitRepositoryManager.getInstance(myProject).repositories.firstOrNull() ?: return emptyList()\n        val branchName = repository.currentBranchName\n\n        /**\n         * Refs to [com.intellij.execution.process.OSProcessHandler.checkEdtAndReadAction], we should handle in this\n         * way, another example can see in [git4idea.GitPushUtil.findOrPushRemoteBranch]\n         */\n        val future = CompletableFuture<List<GitCommit>>()\n        val task = object : Task.Backgroundable(myProject, ShireCoreBundle.message(\"shire.ref.loading\"), false) {\n            override fun run(indicator: ProgressIndicator) {\n                val commits: List<GitCommit> = try {\n                    // in some case, maybe not repo or branch, so we should handle it\n                    GitHistoryUtils.history(project, repository.root, branchName)\n                } catch (e: Exception) {\n                    emptyList()\n                }\n\n                future.complete(commits)\n            }\n        }\n\n        ProgressManager.getInstance()\n            .runProcessWithProgressAsynchronously(task, BackgroundableProcessIndicator(task))\n\n        val results: MutableList<ShireGitCommit> = mutableListOf()\n        runBlocking {\n            future.await().forEach {\n                val commit = ShireGitCommit(\n                    it.id.asString(),\n                    it.author.name,\n                    it.author.email,\n                    it.authorTime,\n                    it.committer.name,\n                    it.committer.email,\n                    it.commitTime,\n                    it.fullMessage,\n                    it.fullMessage,\n                )\n\n                results.add(commit)\n            }\n        }\n\n        return results.toList()\n    }\n}\n"
  },
  {
    "path": "toolsets/git/src/main/kotlin/com/phodal/shire/git/provider/GitRepositoryCommitter.kt",
    "content": "// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage com.phodal.shire.git.provider\n\nimport com.intellij.execution.process.ProcessOutputTypes\nimport com.intellij.notification.NotificationAction\nimport com.intellij.openapi.diagnostic.logger\nimport com.intellij.openapi.help.HelpManager\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.util.Key\nimport com.intellij.openapi.vcs.VcsException\nimport com.intellij.openapi.vcs.changes.CommitContext\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.intellij.util.ThrowableConsumer\nimport com.intellij.vcs.commit.CommitExceptionWithActions\nimport com.intellij.vcs.commit.isAmendCommitMode\nimport com.intellij.vcs.commit.isCleanupCommitMessage\nimport com.intellij.vcs.log.VcsUser\nimport git4idea.checkin.*\nimport git4idea.commands.Git\nimport git4idea.commands.GitCommand\nimport git4idea.commands.GitLineHandler\nimport git4idea.commands.GitLineHandlerListener\nimport git4idea.i18n.GitBundle\nimport git4idea.repo.GitRepository\nimport org.jetbrains.annotations.NonNls\nimport java.io.File\nimport java.io.IOException\nimport java.text.SimpleDateFormat\nimport java.util.*\n\ndata class GitCommitOptions(\n  val isAmend: Boolean = false,\n  val isSignOff: Boolean = false,\n  val isSkipHooks: Boolean = false,\n  val commitAuthor: VcsUser? = null,\n  val commitAuthorDate: Date? = null,\n  val isCleanupCommitMessage: Boolean = false,\n) {\n    constructor(context: CommitContext) : this(\n        context.isAmendCommitMode,\n        context.isSignOffCommit,\n        context.isSkipHooks,\n        context.commitAuthor,\n        context.commitAuthorDate,\n        context.isCleanupCommitMessage\n    )\n}\n\ninternal class GitRepositoryCommitter(val repository: GitRepository, private val commitOptions: GitCommitOptions) {\n    val project: Project get() = repository.project\n    val root: VirtualFile get() = repository.root\n\n    @Throws(VcsException::class)\n    fun commitStaged(commitMessage: String) {\n        runWithMessageFile(project, root, commitMessage) { messageFile -> commitStaged(messageFile) }\n    }\n\n    private fun runWithMessageFile(\n      project: Project, root: VirtualFile, message: @NonNls String,\n      task: ThrowableConsumer<in File, out VcsException>,\n    ) {\n        val messageFile = try {\n            GitCheckinEnvironment.createCommitMessageFile(project, root, message)\n        } catch (ex: IOException) {\n            throw VcsException(GitBundle.message(\"error.commit.cant.create.message.file\"), ex)\n        }\n\n        try {\n            task.consume(messageFile)\n        } finally {\n            if (!messageFile.delete()) {\n                logger<GitRepositoryCommitter>().warn(\"Failed to remove temporary file: $messageFile\")\n            }\n        }\n    }\n\n    @Throws(VcsException::class)\n    fun commitStaged(messageFile: File) {\n        val gpgProblemDetector = GitGpgProblemDetector()\n        val emptyCommitProblemDetector = GitEmptyCommitProblemDetector()\n        val handler = GitLineHandler(project, root, GitCommand.COMMIT)\n        handler.setStdoutSuppressed(false)\n        handler.addLineListener(gpgProblemDetector)\n        handler.addLineListener(emptyCommitProblemDetector)\n\n        handler.setCommitMessage(messageFile)\n        handler.setCommitOptions(commitOptions)\n        handler.endOptions()\n\n        val command = Git.getInstance().runCommand(handler)\n\n        try {\n            command.throwOnError()\n        } catch (e: VcsException) {\n            if (gpgProblemDetector.isDetected) {\n                throw GitGpgCommitException(e)\n            }\n            if (emptyCommitProblemDetector.isDetected) {\n                throw VcsException(GitBundle.message(\"git.commit.nothing.to.commit.error.message\"))\n            }\n            throw e\n        }\n    }\n}\n\nval COMMIT_DATE_FORMAT: SimpleDateFormat = SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss\")\n\nprivate fun GitLineHandler.setCommitOptions(options: GitCommitOptions) {\n    if (options.isAmend) addParameters(\"--amend\")\n    if (options.isSignOff) addParameters(\"--signoff\")\n    if (options.isSkipHooks) addParameters(\"--no-verify\")\n    if (options.isCleanupCommitMessage) addParameters(\"--cleanup=strip\")\n\n    options.commitAuthor?.let { addParameters(\"--author=$it\") }\n    options.commitAuthorDate?.let { addParameters(\"--date\", COMMIT_DATE_FORMAT.format(it)) }\n}\n\nprivate fun GitLineHandler.setCommitMessage(messageFile: File) {\n    addParameters(\"-F\")\n    addAbsoluteFile(messageFile)\n}\n\nprivate class GitGpgProblemDetector : GitLineHandlerListener {\n    var isDetected = false\n        private set\n\n    override fun onLineAvailable(line: String, outputType: Key<*>) {\n        if (outputType === ProcessOutputTypes.STDERR && line.contains(PATTERN)) {\n            isDetected = true\n        }\n    }\n\n    companion object {\n        private const val PATTERN = \"gpg failed to sign the data\"\n    }\n}\n\nprivate class GitEmptyCommitProblemDetector : GitLineHandlerListener {\n    var isDetected = false\n        private set\n\n    override fun onLineAvailable(line: String, outputType: Key<*>) {\n        if (outputType === ProcessOutputTypes.STDOUT && PATTERNS.any { line.startsWith(it, ignoreCase = true) }) {\n            isDetected = true\n        }\n    }\n\n    companion object {\n        private val PATTERNS = listOf(\n            \"No changes\",\n            \"no changes added to commit\",\n            \"nothing added to commit\",\n            \"nothing to commit\"\n        )\n    }\n}\n\nprivate class GitGpgCommitException(cause: VcsException) : VcsException(cause), CommitExceptionWithActions {\n    override val actions: List<NotificationAction>\n        get() = listOf(NotificationAction.createSimple(GitBundle.message(\"gpg.error.see.documentation.link.text\")) {\n            HelpManager.getInstance().invokeHelp(GitBundle.message(\"gpg.jb.manual.link\"))\n        })\n}\n"
  },
  {
    "path": "toolsets/git/src/main/kotlin/com/phodal/shire/git/provider/GitRevisionProvider.kt",
    "content": "package com.phodal.shire.git.provider\n\nimport com.intellij.codeInsight.completion.CompletionResultSet\nimport com.intellij.codeInsight.lookup.LookupElementBuilder\nimport com.intellij.icons.AllIcons\nimport com.intellij.openapi.diagnostic.logger\nimport com.intellij.openapi.progress.ProgressIndicator\nimport com.intellij.openapi.progress.ProgressManager\nimport com.intellij.openapi.progress.Task\nimport com.intellij.openapi.progress.impl.BackgroundableProcessIndicator\nimport com.intellij.openapi.progress.runBlockingCancellable\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.vcs.FilePath\nimport com.intellij.openapi.vcs.changes.Change\nimport com.intellij.openapi.vcs.changes.ChangeListManager\nimport com.intellij.openapi.vcs.changes.CommitContext\nimport com.intellij.openapi.vcs.changes.LocalChangeList\nimport com.intellij.psi.PsiElement\nimport com.intellij.vcs.commit.ChangeListCommitState\nimport com.intellij.vcs.commit.LocalChangesCommitter\nimport com.intellij.vcsUtil.VcsUtil\nimport com.phodal.shire.git.VcsPrompting\nimport com.phodal.shirecore.ShireCoreBundle\nimport com.phodal.shirecore.provider.shire.RevisionProvider\nimport git4idea.GitCommit\nimport git4idea.GitRevisionNumber\nimport git4idea.changes.GitCommittedChangeListProvider\nimport git4idea.history.GitFileHistory\nimport git4idea.history.GitHistoryUtils\nimport git4idea.repo.GitRepositoryManager\nimport kotlinx.coroutines.future.await\nimport kotlinx.coroutines.runBlocking\nimport java.util.concurrent.CompletableFuture\n\n\nclass GitRevisionProvider : RevisionProvider {\n    private val logger = logger<GitRevisionProvider>()\n\n    override fun fetchChanges(myProject: Project, revision: String): String? {\n        val repository = GitRepositoryManager.getInstance(myProject).repositories.firstOrNull() ?: return null\n        val future = CompletableFuture<List<Change>>()\n\n        val task = object : Task.Backgroundable(myProject, ShireCoreBundle.message(\"shire.ref.loading\"), false) {\n            override fun run(indicator: ProgressIndicator) {\n                val committedChangeList = GitCommittedChangeListProvider.getCommittedChangeList(\n                    myProject, repository.root, GitRevisionNumber(revision)\n                )?.changes?.toList()\n\n                future.complete(committedChangeList)\n            }\n        }\n\n        ProgressManager.getInstance()\n            .runProcessWithProgressAsynchronously(task, BackgroundableProcessIndicator(task))\n\n\n        return runBlocking {\n            val changes = future.await()\n            val diffContext = myProject.getService(VcsPrompting::class.java).prepareContext(changes)\n            \"\\n```diff\\n${diffContext}\\n```\\n\"\n        }\n    }\n\n    override fun fetchCompletions(project: Project, result: CompletionResultSet) {\n        val repository = GitRepositoryManager.getInstance(project).repositories.firstOrNull() ?: return\n        val branchName = repository.currentBranchName\n\n        /**\n         * Refs to [com.intellij.execution.process.OSProcessHandler.checkEdtAndReadAction], we should handle in this\n         * way, another example can see in [git4idea.GitPushUtil.findOrPushRemoteBranch]\n         */\n        val future = CompletableFuture<List<GitCommit>>()\n        val task = object : Task.Backgroundable(project, ShireCoreBundle.message(\"shire.ref.loading\"), false) {\n            override fun run(indicator: ProgressIndicator) {\n                val commits: List<GitCommit> = try {\n                    // in some case, maybe not repo or branch, so we should handle it\n                    GitHistoryUtils.history(project, repository.root, branchName)\n                } catch (e: Exception) {\n                    logger.error(\"Failed to fetch commits\", e)\n                    emptyList()\n                }\n\n                future.complete(commits)\n            }\n        }\n\n        ProgressManager.getInstance()\n            .runProcessWithProgressAsynchronously(task, BackgroundableProcessIndicator(task))\n\n        runBlockingCancellable {\n            val commits = future.await()\n            commits.forEach {\n                val element = LookupElementBuilder.create(it.id.toShortString())\n                    .withIcon(AllIcons.Vcs.Branch)\n                    .withPresentableText(it.fullMessage)\n                    .withTypeText(it.id.toShortString(), true)\n\n                result.addElement(element)\n            }\n        }\n    }\n\n    override fun commitCode(project: Project, commitMessage: String): String {\n        val changeListManager = ChangeListManager.getInstance(project)\n        changeListManager.changeLists.forEach {\n            val list: LocalChangeList = changeListManager.getChangeList(it.id) ?: return@forEach\n            val commitState = ChangeListCommitState(list, list.changes.toList(), commitMessage)\n            val committer = LocalChangesCommitter(project, commitState, CommitContext())\n            committer.runCommit(\"Commit\", false)\n        }\n\n        return \"Committing...\"\n    }\n\n    override fun countHistoryChange(project: Project, element: PsiElement): Int {\n        val file = element.containingFile.virtualFile ?: return 0\n        val filePath: FilePath = VcsUtil.getFilePath(file)\n\n        return try {\n            GitFileHistory.collectHistory(project, filePath).size\n        } catch (e: Exception) {\n            logger.error(\"Failed to count history changes for file: ${file.path}\", e)\n            0\n        }\n    }\n}\n"
  },
  {
    "path": "toolsets/git/src/main/kotlin/com/phodal/shire/git/provider/GitToolchainVariableProvider.kt",
    "content": "package com.phodal.shire.git.provider\n\nimport com.intellij.openapi.actionSystem.DataContext\nimport com.intellij.openapi.diagnostic.logger\nimport com.intellij.openapi.editor.Editor\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.vcs.VcsDataKeys\nimport com.intellij.openapi.vcs.changes.Change\nimport com.intellij.openapi.vcs.changes.CurrentContentRevision\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.intellij.psi.PsiElement\nimport com.intellij.vcs.commit.CommitWorkflowUi\nimport com.intellij.vcs.log.VcsFullCommitDetails\nimport com.intellij.vcs.log.VcsLogDataKeys\nimport com.intellij.vcs.log.VcsLogFilterCollection\nimport com.intellij.vcs.log.VcsLogProvider\nimport com.intellij.vcs.log.impl.VcsProjectLog\nimport com.intellij.vcs.log.visible.filters.VcsLogFilterObject\nimport com.phodal.shirecore.provider.variable.ToolchainVariableProvider\nimport com.phodal.shirecore.provider.variable.model.ToolchainVariable\nimport com.phodal.shirecore.provider.variable.model.toolchain.VcsToolchainVariable\nimport com.phodal.shire.git.VcsPrompting\nimport com.phodal.shirecore.variable.template.VariableActionEventDataHolder\nimport java.awt.EventQueue.invokeAndWait\n\n\nclass GitToolchainVariableProvider : ToolchainVariableProvider {\n    private val logger = logger<GitToolchainVariableProvider>()\n\n    override fun isResolvable(variable: ToolchainVariable, psiElement: PsiElement?, project: Project): Boolean {\n        return when (variable) {\n            VcsToolchainVariable.CurrentChanges -> true\n            VcsToolchainVariable.HistoryCommitMessages -> true\n            VcsToolchainVariable.CurrentBranch -> true\n            VcsToolchainVariable.Diff -> true\n            else -> false\n        }\n    }\n\n    override fun resolve(\n        variable: ToolchainVariable,\n        project: Project,\n        editor: Editor,\n        psiElement: PsiElement?,\n    ): ToolchainVariable {\n        when (variable) {\n            VcsToolchainVariable.CurrentChanges -> {\n                val commitWorkflowUi = getCommitWorkflowUi()\n                if (commitWorkflowUi !is CommitWorkflowUi) {\n                    logger.warn(\"Cannot get commit workflow UI, you may not be in a commit workflow.\")\n                    return variable\n                }\n                var changes: List<Change>? = null\n                invokeAndWait {\n                    changes = getDiff(commitWorkflowUi)\n                }\n\n                if (changes == null) {\n                    logger.warn(\"Cannot get changes.\")\n                    return variable\n                }\n\n                val diffContext = project.getService(VcsPrompting::class.java).prepareContext(changes!!)\n\n                if (diffContext.isEmpty() || diffContext == \"\\n\") {\n                    logger.warn(\"Diff context is empty or cannot get enough useful context.\")\n                    return variable\n                }\n\n                variable.value = diffContext\n                return variable\n            }\n\n            VcsToolchainVariable.CurrentBranch -> {\n                val logProviders = VcsProjectLog.getLogProviders(project)\n                val entry = logProviders.entries.firstOrNull() ?: return variable\n\n                val logProvider = entry.value\n                val branch = logProvider.getCurrentBranch(entry.key) ?: return variable\n\n                variable.value = branch\n            }\n\n            VcsToolchainVariable.HistoryCommitMessages -> {\n                val exampleCommitMessages = getHistoryCommitMessages(project)\n                if (exampleCommitMessages != null) {\n                    variable.value = exampleCommitMessages\n                }\n            }\n\n            VcsToolchainVariable.Diff -> {\n                val dataContext = VariableActionEventDataHolder.getData()?.dataContext\n                variable.value = analysisLog(dataContext, project)\n            }\n        }\n\n        return variable\n    }\n\n    private fun analysisLog(dataContext: DataContext?, project: Project): String {\n        if (dataContext == null) {\n            return \"\"\n        }\n\n\n        val vcsLog = dataContext.getData(VcsLogDataKeys.VCS_LOG) ?: return \"\"\n\n        val details: List<VcsFullCommitDetails> = vcsLog.selectedDetails.toList()\n        val selectList = dataContext.getData(VcsDataKeys.SELECTED_CHANGES).orEmpty().toList()\n\n        val vcsPrompting = project.getService(VcsPrompting::class.java)\n        val fullChangeContent =\n            vcsPrompting.buildDiffPrompt(details, selectList.toList(), project)\n\n        return fullChangeContent ?: \"\"\n    }\n\n    /**\n     * Finds example commit messages based on the project's VCS log, takes the first three commits.\n     * If the no user or user has committed anything yet, the current branch name is used instead.\n     *\n     * @param project The project for which to find example commit messages.\n     * @return A string containing example commit messages, or null if no example messages are found.\n     */\n    private fun getHistoryCommitMessages(project: Project): String? {\n        val logProviders = VcsProjectLog.getLogProviders(project)\n        val entry = logProviders.entries.firstOrNull() ?: return null\n\n        val logProvider = entry.value\n        val branch = logProvider.getCurrentBranch(entry.key) ?: return null\n        val user = logProvider.getCurrentUser(entry.key)\n\n        val logFilter = if (user != null) {\n            VcsLogFilterObject.collection(VcsLogFilterObject.fromUser(user, setOf()))\n        } else {\n            VcsLogFilterObject.collection(VcsLogFilterObject.fromBranch(branch))\n        }\n\n        return collectExamples(logProvider, entry.key, logFilter)\n    }\n\n    /**\n     * Collects examples from the VcsLogProvider based on the provided filter.\n     *\n     * @param logProvider The VcsLogProvider used to retrieve commit information.\n     * @param root The root VirtualFile of the project.\n     * @param filter The VcsLogFilterCollection used to filter the commits.\n     * @return A string containing the collected examples, or null if no examples are found.\n     */\n    private fun collectExamples(\n        logProvider: VcsLogProvider,\n        root: VirtualFile,\n        filter: VcsLogFilterCollection,\n    ): String? {\n        val commits = logProvider.getCommitsMatchingFilter(root, filter, 3)\n\n        if (commits.isEmpty()) return null\n\n        val builder = StringBuilder(\"\")\n        val commitIds = commits.map { it.id.asString() }\n\n        logProvider.readMetadata(root, commitIds) {\n            val shortMsg = it.fullMessage.split(\"\\n\").firstOrNull() ?: it.fullMessage\n            builder.append(shortMsg).append(\"\\n\")\n        }\n\n        return builder.toString()\n    }\n\n    private fun getDiff(commitWorkflowUi: CommitWorkflowUi): List<Change>? {\n        val changes = commitWorkflowUi.getIncludedChanges()\n        val unversionedFiles = commitWorkflowUi.getIncludedUnversionedFiles()\n\n        val changeList = unversionedFiles.map {\n            Change(null, CurrentContentRevision(it))\n        }\n\n        if (changes.isNotEmpty() || changeList.isNotEmpty()) {\n            return changes + changeList\n        }\n\n        return null\n    }\n\n}\n"
  },
  {
    "path": "toolsets/git/src/main/resources/com.phodal.shire.git.xml",
    "content": "<idea-plugin package=\"com.phodal.shire.git\">\n    <!--suppress PluginXmlValidity -->\n    <dependencies>\n        <plugin id=\"Git4Idea\"/>\n    </dependencies>\n\n    <extensions defaultExtensionNs=\"com.phodal\">\n        <shireRevisionProvider implementation=\"com.phodal.shire.git.provider.GitRevisionProvider\"/>\n\n        <shireToolchainVariableProvider implementation=\"com.phodal.shire.git.provider.GitToolchainVariableProvider\"/>\n\n        <shireToolchainFunctionProvider implementation=\"com.phodal.shire.git.provider.GitFunctionProvider\"/>\n\n        <shireActionLocationEditor implementation=\"com.phodal.shire.git.provider.GitActionLocationEditor\"/>\n\n        <shireQLDataProvider implementation=\"com.phodal.shire.git.provider.GitQLDataProvider\"/>\n    </extensions>\n</idea-plugin>\n"
  },
  {
    "path": "toolsets/httpclient/README.md",
    "content": "# HttpClient Module\n\nHttpClient\n\n- convert cURL to OkHttp Request\n- execute HttpRequest code\n\n"
  },
  {
    "path": "toolsets/httpclient/src/main/kotlin/com/phodal/shire/httpclient/HttpClientFileRunService.kt",
    "content": "package com.phodal.shire.httpclient\n\nimport com.intellij.execution.Executor\nimport com.intellij.execution.RunManager\nimport com.intellij.execution.RunnerAndConfigurationSettings\nimport com.intellij.execution.actions.ConfigurationContext\nimport com.intellij.execution.configurations.RunConfiguration\nimport com.intellij.execution.configurations.RunProfile\nimport com.intellij.httpClient.http.request.HttpRequestPsiFile\nimport com.intellij.httpClient.http.request.run.HttpRequestExecutorExtensionFactory\nimport com.intellij.httpClient.http.request.run.HttpRequestRunConfigurationExecutor\nimport com.intellij.httpClient.http.request.run.config.HttpRequestRunConfiguration\nimport com.intellij.httpClient.http.request.run.config.HttpRequestRunConfigurationType\nimport com.intellij.openapi.application.runReadAction\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.PsiManager\nimport com.phodal.shirecore.provider.shire.FileRunService\n\nclass HttpClientFileRunService : FileRunService {\n    override fun isApplicable(project: Project, file: VirtualFile): Boolean {\n        return file.extension == \"http\"\n    }\n\n    override fun runConfigurationClass(project: Project): Class<out RunProfile> {\n        return HttpRequestRunConfiguration::class.java\n    }\n\n    override fun runFile(project: Project, virtualFile: VirtualFile, psiElement: PsiElement?): String? {\n        val runner: RunnerAndConfigurationSettings = runReadAction {\n            val psiFile = PsiManager.getInstance(project).findFile(virtualFile)\n                ?: return@runReadAction null\n\n            ConfigurationContext(psiFile).configurationsFromContext?.firstOrNull()?.configurationSettings\n        } ?: return null\n\n        val factory = HttpRequestRunConfigurationType.getInstance().configurationFactories[0]\n        val configuration = HttpRequestRunConfiguration(project, factory, \"HttpRequest\")\n\n        val runManager: RunManager = RunManager.getInstance(project)\n        configuration.settings.filePath = virtualFile.path\n\n        runManager.setUniqueNameIfNeeded(configuration)\n        runner.isTemporary = true\n        runManager.addConfiguration(runner)\n\n        val selectedRunner = runManager.selectedConfiguration\n        if ((selectedRunner == null || selectedRunner.isTemporary) && runManager.shouldSetRunConfigurationFromContext()) {\n            runManager.selectedConfiguration = runner\n        }\n\n        val executor: Executor = HttpRequestExecutorExtensionFactory.getRunExtension().executor ?: return null\n        HttpRequestRunConfigurationExecutor.getInstance().execute(\n            project, runner, executor\n        )\n\n        return \"Run Success\"\n    }\n\n    override fun createConfiguration(project: Project, virtualFile: VirtualFile): RunConfiguration? {\n        val factory = HttpRequestRunConfigurationType.getInstance().configurationFactories[0]\n        val configuration = HttpRequestRunConfiguration(project, factory, \"HttpRequest\")\n        configuration.settings.filePath = virtualFile.path\n\n        return configuration\n    }\n}\n"
  },
  {
    "path": "toolsets/httpclient/src/main/kotlin/com/phodal/shire/httpclient/converter/CUrlConverter.kt",
    "content": "package com.phodal.shire.httpclient.converter\n\nimport com.intellij.httpClient.execution.RestClientRequest\nimport com.intellij.httpClient.http.request.HttpRequestHeaderFields\nimport okhttp3.MediaType.Companion.toMediaTypeOrNull\nimport okhttp3.Request\nimport okhttp3.RequestBody.Companion.toRequestBody\n\nobject CUrlConverter {\n    fun convert(request: RestClientRequest): Request {\n        val builder = Request.Builder()\n\n        builder.url(request.buildFullUrl())\n        request.headers.forEach {\n            try {\n                builder.header(it.key, it.value)\n            } catch (e: IllegalArgumentException) {\n                // ignore\n            }\n        }\n\n        val body = request.textToSend\n\n        val mediaType = request\n            .getHeadersValue(HttpRequestHeaderFields.CONTENT_TYPE)\n            ?.firstOrNull()\n            ?.toMediaTypeOrNull()\n\n        when (request.httpMethod) {\n            \"GET\" -> builder.get()\n            \"POST\" -> builder.post(body.toRequestBody(mediaType))\n            \"PUT\" -> builder.put(body.toRequestBody(mediaType))\n            \"DELETE\" -> builder.delete(body.toRequestBody(mediaType))\n            else -> builder.method(request.httpMethod, body.toRequestBody(mediaType))\n        }\n\n        return builder.build()\n    }\n}\n"
  },
  {
    "path": "toolsets/httpclient/src/main/kotlin/com/phodal/shire/httpclient/converter/RestClientUtil.kt",
    "content": "package com.phodal.shire.httpclient.converter\n\nimport com.intellij.httpClient.execution.RestClientRequest\nimport com.intellij.httpClient.execution.auth.HttpClientAuthData\nimport com.intellij.httpClient.execution.auth.HttpRequestAuthCredentials\nimport com.intellij.httpClient.execution.auth.HttpRequestAuthScope\nimport com.intellij.httpClient.execution.auth.HttpRequestCommonAuthSchemes\nimport com.intellij.httpClient.http.request.*\nimport com.intellij.httpClient.http.request.psi.impl.HttpRequestPsiImplUtil\nimport com.intellij.ide.scratch.ScratchFileService\nimport com.intellij.ide.scratch.ScratchRootType\nimport com.intellij.openapi.command.UndoConfirmationPolicy\nimport com.intellij.openapi.command.WriteCommandAction\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.ui.Messages\nimport com.intellij.openapi.util.text.StringUtil\nimport com.intellij.psi.NavigatablePsiElement\nimport com.intellij.psi.PsiDocumentManager\nimport com.intellij.psi.PsiManager\nimport com.intellij.ui.UIBundle\nimport com.intellij.util.PathUtil\nimport com.oracle.truffle.js.runtime.objects.DefaultESModuleLoader\nimport java.io.IOException\nimport java.net.URL\n\nfun RestClientRequest.buildFullUrl(): URL {\n    val url = URL(getFullUri(this.url, this))\n    if (url.userInfo == null) {\n        return url\n    }\n    val userInfo = url.userInfo ?: throw IllegalStateException(\"getUserInfo(...) returned null\")\n    val usernameAndPassword = userInfo.split(\":\")\n    val httpRequestAuthScope = HttpRequestAuthScope(HttpRequestCommonAuthSchemes.BASIC)\n    val username = usernameAndPassword[0]\n    val password = usernameAndPassword.getOrNull(1) ?: \"\"\n    this.authData =\n        HttpClientAuthData(httpRequestAuthScope, HttpRequestAuthCredentials.UsernamePassword(username, password))\n\n    val fullUrl = buildString {\n        url.protocol?.let {\n            append(it).append(HttpRequestPsiImplUtil.SCHEME_SEPARATOR)\n        }\n        url.host?.let {\n            append(it)\n        }\n        val port = url.port.takeIf { it > 0 }\n        port?.let {\n            append(\":\").append(it)\n        }\n        url.path?.let {\n            append(DefaultESModuleLoader.SLASH).append(it)\n        }\n    }\n\n    return URL(fullUrl)\n}\n\nfun getFullUri(uri: String, request: RestClientRequest): String {\n    var uriStr = uri\n    if (request.parametersEnabled) {\n        val query = request.createQueryString()\n        if (StringUtil.isNotEmpty(query)) {\n            uriStr = uriStr + (if (uriStr.contains(\"?\")) \"&\" else \"?\") + query\n        }\n    }\n\n    return uriStr\n}\n\n\nfun createAndOpenScratchFile(project: Project, request: RestClientRequest, comment: String?) {\n    val fileName = PathUtil.makeFileName(\"rest-api\", HttpRequestFileType.INSTANCE.defaultExtension)\n    try {\n        WriteCommandAction.writeCommandAction(project).withName(\"Create HTTP Request scratch file\")\n            .withGlobalUndo()\n            .shouldRecordActionForActiveDocument(false)\n            .withUndoConfirmationPolicy(UndoConfirmationPolicy.REQUEST_CONFIRMATION)\n            .compute<NavigatablePsiElement, Exception> {\n                val convertedRequest: String\n                val fileService = ScratchFileService.getInstance()\n                try {\n                    val file = fileService.findFile(\n                        ScratchRootType.getInstance(),\n                        fileName,\n                        ScratchFileService.Option.create_new_always\n                    )\n                    fileService.scratchesMapping.setMapping(file, HttpRequestLanguage.INSTANCE)\n                    val psiFile = PsiManager.getInstance(project).findFile(file) as? HttpRequestPsiFile\n                        ?: throw Exception(\"Failed to create HTTP Request scratch file\")\n\n                    val manager = PsiDocumentManager.getInstance(project)\n                    val document = manager.getDocument(psiFile)\n                        ?: throw Exception(\"Created HTTP Request scratch file is invalid\")\n\n                    convertedRequest = if (comment != null) {\n                        comment + HttpRequestPsiConverter.toPsiHttpRequest(request)\n                    } else {\n                        HttpRequestPsiConverter.toPsiHttpRequest(request)\n                    }\n\n                    document.insertString(document.textLength, convertedRequest)\n                    manager.commitDocument(document)\n                    val updated = HttpRequestPsiUtils.getRequestBlocks(psiFile)\n                    if (updated.isNotEmpty()) {\n                        return@compute updated[updated.size - 1]\n                    }\n\n                    return@compute psiFile\n                } catch (e: IOException) {\n                    throw Exception(\"Could not create file: $e.\")\n                }\n            }?.navigate(true)\n    } catch (e: Exception) {\n        Messages.showErrorDialog(project, e.message, UIBundle.message(\"error.dialog.title\", *arrayOfNulls(0)))\n    }\n}"
  },
  {
    "path": "toolsets/httpclient/src/main/kotlin/com/phodal/shire/httpclient/handler/CUrlHttpHandler.kt",
    "content": "package com.phodal.shire.httpclient.handler\n\nimport com.intellij.execution.console.ConsoleViewWrapperBase\nimport com.intellij.execution.ui.ConsoleViewContentType\nimport com.intellij.execution.ui.RunContentManager\nimport com.intellij.httpClient.converters.curl.parser.CurlParser\nimport com.intellij.httpClient.execution.RestClientRequest\nimport com.intellij.httpClient.http.request.HttpRequestCollectionProvider\nimport com.intellij.httpClient.http.request.notification.HttpClientWhatsNewContentService\nimport com.intellij.ide.scratch.ScratchUtil\nimport com.intellij.ide.scratch.ScratchesSearchScope\nimport com.intellij.openapi.application.runReadAction\nimport com.intellij.openapi.project.Project\nimport com.intellij.psi.PsiFile\nimport com.intellij.psi.search.GlobalSearchScope\nimport com.intellij.psi.search.GlobalSearchScopesCore\nimport com.intellij.psi.search.ProjectScope\nimport com.intellij.psi.util.PsiUtilCore\nimport com.phodal.shire.httpclient.converter.CUrlConverter\nimport com.phodal.shirecore.provider.http.HttpHandler\nimport com.phodal.shirecore.provider.http.HttpHandlerType\nimport com.phodal.shire.json.ShireEnvReader\nimport com.phodal.shire.json.ShireEnvVariableFiller\nimport okhttp3.OkHttpClient\nimport com.intellij.openapi.application.ApplicationManager\nimport okhttp3.Request\n\nclass CUrlHttpHandler : HttpHandler {\n    override fun isApplicable(type: HttpHandlerType): Boolean = type == HttpHandlerType.CURL\n\n    override fun execute(\n        project: Project,\n        content: String,\n        variablesName: Array<String>,\n        variableTable: MutableMap<String, Any?>,\n    ): String? {\n        val processVariables: Map<String, String> = variableTable.mapValues { it.value.toString() }\n\n        var filledShell: String = content\n        val client = OkHttpClient()\n        var restClientRequest: RestClientRequest? = null\n        val request = ApplicationManager.getApplication().executeOnPooledThread<Request?> {\n            runReadAction {\n                val scope = getSearchScope(project)\n\n                val envName =\n                    ShireEnvReader.getAllEnvironments(project, scope).firstOrNull() ?: ShireEnvReader.DEFAULT_ENV_NAME\n                val envObject = ShireEnvReader.getEnvObject(envName, scope, project)\n\n                val enVariables: List<Set<String>> = ShireEnvReader.fetchEnvironmentVariables(envName, scope)\n                filledShell = ShireEnvVariableFiller.fillVariables(content, enVariables, envObject, processVariables)\n                restClientRequest = CurlParser().parseToRestClientRequest(filledShell)\n\n                CUrlConverter.convert(restClientRequest!!)\n            }\n        }.get()\n\n        if (restClientRequest == null || request == null) {\n            return null\n        }\n\n        restClientRequest?.let {\n            showLogInConsole(project, filledShell, it)\n        }\n\n        val response = client.newCall(request).execute()\n        return response.body?.string()\n    }\n\n    private fun showLogInConsole(project: Project, content: String, request: RestClientRequest) {\n        val contentManager = RunContentManager.getInstance(project)\n        val console = contentManager.selectedContent?.executionConsole as? ConsoleViewWrapperBase ?: return\n\n        ///-----\n        console.print(\"--------------------\\n\", ConsoleViewContentType.LOG_INFO_OUTPUT)\n        /// original content\n        console.print(content, ConsoleViewContentType.LOG_INFO_OUTPUT)\n        /// new line\n        console.print(\"\\n\", ConsoleViewContentType.LOG_INFO_OUTPUT)\n        /// converted content\n        console.print(request.httpMethod + \" \" + request.url, ConsoleViewContentType.LOG_INFO_OUTPUT)\n        /// headers\n        request.headers.forEach {\n            console.print(\"\\n${it.key}: ${it.value}\", ConsoleViewContentType.LOG_INFO_OUTPUT)\n        }\n        /// request.body\n        console.print(\"\\n\" + request.textToSend.toString(), ConsoleViewContentType.LOG_INFO_OUTPUT)\n        console.print(\"\\n--------------------\\n\", ConsoleViewContentType.LOG_INFO_OUTPUT)\n    }\n\n    private fun getSearchScope(project: Project, contextFile: PsiFile? = null): GlobalSearchScope {\n        val projectScope = ProjectScope.getContentScope(project)\n        if (contextFile == null) return projectScope\n\n        val context = PsiUtilCore.getVirtualFile(contextFile)\n        val whatsNewFile = HttpClientWhatsNewContentService.getInstance().getWhatsNewFileIfCreated()\n\n        if (contextFile.virtualFile == whatsNewFile) {\n            HttpRequestCollectionProvider.getCollectionFolder()?.let { folder ->\n                return GlobalSearchScopesCore.directoryScope(project, folder, true)\n            }\n        }\n\n        if (context != null && !ScratchUtil.isScratch(context) && !projectScope.contains(context)) {\n            contextFile.parent?.let { parent ->\n                return GlobalSearchScopesCore.directoryScope(parent, true)\n            }\n        }\n\n        if (ScratchUtil.isScratch(context)) {\n            return projectScope.uniteWith(ScratchesSearchScope.getScratchesScope(project))\n        }\n\n        return projectScope\n    }\n}\n"
  },
  {
    "path": "toolsets/httpclient/src/main/resources/com.phodal.shire.httpclient.xml",
    "content": "<idea-plugin package=\"com.phodal.shire.httpclient\">\n    <!--suppress PluginXmlValidity -->\n    <dependencies>\n        <plugin id=\"com.jetbrains.restClient\"/>\n    </dependencies>\n\n    <extensions defaultExtensionNs=\"com.phodal\">\n        <shireFileRunService implementation=\"com.phodal.shire.httpclient.HttpClientFileRunService\"/>\n\n        <shireHttpHandler implementation=\"com.phodal.shire.httpclient.handler.CUrlHttpHandler\"/>\n    </extensions>\n</idea-plugin>\n"
  },
  {
    "path": "toolsets/httpclient/src/test/kotlin/com/phodal/shire/httpclient/converter/CUrlConverterTest.kt",
    "content": "package com.phodal.shire.httpclient.converter\n\nimport com.intellij.httpClient.converters.curl.parser.CurlParser\nimport com.intellij.testFramework.fixtures.BasePlatformTestCase\nimport com.phodal.shire.json.ShireEnvReader\nimport com.phodal.shire.json.ShireEnvVariableFiller\n\nclass CUrlConverterTest : BasePlatformTestCase() {\n    fun testShouldConvertCurlToRestClientRequest() {\n        // Given\n        val content = \"curl -X POST http://example.com/api/resource -d 'data'\"\n        val request = CurlParser().parseToRestClientRequest(content)\n\n        // When\n        val restClientRequest = CUrlConverter.convert(request = request)\n\n        // Then\n        assertEquals(\"http://example.com/api/resource\", restClientRequest.url.toString())\n    }\n\n    fun testShouldCallHttpClientWithConvertedRequest() {\n        // Given\n        val content = \"curl --location 'https://open.bigmodel.cn/api/paas/v4/chat/completions' \\\\\\n\" +\n                \"--header 'Authorization: Bearer \\$YourKey' \\\\\\n\" +\n                \"--header 'Content-Type: application/json' \\\\\\n\" +\n                \"--data '{\\n\" +\n                \"    \\\"model\\\": \\\"glm-4\\\",\\n\" +\n                \"    \\\"messages\\\": [\\n\" +\n                \"        {\\n\" +\n                \"            \\\"role\\\": \\\"user\\\",\\n\" +\n                \"            \\\"content\\\": \\\"你好\\\"\\n\" +\n                \"        }\\n\" +\n                \"    ]\\n\" +\n                \"}'\"\n        val req = CurlParser().parseToRestClientRequest(content)\n        // When\n        val request = CUrlConverter.convert(request = req)\n\n        // Then\n        assertEquals(\"https://open.bigmodel.cn/api/paas/v4/chat/completions\", request.url.toString())\n    }\n\n    fun testShouldHandleForVariable() {\n        val jsonEnv = \"\"\"\n            {\n              \"development\": {\n                \"name\": \"Phodal\"\n              }\n            }\n             \"\"\".trimIndent()\n\n        val envPsiFile = myFixture.addFileToProject(\"demo.shireEnv.json\", jsonEnv)\n\n        val variables = listOf(setOf(\"development\"))\n        val obj = ShireEnvReader.getEnvObject(\"development\", envPsiFile)\n\n        // Given\n        val messageBody = \"Hello \\${name}, my name is \\${myName}!\"\n\n        // When\n        val result = ShireEnvVariableFiller.fillVariables(messageBody, variables, obj, mapOf(\"myName\" to \"Shire\"))\n\n        // Then\n        assertEquals(\"Hello Phodal, my name is Shire!\", result)\n    }\n}\n"
  },
  {
    "path": "toolsets/httpclient/src/test/kotlin/com/phodal/shirecore/agent/agenttool/browse/BrowseToolTest.kt",
    "content": "package com.phodal.shirecore.agent.agenttool.browse\n\nimport junit.framework.TestCase.assertNotNull\nimport org.junit.Test\n\nclass BrowseToolTest {\n    @Test\n    fun should_parseHtml_correctly() {\n        // given\n        val url = \"https://shire.phodal.com\"\n\n        // when\n        val result = BrowseTool.parse(url).body\n\n        // then\n        println(result)\n        assertNotNull(result)\n    }\n}"
  },
  {
    "path": "toolsets/httpclient/src/test/resources/META-INF/plugin.xml",
    "content": "<!-- Plugin Configuration File. Read more: https://plugins.jetbrains.com/docs/intellij/plugin-configuration-file.html -->\n<idea-plugin xmlns:xi=\"http://www.w3.org/2001/XInclude\" allow-bundled-update=\"true\">\n    <id>com.phodal.shire</id>\n    <name>Shire - AI Coding Agent Language</name>\n    <vendor>Phodal Huang</vendor>\n    <depends>com.intellij.modules.platform</depends>\n    <resource-bundle>messages.ShireMainBundle</resource-bundle>\n\n    <xi:include href=\"/META-INF/shire-main.xml\" xpointer=\"xpointer(/idea-plugin/*)\"/>\n    <xi:include href=\"/com.phodal.shirecore.xml\" xpointer=\"xpointer(/idea-plugin/*)\"/>\n\n    <content>\n        <module name=\"com.phodal.shirelang\"/>\n        <module name=\"com.phodal.shirelang.java\"/>\n        <module name=\"com.phodal.shirelang.git\"/>\n\n        <module name=\"com.phodal.shire.httpclient\"/>\n    </content>\n</idea-plugin>\n"
  },
  {
    "path": "toolsets/mermaid/src/main/kotlin/com/phodal/shire/mermaid/provider/MermaidSketchProvider.kt",
    "content": "package com.phodal.shire.mermaid.provider\n\nimport com.intellij.lang.Language\nimport com.intellij.openapi.fileEditor.FileEditorProvider\nimport com.intellij.openapi.fileEditor.TextEditorWithPreview\nimport com.intellij.openapi.fileEditor.impl.text.TextEditorProvider\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.intellij.openapi.vfs.readText\nimport com.intellij.testFramework.LightVirtualFile\nimport com.intellij.ui.dsl.builder.Align\nimport com.intellij.ui.dsl.builder.panel\nimport com.intellij.util.ui.JBUI\nimport com.phodal.shirecore.provider.sketch.ExtensionLangSketch\nimport com.phodal.shirecore.provider.sketch.LanguageSketchProvider\nimport javax.swing.JComponent\nimport javax.swing.JPanel\n\nclass MermaidSketchProvider : LanguageSketchProvider {\n    override fun isSupported(lang: String): Boolean {\n        return lang == \"mermaid\" || lang == \"mmd\"\n    }\n\n    override fun create(project: Project, content: String): ExtensionLangSketch {\n        val file = LightVirtualFile(\"mermaid.mermaid\", content)\n        return MermaidSketch(project, file)\n    }\n}\n\nclass MermaidSketch(project: Project, private val virtualFile: VirtualFile) : ExtensionLangSketch {\n    private var mainPanel: JPanel\n\n    init {\n        val editor = getEditorProvider().createEditor(project, virtualFile) as TextEditorWithPreview\n        mainPanel = panel {\n            row {\n                cell(editor.component).align(Align.FILL)\n            }\n        }.apply {\n            border = JBUI.Borders.empty(0, 10)\n        }\n    }\n\n    private fun getEditorProvider(): FileEditorProvider =\n        FileEditorProvider.EP_FILE_EDITOR_PROVIDER.extensionList.firstOrNull {\n            it.javaClass.simpleName == \"MermaidEditorWithPreviewProvider\"\n        } ?: TextEditorProvider.getInstance()\n\n    override fun getExtensionName(): String = \"mermaid\"\n\n    override fun getViewText(): String = virtualFile.readText()\n\n    override fun updateViewText(text: String) {}\n\n    override fun getComponent(): JComponent = mainPanel\n\n    override fun updateLanguage(language: Language?, originLanguage: String?) {}\n\n    override fun dispose() {}\n}\n"
  },
  {
    "path": "toolsets/mermaid/src/main/resources/com.phodal.shire.mermaid.xml",
    "content": "<idea-plugin package=\"com.phodal.shire.mermaid\">\n    <!--suppress PluginXmlValidity -->\n    <dependencies>\n        <plugin id=\"com.intellij.mermaid\"/>\n    </dependencies>\n\n    <extensions defaultExtensionNs=\"com.phodal\">\n        <shireLangSketchProvider implementation=\"com.phodal.shire.mermaid.provider.MermaidSketchProvider\"/>\n    </extensions>\n</idea-plugin>\n"
  },
  {
    "path": "toolsets/mock/README.md",
    "content": "# WireMock\n\nStart WireMock with IntelliJ IDEA:\n\n```bash\n\"/Users/phodal/Applications/IntelliJ IDEA Ultimate.app/Contents/jbr/Contents/Home/bin/java\"\n -jar \"/Users/phodal/Library/Application Support/JetBrains/IntelliJIdea2024.2/plugins/wiremock/server/wiremock-standalone-rt.jar\"\n  --port 8080 --root-dir /var/folders/rt/gw2rs2td209ck8nlqdlt_v8m0000gn/T/wiremock-run-4031184994358890732 --disable-banner\n\nThe WireMock server is started .....\nversion:                      3.5.2\nport:                         8080\nenable-browser-proxying:      false\ndisable-banner:               true\nno-request-journal:           false\nverbose:                      false\n\nextensions:                   response-template,webhook\n```\n\n"
  },
  {
    "path": "toolsets/mock/src/main/kotlin/com/phodal/shire/mock/provider/WiremockFunction.kt",
    "content": "package com.phodal.shire.mock.provider\n\nenum class WiremockFunction(val funName: String) {\n    Mock(\"mock\")\n    ;\n\n    companion object {\n        fun fromString(value: String): WiremockFunction? {\n            return entries.firstOrNull { it.funName == value }\n        }\n    }\n}"
  },
  {
    "path": "toolsets/mock/src/main/kotlin/com/phodal/shire/mock/provider/WiremockFunctionProvider.kt",
    "content": "package com.phodal.shire.mock.provider\n\nimport com.intellij.execution.RunManager\nimport com.intellij.execution.actions.ConfigurationContext\nimport com.intellij.openapi.application.runReadAction\nimport com.intellij.openapi.project.Project\nimport com.intellij.psi.PsiFile\nimport com.intellij.psi.PsiManager\nimport com.phodal.shirecore.provider.function.ToolchainFunctionProvider\nimport com.phodal.shirecore.runner.ConfigurationRunner\n\n\nclass WiremockFunctionProvider : ToolchainFunctionProvider, ConfigurationRunner {\n    override fun isApplicable(project: Project, funcName: String): Boolean {\n        return WiremockFunction.values().any { it.funName == funcName }\n    }\n\n    override fun execute(project: Project, funcName: String, args: List<Any>, allVariables: Map<String, Any?>): Any {\n        val wiremockFunction = WiremockFunction.fromString(funcName)\n            ?: throw IllegalArgumentException(\"Shire[Wiremock]: Invalid Wiremock function name\")\n\n        return when (wiremockFunction) {\n            WiremockFunction.Mock -> {\n                if (args.isEmpty()) {\n                    return \"ShireError[Wiremock]: No args found\"\n                }\n\n                val mockFilepath = args.first()\n                val mockFile = project.baseDir.findFileByRelativePath(mockFilepath.toString())\n                    ?: throw IllegalArgumentException(\"ShireError[Wiremock]: No file found: $mockFilepath\")\n\n                val jsonFile = runReadAction {\n                    PsiManager.getInstance(project).findFile(mockFile)\n                } ?: throw IllegalArgumentException(\"ShireError[Wiremock]: No JsonFile found: $mockFilepath\")\n\n                runMock(project, jsonFile)\n            }\n        }\n    }\n\n    private fun runMock(project: Project, configFile: PsiFile): Any {\n        val configurationSettings = runReadAction {\n            ConfigurationContext(configFile).configurationsFromContext?.firstOrNull()?.configurationSettings\n        } ?: throw IllegalArgumentException(\"ShireError[Wiremock]: Please install Wiremock plugin\")\n\n        if (!configurationSettings.name.startsWith(\"WireMock\")) {\n            throw IllegalArgumentException(\"ShireError[Wiremock]: No a valid WireMock configure found\")\n        }\n\n        val runManager = RunManager.getInstance(project)\n        // java.lang.Throwable: WireMock.WireMock mock_v0-stubs.json must be added before selecting\n        //\tat com.intellij.openapi.diagnostic.Logger.error(Logger.java:376)\n        //\tat com.intellij.execution.impl.RunManagerImpl.setSelectedConfiguration(Run\n        runManager.addConfiguration(configurationSettings)\n        runManager.selectedConfiguration = configurationSettings\n\n        configurationSettings.isActivateToolWindowBeforeRun = true\n        configurationSettings.isFocusToolWindowBeforeRun = true\n        configurationSettings.isTemporary = true\n\n        val runContext = createRunContext()\n        executeRunConfigurations(null, configurationSettings, runContext, null, null)\n\n        return \"Done\"\n    }\n}\n"
  },
  {
    "path": "toolsets/mock/src/main/resources/com.phodal.shire.mock.xml",
    "content": "<idea-plugin package=\"com.phodal.shire.mock\">\n    <!--suppress PluginXmlValidity -->\n    <dependencies>\n        <plugin id=\"com.intellij.wiremock\"/>\n    </dependencies>\n\n    <extensions defaultExtensionNs=\"com.phodal\">\n        <shireToolchainFunctionProvider implementation=\"com.phodal.shire.mock.provider.WiremockFunctionProvider\"/>\n    </extensions>\n</idea-plugin>\n"
  },
  {
    "path": "toolsets/openrewrite/README.md",
    "content": "# OpenRewrite Module\n\nSince JetBrains don't provide the OpenRewrite API, we use reflection to access the OpenRewrite API. \nThis means that the OpenRewrite module is not guaranteed to work with all versions of OpenRewrite.\n\n- [com.intellij.openRewrite.OpenRewriteFileService]\n- [com.intellij.openRewrite.run.OpenRewriteRunConfiguration]\n- [com.intellij.openRewrite.run.OpenRewriteRunConfigurationType]\n- [com.intellij.openRewrite.recipe.OpenRewriteRecipeService]\n"
  },
  {
    "path": "toolsets/openrewrite/src/main/kotlin/com/phodal/shire/openrewrite/OpenRewriteFileRunService.kt",
    "content": "package com.phodal.shire.openrewrite\n\nimport com.intellij.execution.ExecutionManager\nimport com.intellij.execution.RunManager\nimport com.intellij.execution.configurations.ConfigurationTypeUtil\nimport com.intellij.execution.configurations.RunConfiguration\nimport com.intellij.execution.configurations.RunProfile\nimport com.intellij.execution.executors.DefaultRunExecutor\nimport com.intellij.execution.runners.ExecutionEnvironmentBuilder\nimport com.intellij.openapi.application.runReadAction\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.intellij.psi.PsiElement\nimport com.intellij.psi.PsiFile\nimport com.intellij.psi.PsiManager\nimport com.phodal.shirecore.provider.shire.FileRunService\n\n\nclass OpenRewriteFileRunService : FileRunService {\n    override fun isApplicable(project: Project, file: VirtualFile): Boolean {\n        return file.extension == \"yml\" && isOpenWriteFile(project, file)\n    }\n\n    private fun isOpenWriteFile(project: Project, file: VirtualFile): Boolean {\n        try {\n            val clazz = Class.forName(\"com.intellij.openRewrite.OpenRewriteFileService\")\n            val getInstanceMethod = clazz.getDeclaredMethod(\"getInstance\")\n            val isRecipeMethod = clazz.getDeclaredMethod(\"isRecipe\", PsiFile::class.java)\n\n            val fileService = getInstanceMethod.invoke(null)\n            val psiFile = runReadAction {\n                PsiManager.getInstance(project).findFile(file)\n            } ?: return false\n\n            isRecipeMethod.isAccessible = true\n\n            val result = isRecipeMethod.invoke(fileService, psiFile) as Boolean\n            return result\n        } catch (e: Exception) {\n            return false\n        }\n    }\n\n    override fun runConfigurationClass(project: Project): Class<out RunProfile>? = null\n\n    override fun runFile(project: Project, virtualFile: VirtualFile, psiElement: PsiElement?): String? {\n        if (!isOpenWriteFile(project, virtualFile)) {\n            return \"\"\n        }\n\n        val runManager = RunManager.getInstance(project)\n        val allSettings = runManager.allSettings\n\n        val workingPath = virtualFile.parent.path\n\n        var settings = allSettings.firstOrNull {\n            try {\n                val config = it.configuration\n                val configClass = config::class.java\n\n                if (configClass.name == \"com.intellij.openRewrite.run.OpenRewriteRunConfiguration\") {\n                    val getExpandedWorkingDirectoryMethod = configClass.getMethod(\"getExpandedWorkingDirectory\")\n                    val workingDirectory = getExpandedWorkingDirectoryMethod.invoke(config) as? String\n                    workingDirectory == workingPath\n                } else {\n                    false\n                }\n            } catch (e: Exception) {\n                false\n            }\n        }\n\n        val list = try {\n            getRecipeDescriptors(project, virtualFile) ?: return null\n        } catch (e: Exception) {\n            return null\n        }\n        if (list.isEmpty()) return null\n\n        if (settings == null) {\n            try {\n                val configurationType = ConfigurationTypeUtil.findConfigurationType(\"OpenRewriteRunConfigurationType\")!!\n                settings = runManager.createConfiguration(\"\", configurationType.configurationFactories[0])\n                val configuration = settings.configuration\n\n                if (configuration.javaClass.name == \"com.intellij.openRewrite.run.OpenRewriteRunConfiguration\") {\n                    configureOpenRewrite(configuration, project, virtualFile, list)\n                }\n\n                runManager.setUniqueNameIfNeeded(settings)\n                runManager.setTemporaryConfiguration(settings)\n            } catch (e: Exception) {\n                return null\n            }\n        }\n\n        val builder = ExecutionEnvironmentBuilder.createOrNull(DefaultRunExecutor.getRunExecutorInstance(), settings)\n        builder?.let {\n            ExecutionManager.getInstance(project).restartRunProfile(it.build())\n        }\n\n        return \"\"\n    }\n\n    private fun configureOpenRewrite(\n        configuration: RunConfiguration,\n        project: Project,\n        virtualFile: VirtualFile,\n        list: java.util.LinkedHashMap<*, *>,\n    ) {\n        val directoryMethod =\n            configuration::class.java.getMethod(\"setWorkingDirectory\", String::class.java)\n        val projectRoot = project.basePath ?: \"\"\n        directoryMethod.invoke(configuration, projectRoot)\n\n        val setConfigLocationMethod =\n            configuration::class.java.getMethod(\"setConfigLocation\", String::class.java)\n        setConfigLocationMethod.invoke(configuration, virtualFile.path)\n\n        val setActiveRecipesMethod =\n            configuration::class.java.getMethod(\"setActiveRecipes\", String::class.java)\n        setActiveRecipesMethod.invoke(configuration, list.keys.first())\n\n        val nameMethod = configuration::class.java.getMethod(\"setGeneratedName\")\n        nameMethod.invoke(configuration)\n    }\n\n    private fun getRecipeDescriptors(project: Project, virtualFile: VirtualFile): LinkedHashMap<*, *>? {\n        val clazz = Class.forName(\"com.intellij.openRewrite.recipe.OpenRewriteRecipeService\")\n        val getInstanceMethod = clazz.getDeclaredMethod(\"getInstance\", Project::class.java)\n        val recipeService = getInstanceMethod.invoke(null, project)\n        val OpenRewriteType = Class.forName(\"com.intellij.openRewrite.recipe.OpenRewriteType\").getEnumConstants()[0]\n\n        val method =\n            clazz.getDeclaredMethod(\"getLocalDescriptors\", PsiFile::class.java, OpenRewriteType::class.java)\n        method.isAccessible = true\n\n        val psiFile = runReadAction {\n            PsiManager.getInstance(project).findFile(virtualFile)\n        } ?: return null\n\n        val list = runReadAction {\n            val type = OpenRewriteType::class.java.enumConstants[0]\n            method.invoke(recipeService, psiFile, type) as LinkedHashMap<*, *>\n        }\n\n        return list\n    }\n}"
  },
  {
    "path": "toolsets/openrewrite/src/main/resources/com.phodal.shire.openrewrite.xml",
    "content": "<idea-plugin package=\"com.phodal.shire.openrewrite\">\n    <!--suppress PluginXmlValidity -->\n    <dependencies>\n        <plugin id=\"com.intellij.openRewrite\"/>\n    </dependencies>\n\n    <extensions defaultExtensionNs=\"com.phodal\">\n        <shireFileRunService implementation=\"com.phodal.shire.openrewrite.OpenRewriteFileRunService\"/>\n    </extensions>\n</idea-plugin>\n"
  },
  {
    "path": "toolsets/plantuml/README.md",
    "content": "# PlantUml Module\n\nTodos:\n\n- [ ] Export images to a specific directory\n\n"
  },
  {
    "path": "toolsets/plantuml/src/main/kotlin/com/phodal/shire/plantuml/PlantUmlSketchProvider.kt",
    "content": "package com.phodal.shire.plantuml\n\nimport com.intellij.lang.Language\nimport com.intellij.openapi.fileEditor.TextEditor\nimport com.intellij.openapi.fileEditor.impl.text.TextEditorProvider\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.intellij.testFramework.LightVirtualFile\nimport com.intellij.ui.dsl.builder.panel\nimport com.intellij.util.ui.JBUI\nimport com.phodal.shirecore.provider.sketch.ExtensionLangSketch\nimport com.phodal.shirecore.provider.sketch.LanguageSketchProvider\nimport org.plantuml.idea.preview.PlantUmlPreviewPanel\nimport org.plantuml.idea.preview.editor.PlantUmlPreviewEditor\nimport org.plantuml.idea.preview.editor.PlantUmlSplitEditor\nimport org.plantuml.idea.preview.editor.SplitFileEditor\nimport org.plantuml.idea.rendering.LazyApplicationPoolExecutor\nimport org.plantuml.idea.rendering.RenderCommand\nimport org.plantuml.idea.settings.PlantUmlSettings\nimport javax.swing.JComponent\nimport javax.swing.JPanel\n\nclass PlantUmlSketchProvider : LanguageSketchProvider {\n    override fun isSupported(lang: String): Boolean {\n        return lang == \"plantuml\" || lang == \"puml\" || lang == \"uml\"\n    }\n\n    override fun create(project: Project, content: String): ExtensionLangSketch {\n        val virtualFile = LightVirtualFile(\"plantuml.puml\", content)\n        return PlantUmlSketch(project, virtualFile)\n    }\n}\n\nclass PlantUmlSketch(private val project: Project, private val virtualFile: VirtualFile) : ExtensionLangSketch {\n    private var mainPanel: JPanel\n    private var umlPreviewEditor: PlantUmlPreviewEditor\n\n    init {\n        val editor = TextEditorProvider.getInstance().createEditor(project, virtualFile) as TextEditor\n        umlPreviewEditor = PlantUmlPreviewEditor(virtualFile, project)\n        umlPreviewEditor.editor = editor.editor\n        val splitEditor = PlantUmlSplitEditor(editor, umlPreviewEditor)\n\n        splitEditor.component.preferredSize = null\n\n        mainPanel = panel {\n            row {\n                cell(editor.component)\n            }\n\n            row {\n                cell(splitEditor.component)\n            }\n        }.apply {\n            border = JBUI.Borders.empty(0, 10)\n        }\n\n        PlantUmlSettings.getInstance().previewSettings.splitEditorLayout = SplitFileEditor.SplitEditorLayout.SECOND\n    }\n\n    override fun doneUpdateText(text: String) {\n        (umlPreviewEditor.component as PlantUmlPreviewPanel).processRequest(LazyApplicationPoolExecutor.Delay.NOW, RenderCommand.Reason.FILE_SWITCHED)\n    }\n\n    override fun getExtensionName(): String {\n        return \"plantuml\"\n    }\n\n    override fun getViewText(): String {\n        return virtualFile.inputStream.bufferedReader().use { it.readText() }\n    }\n\n    override fun updateViewText(text: String) {\n        virtualFile.setBinaryContent(text.toByteArray())\n    }\n\n    override fun getComponent(): JComponent {\n        return mainPanel\n    }\n\n    override fun updateLanguage(language: Language?, originLanguage: String?) {\n    }\n\n    override fun dispose() {\n    }\n}\n"
  },
  {
    "path": "toolsets/plantuml/src/main/kotlin/com/phodal/shire/plantuml/PlantUmlToolchainProvider.kt",
    "content": "package com.phodal.shire.plantuml\n\nimport com.intellij.openapi.project.Project\nimport com.phodal.shirecore.provider.context.LanguageToolchainProvider\nimport com.phodal.shirecore.provider.context.ToolchainContextItem\nimport com.phodal.shirecore.provider.context.ToolchainPrepareContext\nimport org.plantuml.idea.lang.PlantUmlLanguage\n\nclass PlantUmlToolchainProvider : LanguageToolchainProvider {\n    override fun isApplicable(project: Project, context: ToolchainPrepareContext): Boolean {\n        return context.sourceFile?.language == PlantUmlLanguage.INSTANCE\n    }\n\n    override suspend fun collect(project: Project, context: ToolchainPrepareContext): List<ToolchainContextItem> {\n        return emptyList()\n    }\n}\n"
  },
  {
    "path": "toolsets/plantuml/src/main/resources/com.phodal.shire.plantuml.xml",
    "content": "<idea-plugin package=\"com.phodal.shire.plantuml\">\n    <!--suppress PluginXmlValidity -->\n    <dependencies>\n        <plugin id=\"PlantUML integration\"/>\n    </dependencies>\n\n    <extensions defaultExtensionNs=\"com.phodal\">\n        <shireLanguageToolchainProvider\n                language=\"PUML\"\n                implementationClass=\"com.phodal.shire.plantuml.PlantUmlToolchainProvider\"/>\n\n        <shireLangSketchProvider implementation=\"com.phodal.shire.plantuml.PlantUmlSketchProvider\"/>\n    </extensions>\n</idea-plugin>\n"
  },
  {
    "path": "toolsets/sonarqube/README.md",
    "content": "# Sonarqube Module\n\nProvide variable of Sonarqube, see in [SonarqubeVariable]\n\n- `$sonarIssue`\n- `$sonarResults`\n"
  },
  {
    "path": "toolsets/sonarqube/src/main/kotlin/com/phodal/shire/sonarqube/SonarLintProvider.kt",
    "content": "package com.phodal.shire.sonarqube\n\nimport com.intellij.openapi.application.ReadAction\nimport com.intellij.openapi.diagnostic.logger\nimport com.intellij.openapi.editor.Document\nimport com.intellij.openapi.progress.ProgressIndicator\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.util.TextRange\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.intellij.psi.PsiDocumentManager\nimport com.intellij.psi.PsiManager\nimport org.sonarlint.intellij.analysis.Analysis\nimport org.sonarlint.intellij.analysis.AnalysisCallback\nimport org.sonarlint.intellij.analysis.AnalysisResult\nimport org.sonarlint.intellij.analysis.AnalysisSubmitter\nimport org.sonarlint.intellij.tasks.startBackgroundableModalTask\nimport org.sonarlint.intellij.trigger.TriggerType\nimport java.util.concurrent.CompletableFuture\nimport java.util.stream.Stream\n\nobject SonarLintProvider {\n    fun analysisFile(project: Project, file: VirtualFile): String? {\n        return analysis(file, project) {\n            ReadAction.compute<String, Throwable> {\n                val result = StringBuilder()\n                it.findings.issuesPerFile.forEach { (file, issues) ->\n                    result.append(\"File: $file\\n\")\n                    issues.forEach { issue ->\n                        result.append(\"  - ${issue.userSeverity}: ${issue.message}\\n\")\n                    }\n                }\n\n                result.toString()\n            }\n        }\n    }\n\n    fun analysisResults(project: Project, file: VirtualFile): String? {\n        return analysis(file, project) {\n            ReadAction.compute<String, Throwable> {\n                val psiFile = PsiManager.getInstance(project).findFile(file) ?: return@compute \"\"\n                val document = PsiDocumentManager.getInstance(project).getDocument(psiFile) ?: return@compute \"\"\n\n                val result = StringBuilder()\n                it.findings.securityHotspotsPerFile.forEach { (file, issues) ->\n                    result.append(\"File: $file\\n\")\n                    issues.forEach { issue ->\n                        val range = issue.validTextRange\n                        val lineContent: String = if (range != null) {\n                            val (startLine, endLine) = textRangeToLineNumbers(document, range)\n                            \"($startLine-$endLine) ${document.getText(range)}\"\n                        } else {\n                            \"\"\n                        }\n\n                        result.append(\"  -  ${lineContent} has issue : ${issue.message}\\n\")\n                        if (issue.quickFixes().isNotEmpty()) {\n                            result.append(\"    Quick Fixes suggestion: \")\n                            issue.quickFixes().forEach { quickFix ->\n                                result.append(\"      - ${quickFix.message}\\n\")\n                            }\n                        }\n                    }\n                }\n\n                result.toString()\n            }\n        }\n    }\n\n    private fun textRangeToLineNumbers(document: Document, textRange: TextRange): Pair<Int, Int> {\n        val startLine = document.getLineNumber(textRange.startOffset) + 1\n        val endLine = document.getLineNumber(textRange.endOffset) + 1\n        return startLine to endLine\n    }\n\n    private fun analysis(\n        file: VirtualFile,\n        project: Project,\n        callback: (AnalysisResult) -> String,\n    ): String? {\n        val hasProject = Stream.of(file).anyMatch { f: VirtualFile -> f.path == project.basePath }\n        if (hasProject) return null\n\n        logger<SonarLintProvider>().info(\"Analysis file: ${file.path}\")\n        val future = CompletableFuture<String>()\n        val analysis = Analysis(project, listOf(file), TriggerType.CURRENT_FILE_ACTION, object : AnalysisCallback {\n            override fun onSuccess(analysisResult: AnalysisResult) {\n                future.complete(callback(analysisResult))\n            }\n\n            override fun onError(throwable: Throwable) {\n                future.completeExceptionally(throwable)\n            }\n        })\n\n        startBackgroundableModalTask(project, AnalysisSubmitter.ANALYSIS_TASK_TITLE) { indicator: ProgressIndicator? ->\n            if (indicator != null) {\n                analysis.run(indicator)\n            }\n        }\n\n        logger<SonarLintProvider>().info(\"Analysis file: ${file.path} finished\")\n        return future.get()\n    }\n}"
  },
  {
    "path": "toolsets/sonarqube/src/main/kotlin/com/phodal/shire/sonarqube/SonarLintVariableProvider.kt",
    "content": "package com.phodal.shire.sonarqube\n\nimport com.intellij.openapi.editor.Editor\nimport com.intellij.openapi.fileEditor.FileDocumentManager\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.intellij.psi.PsiElement\nimport com.phodal.shirecore.provider.variable.ToolchainVariableProvider\nimport com.phodal.shirecore.provider.variable.model.toolchain.SonarqubeVariable\nimport com.phodal.shirecore.provider.variable.model.ToolchainVariable\n\nclass SonarLintVariableProvider : ToolchainVariableProvider {\n    override fun isResolvable(variable: ToolchainVariable, psiElement: PsiElement?, project: Project): Boolean {\n        return variable is SonarqubeVariable\n    }\n\n    override fun resolve(variable: ToolchainVariable, project: Project, editor: Editor, psiElement: PsiElement?): Any {\n        return when (variable) {\n            SonarqubeVariable.Issue -> {\n                val file: VirtualFile = FileDocumentManager.getInstance().getFile(editor.document)\n                    ?: throw IllegalStateException(\"No file found for editor\")\n\n                SonarLintProvider.analysisFile(project, file) ?: \"\"\n            }\n            SonarqubeVariable.Results -> {\n                val file: VirtualFile = FileDocumentManager.getInstance().getFile(editor.document)\n                    ?: throw IllegalStateException(\"No file found for editor\")\n\n                SonarLintProvider.analysisResults(project, file) ?: \"\"\n            }\n            else -> \"\"\n        }\n    }\n}\n"
  },
  {
    "path": "toolsets/sonarqube/src/main/resources/com.phodal.shire.sonarqube.xml",
    "content": "<idea-plugin package=\"com.phodal.shire.sonarqube\">\n    <!--suppress PluginXmlValidity -->\n    <dependencies>\n        <plugin id=\"org.sonarlint.idea\"/>\n    </dependencies>\n\n    <extensions defaultExtensionNs=\"com.phodal\">\n        <shireToolchainVariableProvider implementation=\"com.phodal.shire.sonarqube.SonarLintVariableProvider\"/>\n    </extensions>\n</idea-plugin>\n"
  },
  {
    "path": "toolsets/terminal/src/main/kotlin/com/phodal/shire/terminal/ShireTerminalExecutor.kt",
    "content": "package com.phodal.shire.terminal\n\nimport com.intellij.openapi.actionSystem.AnActionEvent\nimport com.intellij.openapi.actionSystem.PlatformCoreDataKeys\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.wm.ToolWindowManager\nimport com.intellij.ui.content.Content\nimport com.phodal.shirecore.provider.action.TerminalLocationExecutor\nimport com.phodal.shirecore.provider.action.terminal.TerminalHandler\nimport org.jetbrains.plugins.terminal.TerminalToolWindowFactory\nimport org.jetbrains.plugins.terminal.TerminalToolWindowManager\nimport java.awt.Component\n\nclass ShireTerminalExecutor : TerminalLocationExecutor {\n    override fun getComponent(e: AnActionEvent): Component? {\n        return e.getData(PlatformCoreDataKeys.CONTEXT_COMPONENT)\n    }\n\n    override fun bundler(project: Project, userInput: String): TerminalHandler? {\n        val content = getContent(project) ?: return null\n        return trySendMsgInOld(project, userInput, content)\n    }\n\n    private fun trySendMsgInOld(project: Project, userInput: String, content: Content): TerminalHandler? {\n        val widget = TerminalToolWindowManager.getWidgetByContent(content) ?: return null\n        return TerminalHandler(\n            userInput,\n            project,\n            onChunk = { string ->\n                widget.terminalStarter?.sendString(string, true)\n            },\n            onFinish = {})\n    }\n\n    private fun getContent(project: Project): Content? {\n        val toolWindow = ToolWindowManager.getInstance(project).getToolWindow(TerminalToolWindowFactory.TOOL_WINDOW_ID)\n        return toolWindow?.contentManager?.selectedContent\n    }\n}\n"
  },
  {
    "path": "toolsets/terminal/src/main/kotlin/com/phodal/shire/terminal/TerminalToolchainVariableProvider.kt",
    "content": "package com.phodal.shire.terminal\n\nimport com.intellij.openapi.editor.Editor\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.project.guessProjectDir\nimport com.intellij.psi.PsiElement\nimport com.phodal.shirecore.provider.variable.ToolchainVariableProvider\nimport com.phodal.shirecore.provider.variable.model.toolchain.TerminalToolchainVariable\nimport com.phodal.shirecore.provider.variable.model.ToolchainVariable\nimport org.jetbrains.plugins.terminal.TerminalProjectOptionsProvider\n\nclass TerminalToolchainVariableProvider : ToolchainVariableProvider {\n    override fun isResolvable(variable: ToolchainVariable, psiElement: PsiElement?, project: Project): Boolean {\n        return variable is TerminalToolchainVariable\n    }\n\n    override fun resolve(variable: ToolchainVariable, project: Project, editor: Editor, psiElement  : PsiElement?): Any {\n        val options = TerminalProjectOptionsProvider.getInstance(project)\n\n        return when (variable) {\n            TerminalToolchainVariable.SHELL_PATH -> options.shellPath\n            TerminalToolchainVariable.PWD -> {\n                options.startingDirectory ?: project.guessProjectDir()?.path ?: System.getProperty(\"user.home\")\n            }\n\n            else -> \"\"\n        } ?: \"\"\n    }\n}\n"
  },
  {
    "path": "toolsets/terminal/src/main/kotlin/com/phodal/shire/terminal/sketch/ShellUtil.kt",
    "content": "// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage com.phodal.shire.terminal.sketch\n\nimport com.intellij.execution.configuration.EnvironmentVariablesData\nimport com.intellij.execution.configurations.PathEnvironmentVariableUtil\nimport com.intellij.execution.wsl.WSLDistribution\nimport com.intellij.execution.wsl.WslDistributionManager\nimport com.intellij.openapi.util.SystemInfo\nimport com.intellij.openapi.util.text.StringUtil\nimport com.intellij.util.EnvironmentUtil\nimport java.io.File\nimport java.nio.file.Files\nimport java.nio.file.Path\n\nobject ShellUtil {\n    private fun listWslShell() : List<String>? {\n        if (WSLDistribution.findWslExe() == null) return listOf()\n        val distributions = WslDistributionManager.getInstance().installedDistributions\n        return distributions.mapNotNull { it.shellPath }\n    }\n\n    fun detectShells(): List<String> {\n        val shells: MutableList<String> = ArrayList()\n        if (SystemInfo.isUnix) {\n            addIfExists(shells, \"/bin/bash\")\n            addIfExists(shells, \"/usr/bin/bash\")\n            addIfExists(shells, \"/usr/local/bin/bash\")\n            addIfExists(shells, \"/opt/homebrew/bin/bash\")\n\n            addIfExists(shells, \"/bin/zsh\")\n            addIfExists(shells, \"/usr/bin/zsh\")\n            addIfExists(shells, \"/usr/local/bin/zsh\")\n            addIfExists(shells, \"/opt/homebrew/bin/zsh\")\n\n            addIfExists(shells, \"/bin/fish\")\n            addIfExists(shells, \"/usr/bin/fish\")\n            addIfExists(shells, \"/usr/local/bin/fish\")\n            addIfExists(shells, \"/opt/homebrew/bin/fish\")\n\n            addIfExists(shells, \"/opt/homebrew/bin/pwsh\")\n        } else if (SystemInfo.isWindows) {\n            val powershell = PathEnvironmentVariableUtil.findInPath(\"powershell.exe\")\n            if (powershell != null &&\n                StringUtil.startsWithIgnoreCase(powershell.absolutePath, \"C:\\\\Windows\\\\System32\\\\WindowsPowerShell\\\\\")\n            ) {\n                shells.add(powershell.absolutePath)\n            }\n            val cmd = PathEnvironmentVariableUtil.findInPath(\"cmd.exe\")\n            if (cmd != null && StringUtil.startsWithIgnoreCase(cmd.absolutePath, \"C:\\\\Windows\\\\System32\\\\\")) {\n                shells.add(cmd.absolutePath)\n            }\n            val pwsh = PathEnvironmentVariableUtil.findInPath(\"pwsh.exe\")\n            if (pwsh != null && StringUtil.startsWithIgnoreCase(pwsh.absolutePath, \"C:\\\\Program Files\\\\PowerShell\\\\\")) {\n                shells.add(pwsh.absolutePath)\n            }\n            val gitBash = File(\"C:\\\\Program Files\\\\Git\\\\bin\\\\bash.exe\")\n            if (gitBash.isFile) {\n                shells.add(gitBash.absolutePath)\n            }\n            var cmderRoot = EnvironmentUtil.getValue(\"CMDER_ROOT\")\n            if (cmderRoot == null) {\n                cmderRoot = EnvironmentVariablesData.DEFAULT.envs[\"CMDER_ROOT\"]\n            }\n            if (cmderRoot != null && cmd != null &&\n                StringUtil.startsWithIgnoreCase(cmd.absolutePath, \"C:\\\\Windows\\\\System32\\\\\")\n            ) {\n                shells.add(\"cmd.exe /k \\\"%CMDER_ROOT%\\\\vendor\\\\init.bat\\\"\")\n            }\n        }\n\n        return shells\n    }\n\n    private fun addIfExists(shells: MutableList<String>, filePath: String) {\n        if (Files.exists(Path.of(filePath))) {\n            shells.add(filePath)\n        }\n    }\n}"
  },
  {
    "path": "toolsets/terminal/src/main/kotlin/com/phodal/shire/terminal/sketch/TerminalLangSketchProvider.kt",
    "content": "package com.phodal.shire.terminal.sketch\n\nimport com.intellij.execution.configurations.GeneralCommandLine\nimport com.intellij.execution.configurations.PtyCommandLine\nimport com.intellij.execution.process.KillableProcessHandler\nimport com.intellij.execution.process.ProcessAdapter\nimport com.intellij.execution.process.ProcessEvent\nimport com.intellij.icons.AllIcons\nimport com.intellij.lang.Language\nimport com.intellij.openapi.application.ApplicationManager\nimport com.intellij.openapi.fileEditor.FileEditorManager\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.project.guessProjectDir\nimport com.intellij.openapi.ui.popup.JBPopup\nimport com.intellij.openapi.ui.popup.JBPopupFactory\nimport com.intellij.openapi.ui.popup.util.MinimizeButton\nimport com.intellij.terminal.JBTerminalWidget\nimport com.intellij.util.ui.JBUI\nimport com.phodal.shirecore.ShirelangNotifications\nimport com.phodal.shirecore.provider.sketch.ExtensionLangSketch\nimport com.phodal.shirecore.provider.sketch.LanguageSketchProvider\nimport org.jetbrains.plugins.terminal.LocalTerminalDirectRunner\nimport java.awt.BorderLayout\nimport java.awt.Dimension\nimport java.awt.event.MouseAdapter\nimport java.awt.event.MouseEvent\nimport javax.swing.JButton\nimport javax.swing.JComponent\nimport javax.swing.JLabel\nimport javax.swing.JPanel\n\nclass TerminalLangSketchProvider : LanguageSketchProvider {\n    override fun isSupported(lang: String): Boolean = lang == \"bash\"\n\n    override fun create(project: Project, content: String): ExtensionLangSketch {\n        var content = content\n        return object : ExtensionLangSketch {\n            var terminalWidget: JBTerminalWidget? = null\n            var panelLayout: JPanel? = null\n\n            init {\n                val projectDir = project.guessProjectDir()?.path\n                val terminalRunner = LocalTerminalDirectRunner.createTerminalRunner(project)\n                terminalWidget = terminalRunner.createTerminalWidget(this, projectDir, true).also {\n                    it.preferredSize = Dimension(it.preferredSize.width, 120)\n                }\n\n                panelLayout = object : JPanel(BorderLayout()) {\n                    init {\n                        add(JLabel(\"Terminal\").also {\n                            it.border = JBUI.Borders.empty(5, 0)\n                        }, BorderLayout.NORTH)\n\n                        add(terminalWidget!!.component, BorderLayout.CENTER)\n\n                        val buttonPanel = JPanel(BorderLayout())\n                        val runButton = JButton(AllIcons.Toolwindows.ToolWindowRun)\n                            .apply {\n                                addMouseListener(executeShellScriptOnClick(project, content, terminalWidget))\n                            }\n\n                        val popupButton = JButton(\"Pop up Terminal\").apply {\n                            addMouseListener(executePopup(terminalWidget, project))\n                        }\n\n                        buttonPanel.add(runButton, BorderLayout.WEST)\n                        buttonPanel.add(popupButton, BorderLayout.EAST)\n                        add(buttonPanel, BorderLayout.SOUTH)\n                    }\n                }\n\n                panelLayout!!.border = JBUI.Borders.compound(\n                    JBUI.Borders.empty(5, 10),\n                )\n            }\n\n\n            private fun executePopup(terminalWidget: JBTerminalWidget?, project: Project): MouseAdapter =\n                object : MouseAdapter() {\n                    override fun mouseClicked(e: MouseEvent?) {\n                        var popup: JBPopup? = null\n                        popup = JBPopupFactory.getInstance()\n                            .createComponentPopupBuilder(terminalWidget!!.component, null)\n                            .setProject(project)\n                            .setResizable(true)\n                            .setMovable(true)\n                            .setTitle(\"Terminal\")\n                            .setCancelButton(MinimizeButton(\"Hide\"))\n                            .setCancelCallback {\n                                popup?.cancel()\n                                panelLayout!!.remove(terminalWidget.component)\n                                panelLayout!!.add(terminalWidget.component)\n\n                                panelLayout!!.revalidate()\n                                panelLayout!!.repaint()\n                                true\n                            }\n                            .setFocusable(true)\n                            .setRequestFocus(true)\n                            .createPopup()\n\n                        val editor = FileEditorManager.getInstance(project).selectedTextEditor\n                        if (editor != null) {\n                            popup.showInBestPositionFor(editor)\n                        } else {\n                            popup.showInFocusCenter()\n                        }\n                    }\n                }\n\n            override fun getExtensionName(): String = \"Terminal\"\n            override fun getViewText(): String = content\n            override fun updateViewText(text: String) {\n                content = text\n            }\n\n            override fun doneUpdateText(text: String) {\n                ApplicationManager.getApplication().invokeLater {\n                    Thread.sleep(1000) // todo: change to when terminal ready\n                    terminalWidget!!.terminalStarter?.sendString(content, false)\n                }\n            }\n\n            override fun getComponent(): JComponent = panelLayout!!\n\n            override fun updateLanguage(language: Language?, originLanguage: String?) {}\n\n            override fun dispose() {}\n        }\n    }\n\n    fun executeShellScriptOnClick(\n        project: Project,\n        content: String,\n        terminalWidget: JBTerminalWidget?,\n    ): MouseAdapter = object : MouseAdapter() {\n        override fun mouseClicked(e: MouseEvent?) {\n            val commandLine = createCommandLineForScript(project, content)\n            val processBuilder = commandLine.toProcessBuilder()\n            val process = processBuilder.start()\n            val processHandler = KillableProcessHandler(process, commandLine.commandLineString)\n            processHandler.startNotify()\n\n            processHandler.addProcessListener(object : ProcessAdapter() {\n                override fun processTerminated(event: ProcessEvent) {\n                    ShirelangNotifications.warn(\n                        project,\n                        \"Process terminated with exit code ${event.exitCode}, ${event.text}\"\n                    )\n                    processHandler.destroyProcess()\n                }\n            })\n        }\n    }\n\n    fun createCommandLineForScript(project: Project, scriptText: String): GeneralCommandLine {\n        val workingDirectory = project.basePath\n        val commandLine = PtyCommandLine()\n        commandLine.withConsoleMode(false)\n        commandLine.withInitialColumns(120)\n        commandLine.withParentEnvironmentType(GeneralCommandLine.ParentEnvironmentType.CONSOLE)\n        commandLine.setWorkDirectory(workingDirectory!!)\n        commandLine.withExePath(ShellUtil.detectShells().first())\n        commandLine.withParameters(\"-c\")\n        commandLine.withParameters(scriptText)\n        return commandLine\n    }\n}\n"
  },
  {
    "path": "toolsets/terminal/src/main/resources/com.phodal.shire.terminal.xml",
    "content": "<idea-plugin package=\"com.phodal.shire.terminal\">\n    <!--suppress PluginXmlValidity -->\n    <dependencies>\n        <plugin id=\"org.jetbrains.plugins.terminal\"/>\n    </dependencies>\n\n    <extensions defaultExtensionNs=\"com.phodal\">\n        <shireTerminalExecutor implementation=\"com.phodal.shire.terminal.ShireTerminalExecutor\"/>\n\n        <shireToolchainVariableProvider implementation=\"com.phodal.shire.terminal.TerminalToolchainVariableProvider\"/>\n\n        <shireLangSketchProvider implementation=\"com.phodal.shire.terminal.sketch.TerminalLangSketchProvider\"/>\n    </extensions>\n</idea-plugin>\n"
  },
  {
    "path": "toolsets/uitest/src/main/kotlin/com/phodal/shire/uitest/provider/PlaywrightFileRunService.kt",
    "content": "package com.phodal.shire.uitest.provider\n\nimport com.intellij.aqua.runners.playwright.js.PlaywrightRunConfigurationProducer\nimport com.intellij.execution.RunManager\nimport com.intellij.execution.actions.ConfigurationContext\nimport com.intellij.execution.actions.RunConfigurationProducer\nimport com.intellij.execution.configurations.RunConfiguration\nimport com.intellij.execution.configurations.RunProfile\nimport com.intellij.openapi.application.runReadAction\nimport com.intellij.openapi.project.Project\nimport com.intellij.openapi.vfs.VirtualFile\nimport com.intellij.psi.PsiManager\nimport com.phodal.shirecore.provider.shire.FileRunService\n\nclass PlaywrightFileRunService : FileRunService {\n    override fun isApplicable(project: Project, file: VirtualFile): Boolean {\n        val isSpecFile = file.name.endsWith(\".spec.ts\") || file.name.endsWith(\".spec.js\")\n        if (isSpecFile) {\n            return true\n        }\n\n        val content = file.inputStream.bufferedReader().use { it.readText() }\n        val hasPlaywright = content.contains(\"playwright\")\n        return hasPlaywright\n    }\n\n    override fun runConfigurationClass(project: Project): Class<out RunProfile>? {\n        return PlaywrightRunConfiguration::class.java\n        return null\n    }\n\n    override fun createConfiguration(project: Project, virtualFile: VirtualFile): RunConfiguration? {\n        val configurationSetting = runReadAction {\n            val psiFile = PsiManager.getInstance(project).findFile(virtualFile) ?: return@runReadAction null\n            val runManager = RunManager.getInstance(project)\n\n            val configProducer = RunConfigurationProducer.getInstance(\n                PlaywrightRunConfigurationProducer::class.java\n            )\n\n            val configurationType = configProducer.findOrCreateConfigurationFromContext(\n                ConfigurationContext(psiFile)\n            )?.configurationType?.javaClass ?: return@runReadAction null\n\n            val configuration = runManager.createConfiguration(\"Playwright\", configurationType)\n\n            runManager.addConfiguration(configuration)\n            configuration\n        }\n\n        return configurationSetting?.configuration\n    }\n}"
  },
  {
    "path": "toolsets/uitest/src/main/resources/com.phodal.shire.uitest.xml",
    "content": "<idea-plugin package=\"com.phodal.shire.uitest\">\n    <!--suppress PluginXmlValidity -->\n    <dependencies>\n        <plugin id=\"com.intellij.aqua\"/>\n    </dependencies>\n\n    <extensions defaultExtensionNs=\"com.phodal\">\n        <shireFileRunService implementation=\"com.phodal.shire.uitest.provider.PlaywrightFileRunService\"/>\n    </extensions>\n</idea-plugin>\n"
  }
]