1
0
mirror of https://github.com/chylex/IntelliJ-Rainbow-Brackets.git synced 2025-01-30 22:46:00 +01:00

Compare commits

...

4 Commits

Author SHA1 Message Date
2977a08009
Fix service initialization
Closes #1
2024-11-30 22:26:27 +01:00
ec78647a17
Fix support for CLion and Rider 2024-11-30 20:58:21 +01:00
4ad30ca3dc
Migrate packages 2024-11-30 20:56:26 +01:00
06765e9d7f
Fork Rainbow Brackets 6.26 2024-11-30 20:56:10 +01:00
181 changed files with 5693 additions and 8095 deletions

1
.gitattributes vendored Normal file
View File

@ -0,0 +1 @@
* text=auto eol=lf

View File

@ -1,128 +0,0 @@
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or
advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series
of actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within
the community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.

View File

@ -1,69 +0,0 @@
## Contribute
NOTE: To view the PSI tree and explore the internal PSI structure of source code, you need to set up your IDE by following [this](https://www.jetbrains.com/help/idea/psi-viewer.html).
For the Rider IDE, please follow: [this](https://rider-support.jetbrains.com/hc/en-us/articles/207327910-How-to-run-JetBrains-Rider-in-Internal-Mode)
For small changes, [Gitpod](https://gitpod.io/#https://github.com/izhangzhihao/intellij-rainbow-brackets) is recommended. [![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/izhangzhihao/intellij-rainbow-brackets)
* `gradle test`
* `gradle runIde`
* `gradle buildPlugin`
## Support Us
You can support us by the following actions:
* Star this project
* Share this plugin with your friends
* Rate this plugin on [JetBrains plugin repository](https://plugins.jetbrains.com/plugin/10080-rainbow-brackets)
* Make pull requests
* Report bugs
* Tell us your ideas
* Become a sponsor by donating on [Open Collective](https://opencollective.com/intellij-rainbow-brackets)
* Become a sponsor by donating with AliPay or WeChatPay
* Don't want to use AliPay's mobile App? If you could read Chinese, please use [the official website](https://shenghuo.alipay.com/send/payment/fill.htm) to transfer your donation to my Alipay account(izhangzhihao@hotmail.com). Otherwise, you can try [the English version of Alipay](https://global.alipay.com/) to transfer your donation.
<table>
<tr>
<th width="33%">Open Collective</th>
<th width="33%">AliPay</th>
<th width="33%">WeChatPay</th>
</tr>
<tr></tr>
<tr align="center">
<td>
<a href="https://opencollective.com/intellij-rainbow-brackets" target="_blank">
<img src="https://cdn.jsdelivr.net/gh/YiiGuxing/TranslationPlugin@master/images/open-collective.svg" width="171px" alt="OpenCollective">
</a>
</td>
<td><img src="https://user-images.githubusercontent.com/12044174/85197261-77dd5a80-b312-11ea-9630-51caf7d634f2.jpg"></td>
<td><img src="https://user-images.githubusercontent.com/12044174/85197263-7b70e180-b312-11ea-917a-35eab2ea08ae.jpg"></td>
</tr>
</table>
## Backers
Thank you to all our backers! ❤️ [[Become a backer](https://opencollective.com/intellij-rainbow-brackets#backer)]
<a href="https://opencollective.com/intellij-rainbow-brackets#backers" target="_blank"><img src="https://opencollective.com/intellij-rainbow-brackets/backers.svg?width=890"></a>
## Sponsors
Support this project by becoming a sponsor! 🌈 Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/intellij-rainbow-brackets#sponsor)]
<a href="https://opencollective.com/intellij-rainbow-brackets/sponsor/0/website" target="_blank"><img src="https://opencollective.com/intellij-rainbow-brackets/sponsor/0/avatar.svg"></a>
<a href="https://opencollective.com/intellij-rainbow-brackets/sponsor/1/website" target="_blank"><img src="https://opencollective.com/intellij-rainbow-brackets/sponsor/1/avatar.svg"></a>
<a href="https://opencollective.com/intellij-rainbow-brackets/sponsor/2/website" target="_blank"><img src="https://opencollective.com/intellij-rainbow-brackets/sponsor/2/avatar.svg"></a>
<a href="https://opencollective.com/intellij-rainbow-brackets/sponsor/3/website" target="_blank"><img src="https://opencollective.com/intellij-rainbow-brackets/sponsor/3/avatar.svg"></a>
<a href="https://opencollective.com/intellij-rainbow-brackets/sponsor/4/website" target="_blank"><img src="https://opencollective.com/intellij-rainbow-brackets/sponsor/4/avatar.svg"></a>
<a href="https://opencollective.com/intellij-rainbow-brackets/sponsor/5/website" target="_blank"><img src="https://opencollective.com/intellij-rainbow-brackets/sponsor/5/avatar.svg"></a>
<a href="https://opencollective.com/intellij-rainbow-brackets/sponsor/6/website" target="_blank"><img src="https://opencollective.com/intellij-rainbow-brackets/sponsor/6/avatar.svg"></a>
<a href="https://opencollective.com/intellij-rainbow-brackets/sponsor/7/website" target="_blank"><img src="https://opencollective.com/intellij-rainbow-brackets/sponsor/7/avatar.svg"></a>
<a href="https://opencollective.com/intellij-rainbow-brackets/sponsor/8/website" target="_blank"><img src="https://opencollective.com/intellij-rainbow-brackets/sponsor/8/avatar.svg"></a>
<a href="https://opencollective.com/intellij-rainbow-brackets/sponsor/9/website" target="_blank"><img src="https://opencollective.com/intellij-rainbow-brackets/sponsor/9/avatar.svg"></a>
<a href="https://opencollective.com/intellij-rainbow-brackets/sponsor/10/website" target="_blank"><img src="https://opencollective.com/intellij-rainbow-brackets/sponsor/10/avatar.svg"></a>
<a href="https://opencollective.com/intellij-rainbow-brackets/sponsor/11/website" target="_blank"><img src="https://opencollective.com/intellij-rainbow-brackets/sponsor/11/avatar.svg"></a>
<a href="https://opencollective.com/intellij-rainbow-brackets/sponsor/12/website" target="_blank"><img src="https://opencollective.com/intellij-rainbow-brackets/sponsor/12/avatar.svg"></a>
<a href="https://opencollective.com/intellij-rainbow-brackets/sponsor/13/website" target="_blank"><img src="https://opencollective.com/intellij-rainbow-brackets/sponsor/13/avatar.svg"></a>
<a href="https://opencollective.com/intellij-rainbow-brackets/sponsor/14/website" target="_blank"><img src="https://opencollective.com/intellij-rainbow-brackets/sponsor/14/avatar.svg"></a>
<a href="https://opencollective.com/intellij-rainbow-brackets/sponsor/15/website" target="_blank"><img src="https://opencollective.com/intellij-rainbow-brackets/sponsor/15/avatar.svg"></a>

2
.github/FUNDING.yml vendored
View File

@ -1,2 +0,0 @@
open_collective: intellij-rainbow-brackets
custom: https://github.com/izhangzhihao/intellij-rainbow-brackets#support-us

View File

@ -1,57 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug?
assignees: ''
---
Your issue may already be reported!
Please search on the [issues](https://github.com/izhangzhihao/intellij-rainbow-brackets/issues) and the [document](https://github.com/izhangzhihao/intellij-rainbow-brackets/blob/2020.3/README.md) before creating one.
## Please check
- [ ] I already support this project.
- [ ] If you are submitting a feature request, please do consider donating us on [Open Collective](https://opencollective.com/intellij-rainbow-brackets) Or by AliPay/WeChatPay.
- [ ] This issue/feature request is not reported before.
## Your programming languages
## Expected Behavior
* If you're describing a bug, tell us what should happen
* If you're suggesting a change/improvement, tell us how it should work
## Current Behavior
* If describing a bug, tell us what happens instead of the expected behavior
* If suggesting a change/improvement, explain the difference from current behavior
## Possible Solution
Not obligatory, but suggest a fix/reason for the bug, or ideas how to implement the addition or change.
Or what have you tried to resolve this issue.
## Code snippet for reproduce (for bugs)
Please provide code snippet for reproduce bugs.
## Your Environment
* Plugin version:
* IDE & Operating System version, comment your env as below(go to "About IntelliJ IDEA" -> click the "copy" icon):
* NOTE: If you are going to report a bug but WITHOUT your env information, your issue might be closed directly.
```
IntelliJ IDEA 2021.2 (Ultimate Edition)
Build #IU-212.4746.92, built on July 27, 2021
Licensed to IntelliJ Rainbow Brackets / Zhang Zhihao
Subscription is active until February 3, 2022.
For non-commercial open source development only.
Runtime version: 11.0.11+9-b1504.13 x86_64
VM: OpenJDK 64-Bit Server VM by JetBrains s.r.o.
macOS 11.4
GC: G1 Young Generation, G1 Old Generation
Memory: 4096M
Cores: 16
Registry: ide.tooltip.initialDelay=900, ide.balloon.shadow.size=0, scala.erase.compiler.process.jdk.once=false
Non-Bundled Plugins: com.intellij.properties.bundle.editor (212.4746.57), com.markskelton.one-dark-theme (5.3.0), lermitage.intellij.extra.icons (1.59.0.203), org.nik.presentation-assistant (1.0.9), kotest-plugin-intellij (1.1.36-IC-2021.1), Pythonid (212.4746.96), org.intellij.scala (2021.2.15), izhangzhihao.rainbow.brackets (6.19), com.intellij.bigdatatools (212.4037.55)
Kotlin: 212-1.5.10-release-IJ4746.92
```

View File

@ -1,24 +0,0 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: feature request
assignees: ''
---
**Please [support us](https://github.com/izhangzhihao/intellij-rainbow-brackets#support-us) before creating a feature request, thanks for your support 😁**
## Please check
- [ ] I already support this project.
- [ ] If you are submitting a feature request, please do consider donating us on [Open Collective](https://opencollective.com/intellij-rainbow-brackets) Or by AliPay/WeChatPay.
- [ ] This issue/feature request is not reported before.
## Your programming languages
## Expected Behavior
* tell us how it will works
## Current Behavior
* explain the difference from current behavior

View File

@ -1,13 +0,0 @@
collective: intellij-rainbow-brackets
tiers:
- tiers: "*"
labels: ["backer"]
message: "Hey <author> , thanks for supporting us on Open Collective :heart::heart::heart: We'll give a special attention to this issue!"
invitation: |
Hey <author> :wave:,
Thank you for opening an issue/feature request. We will get back to you as soon as we can.
Also, check out our <link> and consider backing us - every little helps!
PS.: We offer `priority` support for all backers. Don't forget to
add `priority` label when you start backing us :smile:

View File

@ -1,75 +0,0 @@
name: Build
on:
push:
branches:
- '2020.3'
pull_request:
jobs:
test:
name: Test
runs-on: ubuntu-latest
steps:
# Setup Java 11 environment for the next steps
- name: Setup Java
uses: actions/setup-java@v2
with:
distribution: 'adopt'
java-version: 11
# Check out current repository
- name: Fetch Sources
uses: actions/checkout@v2.3.4
# Cache Gradle dependencies
- name: Setup Gradle Dependencies Cache
uses: actions/cache@v2.1.6
with:
path: ~/.gradle/caches
key: ${{ runner.os }}-gradle-caches-${{ hashFiles('**/*.gradle', '**/*.gradle.kts') }}
# Cache Gradle Wrapper
- name: Setup Gradle Wrapper Cache
uses: actions/cache@v2.1.6
with:
path: ~/.gradle/wrapper
key: ${{ runner.os }}-gradle-wrapper-${{ hashFiles('**/gradle/wrapper/gradle-wrapper.properties') }}
# Run detekt, ktlint and tests
- name: Run Linters and Test
run: ./gradlew check
# Run verifyPlugin Gradle task
- name: Verify Plugin
run: ./gradlew verifyPlugin
# Set environment variables
- name: Export Properties
id: properties
shell: bash
run: |
PROPERTIES="$(./gradlew properties --console=plain -q)"
IDE_VERSIONS="$(echo "$PROPERTIES" | grep "^pluginVerifierIdeVersions:" | base64)"
VERSION="$(echo "$PROPERTIES" | grep "^version:" | cut -f2- -d ' ')"
NAME="intellij-rainbow-brackets"
ARTIFACT="${NAME}-${VERSION}.zip"
echo "::set-output name=ideVersions::$IDE_VERSIONS"
echo "::set-output name=pluginVerifierHomeDir::~/.pluginVerifier"
echo "::set-output name=artifact::$ARTIFACT"
# Cache Plugin Verifier IDEs
- name: Setup Plugin Verifier IDEs Cache
uses: actions/cache@v2.1.6
with:
path: ${{ steps.properties.outputs.pluginVerifierHomeDir }}/ides
key: ${{ runner.os }}-plugin-verifier-${{ steps.properties.outputs.ideVersions }}
# Run IntelliJ Plugin Verifier action using GitHub Action
- name: Verify Plugin
run: ./gradlew runPluginVerifier -Pplugin.verifier.home.dir=${{ steps.properties.outputs.pluginVerifierHomeDir }}
- name: Upload artifact
uses: actions/upload-artifact@v2.2.3
with:
name: UnZipMe
path: ./build/distributions/${{ steps.properties.outputs.artifact }}

View File

@ -1,27 +0,0 @@
name: "Label and close wontfix issues"
on:
issues:
types: [opened]
jobs:
Label:
runs-on: ubuntu-latest
steps:
- uses: Naturalclar/issue-action@v2.0.2
with:
title-or-body: "both"
parameters: '[ {"keywords": ["RainbowHighlightVisitor.kt:35", "RainbowHighlightVisitor.kt:68", "RainbowHighlightVisitor.analyze"], "labels": ["invalid"]} ]'
github-token: "${{ secrets.GITHUB_TOKEN }}"
Close:
needs: [Label]
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v3.0.14
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
days-before-stale: 0
days-before-close: 0
stale-issue-message: "This auto generated issue has been automatically marked as wontfix because nothing it's wrong here. If you think there are something really wrong, please reply this issue. Thanks for your cooperation."
stale-issue-label: "wontfix"
only-labels: "invalid"

View File

@ -1,46 +0,0 @@
# GitHub Actions Workflow created for handling the release process based on the draft release prepared
# with the Build workflow. Running the publishPlugin task requires the PUBLISH_TOKEN secret provided.
name: Release
on:
release:
types: [prereleased, released]
jobs:
# Prepare and publish the plugin to the Marketplace repository
release:
name: Publish Plugin
runs-on: ubuntu-latest
steps:
# Setup Java 11 environment for the next steps
- name: Setup Java
uses: actions/setup-java@v2
with:
distribution: "adopt"
java-version: 11
# Check out current repository
- name: Fetch Sources
uses: actions/checkout@v2.3.4
with:
ref: ${{ github.event.release.tag_name }}
# Cache Gradle dependencies
- name: Setup Gradle Dependencies Cache
uses: actions/cache@v2.1.6
with:
path: ~/.gradle/caches
key: ${{ runner.os }}-gradle-caches-${{ hashFiles('**/*.gradle', '**/*.gradle.kts') }}
# Cache Gradle Wrapper
- name: Setup Gradle Wrapper Cache
uses: actions/cache@v2.1.6
with:
path: ~/.gradle/wrapper
key: ${{ runner.os }}-gradle-wrapper-${{ hashFiles('**/gradle/wrapper/gradle-wrapper.properties') }}
# Publish the plugin to the Marketplace
- name: Publish Plugin
env:
token: ${{ secrets.PUBLISH_TOKEN }}
run: ./gradlew publishPlugin

View File

@ -1,19 +0,0 @@
name: 'Stale handler'
on:
workflow_dispatch:
schedule:
- cron: '0 0 * * *'
jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@main
id: stale
with:
stale-issue-message: 'This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days'
days-before-stale: 30
days-before-close: 5
only-labels: 'PR welcome'
- name: Print outputs
run: echo ${{ join(steps.stale.outputs.*, ',') }}

13
.gitignore vendored
View File

@ -1,10 +1,5 @@
/.idea/*
!/.idea/icon.png
!/.idea/icon_dark.png
!/.idea/vcs.xml
.sandbox/
.gradle/
build/
*.iml
.DS_Store
out
!/.idea/runConfigurations
/.gradle/
/build/

5
.gitpod.Dockerfile vendored
View File

@ -1,5 +0,0 @@
FROM gitpod/workspace-full
USER gitpod
RUN brew install kotlin

View File

@ -1,4 +0,0 @@
image:
file: .gitpod.Dockerfile
tasks:
- init: gradle compileKotlin

View File

@ -0,0 +1,24 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Run CLion" type="GradleRunConfiguration" factoryName="Gradle">
<ExternalSystemSettings>
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" value="" />
<option name="taskDescriptions">
<list />
</option>
<option name="taskNames">
<list>
<option value=":clion:runIde" />
</list>
</option>
<option name="vmOptions" />
</ExternalSystemSettings>
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<DebugAllEnabled>false</DebugAllEnabled>
<RunAsTest>false</RunAsTest>
<method v="2" />
</configuration>
</component>

View File

@ -0,0 +1,24 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Run IDEA" type="GradleRunConfiguration" factoryName="Gradle">
<ExternalSystemSettings>
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" value="" />
<option name="taskDescriptions">
<list />
</option>
<option name="taskNames">
<list>
<option value=":runIde" />
</list>
</option>
<option name="vmOptions" />
</ExternalSystemSettings>
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<DebugAllEnabled>false</DebugAllEnabled>
<RunAsTest>false</RunAsTest>
<method v="2" />
</configuration>
</component>

View File

@ -0,0 +1,24 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Run Rider" type="GradleRunConfiguration" factoryName="Gradle">
<ExternalSystemSettings>
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" value="" />
<option name="taskDescriptions">
<list />
</option>
<option name="taskNames">
<list>
<option value=":rider:runIde" />
</list>
</option>
<option name="vmOptions" />
</ExternalSystemSettings>
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<DebugAllEnabled>false</DebugAllEnabled>
<RunAsTest>false</RunAsTest>
<method v="2" />
</configuration>
</component>

View File

@ -1 +0,0 @@
11

View File

@ -1,15 +0,0 @@
pull_request_rules:
- name: Automatic merge on approval
conditions:
- "#approved-reviews-by>=1"
- status-success=Test
actions:
merge:
method: merge
- name: automatically merge dependabot's PRs
conditions:
- author~=^dependabot(|-preview)\[bot\]$
- status-success=Test
actions:
merge:
method: merge

View File

@ -1,742 +0,0 @@
## Change log
<p>2022.3.1</p>
<ul>
<li>Fix <a href="https://github.com/izhangzhihao/intellij-rainbow-brackets/issues/2529">#2529: [paid][C#] fix:
compatibility issues with Rider 2022.3.</a></li>
<li>Fix <a href="https://github.com/izhangzhihao/intellij-rainbow-brackets/issues/2533">#2533: [paid][C#] fix:
colours incorrect when using "Cycle count on all brackets".</a></li>
<li>Fix <a href="https://github.com/izhangzhihao/intellij-rainbow-brackets/issues/2354">#2354: [paid][C#] fix:
colours incorrect when using "Cycle count on all brackets".</a></li>
<li>Fix many compatibility issues with IntelliJ platform 2022.3.</li>
</ul>
<br />
<p>6.26</p>
<ul>
<li>Bump dependencies.</li>
</ul>
<br />
<p>6.25</p>
<ul>
<li><a href="https://github.com/izhangzhihao/intellij-rainbow-brackets/pull/2484">#2484 Add configurable threshold
for number of lines for big files.</a></li>
</ul>
<br />
<p>6.24</p>
<ul>
<li><a href="https://github.com/izhangzhihao/intellij-rainbow-brackets/issues/2478">#2478 Scope highlighting not
working with Hiberbee Theme(and other themes).</a></li>
</ul>
<br />
<p>6.23</p>
<ul>
<li><a href="https://github.com/izhangzhihao/intellij-rainbow-brackets/issues/2465">#2465 Improved performance of
`annotateUtil`.</a></li>
</ul>
<br />
<p>6.22</p>
<ul>
<li><a href="https://github.com/izhangzhihao/intellij-rainbow-brackets/issues/2408">#2408 Support Code With Me
client(Doesn't even need to be installed on the client side)</a></li>
</ul>
<br />
<p>6.21</p>
<ul>
<li>Handle exceptions in `RainbowHighlightVisitor.analyze()`</a></li>
<li>Cleanup 201.* stuff</a></li>
<li>Refactoring</a></li>
<li>Mirror changes from https://github.com/JetBrains/intellij-community</a></li>
<li>Using textMatches instead of text to avoid expensive traverses the whole PSI tree & format code</a></li>
<li>Add support for rainbowify Python keywords</li>
<li>Compatible with DataSpell</li>
</ul>
<br />
<p>6.20</p>
<ul>
<li>Handle exceptions in `RainbowHighlightVisitor.analyze()`</a></li>
<li>Cleanup 201.* stuff</a></li>
<li>Refactoring</a></li>
<li>Mirror changes from https://github.com/JetBrains/intellij-community</a></li>
<li>Using textMatches instead of text to avoid expensive traverses the whole PSI tree & format code</a></li>
<li>Add support for rainbowify Python keywords</li>
<li>Compatible with DataSpell</li>
</ul>
<br />
<p>6.19</p>
<ul>
<li>Fix <a href="https://github.com/izhangzhihao/intellij-rainbow-brackets/issues/2280">#2280: [SQL] the END keyword
is improperly detected as closing a BEGIN scope when the END actually closes a CASE.</a></li>
<li>Fix <a href="https://github.com/izhangzhihao/intellij-rainbow-brackets/issues/1993">#1993: Change the default
color schema in light theme.</a></li>
<li>Nashorn Engine removed, compatible with JDK15+, tested with OpenJDK 17-ea+24 on MacBook Pro (16-inch, 2019) &
IntelliJ IDEA 2021.2 Build #IU-212.4638.7</li>
<li>Compatible with HUAWEI DevEco Studio</li>
</ul>
<br />
<p>6.18</p>
<ul>
<li>Fix <a href="https://github.com/izhangzhihao/intellij-rainbow-brackets/issues/2037">#2037: Wrong coloring in
generic `<` after comparison operator `<`.</a>
</li>
<li>Error report removed.</li>
</ul>
<br />
<p>6.17</p>
<ul>
<li>Feature <a href="https://github.com/izhangzhihao/intellij-rainbow-brackets/pull/1068">#1068: @keeganwitt Add
language exclusions to settings (closes #1046) (#1068) </a></li>
<li>Fix <a href="https://github.com/izhangzhihao/intellij-rainbow-brackets/issues/1067">#1067: Don't rainbowify big
files notification annoying</a></li>
<li>Fix <a href="https://github.com/izhangzhihao/intellij-rainbow-brackets/issues/1057">#1057: Wrong coloring in
lambda expressions with braces</a></li>
<li>Fix <a href="https://github.com/izhangzhihao/intellij-rainbow-brackets/issues/497">#497: Add `<` & `>` support
for C# </a></li>
<li>Fix <a href="https://github.com/izhangzhihao/intellij-rainbow-brackets/issues/191">#191: C# Razor Pages
(.cshtml) Support </a></li>
<li>Deprecated API usage removed.</li>
</ul>
<br />
<p>6.16</p>
<ul>
<li>Feature <a href="https://github.com/izhangzhihao/intellij-rainbow-brackets/issues/458">#458: Color Parentheses
In Go Template</a></li>
<li>Fix <a href="https://github.com/izhangzhihao/intellij-rainbow-brackets/issues/988">#988: (@Grandmother) minor:
fix mispell in notification message</a></li>
<li>Fix <a href="https://github.com/izhangzhihao/intellij-rainbow-brackets/issues/973">#973: File text mismatch</a>
</li>
<li>Fix <a href="https://github.com/izhangzhihao/intellij-rainbow-brackets/issues/497">#497: C# in Rider - only
squiggly brackets are rainbowified </a></li>
</ul>
<br />
<p>6.15</p>
<ul>
<li>Feature <a href="https://github.com/izhangzhihao/intellij-rainbow-brackets/issues/897">#897: Initial support for
Pug/Jade Language</a></li>
<li>Feature <a href="https://github.com/izhangzhihao/intellij-rainbow-brackets/issues/830">#830: New option "Do NOT
rainbowify template string"</a></li>
<li>Feature <a href="https://github.com/izhangzhihao/intellij-rainbow-brackets/issues/784">#784: Disable rainbowify
on big files(>1000 lines for now)</a></li>
<li>Fix <a href="https://github.com/izhangzhihao/intellij-rainbow-brackets/issues/851">#851: Rainbowify tag name
doesn't works in JSX</a></li>
<li>Fix <a href="https://github.com/izhangzhihao/intellij-rainbow-brackets/issues/875">#875: cannot create
configurable component</a></li>
<li>Fix <a href="https://github.com/izhangzhihao/intellij-rainbow-brackets/issues/799">#799 #817: Wrong element
created by ASTFactory</a></li>
</ul>
<br />
<p>6.13</p>
<ul>
<li>Make as a non-dynamic plugin, so it now requires restart.</li>
</ul>
<br />
<p>6.12.1</p>
<ul>
<li>Fix <a href="https://github.com/izhangzhihao/intellij-rainbow-brackets/issues/516">#516: Add option to raibowify
tag name of XML/HTML(disabled by default)</a></li>
<li>Fix <a href="https://github.com/izhangzhihao/intellij-rainbow-brackets/issues/279">#279: Hide update
notification on any click & Disable update notification support</a></li>
<li>Feature: Rainbowify XML/HTML tag name.</li>
<li>Feature: Upgrade to kotlin 1.4.10.</li>
</ul>
<br />
<p>6.12</p>
<ul>
<li>Fix <a href="https://github.com/izhangzhihao/intellij-rainbow-brackets/issues/279">#279: Hide update
notification on any click & Disable update notification support</a></li>
<li>Feature: Rainbowify XML/HTML tag name.</li>
<li>Feature: Upgrade to kotlin 1.4.10.</li>
</ul>
<br />
<p>6.11</p>
<ul>
<li>Fix <a href="https://github.com/izhangzhihao/intellij-rainbow-brackets/issues/481">#481: rainbow-brackets does
not support bash shell properly(for BashSupport Pro and Shell Script)</a></li>
<li>Fix <a href="https://github.com/izhangzhihao/intellij-rainbow-brackets/issues/485">#485: Incorrect coloriziong
after lambda in C#</a></li>
<li>Fix <a href="https://github.com/izhangzhihao/intellij-rainbow-brackets/issues/242">#242: Colorization is applied
only to left parenthesis in for loop(C#) </a></li>
<li>Feature: C# support switch to new implementation.</li>
<li>Fix <a href="https://github.com/izhangzhihao/intellij-rainbow-brackets/issues/476">#476: Cannot create class
com.github.izhangzhihao.rainbow.brackets.provider.SqlProvider</a></li>
</ul>
<br />
<p>6.10</p>
<ul>
<li>Fix <a href="https://github.com/izhangzhihao/intellij-rainbow-brackets/issues/480">#480: Plugin Incompatibility:
Android Studio 4.2 Alpha 8</a></li>
</ul>
<br />
<p>6.9.1</p>
<ul>
<li>Fix <a href="https://github.com/izhangzhihao/intellij-rainbow-brackets/issues/445">#445: No working on Android
Studio canary 4.2</a></li>
<li>Some bug fixs and code refactorings</li>
</ul>
<br />
<p>6.7</p>
<ul>
<li>Fix NPE</li>
</ul>
<br />
<p>6.6</p>
<ul>
<li>Fix <a href="https://github.com/izhangzhihao/intellij-rainbow-brackets/issues/436">#436: Duplicated indent
guides in 2020.2 EAP</a></li>
</ul>
<br />
<p>6.5</p>
<ul>
<li>Fix <a href="https://github.com/izhangzhihao/intellij-rainbow-brackets/issues/410">#410:
ArrayIndexOutOfBoundsException</a></li>
<li>Fix <a href="https://github.com/izhangzhihao/intellij-rainbow-brackets/issues/429">#429:
NullPointerException</a></li>
<li>Feature <a href="https://github.com/izhangzhihao/intellij-rainbow-brackets/issues/427">#427: Colorizing angle
brackets for Typescript generics</a></li>
<li>Some refactoring!</li>
</ul>
<br />
<p>6.4</p>
<ul>
<li>Fix <a href="https://github.com/izhangzhihao/intellij-rainbow-brackets/issues/420">#420: Exceptions in random
color generator</a></li>
<li>Fix <a href="https://github.com/izhangzhihao/intellij-rainbow-brackets/issues/423">#423: Don't rainbow php and
echo tag</a></li>
<li>Fix anonymous feedback</li>
<li>Some refactoring!</li>
</ul>
<br />
<p>6.3</p>
<ul>
<li>Feature <a href="https://github.com/izhangzhihao/intellij-rainbow-brackets/issues/417">#417: Disable Rainbow
Indents in Zen mode</a></li>
<li>Feature <a href="https://github.com/izhangzhihao/intellij-rainbow-brackets/issues/227">#227: Coloring for angle
bracket for C++ code</a></li>
<li>Fix some exceptions and refactoring!</li>
<li>Feature <a href="https://github.com/izhangzhihao/intellij-rainbow-brackets/issues/214">#214: New color generator
to generate your color schema</a></li>
</ul>
<br />
<p>6.2</p>
<ul>
<li>Fix <a href="https://github.com/izhangzhihao/intellij-rainbow-brackets/issues/410">#410:
ArrayIndexOutOfBoundsException when number of colors to less than 5</a></li>
</ul>
<br />
<p>6.1</p>
<ul>
<li>First release of 2020.1 and Java 11!</li>
<li>Feature <a href="https://github.com/izhangzhihao/intellij-rainbow-brackets/issues/235">#235: support SQL begin
end colorization</a></li>
<li>Notification improved</li>
<li>Error report improved</li>
<li>Deprecated API usage removed</li>
<li>Initial support for <a
href="https://www.jetbrains.org/intellij/sdk/docs/basics/plugin_structure/dynamic_plugins.html">dynamic
plugin</a></li>
</ul>
<br />
<p>6.0</p>
<ul>
<li>First release of 2020.1 and Java 11!</li>
<li>Feature <a href="https://github.com/izhangzhihao/intellij-rainbow-brackets/issues/235">#235: support SQL begin
end colorization</a></li>
<li>Notification improved</li>
<li>Error report improved</li>
<li>Deprecated API usage removed</li>
<li>Initial support for <a
href="https://www.jetbrains.org/intellij/sdk/docs/basics/plugin_structure/dynamic_plugins.html">dynamic
plugin</a></li>
</ul>
<br />
<p>5.35</p>
<ul>
<li>Final release of 2017.2 and Java 8! start from the next release, we will build against with 2020.1 and Java 11
</li>
</ul>
<br />
<p>5.34</p>
<ul>
<li>Compatible with Material Theme UI Plugin</li>
<li>Fix typo</li>
</ul>
<br />
<p>5.33</p>
<ul>
<li>Fix ArrayIndexOutOfBoundsException</li>
</ul>
<br />
<p>5.32</p>
<ul>
<li>Feature #391: Support cycle color on all bracket types(new option `Cycle count on all bracket types`)</li>
<li>Fix ArrayIndexOutOfBoundsException</li>
</ul>
<br />
<p>5.31</p>
<ul>
<li>#187 Feature: Ability to increase the number of colours in the IDE</li>
<li>#247 Feature: Add a button to apply the color code to all kind of brackets</li>
<li>#374 Feature: Add support for IntelliJ-Haskell</li>
<li>Fix #54: Disable rainbow for mxml files</li>
<li>Fix typo</li>
</ul>
<br />
<p>5.30</p>
<ul>
<li>Rollback indent guides</li>
</ul>
<br />
<p>5.29</p>
<ul>
<li>Compatible color schema with the latest version of material-theme-jetbrains</li>
<li>Fix some errors</li>
</ul>
<br />
<p>5.28</p>
<ul>
<li>Improve error report</li>
<li>Fix some errors with Nginx plugin</li>
</ul>
<br />
<p>5.27</p>
<ul>
<li>Improve rainbow indent guide lines</li>
</ul>
<br />
<p>5.26</p>
<ul>
<li>Improve error report.</li>
</ul>
<br />
<p>5.25</p>
<ul>
<li>Fix #259 Runtime error in rainbow indent guide lines.</li>
</ul>
<br />
<p>5.24</p>
<ul>
<li>Fix #252 Runtime error in rainbow indent guide lines.</li>
<li>Fix #254 Nginx support is been disabled from now on.</li>
<li>Re-enable anonymous feedback</li>
</ul>
<br />
<p>5.23</p>
<ul>
<li>#164 #251 New feature: Rainbow indent guide lines(experimental), Thanks https://github.com/YiiGuxing !</li>
</ul>
<br />
<p>5.22</p>
<ul>
<li>Fix #243 #180, allow users custom matched brace by setting `overrideMatchedBraceAttributes = false`</li>
</ul>
<br />
<p>5.21</p>
<ul>
<li>#65 [Scope Highlighting] now the effects will not been removed after shortcut released, users could press the
key `ESC` to do this. There also have an option `Press any key to remove the highlighting effects`</li>
<li>Refactoring & Remove dead code</li>
</ul>
<br />
<p>5.20</p>
<ul>
<li>#233 Option to not rainbowify brackets of the first level</li>
<li>#234 Color is displayed with wrong order in C# code</li>
</ul>
<br />
<p>5.19</p>
<ul>
<li>Fix notification.</li>
</ul>
<br />
<p>5.18</p>
<ul>
<li>Add notification for custom your own rainbow colors.</li>
</ul>
<br />
<p>5.17</p>
<ul>
<li>More color options for squiggly brackets before cycle(#215).</li>
<li>NullPointerException in while analyse code(#216).</li>
</ul>
<br />
<p>5.16</p>
<ul>
<li>Add shiny new icon.</li>
</ul>
<br />
<p>5.15</p>
<ul>
<li>Fix support for kotlin scheme attribute "KOTLIN_FUNCTION_LITERAL_BRACES_AND_ARROW".</li>
</ul>
<br />
<p>5.14</p>
<ul>
<li>Remove deprecated API usages.</li>
<li>Refactoring.</li>
<li>Added Dart to the supported languages list(#205).</li>
</ul>
<br />
<p>5.13</p>
<ul>
<li>Fix macro support of Clang(#198)</li>
<li>Remove red-variation colors from default configuration(#192)</li>
</ul>
<br />
<p>5.12</p>
<ul>
<li>Intellij-rainbow-brackets now support C# language(#6)!</li>
</ul>
<br />
<p>5.11</p>
<ul>
<li>Now you could disable rainbow brackets for specific languages, see more info <a
href="https://github.com/izhangzhihao/intellij-rainbow-brackets#disable-rainbow-brackets-for-specific-languages">here</a>.
</li>
</ul>
<br />
<p>5.10</p>
<ul>
<li>New color settings page!!! Thanks this PR(#179) from https://github.com/YiiGuxing.</li>
<li>See the new settings page in Settings/Preferences > Editor > Color Scheme > Rainbow Brackets.</li>
</ul>
<br />
<p>5.9.1</p>
<ul>
<li>Fix wrong background color on a light theme of "MATCHED_BRACE_ATTRIBUTES"(#155).</li>
</ul>
<br />
<p>5.9</p>
<ul>
<li>Rainbow Kotlin lambda expression arrow(#142).</li>
<li>Experimental feature: Highlight Kotlin label(#143).</li>
<li>Override "MATCHED_BRACE_ATTRIBUTES".</li>
<li>Improve configs & docs.</li>
<li>Cleanup temp code & deprecated code.</li>
<li>Remove anonymous feedback.</li>
</ul>
<br />
<p>5.8.3</p>
<ul>
<li>Improve anonymous feedback</li>
</ul>
<br />
<p>5.8.2</p>
<ul>
<li>Override kotlin plugin setting `KOTLIN_FUNCTION_LITERAL_BRACES_AND_ARROW` to empty so that we could
rainbowify multiple level lambda expressions.
</li>
</ul>
<br />
<p>5.8.1</p>
<ul>
<li>Fix #67: Can't find resource for bundle java.util.PropertyResourceBundle, key version</li>
<li>Improve anonymous feedback</li>
</ul>
<br />
<p>5.8</p>
<ul>
<li>Feature #52 Flat out all text other than brackets on key (Alt + Button3) press. (via pull request#63)
</li>
<li>Feature #61 Change Highlight Current Scope Keymap to Ctrl + Button3 (Windows & Linux) or Meta+ Button3
(Mac) (via pull request#63)
</li>
<li>Add anonymous feedback support</li>
</ul>
<br />
<p>5.7.1</p>
<ul>
<li>Fix #60 :Exception in v5.7</li>
<li>Experimental feature: Highlight current scope when Ctrl(Windows & Linux)/Meta(Mac) key pressed (feature
#37 / pull request#59)
</li>
</ul>
<br />
<p>5.7</p>
<ul>
<li>Experimental feature: Highlight current scope when Ctrl(Windows & Linux)/Meta(Mac) key pressed (feature
#37 / pull request#59)
</li>
</ul>
<br />
<p>5.6</p>
<ul>
<li>Performance improvement</li>
</ul>
<br />
<p>5.5</p>
<ul>
<li>Fix #53 The closing brackets or keywords are not highlighted (Ruby & PHP)</li>
</ul>
<br />
<p>5.4</p>
<ul>
<li>Fix #53 The closing brackets or keywords are not highlighted (Ruby & PHP)</li>
</ul>
<br />
<p>5.3</p>
<ul>
<li>Improve angle bracket support for Groovy</li>
</ul>
<br />
<p>5.2</p>
<ul>
<li>#48 Performance improvement</li>
<li>#49 Fix images size</li>
</ul>
<br />
<p>5.1</p>
<ul>
<li>#39 Enable rainbow html in js</li>
</ul>
<br />
<p>5.0</p>
<ul>
<li>Finally, intellij-rainbow-brackets released version 5.0 with all RC features & bug fix</li>
<li><b>Thanks for https://github.com/YiiGuxing, which helps move this plugin from `Annotator` to
`HighlightVisitor`!</b></li>
<li>Check more info at <a href="https://github.com/izhangzhihao/intellij-rainbow-brackets/pull/25">here</a>
</li>
<li>From 5.x series we didn't need specific implementations like java/scala/kotlin specific implementations
in 3.x series anymore!
</li>
<br />
<li>#13 Add test for dart support & add `DartAngleBracketProvider` for support dart angle brackets</li>
<li>#18 where to customize brackets color? See the config guide in <a
href="https://github.com/izhangzhihao/intellij-rainbow-brackets#Config-brackets-colors">here</a>
</li>
<li>Add test for #39</li>
<li>#38 Add support for JSX (React)</li>
<li>Fix #27 Settings no longer works</li>
<li>#30 Adjust color: remove red, purple from color palettes, add some material design color to color
palettes.
</li>
<li>#32 Add version info in setting page</li>
<li>#31 Fix 'Enablement of round brackets enables all but angle brackets'</li>
<li>#10 #2 Add setting to disable rainbow-ify brackets without content</li>
<li>Show update notification after plugin updated</li>
<li>Add a lot of tests</li>
<li>Convert all java code to kotlin</li>
<br />
<li>And with much more features not documented in release notes.</li>
<li>NOTE: this version are build against with IU-2017.2.7, but verified by IC-2017.2</li>
</ul>
<br />
<p>5.0-RC4</p>
<ul>
<li>#10 #2 Add setting to disable rainbow-ify brackets without content</li>
<li>Show update notification after plugin updated</li>
<li>Add a lot of tests</li>
<li>Convert all java code to kotlin</li>
<li>NOTE: this version are build against with IU-2017.2.7, but verified by IC-2017.2</li>
</ul>
<br />
<p>5.0-RC3</p>
<ul>
<li>#32 Add version info in setting page</li>
<li>#31 Fix 'Enablement of round brackets enables all but angle brackets(#31)'</li>
</ul>
<br />
<p>5.0-RC2</p>
<ul>
<li>#30 Adjust color: remove red, purple from color palettes, add some material design color to color
palettes.
</li>
</ul>
<br />
<p>5.0-RC1</p>
<ul>
<li>Fix #27 Settings no longer works</li>
</ul>
<br />
<p>5.0-RC0</p>
<ul>
<li>This is the first RC releases on 5.x series!</li>
<li><b>Thanks for https://github.com/YiiGuxing, which helps move this plugin from `Annotator` to
`HighlightVisitor`!</b></li>
<li>Check more info at https://github.com/izhangzhihao/intellij-rainbow-brackets/pull/25</li>
<li>This RC release has fantastic compatibility with previous release(3.x series).</li>
<li>From 5.x series we didn't need specific implementations like java/scala/kotlin specific implementations
in 3.x series anymore!
</li>
<li>And with much more features not documented in release notes.</li>
</ul>
<br />
<p>3.1</p>
<ul>
<li>Add a specific implementation for PHP language</li>
</ul>
<br />
<p>3.0</p>
<ul>
<li>Version 3.0 has been released, with all RC features & bug fix</li>
<li>Fix #23 Inconsistent colors</li>
<li>Fix #21 Wrong bracket colorization based on spaces</li>
<li>Fix #19 Kotlin expression inside string bug</li>
<li>Fix #12 Symbol less ">" is considered as a bracket even without leading "<"< /li>
<li>Fix #11 Same level brackets should have same color</li>
<li>And much more!</li>
<li>Add specific implement for java/kotlin/scala/javascript</li>
<li>Add example to help people add specific implementation for specific language!</li>
<li>Check out README.md on github https://github.com/izhangzhihao/intellij-rainbow-brackets</li>
</ul>
<br />
<p>3.0-RC5</p>
<ul>
<li>Adjust colors for default light theme. Thanks to https://github.com/YiiGuxing</li>
</ul>
<br />
<p>3.0-RC4</p>
<ul>
<li>Add specific implement for java/kotlin/scala</li>
<li>So now in java/kotlin/scala same level brackets should have same color.</li>
<li>Fix: #19:Kotlin expression inside string bug</li>
</ul>
<br />
<p>3.0-RC2</p>
<ul>
<li>Remove option for enable/disable rainbow for HTML/XML</li>
</ul>
<br />
<p>3.0-RC1</p>
<ul>
<li>Add new setting page to control what/how to colorify:</li>
<li>1. Add option to Enable/disable rainbow</li>
<li>2. Add option to Enable rainbow for any unsupported languages</li>
<li>3. Add option to Enable/disable rainbow for HTML/XML</li>
<li>4. Add option to Enable/disable rainbow for round brackets</li>
<li>5. Add option to Enable/disable rainbow for squiggly brackets</li>
<li>6. Add option to Enable/disable rainbow for square brackets</li>
<li>7. Add option to Enable/disable rainbow for angle brackets</li>
</ul>
<br />
<p>2.6</p>
<ul>
<li>Add support for salesforce apex language, thanks for https://github.com/onisuly</li>
</ul>
<br />
<p>2.5</p>
<ul>
<li>Fix Rust support, thanks for https://github.com/fst3a</li>
</ul>
<br />
<p>2.4</p>
<ul>
<li>Add support for SQL</li>
</ul>
<br />
<p>2.3</p>
<ul>
<li>Add support for HTML/XML</li>
</ul>
<br />
<p>2.2</p>
<ul>
<li>Add support for C#</li>
</ul>
<br />
<p>2.1</p>
<ul>
<li>New identifiable colors</li>
</ul>
<br />
<p>2.0</p>
<ul>
<li>Rainbowify brackets faster!</li>
</ul>
<br />
<p>1.1</p>
<ul>
<li>Support IntelliJ IDEA based IDEs version 14 and above</li>
</ul>
<br />
<p>1.0</p>
<ul>
<li>Initial release</li>
</ul>

299
README.md
View File

@ -1,297 +1,8 @@
# Colored Brackets
<div align="center">
<a href="https://plugins.jetbrains.com/plugin/10080-rainbow-brackets">
<img src="./src/main/resources/META-INF/pluginIcon.svg" width="320" height="320" alt="logo"/>
</a>
</div>
<h1 align="center">Intellij rainbow brackets</h1>
<p align="center">🌈Rainbow Brackets for IntelliJ based IDEs/Android Studio/HUAWEI DevEco Studio.</p>
This is a fork of the [🌈Rainbow Brackets](https://github.com/izhangzhihao/intellij-rainbow-brackets) plugin by [izhangzhihao](https://github.com/izhangzhihao), based on version 6.26.
<p align="center">
<!-- <a href="https://actions-badge.atrox.dev/izhangzhihao/intellij-rainbow-brackets/goto?ref=2020.3"><img alt="Build Status" src="https://img.shields.io/endpoint.svg?url=https%3A%2F%2Factions-badge.atrox.dev%2Fizhangzhihao%2Fintellij-rainbow-brackets%2Fbadge%3Fref%3D2020.3&style=flat-square" /></a> -->
<!-- <a href="https://github.com/izhangzhihao/intellij-rainbow-brackets/releases"><img src="https://img.shields.io/github/release/izhangzhihao/intellij-rainbow-brackets.svg?style=flat-square"></a> -->
<a href="https://plugins.jetbrains.com/plugin/10080-rainbow-brackets"><img src="https://img.shields.io/jetbrains/plugin/r/stars/10080?style=flat-square"></a>
<a href="https://plugins.jetbrains.com/embeddable/install/10080"><img src="https://img.shields.io/jetbrains/plugin/d/10080-rainbow-brackets.svg?style=flat-square"></a>
<a href="https://plugins.jetbrains.com/plugin/10080-rainbow-brackets"><img src="https://img.shields.io/jetbrains/plugin/v/10080-rainbow-brackets.svg?style=flat-square"></a>
<a href="https://opencollective.com/intellij-rainbow-brackets#backer"><img src="https://img.shields.io/opencollective/backers/intellij-rainbow-brackets?style=flat-square"></a>
<a href="https://opencollective.com/intellij-rainbow-brackets#sponsor"><img src="https://img.shields.io/opencollective/sponsors/intellij-rainbow-brackets?style=flat-square"></a>
</p>
## Key Changes
<br>
- [Change log](https://github.com/izhangzhihao/intellij-rainbow-brackets/blob/2020.3/CHANGELOG.md)
- [Compatibility](#compatibility)
- [Supported languages](#supported-languages)
- [Author's choice](#authors-choice)
- [Sponsored By CodeStream](#sponsored-by-codestream)
- [Install](#install)
- [Screenshots](#screenshots)
- [Customize colors](#customize-colors)
- [Use color generator](#use-color-generator)
- [Config file path](#config-file-path)
- [HTML code in js](#html-code-in-js)
- [Kotlin function literal braces and arrow](#kotlin-function-literal-braces-and-arrow)
- [Disable rainbow brackets for specific languages](#disable-rainbow-brackets-for-specific-languages)
- [Contribute](#contribute)
- [Support Us](#support-us)
- [Backers](#backers)
- [Sponsors](#sponsors)
- [Acknowledgements](#acknowledgements)
## Compatibility
IntelliJ IDEA, PhpStorm, WebStorm, PyCharm, RubyMine, AppCode, CLion, Gogland, DataGrip, Rider, MPS, Android Studio, HUAWEI DevEco Studio, DataSpell, Code With Me
## Supported languages
Java, Scala, Clojure, Kotlin, Python, Haskell, Agda, Rust, JavaScript, TypeScript, Erlang, Go, Groovy, Ruby, Elixir, ObjectiveC, PHP, HTML, XML, SQL, Apex language, C#, Dart, Pug/Jade, Bash, Vue.js, C# Razor Pages, GLSL(the OpenGL Shading Language), Go Template, C++, C...
## Author's choice
Rainbow Brackets + One Dark Theme + Nyan Progress Bar + [Fira Code](https://github.com/tonsky/FiraCode) (Font)
## Sponsored By [CodeStream](https://sponsorlink.codestream.com/?utm_source=jbmarket&utm_campaign=jbrainbowbrackets&utm_medium=banner)
[![https://codestream.com](https://alt-images.codestream.com/codestream_logo_jbrainbowbrackets.png)](https://sponsorlink.codestream.com/?utm_source=jbmarket&utm_campaign=jbrainbowbrackets&utm_medium=banner)
Eliminate context switching and costly distractions. Create and merge PRs and perform code reviews from inside your IDE while using jump-to-definition, your keybindings, and other IDE favorites. [Learn more](https://sponsorlink.codestream.com/?utm_source=jbmarket&utm_campaign=jbrainbowbrackets&utm_medium=banner)!
Development powered by [JetBrains](https://www.jetbrains.com/?from=IntelliJRainbowBrackets).
[![https://www.jetbrains.com/?from=IntelliJRainbowBrackets](./screenshots/jetbrains.svg)](https://www.jetbrains.com/?from=IntelliJRainbowBrackets)
Whichever technologies you use, there's a JetBrains tool to match.
## Install
<a href="https://plugins.jetbrains.com/embeddable/install/10080">
<img src="https://user-images.githubusercontent.com/12044174/123105697-94066100-d46a-11eb-9832-338cdf4e0612.png" width="300"/>
</a>
Or you could install it inside your IDE:
For Windows & Linux - <kbd>File</kbd> > <kbd>Settings</kbd> > <kbd>Plugins</kbd> > <kbd>Marketplace</kbd> > <kbd>Search for "Rainbow Brackets"</kbd> > <kbd>Install Plugin</kbd> > <kbd>Restart IntelliJ IDEA</kbd>
For Mac - <kbd>IntelliJ IDEA</kbd> > <kbd>Preferences</kbd> > <kbd>Plugins</kbd> > <kbd>Marketplace</kbd> > <kbd>Search for "Rainbow Brackets"</kbd> > <kbd>Install Plugin</kbd> > <kbd>Restart IntelliJ IDEA</kbd>
### Install snapshot build
You can download the latest snapshot build from [here](https://github.com/izhangzhihao/intellij-rainbow-brackets/actions/workflows/build.yml?query=workflow%3ABuild), just click the latest build and click the 'Artifacts' tab.(You need logged in via github) After the snapshot build downloaded, unzip it(via `unzip UnZipMe.zip`), and install the zip file(intellij-rainbow-brackets.zip[]()) by `Install Plugin from Disk...`.
## Screenshots
* Java
![](./screenshots/with-java.png)
![](./screenshots/with-material-theme-ui.png)
* Scala
![](./screenshots/with-scala.png)
* Clojure
![](./screenshots/with-Clojure.png)
* Kotlin
![](./screenshots/with-kotlin.png)
* HTML/XML
![](./screenshots/with-HTML.png)
* Scope highlighting
**The highlight effects will not remove after release the shortcuts, but press `ESC` key can do this. You could also config `Press any key to remove the highlighting effect` in setting page.**
<kbd>Ctrl + Button3</kbd>(Windows & Linux) or <kbd>Meta+ Button3</kbd>(Mac):
NOTE: <kbd>Button3</kbd> means "Secondary Click (click or tap with two fingers)" on Mac os, "Right click" for Windows or Linux.
![](https://user-images.githubusercontent.com/10737066/40234968-46593fe2-5adb-11e8-8ea8-0026fad86ca9.gif)
<kbd>Alt + Button3</kbd>:
NOTE: <kbd>Button3</kbd> means "Secondary Click (click or tap with two fingers)" on Mac os, "Right click" for Windows or Linux.
![](https://user-images.githubusercontent.com/10737066/40235004-642dfe54-5adb-11e8-9fd7-648b92fab8f5.gif)
* Rainbow indent guide lines
![](https://user-images.githubusercontent.com/10737066/65765792-c41cb500-e15b-11e9-8877-2239c6afa7bf.gif)
* looking forward to your screenshots(PR welcome!)
## Customize colors
<kbd>Settings/Preferences</kbd> > <kbd>Editor</kbd> > <kbd>Color Scheme</kbd> > <kbd>Rainbow Brackets</kbd>:
![Customize colors](./screenshots/customize-colors.png)
## Use color generator
If you didn't like the build-in colors, or want some new color but don't want to try out and pick colors. Please use color generator.
<kbd>Settings/Preferences</kbd> > <kbd>Other Settings</kbd> > <kbd>Rainbow Brackets</kbd> > <kbd>Use color generator</kbd>
If you turn on this option, we will auto generate some colors for you.
### Advanced options of color generator
If you need more control, you can decide the options of color generator by edit [the config file](#config-file-path),
The value of the option `customColorGeneratorOption` is a json object, available options are:
```hue``` Controls the hue of the generated color. You can pass a string representing a color name: ```red```, ```orange```, ```yellow```, ```green```, ```blue```, ```purple```, ```pink``` and ```monochrome``` are currently supported. If you pass a hexidecimal color string such as ```#00FFFF```, color generator will extract its hue value and use that to generate colors.
```luminosity``` Controls the luminosity of the generated color. You can specify a string containing ```bright```, ```light``` or ```dark```.
Please make sure the value of the option `customColorGeneratorOption` is escaped in xml format,
you can use [this tool](https://www.freeformatter.com/xml-escape.html#ad-output) to do this. Please don't forget to restart your IDE after change the config file.
```xml
<application>
<component name="RainbowSettings">
<option name="useColorGenerator" value="true" />
<option name="customColorGeneratorOption" value="{&quot;luminosity&quot;: &quot;light&quot;,&quot;hue&quot;: &quot;blue&quot;}"/>
</component>
</application>
```
### Want to config the number of colors?
<kbd>Settings/Preferences</kbd> > <kbd>Other Settings</kbd> > <kbd>Rainbow Brackets</kbd> > `Number of colors`: 5 or more
And you can use the color generator and config you number of colors at the same time.
NOTE: For default and darcula color scheme(`Editor -> Color Scheme -> Rainbow Brackets -> Scheme`) the color number is 10, for the other scheme the number is 5, if your number is bigger than the number, you can config them in the config file.
If the color is not exist, we will use color generator to generator it for you.
Please follow [the official guide](https://www.jetbrains.com/help/idea/configuring-colors-and-fonts.html#share-color-scheme):
* `Export a color scheme as XML`
* Edit the xml file, put `ROUND_BRACKETS_RAINBOW_COLOR5` to `ROUND_BRACKETS_RAINBOW_COLOR100` to match your number just like [the default color scheme](./src/main/resources/colorSchemes/rainbow-color-default-darcula.xml).
* `Import a color scheme`
## Config file path
If you want to customize the advanced configuration, you could edit the config file then restart your IDE.
Config file path in `APP_CONFIG/rainbow_brackets.xml`.
In MAC OS env maybe like `~/Library/Preferences/IntelliJIdea2020.2/options/rainbow_brackets.xml`.
If you are using the ToolBox, then it will be like `~/Library/ApplicationSupport/JetBrains/IntelliJIdea2020.2/options/rainbow_brackets.xml`
In Linux env maybe like `~/.IntelliJIdea/config/options/rainbow_brackets.xml`.
In Windows env maybe like `C:\Users\izhangzhihao\.IntelliJIdea2020.2\config\options\rainbow_brackets.xml`.
## HTML code in js
To enable rainbow brackets for HTML inside js code like this:
```javascript
var html = '<div><div><div>Hello</div></div></div>';
```
This plugin will automatically override color scheme property "HTML_CODE" [cause our rainbow color been covered by intellij built-in functionality](https://intellij-support.jetbrains.com/hc/en-us/community/posts/360000117450-My-HighlightVisitor-been-covered-by-intellij-built-in-functionality).
You still could set `<option name="rainbowifyHTMLInsideJS" value="false" />` in config file to disable.
## Kotlin function literal braces and arrow
To enable rainbow brackets for multiple level lambda Kotlin code like this:
```kotlin
event.throwable?.let { throwable ->
IdeErrorsDialog.findPluginId(throwable)?.let { pluginId ->
PluginManager.getPlugin(pluginId)?.let { ideaPluginDescriptor ->
if (!ideaPluginDescriptor.isBundled) {
bean.pluginName = ideaPluginDescriptor.name
bean.pluginVersion = ideaPluginDescriptor.version
}
}
}
}
```
This plugin will automatically override color scheme property "KOTLIN_FUNCTION_LITERAL_BRACES_AND_ARROW" cause our rainbow color is being covered by kotlin plugin built-in functionality.
You still could set `<option name="rainbowifyKotlinFunctionLiteralBracesAndArrow" value="false" />` in config file to disable.
## Disable rainbow brackets for specific languages
<kbd>Settings/Preferences</kbd> > <kbd>Other Settings</kbd> > <kbd>Rainbow Brackets</kbd> > `Do NOT rainbowify these languages (name or extension, comma separated)`:
NOTE: You can use **name** of language or **extension** of file name(The names should be **lowercase**).
## Contribute
NOTE: To view the PSI tree and explore the internal PSI structure of source code, you need to set up your IDE by following [this](https://www.jetbrains.com/help/idea/psi-viewer.html).
For the Rider IDE, please follow: [this](https://rider-support.jetbrains.com/hc/en-us/articles/207327910-How-to-run-JetBrains-Rider-in-Internal-Mode)
* `gradle test`
* `gradle runIde`
* `gradle buildPlugin`
## Support Us
You can support us by the following actions:
* Star this project
* Share this plugin with your friends
* Rate this plugin on [JetBrains plugin repository](https://plugins.jetbrains.com/plugin/10080-rainbow-brackets)
* Make pull requests
* Report bugs
* Tell us your ideas
* Become a sponsor by donating on [Open Collective](https://opencollective.com/intellij-rainbow-brackets)
* Become a sponsor by donating with AliPay or WeChatPay
* Don't want to use AliPay's mobile App? If you could read Chinese, please use [the official website](https://shenghuo.alipay.com/send/payment/fill.htm) to transfer your donation to my Alipay account(izhangzhihao@hotmail.com). Otherwise, you can try [the English version of Alipay](https://global.alipay.com/) to transfer your donation.
<table>
<tr>
<th width="33%">Open Collective</th>
<th width="33%">AliPay</th>
<th width="33%">WeChatPay</th>
</tr>
<tr></tr>
<tr align="center">
<td>
<a href="https://opencollective.com/intellij-rainbow-brackets" target="_blank">
<img src="https://opencollective.com/static/images/opencollectivelogo-footer-n.svg" width="171px" alt="OpenCollective">
</a>
</td>
<td><img src="https://user-images.githubusercontent.com/12044174/85197261-77dd5a80-b312-11ea-9630-51caf7d634f2.jpg"></td>
<td><img src="https://user-images.githubusercontent.com/12044174/85197263-7b70e180-b312-11ea-917a-35eab2ea08ae.jpg"></td>
</tr>
</table>
## Backers
Thank you to all our backers! ❤️ [[Become a backer](https://opencollective.com/intellij-rainbow-brackets#backer)]
<a href="https://opencollective.com/intellij-rainbow-brackets#backers" target="_blank"><img src="https://opencollective.com/intellij-rainbow-brackets/backers.svg?width=890"></a>
## Sponsors
Support this project by becoming a sponsor! 🌈 Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/intellij-rainbow-brackets#sponsor)]
<a href="https://opencollective.com/intellij-rainbow-brackets/sponsor/0/website" target="_blank"><img src="https://opencollective.com/intellij-rainbow-brackets/sponsor/0/avatar.svg"></a>
<a href="https://opencollective.com/intellij-rainbow-brackets/sponsor/1/website" target="_blank"><img src="https://opencollective.com/intellij-rainbow-brackets/sponsor/1/avatar.svg"></a>
<a href="https://opencollective.com/intellij-rainbow-brackets/sponsor/2/website" target="_blank"><img src="https://opencollective.com/intellij-rainbow-brackets/sponsor/2/avatar.svg"></a>
<a href="https://opencollective.com/intellij-rainbow-brackets/sponsor/3/website" target="_blank"><img src="https://opencollective.com/intellij-rainbow-brackets/sponsor/3/avatar.svg"></a>
<a href="https://opencollective.com/intellij-rainbow-brackets/sponsor/4/website" target="_blank"><img src="https://opencollective.com/intellij-rainbow-brackets/sponsor/4/avatar.svg"></a>
<a href="https://opencollective.com/intellij-rainbow-brackets/sponsor/5/website" target="_blank"><img src="https://opencollective.com/intellij-rainbow-brackets/sponsor/5/avatar.svg"></a>
<a href="https://opencollective.com/intellij-rainbow-brackets/sponsor/6/website" target="_blank"><img src="https://opencollective.com/intellij-rainbow-brackets/sponsor/6/avatar.svg"></a>
<a href="https://opencollective.com/intellij-rainbow-brackets/sponsor/7/website" target="_blank"><img src="https://opencollective.com/intellij-rainbow-brackets/sponsor/7/avatar.svg"></a>
<a href="https://opencollective.com/intellij-rainbow-brackets/sponsor/8/website" target="_blank"><img src="https://opencollective.com/intellij-rainbow-brackets/sponsor/8/avatar.svg"></a>
<a href="https://opencollective.com/intellij-rainbow-brackets/sponsor/9/website" target="_blank"><img src="https://opencollective.com/intellij-rainbow-brackets/sponsor/9/avatar.svg"></a>
<a href="https://opencollective.com/intellij-rainbow-brackets/sponsor/10/website" target="_blank"><img src="https://opencollective.com/intellij-rainbow-brackets/sponsor/10/avatar.svg"></a>
<a href="https://opencollective.com/intellij-rainbow-brackets/sponsor/11/website" target="_blank"><img src="https://opencollective.com/intellij-rainbow-brackets/sponsor/11/avatar.svg"></a>
<a href="https://opencollective.com/intellij-rainbow-brackets/sponsor/12/website" target="_blank"><img src="https://opencollective.com/intellij-rainbow-brackets/sponsor/12/avatar.svg"></a>
<a href="https://opencollective.com/intellij-rainbow-brackets/sponsor/13/website" target="_blank"><img src="https://opencollective.com/intellij-rainbow-brackets/sponsor/13/avatar.svg"></a>
<a href="https://opencollective.com/intellij-rainbow-brackets/sponsor/14/website" target="_blank"><img src="https://opencollective.com/intellij-rainbow-brackets/sponsor/14/avatar.svg"></a>
<a href="https://opencollective.com/intellij-rainbow-brackets/sponsor/15/website" target="_blank"><img src="https://opencollective.com/intellij-rainbow-brackets/sponsor/15/avatar.svg"></a>
## Acknowledgements
Intellij-rainbow-brackets is heavily inspired by [Rainbow Brackets for Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=2gua.rainbow-brackets)
- Support for CLion and Rider
- Fixed service initialization warnings reported by 2024.2+

View File

@ -1,103 +1,129 @@
import org.jetbrains.intellij.tasks.RunPluginVerifierTask.FailureLevel.*
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
@file:Suppress("ConvertLambdaToReference")
val name: String by project
val ideaVersion: String by project
val kotlinVersion: String by project
val javaVersion: String by project
val kotlinLanguageVersion: String by project
val kotlinTargetVersion: String by project
val pluginVerifierIdeVersions: String by project
val publishChannels: String by project
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import kotlin.io.path.Path
plugins {
id("org.jetbrains.intellij") version "1.9.0"
id("com.adarshr.test-logger") version "3.2.0"
id("org.jetbrains.kotlin.jvm") version "1.7.20"
id("idea")
kotlin("jvm")
id("org.jetbrains.intellij")
}
repositories {
mavenLocal()
group = "com.chylex.intellij.coloredbrackets"
version = "0.0.1"
allprojects {
apply(plugin = "org.jetbrains.kotlin.jvm")
apply(plugin = "org.jetbrains.intellij")
repositories {
mavenCentral()
maven(url = "https://maven-central.storage-download.googleapis.com/repos/central/data/")
maven(url = "https://maven.aliyun.com/nexus/content/groups/public/")
maven(url = "https://www.jetbrains.com/intellij-repository/releases")
maven(url = "https://www.jetbrains.com/intellij-repository/snapshots")
}
intellij {
version.set("2023.3")
updateSinceUntilBuild.set(false)
}
kotlin {
jvmToolchain(17)
}
tasks.withType<KotlinCompile> {
kotlinOptions.freeCompilerArgs = listOf(
"-Xjvm-default=all"
)
}
}
subprojects {
tasks.buildSearchableOptions {
enabled = false
}
}
idea {
module {
excludeDirs.add(file("gradle"))
}
}
intellij {
pluginName.set(name)
version.set(ideaVersion)
//localPath = '/Users/izhangzhihao/Library/Application Support/JetBrains/Toolbox/apps/IDEA-U/ch-0/201.6668.121/IntelliJ IDEA 2020.1 EAP.app/Contents'
//localPath = '/Users/izhangzhihao/Library/Application Support/JetBrains/Toolbox/apps/CLion/ch-0/201.6668.126/CLion.app/Contents'
updateSinceUntilBuild.set(false)
type.set("IU")
plugins.set(
listOf(
"java",
"java-i18n",
"JavaScriptLanguage",
"DatabaseTools",
"CSS",
"platform-images",
// Built-in
"Groovy",
"properties",
"yaml",
"org.jetbrains.kotlin:203-$kotlinVersion-release-IJ5981.133-1",
"org.intellij.scala:2020.3.21",
"Dart:203.5981.155",
"org.jetbrains.plugins.ruby:203.5981.155",
"com.jetbrains.php:203.5981.155",
"com.jetbrains.sh:203.5981.37",
"com.jetbrains.plugins.jade:203.5981.155",
"org.jetbrains.plugins.go-template:203.5981.155",
"Pythonid:203.5981.155",
"JavaScript",
"com.intellij.css",
"com.intellij.database",
"com.intellij.java",
"org.intellij.plugins.markdown",
"org.jetbrains.kotlin",
"org.jetbrains.plugins.yaml",
// Downloaded
"Dart:233.11799.172", // https://plugins.jetbrains.com/plugin/6351-dart/versions/stable
"Pythonid:233.11799.300", // https://plugins.jetbrains.com/plugin/631-python/versions
"com.jetbrains.php:233.11799.300", // https://plugins.jetbrains.com/plugin/6610-php/versions
"com.jetbrains.sh:233.11799.165", // https://plugins.jetbrains.com/plugin/13122-shell-script/versions
"org.intellij.scala:2023.3.19", // https://plugins.jetbrains.com/plugin/1347-scala/versions
"org.jetbrains.plugins.go-template:233.11799.172", // https://plugins.jetbrains.com/plugin/10581-go-template/versions
"org.jetbrains.plugins.ruby:233.11799.300", // https://plugins.jetbrains.com/plugin/1293-ruby/versions
)
)
}
tasks {
runIde {
systemProperties["idea.auto.reload.plugins"] = false
jvmArgs = listOf(
"-Xms512m",
"-Xmx2048m",
)
}
publishPlugin {
token.set(System.getenv("token"))
channels.set(publishChannels.split(",").map { it.trim() }.toList())
}
runPluginVerifier {
ideVersions.set(pluginVerifierIdeVersions.split(",").map { it.trim() }.toList())
failureLevel.set(listOf(COMPATIBILITY_PROBLEMS))
}
testlogger {
theme = com.adarshr.gradle.testlogger.theme.ThemeType.MOCHA
}
}
dependencies {
//implementation("org.eclipse.mylyn.github:org.eclipse.egit.github.core:5.11.0.202103091610-r") {
// exclude("gson")
//}
compileOnly(fileTree("libs"))
testImplementation("io.kotest:kotest-assertions-core:5.5.0")
testImplementation("junit:junit:4.13.2")
testImplementation("io.kotest:kotest-assertions-core:5.8.0") {
exclude(group = "org.jetbrains.kotlin")
}
}
java {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
tasks.patchPluginXml {
sinceBuild.set("233")
}
tasks.withType<KotlinCompile> {
kotlinOptions.languageVersion = kotlinLanguageVersion
kotlinOptions.apiVersion = kotlinTargetVersion
kotlinOptions.jvmTarget = javaVersion
kotlinOptions.freeCompilerArgs = listOf("-Xskip-runtime-version-check", "-Xjsr305=strict")
tasks.test {
useJUnit()
}
tasks.buildPlugin {
val projectName = rootProject.name
val instrumentedJarName = "instrumented-$projectName-$version"
for (ide in listOf("clion", "rider")) {
val task = project(":$ide").tasks.buildPlugin
dependsOn(task)
from(task.map { it.outputs.files.map(::zipTree) }) {
include("$ide/lib/instrumented-$ide.jar")
into("lib")
eachFile {
val newName = name.replace("instrumented-", "${instrumentedJarName}-")
val newPath = relativePath.segments.dropLast(3).plus(newName)
relativePath = RelativePath(true, *newPath.toTypedArray())
}
includeEmptyDirs = false
}
}
doLast {
val expectedPaths = listOf(
Path(projectName, "lib", "instrumented-$projectName-$version-clion.jar"),
Path(projectName, "lib", "instrumented-$projectName-$version-rider.jar"),
Path(projectName, "lib", "instrumented-$projectName-$version.jar"),
Path(projectName, "lib", "searchableOptions-$version.jar"),
)
val jarFiles = zipTree(outputs.files.singleFile)
for (expectedPath in expectedPaths) {
val found = jarFiles.find { it.toPath().endsWith(expectedPath) }
checkNotNull(found) { "Expected path not found: $expectedPath" }
}
}
}

1
clion/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/build/

12
clion/build.gradle.kts Normal file
View File

@ -0,0 +1,12 @@
intellij {
type.set("CL")
plugins.set(listOf(
// Built-in
"cidr-base-plugin"
))
}
dependencies {
implementation(rootProject)
}

View File

@ -0,0 +1,8 @@
package com.chylex.intellij.coloredbrackets.provider
import com.intellij.lang.BracePair
import com.jetbrains.cidr.lang.parser.OCLexerTokenTypes
class OCBracePairProvider : BracePairProvider {
override fun pairs(): List<BracePair> = listOf(BracePair(OCLexerTokenTypes.LT, OCLexerTokenTypes.GT, false))
}

View File

@ -0,0 +1,6 @@
<idea-plugin>
<extensions defaultExtensionNs="com.chylex.coloredbrackets">
<bracePairProvider language="ObjectiveC"
implementationClass="com.chylex.intellij.coloredbrackets.provider.OCBracePairProvider" />
</extensions>
</idea-plugin>

View File

@ -1,12 +1 @@
group="com.github.izhangzhihao"
name="Rainbow Brackets"
org.gradle.parallel=true
ideaVersion=IU-203.5981.155
javaVersion=11
kotlinVersion=1.4.21
kotlinLanguageVersion=1.4
kotlinTargetVersion=1.4
version=6.26
publishChannels=Stable
kotlin.stdlib.default.dependency=false
pluginVerifierIdeVersions=WS-2020.3, IIC-2021.1, IIU-2021.2

Binary file not shown.

View File

@ -1,5 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

294
gradlew vendored
View File

@ -1,7 +1,7 @@
#!/usr/bin/env sh
#!/bin/sh
#
# Copyright 2015 the original author or authors.
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@ -17,67 +17,99 @@
#
##############################################################################
##
## Gradle start up script for UN*X
##
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
MAX_FD=maximum
warn () {
echo "$*"
}
} >&2
die () {
echo
echo "$*"
echo
exit 1
}
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MSYS* | MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
@ -87,9 +119,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD="$JAVA_HOME/bin/java"
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
@ -98,88 +130,120 @@ Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
JAVACMD=java
if ! command -v java >/dev/null 2>&1
then
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=`expr $i + 1`
done
case $i in
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=`save "$@"`
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"

15
gradlew.bat vendored
View File

@ -14,7 +14,7 @@
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@ -25,7 +25,8 @@
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@ -40,7 +41,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
if %ERRORLEVEL% equ 0 goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
@ -75,13 +76,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal

Binary file not shown.

Binary file not shown.

1
rider/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/build/

7
rider/build.gradle.kts Normal file
View File

@ -0,0 +1,7 @@
intellij {
type.set("RD")
}
dependencies {
implementation(rootProject)
}

View File

@ -0,0 +1,13 @@
package com.chylex.intellij.coloredbrackets.provider
import com.intellij.lang.BracePair
import com.jetbrains.rider.languages.fileTypes.csharp.kotoparser.lexer.CSharpTokenType
class CSharpBracePairProvider : BracePairProvider {
override fun pairs(): List<BracePair> = listOf(
BracePair(CSharpTokenType.LPARENTH, CSharpTokenType.RPARENTH, false),
BracePair(CSharpTokenType.LBRACE, CSharpTokenType.RBRACE, false),
BracePair(CSharpTokenType.LBRACKET, CSharpTokenType.RBRACKET, false),
BracePair(CSharpTokenType.LT, CSharpTokenType.GT, false),
)
}

View File

@ -0,0 +1,131 @@
package com.chylex.intellij.coloredbrackets.visitor
import com.chylex.intellij.coloredbrackets.settings.RainbowSettings
import com.intellij.codeInsight.daemon.impl.HighlightVisitor
import com.intellij.lang.BracePair
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.impl.source.tree.LeafPsiElement
import com.intellij.psi.tree.IElementType
import com.jetbrains.rider.languages.fileTypes.csharp.kotoparser.lexer.CSharpTokenType
import com.jetbrains.rider.languages.fileTypes.csharp.psi.CSharpDummyNode
class CSharpRainbowVisitor : RainbowHighlightVisitor() {
override fun suitableForFile(file: PsiFile)
: Boolean = super.suitableForFile(file) &&
RainbowSettings.instance.isEnableRainbowAngleBrackets &&
(file.language.id == "C#" ||
file.viewProvider.allFiles.any { it.language.id == "C#" }
)
override fun clone(): HighlightVisitor = CSharpRainbowVisitor()
override fun visit(element: PsiElement) {
val type = (element as? LeafPsiElement)?.elementType ?: return
val pair = map[type]
if (pair != null) {
val level = element.getBracketLevel(pair, type)
if (RainbowSettings.instance.isDoNOTRainbowifyTheFirstLevel) {
if (level >= 1) {
rainbowPairs(element, pair, level)
}
}
else {
if (level >= 0) {
rainbowPairs(element, pair, level)
}
}
}
}
private fun rainbowPairs(element: LeafPsiElement, pair: BracePair, level: Int) {
val startElement = element.takeIf { it.elementType == pair.leftBraceType }
val endElement = element.takeIf { it.elementType == pair.rightBraceType }
element.setHighlightInfo(element.parent, level, startElement, endElement)
}
companion object {
val map = mapOf(
CSharpTokenType.LPARENTH to BracePair(CSharpTokenType.LPARENTH, CSharpTokenType.RPARENTH, true),
CSharpTokenType.RPARENTH to BracePair(CSharpTokenType.LPARENTH, CSharpTokenType.RPARENTH, true),
//LT to BracePair(LT, GT, true),
//GT to BracePair(LT, GT, true),
)
private fun LeafPsiElement.getBracketLevel(pair: BracePair, type: IElementType): Int = iterateBracketParents(this, pair, -1, type)
private tailrec fun iterateBracketParents(element: PsiElement?, pair: BracePair, count: Int, type: IElementType): Int {
if (element == null || element is PsiFile) {
return count
}
var nextCount = count
if (element is LeafPsiElement && type == pair.leftBraceType && element.elementType == pair.rightBraceType) {
nextCount--
}
if (element is LeafPsiElement && type == pair.rightBraceType && element.elementType == pair.leftBraceType) {
nextCount--
}
if (element is LeafPsiElement && element.elementType == type) {
nextCount++
}
return if (type == pair.leftBraceType) {
val prev = element.prevSibling
if (prev == null) {
iterateBracketParents(element.parent.prevSibling.iterForPreDummyNode()?.lastChild, pair, nextCount, type)
}
else {
iterateBracketParents(prev, pair, nextCount, type)
}
}
else {
val next = element.nextSibling
if (next == null) {
iterateBracketParents(element.parent.nextSibling.iterForNextDummyNode()?.firstChild, pair, nextCount, type)
}
else {
iterateBracketParents(next, pair, nextCount, type)
}
}
}
private tailrec fun PsiElement?.iterForNextDummyNode(): PsiElement? {
return if (this == null) {
null
}
else if (this is CSharpDummyNode) {
this
}
else {
if (this.nextSibling == null) {
null
}
else {
this.nextSibling.iterForNextDummyNode()
}
}
}
private tailrec fun PsiElement?.iterForPreDummyNode(): PsiElement? {
return if (this == null) {
null
}
else if (this is CSharpDummyNode) {
this
}
else {
if (this.prevSibling == null) {
null
}
else {
this.prevSibling.iterForPreDummyNode()
}
}
}
}
}

View File

@ -0,0 +1,5 @@
<idea-plugin>
<extensions defaultExtensionNs="com.intellij">
<annotator language="C#" implementationClass="com.chylex.intellij.coloredbrackets.annotator.RainbowAnnotator" />
</extensions>
</idea-plugin>

View File

@ -0,0 +1,10 @@
<idea-plugin>
<extensions defaultExtensionNs="com.chylex.coloredbrackets">
<bracePairProvider language="C#"
implementationClass="com.chylex.intellij.coloredbrackets.provider.CSharpBracePairProvider" />
</extensions>
<extensions defaultExtensionNs="com.intellij">
<highlightVisitor implementation="com.chylex.intellij.coloredbrackets.visitor.CSharpRainbowVisitor" />
</extensions>
</idea-plugin>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

View File

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="Layer_1" width="120.1" height="130.2" x="0" style="enable-background:new 0 0 120.1 130.2" y="0" version="1.1" viewBox="0 0 120.1 130.2" xml:space="preserve"><g><linearGradient id="XMLID_2_" x1="31.841" x2="110.24" y1="120.558" y2="73.24" gradientUnits="userSpaceOnUse"><stop offset="0" style="stop-color:#fcee39"/><stop offset="1" style="stop-color:#f37b3d"/></linearGradient><path id="XMLID_3041_" d="M118.6,71.8c0.9-0.8,1.4-1.9,1.5-3.2c0.1-2.6-1.8-4.7-4.4-4.9 c-1.2-0.1-2.4,0.4-3.3,1.1l0,0l-83.8,45.9c-1.9,0.8-3.6,2.2-4.7,4.1c-2.9,4.8-1.3,11,3.6,13.9c3.4,2,7.5,1.8,10.7-0.2l0,0l0,0 c0.2-0.2,0.5-0.3,0.7-0.5l78-54.8C117.3,72.9,118.4,72.1,118.6,71.8L118.6,71.8L118.6,71.8z" style="fill:url(#XMLID_2_)"/><linearGradient id="XMLID_3_" x1="48.361" x2="119.918" y1="6.908" y2="69.555" gradientUnits="userSpaceOnUse"><stop offset="0" style="stop-color:#ef5a6b"/><stop offset=".57" style="stop-color:#f26f4e"/><stop offset="1" style="stop-color:#f37b3d"/></linearGradient><path id="XMLID_3049_" d="M118.8,65.1L118.8,65.1L55,2.5C53.6,1,51.6,0,49.3,0 c-4.3,0-7.7,3.5-7.7,7.7v0c0,2.1,0.8,3.9,2.1,5.3l0,0l0,0c0.4,0.4,0.8,0.7,1.2,1l67.4,57.7l0,0c0.8,0.7,1.8,1.2,3,1.3 c2.6,0.1,4.7-1.8,4.9-4.4C120.2,67.3,119.7,66,118.8,65.1z" style="fill:url(#XMLID_3_)"/><linearGradient id="XMLID_4_" x1="52.947" x2="10.538" y1="63.641" y2="37.156" gradientUnits="userSpaceOnUse"><stop offset="0" style="stop-color:#7c59a4"/><stop offset=".385" style="stop-color:#af4c92"/><stop offset=".765" style="stop-color:#dc4183"/><stop offset=".957" style="stop-color:#ed3d7d"/></linearGradient><path id="XMLID_3042_" d="M57.1,59.5C57,59.5,17.7,28.5,16.9,28l0,0l0,0c-0.6-0.3-1.2-0.6-1.8-0.9 c-5.8-2.2-12.2,0.8-14.4,6.6c-1.9,5.1,0.2,10.7,4.6,13.4l0,0l0,0C6,47.5,6.6,47.8,7.3,48c0.4,0.2,45.4,18.8,45.4,18.8l0,0 c1.8,0.8,3.9,0.3,5.1-1.2C59.3,63.7,59,61,57.1,59.5z" style="fill:url(#XMLID_4_)"/><linearGradient id="XMLID_5_" x1="52.174" x2="10.771" y1="3.702" y2="37.897" gradientUnits="userSpaceOnUse"><stop offset="0" style="stop-color:#ef5a6b"/><stop offset=".364" style="stop-color:#ee4e72"/><stop offset="1" style="stop-color:#ed3d7d"/></linearGradient><path id="XMLID_3057_" d="M49.3,0c-1.7,0-3.3,0.6-4.6,1.5L4.9,28.3c-0.1,0.1-0.2,0.1-0.2,0.2l-0.1,0 l0,0c-1.7,1.2-3.1,3-3.9,5.1C-1.5,39.4,1.5,45.9,7.3,48c3.6,1.4,7.5,0.7,10.4-1.4l0,0l0,0c0.7-0.5,1.3-1,1.8-1.6l34.6-31.2l0,0 c1.8-1.4,3-3.6,3-6.1v0C57.1,3.5,53.6,0,49.3,0z" style="fill:url(#XMLID_5_)"/><g id="XMLID_3008_"><rect id="XMLID_3033_" width="51" height="51" x="34.6" y="37.4" style="fill:#000"/><rect id="XMLID_3032_" width="19.1" height="3.2" x="39" y="78.8" style="fill:#fff"/><g id="XMLID_3009_"><path id="XMLID_3030_" d="M38.8,50.8l1.5-1.4c0.4,0.5,0.8,0.8,1.3,0.8c0.6,0,0.9-0.4,0.9-1.2l0-5.3l2.3,0 l0,5.3c0,1-0.3,1.8-0.8,2.3c-0.5,0.5-1.3,0.8-2.3,0.8C40.2,52.2,39.4,51.6,38.8,50.8z" style="fill:#fff"/><path id="XMLID_3028_" d="M45.3,43.8l6.7,0v1.9l-4.4,0V47l4,0l0,1.8l-4,0l0,1.3l4.5,0l0,2l-6.7,0 L45.3,43.8z" style="fill:#fff"/><path id="XMLID_3026_" d="M55,45.8l-2.5,0l0-2l7.3,0l0,2l-2.5,0l0,6.3l-2.3,0L55,45.8z" style="fill:#fff"/><path id="XMLID_3022_" d="M39,54l4.3,0c1,0,1.8,0.3,2.3,0.7c0.3,0.3,0.5,0.8,0.5,1.4v0 c0,1-0.5,1.5-1.3,1.9c1,0.3,1.6,0.9,1.6,2v0c0,1.4-1.2,2.3-3.1,2.3l-4.3,0L39,54z M43.8,56.6c0-0.5-0.4-0.7-1-0.7l-1.5,0l0,1.5 l1.4,0C43.4,57.3,43.8,57.1,43.8,56.6L43.8,56.6z M43,59l-1.8,0l0,1.5H43c0.7,0,1.1-0.3,1.1-0.8v0C44.1,59.2,43.7,59,43,59z" style="fill:#fff"/><path id="XMLID_3019_" d="M46.8,54l3.9,0c1.3,0,2.1,0.3,2.7,0.9c0.5,0.5,0.7,1.1,0.7,1.9v0 c0,1.3-0.7,2.1-1.7,2.6l2,2.9l-2.6,0l-1.7-2.5h-1l0,2.5l-2.3,0L46.8,54z M50.6,58c0.8,0,1.2-0.4,1.2-1v0c0-0.7-0.5-1-1.2-1 l-1.5,0v2H50.6z" style="fill:#fff"/><path id="XMLID_3016_" d="M56.8,54l2.2,0l3.5,8.4l-2.5,0l-0.6-1.5l-3.2,0l-0.6,1.5l-2.4,0L56.8,54z M58.8,59l-0.9-2.3L57,59L58.8,59z" style="fill:#fff"/><path id="XMLID_3014_" d="M62.8,54l2.3,0l0,8.3l-2.3,0L62.8,54z" style="fill:#fff"/><path id="XMLID_3012_" d="M65.7,54l2.1,0l3.4,4.4l0-4.4l2.3,0l0,8.3l-2,0L68,57.8l0,4.6l-2.3,0L65.7,54z" style="fill:#fff"/><path id="XMLID_3010_" d="M73.7,61.1l1.3-1.5c0.8,0.7,1.7,1,2.7,1c0.6,0,1-0.2,1-0.6v0 c0-0.4-0.3-0.5-1.4-0.8c-1.8-0.4-3.1-0.9-3.1-2.6v0c0-1.5,1.2-2.7,3.2-2.7c1.4,0,2.5,0.4,3.4,1.1l-1.2,1.6 c-0.8-0.5-1.6-0.8-2.3-0.8c-0.6,0-0.8,0.2-0.8,0.5v0c0,0.4,0.3,0.5,1.4,0.8c1.9,0.4,3.1,1,3.1,2.6v0c0,1.7-1.3,2.7-3.4,2.7 C76.1,62.5,74.7,62,73.7,61.1z" style="fill:#fff"/></g></g></g></svg>

Before

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 123 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

View File

@ -1,6 +1,11 @@
rootProject.name = "ColoredBrackets"
pluginManagement {
repositories {
maven(url = "https://oss.sonatype.org/content/repositories/snapshots/")
gradlePluginPortal()
plugins {
kotlin("jvm") version "1.9.21"
id("org.jetbrains.intellij") version "1.17.4"
}
}
include("clion")
include("rider")

View File

@ -0,0 +1,88 @@
package com.chylex.intellij.coloredbrackets
import com.chylex.intellij.coloredbrackets.provider.BracePairProvider
import com.chylex.intellij.coloredbrackets.util.memoize
import com.intellij.codeInsight.highlighting.BraceMatchingUtil
import com.intellij.lang.BracePair
import com.intellij.lang.CompositeLanguage
import com.intellij.lang.Language
import com.intellij.lang.LanguageBraceMatching
import com.intellij.lang.LanguageExtension
import com.intellij.lang.PairedBraceMatcher
import com.intellij.psi.tree.IElementType
@Suppress("ConvertLambdaToReference")
object BracePairs {
private val providers = LanguageExtension<BracePairProvider>("com.chylex.coloredbrackets.bracePairProvider")
private val bracePairs = lazy {
Language.getRegisteredLanguages()
.map { language ->
if (language is CompositeLanguage) {
return@map language.displayName to null
}
val pairs =
LanguageBraceMatching.INSTANCE.forLanguage(language)?.pairs.let {
if (it.isNullOrEmpty()) {
language.associatedFileType
?.let { fileType -> BraceMatchingUtil.getBraceMatcher(fileType, language) as? PairedBraceMatcher }
?.pairs
}
else {
it
}
}
val pairsList = providers.forLanguage(language)?.pairs()?.let {
if (!pairs.isNullOrEmpty()) {
it.toMutableSet().apply { addAll(pairs) }
}
else {
it
}
} ?: pairs?.toList()
val braceMap: MutableMap<String, MutableList<BracePair>> = mutableMapOf()
val blackSet = providers.forLanguage(language)?.blackList()?.map { it.toString() }?.toSet()
pairsList
?.filter {
if (blackSet != null) {
!blackSet.contains(it.toString())
}
else {
true
}
}
?.map { listOf(Pair(it.leftBraceType.toString(), it), Pair(it.rightBraceType.toString(), it)) }
?.flatten()
?.forEach {
val bracePairs = braceMap[it.first]
if (bracePairs == null) {
braceMap[it.first] = mutableListOf(it.second)
}
else {
bracePairs.add(it.second)
}
}
language.displayName to braceMap
}
.toMap()
}
fun getBracePairs(language: Language): MutableMap<String, MutableList<BracePair>>? = bracePairs.value[language.displayName]
private fun getBraceTypeSetOf(language: Language): Set<IElementType> = getBracePairs(language)?.values?.flatten()?.map { listOf(it.leftBraceType, it.rightBraceType) }?.flatten()?.toSet() ?: emptySet()
val braceTypeSet: (Language) -> Set<IElementType> = { language: Language -> getBraceTypeSetOf(language) }.memoize()
}
inline val Language.bracePairs: MutableMap<String, MutableList<BracePair>>?
get() = BracePairs.getBracePairs(this)
inline val Language.braceTypeSet: Set<IElementType>
get() = BracePairs.braceTypeSet(this)

View File

@ -0,0 +1,228 @@
package com.chylex.intellij.coloredbrackets
import com.chylex.intellij.coloredbrackets.settings.RainbowSettings
import com.chylex.intellij.coloredbrackets.util.memoize
import com.intellij.codeInsight.daemon.impl.HighlightInfo
import com.intellij.codeInsight.daemon.impl.HighlightInfoType
import com.intellij.lang.annotation.HighlightSeverity
import com.intellij.openapi.editor.DefaultLanguageHighlighterColors
import com.intellij.openapi.editor.colors.EditorColorsManager
import com.intellij.openapi.editor.colors.EditorColorsScheme
import com.intellij.openapi.editor.colors.TextAttributesKey
import com.intellij.openapi.editor.colors.TextAttributesScheme
import com.intellij.openapi.editor.colors.impl.AbstractColorsScheme
import com.intellij.openapi.editor.markup.EffectType
import com.intellij.openapi.editor.markup.TextAttributes
import com.intellij.psi.PsiElement
import com.intellij.ui.JBColor
import org.intellij.lang.annotations.Language
import org.jetbrains.annotations.TestOnly
import java.awt.Color
import java.awt.Font
object RainbowHighlighter {
val DEFAULT_KOTLIN_LABEL_COLOR = JBColor(0x4a86e8, 0x467cda)
const val NAME_ROUND_BRACKETS = "Round Brackets"
const val NAME_SQUARE_BRACKETS = "Square Brackets"
const val NAME_SQUIGGLY_BRACKETS = "Squiggly Brackets"
const val NAME_ANGLE_BRACKETS = "Angle Brackets"
private const val KEY_ROUND_BRACKETS = "ROUND_BRACKETS_RAINBOW_COLOR"
private const val KEY_SQUARE_BRACKETS = "SQUARE_BRACKETS_RAINBOW_COLOR"
private const val KEY_SQUIGGLY_BRACKETS = "SQUIGGLY_BRACKETS_RAINBOW_COLOR"
private const val KEY_ANGLE_BRACKETS = "ANGLE_BRACKETS_RAINBOW_COLOR"
private val roundBrackets: CharArray = charArrayOf('(', ')')
private val squareBrackets: CharArray = charArrayOf('[', ']')
private val squigglyBrackets: CharArray = charArrayOf('{', '}')
private val angleBrackets: CharArray = charArrayOf('<', '>')
private val settings
get() = RainbowSettings.instance
private val roundBracketsRainbowColorKeys = lazy {
createRainbowAttributesKeys(KEY_ROUND_BRACKETS, settings.numberOfColors)
}
private val squareBracketsRainbowColorKeys = lazy {
createRainbowAttributesKeys(KEY_SQUARE_BRACKETS, settings.numberOfColors)
}
private val squigglyBracketsRainbowColorKeys = lazy {
createRainbowAttributesKeys(KEY_SQUIGGLY_BRACKETS, settings.numberOfColors)
}
private val angleBracketsRainbowColorKeys = lazy {
createRainbowAttributesKeys(KEY_ANGLE_BRACKETS, settings.numberOfColors)
}
private val rainbowElement: HighlightInfoType = HighlightInfoType
.HighlightInfoTypeImpl(HighlightSeverity.INFORMATION, DefaultLanguageHighlighterColors.CONSTANT)
private val PsiElement.isRoundBracket get() = roundBrackets.any { textContains(it) }
private val PsiElement.isSquareBracket get() = squareBrackets.any { textContains(it) }
private val PsiElement.isSquigglyBracket get() = squigglyBrackets.any { textContains(it) }
private val PsiElement.isAngleBracket get() = angleBrackets.any { textContains(it) }
private fun createRainbowAttributesKeys(keyName: String, size: Int): Array<TextAttributesKey> {
return generateSequence(0) { it + 1 }
.map { TextAttributesKey.createTextAttributesKey("$keyName$it") }
.take(size)
.toList()
.toTypedArray()
}
fun getRainbowAttributesKeys(rainbowName: String): Array<TextAttributesKey> {
return when (rainbowName) {
NAME_ROUND_BRACKETS -> roundBracketsRainbowColorKeys.value
NAME_SQUARE_BRACKETS -> squareBracketsRainbowColorKeys.value
NAME_SQUIGGLY_BRACKETS -> squigglyBracketsRainbowColorKeys.value
NAME_ANGLE_BRACKETS -> angleBracketsRainbowColorKeys.value
else -> throw IllegalArgumentException("Unknown rainbow name: $rainbowName")
}
}
// FIXME: Meta properties(SchemeMetaInfo) should be used.
fun isRainbowEnabled(rainbowName: String): Boolean {
return when (rainbowName) {
NAME_ROUND_BRACKETS -> settings.isEnableRainbowRoundBrackets
NAME_SQUARE_BRACKETS -> settings.isEnableRainbowSquareBrackets
NAME_SQUIGGLY_BRACKETS -> settings.isEnableRainbowSquigglyBrackets
NAME_ANGLE_BRACKETS -> settings.isEnableRainbowAngleBrackets
else -> throw IllegalArgumentException("Unknown rainbow name: $rainbowName")
}
}
// FIXME: Meta properties(SchemeMetaInfo) should be used.
fun setRainbowEnabled(rainbowName: String, enabled: Boolean) {
when (rainbowName) {
NAME_ROUND_BRACKETS -> settings.isEnableRainbowRoundBrackets = enabled
NAME_SQUARE_BRACKETS -> settings.isEnableRainbowSquareBrackets = enabled
NAME_SQUIGGLY_BRACKETS -> settings.isEnableRainbowSquigglyBrackets = enabled
NAME_ANGLE_BRACKETS -> settings.isEnableRainbowAngleBrackets = enabled
else -> throw IllegalArgumentException("Unknown rainbow name: $rainbowName")
}
}
private fun isDarkEditor() = EditorColorsManager.getInstance().isDarkEditor
fun getRainbowColorByLevel(colorsScheme: TextAttributesScheme, rainbowName: String, level: Int): TextAttributesKey {
val ind = level % settings.numberOfColors
if (settings.useColorGenerator) {
return memGetRainbowColorByLevel(isDarkEditor(), rainbowName, ind)
}
val key = getRainbowAttributesKeys(rainbowName).getOrNull(ind)
return try {
val result = colorsScheme.getAttributes(key)
if (key == null || result == null || result.foregroundColor == null) {
memGetRainbowColorByLevel(isDarkEditor(), rainbowName, ind)
}
else {
key
}
} catch (e: Exception) {
memGetRainbowColorByLevel(isDarkEditor(), rainbowName, ind)
}
}
private fun generateColor(isDark: Boolean, rainbowName: String, level: Int): TextAttributesKey {
if (!settings.customColorGeneratorOption.isNullOrBlank()) {
return genByOption(settings.customColorGeneratorOption!!, rainbowName, level)
}
if (isDark) {
@Language("JSON") val darkOption = """{"luminosity": "light"}"""
return genByOption(darkOption, rainbowName, level)
}
@Language("JSON") val lightOption = """{"luminosity": "dark"}"""
return genByOption(lightOption, rainbowName, level)
}
private fun genByOption(option: String, rainbowName: String, level: Int) =
com.chylex.intellij.coloredbrackets.util.create(
"$rainbowName-$level",
TextAttributes(randomColor(option), null, null, null, 0)
)
val memGetRainbowColorByLevel = { isDark: Boolean, rainbowName: String, level: Int -> generateColor(isDark, rainbowName, level) }.memoize()
@TestOnly
fun getBrackets(): CharArray = roundBrackets + squareBrackets + squigglyBrackets + angleBrackets
@TestOnly
fun getRainbowColor(rainbowName: String, level: Int): Color? {
return getRainbowColorByLevel(EditorColorsManager.getInstance().globalScheme, rainbowName, level).defaultAttributes.foregroundColor
}
private fun getTextAttributes(
colorsScheme: TextAttributesScheme,
element: PsiElement,
level: Int,
): TextAttributesKey? {
if (!settings.isRainbowEnabled) {
return null
}
val rainbowName = when {
settings.applyColorsOfRoundForAllBrackets -> NAME_ROUND_BRACKETS
element.isRoundBracket -> if (settings.isEnableRainbowRoundBrackets) NAME_ROUND_BRACKETS else null
element.isSquareBracket -> if (settings.isEnableRainbowSquareBrackets) NAME_SQUARE_BRACKETS else null
element.isSquigglyBracket -> if (settings.isEnableRainbowSquigglyBrackets) NAME_SQUIGGLY_BRACKETS else null
element.isAngleBracket -> if (settings.isEnableRainbowAngleBrackets) NAME_ANGLE_BRACKETS else null
else -> NAME_ROUND_BRACKETS
} ?: return null
return getRainbowColorByLevel(colorsScheme, rainbowName, level)
}
fun getHighlightInfo(colorsScheme: TextAttributesScheme, element: PsiElement, level: Int)
: HighlightInfo? = getTextAttributes(colorsScheme, element, level)
?.let { attr ->
HighlightInfo
.newHighlightInfo(rainbowElement)
.textAttributes(attr)
.range(element)
.create()
}
private val KEY_HTML_CODE: TextAttributesKey by lazy { TextAttributesKey.createTextAttributesKey("HTML_CODE") }
private val KEY_KOTLIN_LABEL: TextAttributesKey by lazy { TextAttributesKey.createTextAttributesKey("KOTLIN_LABEL") }
private val KEY_MATCHED_BRACE_ATTRIBUTES: TextAttributesKey by lazy {
TextAttributesKey.createTextAttributesKey("MATCHED_BRACE_ATTRIBUTES")
}
private val KOTLIN_FUNCTION_LITERAL_BRACES_AND_ARROW: TextAttributesKey by lazy {
TextAttributesKey.createTextAttributesKey("KOTLIN_FUNCTION_LITERAL_BRACES_AND_ARROW")
}
fun fixHighlighting(scheme: EditorColorsScheme = EditorColorsManager.getInstance().globalScheme) {
// html code
scheme.setInherited(KEY_HTML_CODE, !settings.isRainbowifyHTMLInsideJS)
// kotlin label
val kotlinLabelColor = DEFAULT_KOTLIN_LABEL_COLOR.takeUnless { settings.isRainbowifyKotlinLabel }
val kotlinLabel = TextAttributes(kotlinLabelColor, null, null, EffectType.BOXED, Font.PLAIN)
scheme.setAttributes(KEY_KOTLIN_LABEL, kotlinLabel)
// matched brace
if (settings.isOverrideMatchedBraceAttributes) {
val matchedBraceAttributes = TextAttributes(null, JBColor(0x99ccbb, 0x3b514d), null, EffectType.BOXED, Font.BOLD)
scheme.setAttributes(KEY_MATCHED_BRACE_ATTRIBUTES, matchedBraceAttributes)
}
if (settings.isRainbowifyKotlinFunctionLiteralBracesAndArrow) {
scheme.setAttributes(
KOTLIN_FUNCTION_LITERAL_BRACES_AND_ARROW,
TextAttributes(null, null, null, EffectType.BOXED, Font.BOLD)
)
}
else {
scheme.setAttributes(
KOTLIN_FUNCTION_LITERAL_BRACES_AND_ARROW,
//TODO: default foregroundColor ???
TextAttributes(JBColor(0x89ddff, 0x89ddff), null, null, EffectType.BOXED, Font.BOLD)
)
}
}
private fun EditorColorsScheme.setInherited(key: TextAttributesKey, inherited: Boolean) {
setAttributes(key, if (inherited) AbstractColorsScheme.INHERITED_ATTRS_MARKER else TextAttributes())
}
}

View File

@ -0,0 +1,40 @@
package com.chylex.intellij.coloredbrackets
import com.intellij.openapi.util.Key
import com.intellij.psi.PsiElement
import java.awt.Color
import java.lang.ref.WeakReference
data class RainbowInfo(var level: Int, var color: Color) {
private var _startElement: WeakReference<PsiElement>? = null
private var _endElement: WeakReference<PsiElement>? = null
var startElement: PsiElement?
get() = _startElement?.get()
set(value) {
_startElement = value?.let { WeakReference(it) }
}
val startOffset get() = startElement?.textRange?.startOffset ?: -1
var endElement: PsiElement?
get() = _endElement?.get()
set(value) {
_endElement = value?.let { WeakReference(it) }
}
val endOffset get() = endElement?.textRange?.endOffset ?: -1
fun containsOffset(offset: Int): Boolean {
val startElement = startElement ?: return false
val endElement = endElement ?: return false
val startOffset = startElement.textRange.startOffset
val endOffset = endElement.textRange.endOffset
return offset in startOffset..endOffset
}
companion object {
val RAINBOW_INFO_KEY: Key<RainbowInfo> = Key.create("RAINBOW_INFO")
}
}

View File

@ -0,0 +1,94 @@
package com.chylex.intellij.coloredbrackets
import com.chylex.intellij.coloredbrackets.settings.RainbowConfigurable
import com.chylex.intellij.coloredbrackets.settings.RainbowSettings
import com.chylex.intellij.coloredbrackets.util.memoizedFileExtension
import com.chylex.intellij.coloredbrackets.util.toPsiFile
import com.chylex.intellij.coloredbrackets.visitor.RainbowHighlightVisitor.Companion.checkForBigFile
import com.intellij.icons.AllIcons
import com.intellij.ide.actions.ShowSettingsUtilImpl
import com.intellij.openapi.fileEditor.FileEditor
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Key
import com.intellij.openapi.util.Ref
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.ui.EditorNotificationPanel
import com.intellij.ui.EditorNotifications
import com.intellij.ui.HyperlinkLabel
class RainbowifyBanner : EditorNotifications.Provider<EditorNotificationPanel>() {
override fun getKey(): Key<EditorNotificationPanel> = KEY
override fun createNotificationPanel(file: VirtualFile, fileEditor: FileEditor, project: Project): EditorNotificationPanel? {
val settings = RainbowSettings.instance
if (!settings.isRainbowEnabled) {
if (settings.suppressDisabledCheck) return null
return EditorNotificationPanel().apply {
text("Colored Brackets is now disabled")
icon(AllIcons.General.GearPlain)
createComponentActionLabel("got it, don't show again") {
settings.suppressDisabledCheck = true
EditorNotifications.getInstance(project).updateAllNotifications()
}
createComponentActionLabel("enable Colored Brackets") {
settings.isRainbowEnabled = true
EditorNotifications.getInstance(project).updateAllNotifications()
}
}
}
val psiFile = file.toPsiFile(project)
if (psiFile != null && !checkForBigFile(psiFile)) {
if (settings.suppressBigFileCheck) return null
return EditorNotificationPanel().apply {
text("Rainbowify is disabled for files > " + settings.bigFilesLinesThreshold + " lines")
icon(AllIcons.General.InspectionsEye)
createComponentActionLabel("got it, don't show again") {
settings.suppressBigFileCheck = true
EditorNotifications.getInstance(project).updateAllNotifications()
}
createComponentActionLabel("open settings") {
ShowSettingsUtilImpl.showSettingsDialog(project, RainbowConfigurable.ID, "")
EditorNotifications.getInstance(project).updateAllNotifications()
}
}
}
if (
settings.languageBlacklist.contains(file.fileType.name) ||
settings.languageBlacklist.contains(memoizedFileExtension(file.name))
) {
if (settings.suppressBlackListCheck) return null
return EditorNotificationPanel().apply {
text("Rainbowify is disabled because the language/file extension is in the black list")
icon(AllIcons.General.InspectionsEye)
createComponentActionLabel("got it, don't show again") {
settings.suppressBlackListCheck = true
EditorNotifications.getInstance(project).updateAllNotifications()
}
createComponentActionLabel("open setting") {
ShowSettingsUtilImpl.showSettingsDialog(project, RainbowConfigurable.ID, "")
EditorNotifications.getInstance(project).updateAllNotifications()
}
}
}
return null
}
companion object {
private val KEY = Key.create<EditorNotificationPanel>("RainbowifyBanner")
fun EditorNotificationPanel.createComponentActionLabel(labelText: String, callback: (HyperlinkLabel) -> Unit) {
val label: Ref<HyperlinkLabel> = Ref.create()
label.set(createActionLabel(labelText) {
callback(label.get())
})
}
}
}

View File

@ -0,0 +1,18 @@
package com.chylex.intellij.coloredbrackets
import com.chylex.intellij.coloredbrackets.color.Luminosity
import com.chylex.intellij.coloredbrackets.color.fromString
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.fasterxml.jackson.module.kotlin.readValue
import java.awt.Color
val mapper: ObjectMapper by lazy { jacksonObjectMapper() }
fun randomColor(options: String): Color {
val ops: Map<String, String> = mapper.readValue(options)
return com.chylex.intellij.coloredbrackets.color.randomColor(
fromString(ops.getOrDefault("hue", "random")),
Luminosity.valueOf(ops.getOrDefault("luminosity", "random"))
)
}

View File

@ -0,0 +1,104 @@
package com.chylex.intellij.coloredbrackets.action
import com.chylex.intellij.coloredbrackets.RainbowInfo
import com.intellij.codeInsight.highlighting.HighlightManager
import com.intellij.openapi.actionSystem.ActionUpdateThread
import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.actionSystem.CommonDataKeys
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.markup.RangeHighlighter
import com.intellij.openapi.editor.textarea.TextComponentEditor
import com.intellij.openapi.util.Key
import com.intellij.psi.PsiDocumentManager
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import java.awt.event.FocusEvent
import java.awt.event.FocusListener
import java.awt.event.KeyAdapter
import java.awt.event.KeyEvent
abstract class AbstractScopeHighlightingAction : AnAction() {
final override fun getActionUpdateThread(): ActionUpdateThread {
return ActionUpdateThread.BGT
}
final override fun update(e: AnActionEvent) {
e.presentation.isEnabledAndVisible = e.editor.let { it != null && it !is TextComponentEditor }
}
final override fun actionPerformed(e: AnActionEvent) {
val editor = e.editor ?: return
val project = editor.project ?: return
val psiFile = project.let { PsiDocumentManager.getInstance(it).getPsiFile(editor.document) } ?: return
val offset = editor.caretModel.offset
val rainbowInfo = psiFile.findRainbowInfoAt(offset) ?: return
val highlightManager = HighlightManager.getInstance(project)
val highlighters = editor.addHighlighter(highlightManager, rainbowInfo)
editor.highlightingDisposer?.dispose()
if (highlighters.isNotEmpty()) {
editor.highlightingDisposer = HighlightingDisposer(editor) {
editor.highlightingDisposer = null
highlighters.forEach { highlightManager.removeSegmentHighlighter(editor, it) }
}
}
}
protected abstract fun Editor.addHighlighter(
highlightManager: HighlightManager,
rainbowInfo: RainbowInfo,
): Collection<RangeHighlighter>
private class HighlightingDisposer(
private val editor: Editor,
private val disposeAction: () -> Unit,
) : KeyAdapter(), FocusListener {
init {
editor.contentComponent.let {
it.addFocusListener(this)
it.addKeyListener(this)
}
}
fun dispose() {
disposeAction()
editor.contentComponent.let {
it.removeFocusListener(this)
it.removeKeyListener(this)
}
}
override fun focusGained(e: FocusEvent) = Unit
override fun focusLost(e: FocusEvent) = Unit
override fun keyReleased(e: KeyEvent) = Unit
}
companion object {
private val HIGHLIGHTING_DISPOSER_KEY: Key<HighlightingDisposer> = Key.create("HIGHLIGHTING_DISPOSER_KEY")
private var Editor.highlightingDisposer: HighlightingDisposer?
get() = HIGHLIGHTING_DISPOSER_KEY[this]
set(value) {
HIGHLIGHTING_DISPOSER_KEY[this] = value
}
private val AnActionEvent.editor: Editor? get() = CommonDataKeys.EDITOR.getData(dataContext)
private fun PsiElement.getRainbowInfo(offset: Int): RainbowInfo? {
return RainbowInfo.RAINBOW_INFO_KEY[this]?.takeIf { it.containsOffset(offset) }
}
private fun PsiFile.findRainbowInfoAt(offset: Int): RainbowInfo? {
var element = findElementAt(offset)
while (element != null) {
element.getRainbowInfo(offset)?.let { return it }
element = element.parent
}
return null
}
}
}

View File

@ -0,0 +1,38 @@
package com.chylex.intellij.coloredbrackets.action
import com.chylex.intellij.coloredbrackets.RainbowInfo
import com.chylex.intellij.coloredbrackets.settings.RainbowSettings
import com.chylex.intellij.coloredbrackets.util.alphaBlend
import com.intellij.codeInsight.highlighting.HighlightManager
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.colors.EditorColorsManager
import com.intellij.openapi.editor.markup.EffectType
import com.intellij.openapi.editor.markup.RangeHighlighter
import com.intellij.openapi.editor.markup.TextAttributes
import java.awt.Font
import java.util.LinkedList
class ScopeHighlightingAction : AbstractScopeHighlightingAction() {
override fun Editor.addHighlighter(
highlightManager: HighlightManager,
rainbowInfo: RainbowInfo,
): Collection<RangeHighlighter> {
val defaultBackground = EditorColorsManager.getInstance().globalScheme.defaultBackground
val background = rainbowInfo.color.alphaBlend(defaultBackground, 0.2f)
val attributes = TextAttributes(null, background, rainbowInfo.color, EffectType.BOXED, Font.PLAIN)
val highlighters = LinkedList<RangeHighlighter>()
highlightManager.addRangeHighlight(
this,
rainbowInfo.startOffset,
rainbowInfo.endOffset,
attributes, //create("ScopeHighlightingAction", attributes),
false, //hideByTextChange
RainbowSettings.instance.pressAnyKeyToRemoveTheHighlightingEffects, //hideByAnyKey
highlighters
)
return highlighters
}
}

View File

@ -0,0 +1,60 @@
package com.chylex.intellij.coloredbrackets.action
import com.chylex.intellij.coloredbrackets.RainbowInfo
import com.chylex.intellij.coloredbrackets.settings.RainbowSettings
import com.chylex.intellij.coloredbrackets.util.alphaBlend
import com.intellij.codeInsight.highlighting.HighlightManager
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.colors.EditorColorsManager
import com.intellij.openapi.editor.markup.EffectType
import com.intellij.openapi.editor.markup.RangeHighlighter
import com.intellij.openapi.editor.markup.TextAttributes
import java.awt.Color
import java.awt.Font
import java.util.LinkedList
class ScopeOutsideHighlightingRestrainAction : AbstractScopeHighlightingAction() {
override fun Editor.addHighlighter(
highlightManager: HighlightManager,
rainbowInfo: RainbowInfo,
): Collection<RangeHighlighter> {
val defaultBackground = EditorColorsManager.getInstance().globalScheme.defaultBackground
val background = Color.GRAY.alphaBlend(defaultBackground, 0.05f)
val foreground = Color.GRAY.alphaBlend(defaultBackground, 0.55f)
val attributes = TextAttributes(foreground, background, background, EffectType.BOXED, Font.PLAIN)
val highlighters = LinkedList<RangeHighlighter>()
val startOffset = rainbowInfo.startOffset
val hideByAnyKey = RainbowSettings.instance.pressAnyKeyToRemoveTheHighlightingEffects
if (startOffset > 0) {
highlightManager.addRangeHighlight(
this,
0,
startOffset,
attributes, //create("ScopeOutsideHighlightingRestrainAction", attributes),
false, //hideByTextChange
hideByAnyKey, //hideByAnyKey
highlighters
)
}
val endOffset = rainbowInfo.endOffset
val lastOffset = document.textLength
if (endOffset < lastOffset) {
highlightManager.addRangeHighlight(
this,
endOffset,
lastOffset,
attributes, //create("ScopeOutsideHighlightingRestrainAction", attributes),
false, //hideByTextChange
hideByAnyKey, //hideByAnyKey
highlighters
)
}
return highlighters
}
}

View File

@ -0,0 +1,97 @@
package com.chylex.intellij.coloredbrackets.annotator
import com.chylex.intellij.coloredbrackets.RainbowHighlighter
import com.chylex.intellij.coloredbrackets.RainbowInfo
import com.chylex.intellij.coloredbrackets.settings.RainbowSettings
import com.intellij.lang.annotation.AnnotationHolder
import com.intellij.lang.annotation.Annotator
import com.intellij.lang.annotation.HighlightSeverity
import com.intellij.openapi.editor.markup.EffectType
import com.intellij.openapi.editor.markup.TextAttributes
import com.intellij.psi.PsiElement
import com.intellij.psi.impl.source.tree.LeafPsiElement
import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.KtBlockExpression
import org.jetbrains.kotlin.psi.KtCallExpression
import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtClassBody
import org.jetbrains.kotlin.psi.KtFunction
import org.jetbrains.kotlin.psi.KtFunctionLiteral
import org.jetbrains.kotlin.psi.KtLabelReferenceExpression
import org.jetbrains.kotlin.psi.KtLabeledExpression
import org.jetbrains.kotlin.psi.KtLambdaExpression
import java.awt.Font
class KotlinLabelAnnotator : Annotator {
override fun annotate(element: PsiElement, holder: AnnotationHolder) {
if (!RainbowSettings.instance.isRainbowifyKotlinLabel) {
return
}
val target: PsiElement
var refElement: PsiElement?
when (element) {
is KtLabelReferenceExpression -> {
if ((element.lastChild as? LeafPsiElement)?.elementType == KtTokens.AT) {
return
}
target = element
refElement = try {
element.reference?.resolve()
} catch (e: Throwable) {
null
}
refElement = when (refElement) {
is KtBlockExpression,
is KtFunctionLiteral,
-> refElement
is KtFunction -> refElement.lastChild.takeIf { it is KtBlockExpression }
is KtClass -> refElement.lastChild.takeIf { it is KtClassBody }
is KtCallExpression,
is KtLambdaExpression,
-> PsiTreeUtil.findChildOfType(refElement, KtFunctionLiteral::class.java)
else -> null
}
}
is KtLabeledExpression -> {
target = element.firstChild.firstChild.takeIf { it is KtLabelReferenceExpression } ?: return
refElement = element.lastChild.let {
when (it) {
is KtBlockExpression,
is KtFunctionLiteral,
-> it
is KtCallExpression,
is KtLambdaExpression,
-> PsiTreeUtil.findChildOfType(it, KtFunctionLiteral::class.java)
else -> null
}
} ?: return
}
else -> return
}
refElement
.let { RainbowInfo.RAINBOW_INFO_KEY[it]?.color ?: RainbowHighlighter.DEFAULT_KOTLIN_LABEL_COLOR }
.let {
holder.newSilentAnnotation(HighlightSeverity.INFORMATION)
.range(target)
.textAttributes(
com.chylex.intellij.coloredbrackets.util.create(
"rainbow-kotlin-label",
TextAttributes(it, null, null, EffectType.BOXED, Font.PLAIN)
)
)
.create()
}
}
}

View File

@ -0,0 +1,30 @@
package com.chylex.intellij.coloredbrackets.annotator
import com.chylex.intellij.coloredbrackets.RainbowInfo
import com.intellij.lang.annotation.AnnotationHolder
import com.intellij.lang.annotation.Annotator
import com.intellij.lang.annotation.HighlightSeverity
import com.intellij.openapi.editor.markup.EffectType
import com.intellij.openapi.editor.markup.TextAttributes
import com.intellij.psi.PsiElement
import com.intellij.psi.impl.source.tree.LeafPsiElement
import org.jetbrains.kotlin.lexer.KtTokens
import java.awt.Font
class KotlinLambdaExpressionArrowAnnotator : Annotator {
override fun annotate(element: PsiElement, holder: AnnotationHolder) {
if ((element as? LeafPsiElement)?.elementType == KtTokens.ARROW) {
RainbowInfo.RAINBOW_INFO_KEY[element.parent]?.color?.let {
holder.newSilentAnnotation(HighlightSeverity.INFORMATION)
.range(element)
.textAttributes(
com.chylex.intellij.coloredbrackets.util.create(
"rainbow-kotlin-arrow",
TextAttributes(it, null, null, EffectType.BOXED, Font.PLAIN)
)
)
.create()
}
}
}
}

View File

@ -0,0 +1,123 @@
package com.chylex.intellij.coloredbrackets.annotator
import com.chylex.intellij.coloredbrackets.RainbowHighlighter.NAME_ANGLE_BRACKETS
import com.chylex.intellij.coloredbrackets.RainbowHighlighter.NAME_ROUND_BRACKETS
import com.chylex.intellij.coloredbrackets.RainbowHighlighter.NAME_SQUARE_BRACKETS
import com.chylex.intellij.coloredbrackets.RainbowHighlighter.NAME_SQUIGGLY_BRACKETS
import com.chylex.intellij.coloredbrackets.RainbowHighlighter.getRainbowColorByLevel
import com.chylex.intellij.coloredbrackets.annotator.RainbowUtils.annotateUtil
import com.chylex.intellij.coloredbrackets.annotator.RainbowUtils.settings
import com.chylex.intellij.coloredbrackets.settings.RainbowSettings
import com.intellij.lang.annotation.AnnotationHolder
import com.intellij.lang.annotation.Annotator
import com.intellij.lang.annotation.HighlightSeverity
import com.intellij.openapi.editor.colors.EditorColorsManager
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.impl.source.tree.LeafPsiElement
class RainbowAnnotator : Annotator {
override fun annotate(element: PsiElement, holder: AnnotationHolder) {
val settings = settings
if (settings.isRainbowEnabled && element is LeafPsiElement) {
if (!settings.applyColorsOfRoundForAllBrackets) {
if (settings.isEnableRainbowRoundBrackets) annotateUtil(element, holder, "(", ")", NAME_ROUND_BRACKETS)
if (settings.isEnableRainbowSquareBrackets) annotateUtil(element, holder, "[", "]", NAME_SQUARE_BRACKETS)
if (settings.isEnableRainbowSquigglyBrackets) annotateUtil(element, holder, "{", "}", NAME_SQUIGGLY_BRACKETS)
if (settings.isEnableRainbowAngleBrackets) annotateUtil(element, holder, "<", ">", NAME_ANGLE_BRACKETS)
}
else {
if (settings.isEnableRainbowRoundBrackets) annotateUtil(element, holder, "(", ")", NAME_ROUND_BRACKETS)
if (settings.isEnableRainbowSquareBrackets) annotateUtil(element, holder, "[", "]", NAME_ROUND_BRACKETS)
if (settings.isEnableRainbowSquigglyBrackets) annotateUtil(element, holder, "{", "}", NAME_ROUND_BRACKETS)
if (settings.isEnableRainbowAngleBrackets) annotateUtil(element, holder, "<", ">", NAME_ROUND_BRACKETS)
}
}
}
}
object RainbowUtils {
private val leftBracketsSet = setOf("(", "[", "{", "<")
private val rightBracketsSet = setOf(")", "]", "}", ">")
val settings
get() = RainbowSettings.instance
private tailrec fun iterateChildren(
LEFT: String,
RIGHT: String,
currentNode: PsiElement,
currentLevel: Int,
currentChild: PsiElement,
): Int {
val calculatedLevel = if (currentChild is LeafPsiElement) {
//Using `currentChild.elementType.toString()` if we didn't want add more dependencies.
if (!settings.cycleCountOnAllBrackets) {
when (currentChild.text) {
LEFT -> currentLevel + 1
RIGHT -> currentLevel - 1
else -> currentLevel
}
}
else {
when {
leftBracketsSet.contains(currentChild.text) -> currentLevel + 1
rightBracketsSet.contains(currentChild.text) -> currentLevel - 1
else -> currentLevel
}
}
}
else currentLevel
return if ((currentChild != currentNode) && (currentChild != currentNode.parent.lastChild))
iterateChildren(LEFT, RIGHT, currentNode, calculatedLevel, currentChild.nextSibling)
else
calculatedLevel
}
private tailrec fun iterateParents(
LEFT: String,
RIGHT: String,
currentNode: PsiElement,
currentLevel: Int,
): Int = if (currentNode.parent !is PsiFile) {
val calculatedLevel = iterateChildren(LEFT, RIGHT, currentNode, currentLevel, currentNode.parent.firstChild)
iterateParents(LEFT, RIGHT, currentNode.parent, calculatedLevel)
}
else currentLevel
private fun getBracketLevel(element: LeafPsiElement, LEFT: String, RIGHT: String): Int {
//Using `element.elementType.toString()` if we didn't want add more dependencies.
val startLevel = if (element.text == RIGHT) 0 else -1
return iterateParents(LEFT, RIGHT, element, startLevel)
}
fun annotateUtil(
element: LeafPsiElement, holder: AnnotationHolder,
LEFT: String, RIGHT: String, rainbowName: String,
) {
//Using `element.elementType.toString()` if we didn't want add more dependencies.
val level = when (element.text) {
LEFT, RIGHT -> getBracketLevel(element, LEFT, RIGHT)
else -> -1
}
val scheme = EditorColorsManager.getInstance().globalScheme
if (RainbowSettings.instance.isDoNOTRainbowifyTheFirstLevel) {
if (level >= 1) {
holder.newSilentAnnotation(HighlightSeverity.INFORMATION)
.range(element.psi)
.textAttributes(getRainbowColorByLevel(scheme, rainbowName, level))
.create()
}
}
else {
if (level >= 0) {
holder.newSilentAnnotation(HighlightSeverity.INFORMATION)
.range(element.psi)
.textAttributes(getRainbowColorByLevel(scheme, rainbowName, level))
.create()
}
}
}
}

View File

@ -0,0 +1,108 @@
package com.chylex.intellij.coloredbrackets.color
import kotlin.math.floor
enum class Color(val hueRange: Pair<Int, Int>, val lowerBounds: List<Pair<Int, Int>>) {
monochrome(
Pair(-1, -1),
listOf(Pair(0, 0), Pair(100, 0))
),
red(
Pair(-26, 18),
listOf(
Pair(20, 100),
Pair(30, 92),
Pair(40, 89),
Pair(50, 85),
Pair(60, 78),
Pair(70, 70),
Pair(80, 60),
Pair(90, 55),
Pair(100, 50)
)
),
orange(
Pair(18, 46),
listOf(Pair(20, 100), Pair(30, 93), Pair(40, 88), Pair(50, 86), Pair(60, 85), Pair(70, 70), Pair(100, 70))
),
yellow(
Pair(46, 62),
listOf(
Pair(25, 100),
Pair(40, 94),
Pair(50, 89),
Pair(60, 86),
Pair(70, 84),
Pair(80, 82),
Pair(90, 80),
Pair(100, 75)
)
),
green(
Pair(62, 178),
listOf(
Pair(30, 100),
Pair(40, 90),
Pair(50, 85),
Pair(60, 81),
Pair(70, 74),
Pair(80, 64),
Pair(90, 50),
Pair(100, 40)
)
),
blue(
Pair(178, 257),
listOf(
Pair(20, 100),
Pair(30, 86),
Pair(40, 80),
Pair(50, 74),
Pair(60, 60),
Pair(70, 52),
Pair(80, 44),
Pair(90, 39),
Pair(100, 35)
)
),
purple(
Pair(257, 282),
listOf(
Pair(20, 100),
Pair(30, 87),
Pair(40, 79),
Pair(50, 70),
Pair(60, 65),
Pair(70, 59),
Pair(80, 52),
Pair(90, 45),
Pair(100, 42)
)
),
pink(
Pair(282, 334),
listOf(Pair(20, 100), Pair(30, 90), Pair(40, 86), Pair(60, 84), Pair(80, 80), Pair(90, 75), Pair(100, 73))
)
}
fun Color.saturationRange(): Pair<Int, Int> {
return Pair(lowerBounds.first().first, lowerBounds.last().first)
}
fun Color.brightnessRange(saturation: Int): Pair<Int, Int> {
for (i in 0 until lowerBounds.size - 1) {
val s1 = lowerBounds[i].first.toFloat()
val v1 = lowerBounds[i].second.toFloat()
val s2 = lowerBounds[i + 1].first.toFloat()
val v2 = lowerBounds[i + 1].second.toFloat()
if (saturation.toFloat() in s1..s2) {
val m = (v2 - v1) / (s2 - s1)
val b = v1 - m * s1
val minBrightness = m * saturation + b
return Pair(floor(minBrightness).toInt(), 100)
}
}
return Pair(0, 100)
}

View File

@ -0,0 +1,22 @@
package com.chylex.intellij.coloredbrackets.color
sealed class Hue
object RandomHue : Hue()
data class NumberHue(val value: Int) : Hue()
data class ColorHue(val color: Color) : Hue()
fun Hue.getHueRange(): Pair<Int, Int> {
return when (this) {
is ColorHue -> color.hueRange
is NumberHue -> if (value in 1..359) Pair(value, value) else Pair(0, 360)
RandomHue -> Pair(0, 360)
}
}
fun fromString(str: String): Hue {
return when {
str == "random" -> RandomHue
str.startsWith("#") -> NumberHue(Integer.parseInt(str.replaceFirst("#", ""), 16))
else -> ColorHue(Color.valueOf(str))
}
}

View File

@ -0,0 +1,8 @@
package com.chylex.intellij.coloredbrackets.color
enum class Luminosity {
random,
bright,
light,
dark
}

View File

@ -0,0 +1,156 @@
package com.chylex.intellij.coloredbrackets.color
import kotlin.math.floor
import kotlin.random.Random
/**
* Generate a single random color with specified (or random) hue and luminosity.
*/
fun randomColor(
hue: Hue = RandomHue,
luminosity: Luminosity = Luminosity.random,
): java.awt.Color {
// First we pick a hue (H)
val hueValue = pickHue(hue)
// Then use H to determine saturation (S)
val saturation = pickSaturation(hueValue, hue, luminosity)
// Then use S and H to determine brightness (B)
val brightness = pickBrightness(hueValue, hue, saturation, luminosity)
return toColor(hueValue, saturation, brightness)
}
private fun pickHue(hue: Hue): Int {
val hueRange = hue.getHueRange()
var hueValue = randomWithin(hueRange)
// Instead of storing red as two separate ranges,
// we group them, using negative numbers
if (hueValue < 0) {
hueValue += 360
}
return hueValue
}
private fun pickSaturation(hueValue: Int, hue: Hue, luminosity: Luminosity): Int {
if (hue == ColorHue(Color.monochrome)) {
return 0
}
val color: Color = matchColor(hueValue, hue)
val sMin = color.saturationRange().first
val sMax = color.saturationRange().second
return when (luminosity) {
Luminosity.random -> randomWithin(Pair(0, 100))
Luminosity.bright -> randomWithin(Pair(55, sMax))
Luminosity.light -> randomWithin(Pair(sMin, 55))
Luminosity.dark -> randomWithin(Pair(sMax - 10, sMax))
}
}
private fun pickBrightness(hueValue: Int, hue: Hue, saturation: Int, luminosity: Luminosity): Int {
val color: Color = matchColor(hueValue, hue)
val bMin = color.brightnessRange(saturation).first
val bMax = color.brightnessRange(saturation).second
return when (luminosity) {
Luminosity.random -> randomWithin(Pair(50, 100)) // I set this to 50 arbitrarily, they look more attractive
Luminosity.bright -> randomWithin(Pair(bMin, bMax))
Luminosity.light -> randomWithin(Pair((bMax + bMin) / 2, bMax))
Luminosity.dark -> randomWithin(Pair(bMin, bMin + 20))
}
}
private fun toColor(hueValue: Int, saturation: Int, brightness: Int): java.awt.Color {
val rgb = HSVtoRGB(hueValue, saturation, brightness)
return java.awt.Color(rgb.first, rgb.second, rgb.third)
}
private fun HSVtoRGB(hueValue: Int, saturation: Int, brightness: Int): Triple<Int, Int, Int> {
// This doesn't work for the values of 0 and 360
// Here's the hacky fix
// Rebase the h,s,v values
val h: Float = hueValue.coerceIn(1, 359) / 360f
val s = saturation / 100f
val v = brightness / 100f
val hI = floor(h * 6f).toInt()
val f = h * 6f - hI
val p = v * (1f - s)
val q = v * (1f - f * s)
val t = v * (1f - (1f - f) * s)
var r = 256f
var g = 256f
var b = 256f
when (hI) {
0 -> {
r = v; g = t; b = p
}
1 -> {
r = q; g = v; b = p
}
2 -> {
r = p; g = v; b = t
}
3 -> {
r = p; g = q; b = v
}
4 -> {
r = t; g = p; b = v
}
5 -> {
r = v; g = p; b = q
}
}
return Triple(floor(r * 255f).toInt(), floor(g * 255f).toInt(), floor(b * 255f).toInt())
}
/**
* Turns hue into a color if it isn't already one.
* First we check if hue was passed in as a color, and just return that if it is.
* If not, we iterate through every color to see which one the given hueValue fits in.
* For some reason if a matching hue is not found, just return Monochrome.
*/
private fun matchColor(hueValue: Int, hue: Hue): Color {
return when (hue) {
is ColorHue -> hue.color
else -> {
// Maps red colors to make picking hue easier
var hueVal = hueValue
if (hueVal in 334..360) {
hueVal -= 360
}
for (color in Color.values()) {
if (hueVal in color.hueRange.first..color.hueRange.second) {
return color
}
}
// Returning Monochrome if we can't find a value, but this should never happen
return Color.monochrome
}
}
}
private fun randomWithin(range: Pair<Int, Int>): Int {
// Generate random evenly distinct number from:
// https://martin.ankerl.com/2009/12/09/how-to-create-random-colors-programmatically/
val goldenRatio = 0.618033988749895
var r = Random.nextDouble()
r += goldenRatio
r %= 1
return floor(range.first + r * (range.second + 1 - range.first)).toInt()
}

View File

@ -0,0 +1,269 @@
package com.chylex.intellij.coloredbrackets.indents
import com.chylex.intellij.coloredbrackets.RainbowInfo
import com.chylex.intellij.coloredbrackets.util.alphaBlend
import com.chylex.intellij.coloredbrackets.util.endOffset
import com.chylex.intellij.coloredbrackets.util.findNextSibling
import com.chylex.intellij.coloredbrackets.util.findPrevSibling
import com.chylex.intellij.coloredbrackets.util.lineNumber
import com.chylex.intellij.coloredbrackets.util.startOffset
import com.intellij.openapi.editor.Document
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.SoftWrap
import com.intellij.openapi.editor.ex.EditorEx
import com.intellij.openapi.editor.impl.EditorImpl
import com.intellij.openapi.editor.impl.view.EditorPainter
import com.intellij.openapi.editor.impl.view.VisualLinesIterator
import com.intellij.openapi.editor.markup.CustomHighlighterRenderer
import com.intellij.openapi.editor.markup.RangeHighlighter
import com.intellij.openapi.util.Condition
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiManager
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.psi.xml.XmlFile
import com.intellij.psi.xml.XmlTag
import com.intellij.psi.xml.XmlToken
import com.intellij.psi.xml.XmlTokenType
import com.intellij.ui.paint.LinePainter2D
import com.intellij.util.text.CharArrayUtil
import java.awt.Graphics
import java.awt.Graphics2D
import kotlin.math.max
/** From [com.intellij.codeInsight.daemon.impl.IndentGuideRenderer]
* Commit history : https://sourcegraph.com/github.com/JetBrains/intellij-community/-/blob/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/IndentGuideRenderer.java#tab=history
* */
class RainbowIndentGuideRenderer : CustomHighlighterRenderer {
override fun paint(editor: Editor, highlighter: RangeHighlighter, g: Graphics) {
if (editor !is EditorEx) return
val rainbowInfo = getRainbowInfo(editor, highlighter) ?: return
val startOffset = highlighter.startOffset
val doc = highlighter.document
if (startOffset >= doc.textLength) return
val endOffset = highlighter.endOffset
var off: Int
var startLine = doc.getLineNumber(startOffset)
val chars = doc.charsSequence
do {
val start = doc.getLineStartOffset(startLine)
val end = doc.getLineEndOffset(startLine)
off = CharArrayUtil.shiftForward(chars, start, end, " \t")
startLine--
} while (startLine > 1 && off < doc.textLength && chars[off] == '\n')
val startPosition = editor.offsetToVisualPosition(off)
val indentColumn = startPosition.column
if (indentColumn <= 0) return
val foldingModel = editor.foldingModel
if (foldingModel.isOffsetCollapsed(off)) return
val headerRegion = foldingModel.getCollapsedRegionAtOffset(doc.getLineEndOffset(doc.getLineNumber(off)))
val tailRegion = foldingModel.getCollapsedRegionAtOffset(doc.getLineStartOffset(doc.getLineNumber(endOffset)))
if (tailRegion != null && tailRegion === headerRegion) return
val guide = editor.indentsModel.caretIndentGuide
val selected = if (guide != null) {
val caretModel = editor.caretModel
val caretOffset = caretModel.offset
caretOffset in off until endOffset && caretModel.logicalPosition.column == indentColumn
}
else false
val lineHeight = editor.getLineHeight()
val start = editor.visualPositionToXY(startPosition)
start.y += lineHeight
val endPosition = editor.offsetToVisualPosition(endOffset)
val end = editor.visualPositionToXY(endPosition)
var maxY = end.y
if (endPosition.line == editor.offsetToVisualPosition(doc.textLength).line) {
maxY += lineHeight
}
val clip = g.clipBounds
if (clip != null) {
if (clip.y >= maxY || clip.y + clip.height <= start.y) {
return
}
maxY = StrictMath.min(maxY, clip.y + clip.height)
}
if (start.y >= maxY) return
val targetX = max(0, start.x + EditorPainter.getIndentGuideShift(editor)).toDouble()
g.color = if (selected) {
rainbowInfo.color
}
else {
val defaultBackground = editor.colorsScheme.defaultBackground
rainbowInfo.color.alphaBlend(defaultBackground, 0.2f)
}
// There is a possible case that indent line intersects soft wrap-introduced text. Example:
// this is a long line <soft-wrap>
// that| is soft-wrapped
// |
// | <- vertical indent
//
// Also it's possible that no additional intersections are added because of soft wrap:
// this is a long line <soft-wrap>
// | that is soft-wrapped
// |
// | <- vertical indent
// We want to use the following approach then:
// 1. Show only active indent if it crosses soft wrap-introduced text;
// 2. Show indent as is if it doesn't intersect with soft wrap-introduced text;
val softWraps = editor.softWrapModel.registeredSoftWraps
if (selected || softWraps.isEmpty()) {
LinePainter2D.paint(g as Graphics2D, targetX, start.y.toDouble(), targetX, maxY - 1.toDouble())
}
else {
var startY = start.y
var startVisualLine = startPosition.line + 1
if (clip != null && startY < clip.y) {
startY = clip.y
startVisualLine = editor.yToVisualLine(clip.y)
}
val it = VisualLinesIterator(editor as EditorImpl, startVisualLine)
while (!it.atEnd()) {
val currY: Int = it.y
if (currY >= startY) {
if (currY >= maxY) break
if (it.startsWithSoftWrap()) {
val softWrap: SoftWrap = softWraps[it.startOrPrevWrapIndex]
if (softWrap.indentInColumns < indentColumn) {
if (startY < currY) {
LinePainter2D.paint((g as Graphics2D), targetX, startY.toDouble(), targetX, currY - 1.toDouble())
}
startY = currY + lineHeight
}
}
}
it.advance()
}
if (startY < maxY) {
LinePainter2D.paint((g as Graphics2D), targetX, startY.toDouble(), targetX, maxY - 1.toDouble())
}
}
}
companion object {
private val XML_TAG_PARENT_CONDITION = Condition<PsiElement> { it is XmlTag }
private val XML_END_TAG_START_CONDITION: (PsiElement) -> Boolean = { element ->
element is XmlToken && element.tokenType == XmlTokenType.XML_END_TAG_START
}
private val XML_TAG_END_CONDITION: (PsiElement) -> Boolean = { element ->
element is XmlToken && element.tokenType == XmlTokenType.XML_TAG_END
}
private fun getRainbowInfo(editor: EditorEx, highlighter: RangeHighlighter): RainbowInfo? {
val virtualFile = editor.virtualFile?.takeIf { it.isValid } ?: return null
val document = editor.document
val project = editor.project ?: return null
val psiFile = PsiManager.getInstance(project).findFile(virtualFile) ?: return null
var element = try {
psiFile.findElementAt(highlighter.endOffset)?.parent ?: return null
} catch (e: Throwable) {
return null
}
var rainbowInfo = RainbowInfo.RAINBOW_INFO_KEY[element]
if (rainbowInfo == null && psiFile is XmlFile && element !is XmlTag) {
element = PsiTreeUtil.findFirstParent(element, true, XML_TAG_PARENT_CONDITION) ?: return null
rainbowInfo = RainbowInfo.RAINBOW_INFO_KEY[element] ?: return null
}
if (!element.isValid || !checkBoundary(document, element, highlighter)) {
return null
}
return rainbowInfo
}
/***
* introduced from https://github.com/izhangzhihao/intellij-rainbow-brackets/commit/d9d40e6910e9c15fbdcba12280df18019ea170b5
*/
private fun checkBoundary(document: Document, element: PsiElement, highlighter: RangeHighlighter): Boolean {
val elementStartLine = document.lineNumber(element.startOffset) ?: return false
val highlighterStartLine = document.lineNumber(highlighter.startOffset) ?: return false
var xmlStartTagEndLine: Int? = null
var xmlEndTagStartLine: Int? = null
val isValidStartBoundary = if (element is XmlTag) {
/*
* <tag // [*] element & highlighter start line
* | <- vertical indent
* > // [*] highlighter start/end line, start tag end line
* | <- vertical indent
* </tag // [*] highlighter start/end line, end tag start line
* | <- vertical indent
* > // [ ] element/highlighter end line
*/
xmlStartTagEndLine = element.getStartTagEndLineNumber(document)
xmlEndTagStartLine = element.getEndTagStartLineNumber(document)
highlighterStartLine == elementStartLine ||
highlighterStartLine == xmlStartTagEndLine ||
highlighterStartLine == xmlEndTagStartLine
}
else {
/*
* Element start line > Highlighter start line:
* function foo(arg1, // highlighter start line
* | arg2) { // element start line
* | <- vertical indent
* } // element & highlighter end line
*/
elementStartLine >= highlighterStartLine
}
if (!isValidStartBoundary) {
return false
}
val elementEndLine = document.lineNumber(element.endOffset) ?: return false
val highlighterEndLine = document.lineNumber(highlighter.endOffset) ?: return false
val isValidEndBoundary = if (element is XmlTag) {
/*
* <tag // [ ] element & highlighter start line
* | <- vertical indent
* > // [*] highlighter start/end line, start tag end line
* | <- vertical indent
* </tag // [*] highlighter start/end line, end tag start line
* | <- vertical indent
* > // [*] element/highlighter end line
*/
highlighterEndLine == elementEndLine ||
highlighterEndLine == xmlStartTagEndLine ||
highlighterEndLine == xmlEndTagStartLine
}
else {
/*
* Element end line != Highlighter end line:
* function foo() { // element & highlighter start line
* | <- vertical indent
* var bar = "bar"; // highlighter end line
* } // element end line
*/
elementEndLine == highlighterEndLine
}
if (!isValidEndBoundary) {
return false
}
return true
}
private fun XmlTag.getStartTagEndLineNumber(document: Document): Int? =
firstChild?.findNextSibling(XML_TAG_END_CONDITION)?.let { document.lineNumber(it.startOffset) }
private fun XmlTag.getEndTagStartLineNumber(document: Document): Int? =
lastChild?.findPrevSibling(XML_END_TAG_START_CONDITION)?.let { document.lineNumber(it.startOffset) }
}
}

View File

@ -0,0 +1,369 @@
package com.chylex.intellij.coloredbrackets.indents
import com.chylex.intellij.coloredbrackets.settings.RainbowSettings
import com.intellij.codeHighlighting.TextEditorHighlightingPass
import com.intellij.codeInsight.highlighting.BraceMatchingUtil
import com.intellij.codeInsight.highlighting.CodeBlockSupportHandler
import com.intellij.ide.actions.ToggleZenModeAction
import com.intellij.lang.LanguageParserDefinitions
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.IndentGuideDescriptor
import com.intellij.openapi.editor.ex.EditorEx
import com.intellij.openapi.editor.ex.util.EditorUtil
import com.intellij.openapi.editor.markup.HighlighterTargetArea
import com.intellij.openapi.editor.markup.MarkupModel
import com.intellij.openapi.editor.markup.RangeHighlighter
import com.intellij.openapi.progress.ProgressIndicator
import com.intellij.openapi.progress.ProgressManager
import com.intellij.openapi.project.DumbAware
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Key
import com.intellij.openapi.util.Segment
import com.intellij.openapi.util.TextRange
import com.intellij.psi.PsiFile
import com.intellij.psi.tree.TokenSet
import com.intellij.util.DocumentUtil
import com.intellij.util.containers.IntStack
import com.intellij.util.text.CharArrayUtil
import java.lang.StrictMath.abs
import java.lang.StrictMath.min
import java.util.Collections
/** From [com.intellij.codeInsight.daemon.impl.IndentsPass]
* Commit history: https://sourcegraph.com/github.com/JetBrains/intellij-community/-/blob/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/IndentsPass.java#tab=history
* mirror changes start from `Make it possible to ignore indent guides more granularly and do so for C#`
* */
class RainbowIndentsPass internal constructor(
project: Project,
editor: Editor,
private val myFile: PsiFile,
) : TextEditorHighlightingPass(project, editor.document, false), DumbAware {
private val myEditor: EditorEx = editor as EditorEx
@Volatile
private var myRanges = emptyList<TextRange>()
@Volatile
private var myDescriptors = emptyList<IndentGuideDescriptor>()
override fun doCollectInformation(progress: ProgressIndicator) {
val stamp = myEditor.getUserData(LAST_TIME_INDENTS_BUILT)
if (stamp != null && stamp.toLong() == nowStamp()) return
myDescriptors = buildDescriptors()
val ranges = ArrayList<TextRange>()
for (descriptor in myDescriptors) {
ProgressManager.checkCanceled()
val endOffset = if (descriptor.endLine < document.lineCount) {
document.getLineStartOffset(descriptor.endLine)
}
else {
document.textLength
}
ranges.add(TextRange(document.getLineStartOffset(descriptor.startLine), endOffset))
}
Collections.sort(ranges, Segment.BY_START_OFFSET_THEN_END_OFFSET)
myRanges = ranges
}
private fun nowStamp(): Long = if (isRainbowIndentGuidesShown(this.myProject)) document.modificationStamp xor (EditorUtil.getTabSize(myEditor).toLong() shl 24) else -1
override fun doApplyInformationToEditor() {
val stamp = myEditor.getUserData(LAST_TIME_INDENTS_BUILT)
val nowStamp = nowStamp()
if (stamp == nowStamp) return
myEditor.putUserData(LAST_TIME_INDENTS_BUILT, nowStamp)
val oldHighlighters = myEditor.getUserData(INDENT_HIGHLIGHTERS_IN_EDITOR_KEY)
if (nowStamp == -1L) {
if (oldHighlighters != null) {
for (oldHighlighter in oldHighlighters) {
oldHighlighter.dispose()
}
oldHighlighters.clear()
}
return
}
val newHighlighters = ArrayList<RangeHighlighter>()
val mm = myEditor.markupModel
var curRange = 0
if (oldHighlighters != null) {
// after document change some range highlighters could have become invalid, or the order could have been broken
oldHighlighters.sortWith(Comparator.comparing { h: RangeHighlighter -> !h.isValid }
.thenComparing(Segment.BY_START_OFFSET_THEN_END_OFFSET))
var curHighlight = 0
while (curRange < myRanges.size && curHighlight < oldHighlighters.size) {
val range = myRanges[curRange]
val highlighter = oldHighlighters[curHighlight]
if (!highlighter.isValid) break
val cmp = compare(range, highlighter)
when {
cmp < 0 -> {
newHighlighters.add(createHighlighter(mm, range))
curRange++
}
cmp > 0 -> {
highlighter.dispose()
curHighlight++
}
else -> {
newHighlighters.add(highlighter)
curHighlight++
curRange++
}
}
}
while (curHighlight < oldHighlighters.size) {
val highlighter = oldHighlighters[curHighlight]
if (!highlighter.isValid) break
highlighter.dispose()
curHighlight++
}
}
val startRangeIndex = curRange
DocumentUtil.executeInBulk(document, myRanges.size > 10000) {
for (i in startRangeIndex until myRanges.size) {
newHighlighters.add(createHighlighter(mm, myRanges[i]))
}
}
myEditor.putUserData(INDENT_HIGHLIGHTERS_IN_EDITOR_KEY, newHighlighters)
myEditor.indentsModel.assumeIndents(myDescriptors)
}
private fun buildDescriptors(): List<IndentGuideDescriptor> {
if (!isRainbowIndentGuidesShown(this.myProject)) return emptyList()
val calculator = IndentsCalculator()
calculator.calculate()
val lineIndents = calculator.lineIndents
val lines = IntStack()
val indents = IntStack()
lines.push(0)
indents.push(0)
val descriptors = ArrayList<IndentGuideDescriptor>()
for (line in 1 until lineIndents.size) {
ProgressManager.checkCanceled()
val curIndent = abs(lineIndents[line])
while (!indents.empty() && curIndent <= indents.peek()) {
ProgressManager.checkCanceled()
val level = indents.pop()
val startLine = lines.pop()
if (level > 0) {
for (i in startLine until line) {
if (level != abs(lineIndents[i])) {
descriptors.add(createDescriptor(level, startLine, line, lineIndents))
break
}
}
}
}
val prevLine = line - 1
val prevIndent = abs(lineIndents[prevLine])
if (curIndent - prevIndent > 1) {
lines.push(prevLine)
indents.push(prevIndent)
}
}
while (!indents.empty()) {
ProgressManager.checkCanceled()
val level = indents.pop()
val startLine = lines.pop()
if (level > 0) {
descriptors.add(createDescriptor(level, startLine, document.lineCount, lineIndents))
}
}
return descriptors
}
private fun createDescriptor(
level: Int,
startLine: Int,
endLine: Int,
lineIndents: IntArray,
): IndentGuideDescriptor {
var sLine = startLine
while (sLine > 0 && lineIndents[sLine] < 0) sLine--
// int codeConstructStartLine = findCodeConstructStartLine(startLine);
return IndentGuideDescriptor(level, sLine, endLine)
}
/*
private fun findCodeConstructStart(startLine: Int): Int? {
val document = myEditor.document
val text = document.immutableCharSequence
val lineStartOffset = document.getLineStartOffset(startLine)
val firstNonWsOffset = CharArrayUtil.shiftForward(text, lineStartOffset, " \t")
val type = PsiUtilBase.getPsiFileAtOffset(myFile, firstNonWsOffset).fileType
val language = PsiUtilCore.getLanguageAtOffset(myFile, firstNonWsOffset)
val braceMatcher = BraceMatchingUtil.getBraceMatcher(type, language)
val iterator = myEditor.highlighter.createIterator(firstNonWsOffset)
return if (braceMatcher.isLBraceToken(iterator, text, type)) {
braceMatcher.getCodeConstructStart(myFile, firstNonWsOffset)
} else null
}
private fun findCodeConstructStartLine(startLine: Int): Int {
val codeConstructStart = findCodeConstructStart(startLine)
return if (codeConstructStart != null) myEditor.document.getLineNumber(codeConstructStart) else startLine
}
*/
private inner class IndentsCalculator {
val myComments: MutableMap<String, TokenSet> = HashMap()
val lineIndents = IntArray(document.lineCount) // negative value means the line is empty (or contains a comment) and indent
// (denoted by absolute value) was deduced from enclosing non-empty lines
val myChars: CharSequence
init {
myChars = document.charsSequence
}
/**
* Calculates line indents for the [target document][.myDocument].
*/
fun calculate() {
val fileType = myFile.fileType
val tabSize = EditorUtil.getTabSize(myEditor)
for (line in lineIndents.indices) {
ProgressManager.checkCanceled()
val lineStart = document.getLineStartOffset(line)
val lineEnd = document.getLineEndOffset(line)
var offset = lineStart
var column = 0
outer@ while (offset < lineEnd) {
when (myChars[offset]) {
' ' -> column++
'\t' -> column = (column / tabSize + 1) * tabSize
else -> break@outer
}
offset++
}
// treating commented lines in the same way as empty lines
// Blank line marker
lineIndents[line] = if (offset == lineEnd || isComment(offset)) -1 else column
}
var topIndent = 0
var line = 0
while (line < lineIndents.size) {
ProgressManager.checkCanceled()
if (lineIndents[line] >= 0) {
topIndent = lineIndents[line]
}
else {
val startLine = line
while (line < lineIndents.size && lineIndents[line] < 0) {
line++
}
val bottomIndent = if (line < lineIndents.size) lineIndents[line] else topIndent
var indent = min(topIndent, bottomIndent)
if (bottomIndent < topIndent) {
val lineStart = document.getLineStartOffset(line)
val lineEnd = document.getLineEndOffset(line)
val nonWhitespaceOffset = CharArrayUtil.shiftForward(myChars, lineStart, lineEnd, " \t")
val iterator = myEditor.highlighter.createIterator(nonWhitespaceOffset)
val tokenType = iterator.tokenType
if (BraceMatchingUtil.isRBraceToken(iterator, myChars, fileType) ||
tokenType != null &&
CodeBlockSupportHandler.findMarkersRanges(myFile, tokenType.language, nonWhitespaceOffset).isNotEmpty()
) {
indent = topIndent
}
}
for (blankLine in startLine until line) {
assert(lineIndents[blankLine] == -1)
lineIndents[blankLine] = -min(topIndent, indent)
}
line-- // will be incremented back at the end of the loop;
}
line++
}
}
private fun isComment(offset: Int): Boolean {
val it = myEditor.highlighter.createIterator(offset)
val tokenType = try {
it.tokenType
} catch (e: Throwable) {
return false
}
val language = tokenType.language
var comments: TokenSet? = myComments[language.id]
if (comments == null) {
val definition = LanguageParserDefinitions.INSTANCE.forLanguage(language)
if (definition != null) {
comments = definition.commentTokens
}
if (comments == null) {
return false
}
else {
myComments[language.id] = comments
}
}
return comments.contains(tokenType)
}
}
companion object {
private val INDENT_HIGHLIGHTERS_IN_EDITOR_KEY = Key.create<MutableList<RangeHighlighter>>("_INDENT_HIGHLIGHTERS_IN_EDITOR_KEY_")
private val LAST_TIME_INDENTS_BUILT = Key.create<Long>("_LAST_TIME_INDENTS_BUILT_")
private val RENDERER = RainbowIndentGuideRenderer()
private fun isRainbowIndentGuidesShown(project: Project): Boolean {
if (RainbowSettings.instance.disableRainbowIndentsInZenMode && isZenModeEnabled(project)) {
return false
}
return RainbowSettings.instance.isRainbowEnabled && RainbowSettings.instance.isShowRainbowIndentGuides
}
private fun isZenModeEnabled(project: Project) =
ToggleZenModeAction.isZenModeEnabled(project)
private fun createHighlighter(mm: MarkupModel, range: TextRange): RangeHighlighter {
return mm.addRangeHighlighter(
range.startOffset,
range.endOffset,
0,
null,
HighlighterTargetArea.EXACT_RANGE
).apply {
customRenderer = RENDERER
}
}
private fun compare(r: TextRange, h: RangeHighlighter): Int {
val answer = r.startOffset - h.startOffset
return if (answer != 0) answer else r.endOffset - h.endOffset
}
}
}

View File

@ -0,0 +1,28 @@
package com.chylex.intellij.coloredbrackets.indents
import com.intellij.codeHighlighting.Pass
import com.intellij.codeHighlighting.TextEditorHighlightingPass
import com.intellij.codeHighlighting.TextEditorHighlightingPassFactory
import com.intellij.codeHighlighting.TextEditorHighlightingPassFactoryRegistrar
import com.intellij.codeHighlighting.TextEditorHighlightingPassRegistrar
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiFile
class RainbowIndentsPassFactory :
TextEditorHighlightingPassFactoryRegistrar, TextEditorHighlightingPassFactory {
override fun createHighlightingPass(file: PsiFile, editor: Editor): TextEditorHighlightingPass {
return RainbowIndentsPass(file.project, editor, file)
}
override fun registerHighlightingPassFactory(registrar: TextEditorHighlightingPassRegistrar, project: Project) {
registrar.registerTextEditorHighlightingPass(
this,
TextEditorHighlightingPassRegistrar.Anchor.LAST,
Pass.LAST_PASS,
false,
false
)
}
}

View File

@ -0,0 +1,12 @@
package com.chylex.intellij.coloredbrackets.listener
import com.chylex.intellij.coloredbrackets.RainbowHighlighter
import com.intellij.openapi.editor.colors.EditorColorsListener
import com.intellij.openapi.editor.colors.EditorColorsScheme
class RainbowColorsSchemeListener : EditorColorsListener {
override fun globalSchemeChange(scheme: EditorColorsScheme?) {
scheme?.let { RainbowHighlighter.fixHighlighting(it) }
}
}

View File

@ -1,4 +1,4 @@
package com.github.izhangzhihao.rainbow.brackets.provider
package com.chylex.intellij.coloredbrackets.provider
import com.intellij.lang.BracePair

View File

@ -1,4 +1,4 @@
package com.github.izhangzhihao.rainbow.brackets.provider
package com.chylex.intellij.coloredbrackets.provider
import com.intellij.lang.BracePair
import com.jetbrains.lang.dart.DartTokenTypes.GT

View File

@ -0,0 +1,14 @@
package com.chylex.intellij.coloredbrackets.provider
import com.goide.template.GoTemplateTypes.LDOUBLE_BRACE
import com.goide.template.GoTemplateTypes.LPAREN
import com.goide.template.GoTemplateTypes.RDOUBLE_BRACE
import com.goide.template.GoTemplateTypes.RPAREN
import com.intellij.lang.BracePair
class GoTemplateProvider : BracePairProvider {
override fun pairs(): List<BracePair> = listOf(
BracePair(LDOUBLE_BRACE, RDOUBLE_BRACE, true),
BracePair(LPAREN, RPAREN, true)
)
}

View File

@ -1,4 +1,4 @@
package com.github.izhangzhihao.rainbow.brackets.provider
package com.chylex.intellij.coloredbrackets.provider
import com.intellij.lang.BracePair
import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes.mGT

View File

@ -1,10 +1,9 @@
package com.github.izhangzhihao.rainbow.brackets.provider
package com.chylex.intellij.coloredbrackets.provider
import com.intellij.lang.BracePair
import org.jetbrains.kotlin.lexer.KtTokens.GT
import org.jetbrains.kotlin.lexer.KtTokens.LT
class KotlinBracePairProvider : BracePairProvider {
override fun pairs(): List<BracePair> = listOf(BracePair(LT, GT, false))
}

View File

@ -0,0 +1,12 @@
package com.chylex.intellij.coloredbrackets.provider
import com.intellij.lang.BracePair
import com.jetbrains.php.lang.lexer.PhpTokenTypes
// https://github.com/izhangzhihao/intellij-rainbow-brackets/issues/423
class PHPBracePairProvider : BracePairProvider {
override fun blackList(): List<BracePair> = listOf(
BracePair(PhpTokenTypes.PHP_OPENING_TAG, PhpTokenTypes.PHP_CLOSING_TAG, false),
BracePair(PhpTokenTypes.PHP_ECHO_OPENING_TAG, PhpTokenTypes.PHP_CLOSING_TAG, false)
)
}

View File

@ -0,0 +1,13 @@
package com.chylex.intellij.coloredbrackets.provider
import com.intellij.lang.BracePair
import com.intellij.sh.ShTypes
class SHBracePairProvider : BracePairProvider {
override fun blackList(): List<BracePair> = listOf(
BracePair(ShTypes.HEREDOC_MARKER_START, ShTypes.HEREDOC_MARKER_END, false),
BracePair(ShTypes.DO, ShTypes.DONE, true),
BracePair(ShTypes.IF, ShTypes.FI, true),
BracePair(ShTypes.CASE, ShTypes.ESAC, true)
)
}

View File

@ -1,4 +1,4 @@
package com.github.izhangzhihao.rainbow.brackets.provider
package com.chylex.intellij.coloredbrackets.provider
import com.intellij.lang.BracePair
import com.intellij.sql.psi.SqlCommonKeywords.SQL_CASE

View File

@ -1,4 +1,4 @@
package com.github.izhangzhihao.rainbow.brackets.provider
package com.chylex.intellij.coloredbrackets.provider
import com.intellij.lang.BracePair
import com.intellij.lang.javascript.JSTokenTypes.GT

View File

@ -0,0 +1,59 @@
package com.chylex.intellij.coloredbrackets.settings
import com.chylex.intellij.coloredbrackets.RainbowHighlighter
import com.chylex.intellij.coloredbrackets.settings.form.RainbowOptionsPanel
import com.intellij.application.options.colors.ColorAndFontOptions
import com.intellij.application.options.colors.ColorAndFontPanelFactory
import com.intellij.application.options.colors.ColorAndFontSettingsListener
import com.intellij.application.options.colors.NewColorAndFontPanel
import com.intellij.application.options.colors.PreviewPanel
import com.intellij.application.options.colors.SchemesPanel
import com.intellij.openapi.options.colors.AttributesDescriptor
import com.intellij.openapi.options.colors.ColorAndFontDescriptorsProvider
import com.intellij.openapi.options.colors.ColorDescriptor
import com.intellij.psi.codeStyle.DisplayPriority
import com.intellij.psi.codeStyle.DisplayPrioritySortable
class RainbowColorsPageFactory : ColorAndFontPanelFactory, ColorAndFontDescriptorsProvider, DisplayPrioritySortable {
override fun getDisplayName(): String = RAINBOW_BRACKETS_GROUP
override fun getPanelDisplayName(): String = RAINBOW_BRACKETS_GROUP
override fun getPriority(): DisplayPriority = DisplayPriority.COMMON_SETTINGS
override fun createPanel(options: ColorAndFontOptions): NewColorAndFontPanel {
val emptyPreview = PreviewPanel.Empty()
val schemesPanel = SchemesPanel(options)
val optionsPanel = RainbowOptionsPanel(options, schemesPanel, RAINBOW_BRACKETS_GROUP)
schemesPanel.addListener(object : ColorAndFontSettingsListener.Abstract() {
override fun schemeChanged(source: Any) {
optionsPanel.updateOptionsList()
}
})
return NewColorAndFontPanel(schemesPanel, optionsPanel, emptyPreview, RAINBOW_BRACKETS_GROUP, null, null)
}
override fun getAttributeDescriptors(): Array<AttributesDescriptor> = ATTRIBUTE_DESCRIPTORS
override fun getColorDescriptors(): Array<ColorDescriptor> = emptyArray()
companion object {
private const val RAINBOW_BRACKETS_GROUP = "Colored Brackets"
private val ATTRIBUTE_DESCRIPTORS: Array<AttributesDescriptor> by lazy {
createDescriptors(RainbowHighlighter.NAME_ROUND_BRACKETS) +
createDescriptors(RainbowHighlighter.NAME_SQUARE_BRACKETS) +
createDescriptors(RainbowHighlighter.NAME_SQUIGGLY_BRACKETS) +
createDescriptors(RainbowHighlighter.NAME_ANGLE_BRACKETS)
}
private fun createDescriptors(name: String): Array<AttributesDescriptor> {
return RainbowHighlighter.getRainbowAttributesKeys(name)
.map { key -> AttributesDescriptor("$name:$key", key) }
.toTypedArray()
}
}
}

View File

@ -0,0 +1,66 @@
package com.chylex.intellij.coloredbrackets.settings
import com.chylex.intellij.coloredbrackets.settings.form.RainbowSettingsForm
import com.intellij.openapi.options.ConfigurationException
import com.intellij.openapi.options.SearchableConfigurable
import org.jetbrains.annotations.Nls
import javax.swing.JComponent
class RainbowConfigurable : SearchableConfigurable {
private var settingsForm: RainbowSettingsForm? = null
override fun createComponent(): JComponent? {
settingsForm = settingsForm ?: RainbowSettingsForm()
return settingsForm?.component()
}
override fun isModified(): Boolean {
return settingsForm?.isModified ?: return false
}
@Throws(ConfigurationException::class)
override fun apply() {
val settings = RainbowSettings.instance
settings.isRainbowEnabled = settingsForm?.isRainbowEnabled() ?: true
settings.isEnableRainbowRoundBrackets = settingsForm?.isRainbowRoundBracketsEnabled() ?: true
settings.isEnableRainbowAngleBrackets = settingsForm?.isRainbowAngleBracketsEnabled() ?: true
settings.isEnableRainbowSquigglyBrackets = settingsForm?.isRainbowSquigglyBracketsEnabled() ?: true
settings.isEnableRainbowSquareBrackets = settingsForm?.isRainbowSquareBracketsEnabled() ?: true
settings.isShowRainbowIndentGuides = settingsForm?.isShowRainbowIndentGuides() ?: false
settings.isDoNOTRainbowifyBracketsWithoutContent = settingsForm?.isDoNOTRainbowifyBracketsWithoutContent()
?: false
settings.isDoNOTRainbowifyTheFirstLevel = settingsForm?.isDoNOTRainbowifyTheFirstLevel() ?: false
settings.pressAnyKeyToRemoveTheHighlightingEffects = settingsForm?.pressAnyKeyToRemoveTheHighlightingEffects()
?: false
settings.applyColorsOfRoundForAllBrackets = settingsForm?.applyColorsOfRoundForAllBrackets()
?: false
settings.cycleCountOnAllBrackets = settingsForm?.cycleCountOnAllBrackets()
?: false
settings.numberOfColors = settingsForm?.numberOfColors() ?: 5
settings.languageBlacklist = settingsForm?.languageBlacklist() ?: emptySet()
settings.disableRainbowIndentsInZenMode = settingsForm?.disableRainbowIndentsInZenMode() ?: true
settings.useColorGenerator = settingsForm?.useColorGenerator() ?: false
settings.rainbowifyTagNameInXML = settingsForm?.rainbowifyTagNameInXML() ?: false
settings.doNOTRainbowifyTemplateString = settingsForm?.doNOTRainbowifyTemplateString() ?: false
settings.doNOTRainbowifyBigFiles = settingsForm?.doNOTRainbowifyBigFiles() ?: true
settings.bigFilesLinesThreshold = settingsForm?.bigFilesLinesThreshold() ?: 1000
settings.rainbowifyPythonKeywords = settingsForm?.rainbowifyPythonKeywords() ?: false
}
override fun reset() {
settingsForm?.loadSettings()
}
override fun disposeUIResources() {
settingsForm = null
}
@Nls
override fun getDisplayName() = "Colored Brackets"
override fun getId(): String = ID
companion object {
const val ID = "com.chylex.coloredbrackets.settings"
}
}

View File

@ -0,0 +1,60 @@
package com.chylex.intellij.coloredbrackets.settings
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.components.PersistentStateComponent
import com.intellij.openapi.components.State
import com.intellij.openapi.components.Storage
import com.intellij.util.xmlb.XmlSerializerUtil.copyBean
import org.jetbrains.annotations.Nullable
@State(name = "ColoredBracketsSettings", storages = [Storage("colored_brackets.xml")])
class RainbowSettings : PersistentStateComponent<RainbowSettings> {
/**
* default value
*/
var isRainbowEnabled = true
var isEnableRainbowRoundBrackets = true
var isEnableRainbowSquigglyBrackets = true
var isEnableRainbowSquareBrackets = true
var isEnableRainbowAngleBrackets = true
var isShowRainbowIndentGuides = true
var isDoNOTRainbowifyBracketsWithoutContent = false
var isDoNOTRainbowifyTheFirstLevel = false
var isRainbowifyHTMLInsideJS = true
var isRainbowifyKotlinLabel = true
var isRainbowifyKotlinFunctionLiteralBracesAndArrow = true
var isOverrideMatchedBraceAttributes = true
var pressAnyKeyToRemoveTheHighlightingEffects = false
var applyColorsOfRoundForAllBrackets = false
//https://github.com/izhangzhihao/intellij-rainbow-brackets/issues/391
var cycleCountOnAllBrackets = false
var numberOfColors = 5
var disableRainbowIndentsInZenMode = true
var useColorGenerator = false
var customColorGeneratorOption: String? = null
var rainbowifyTagNameInXML = false
var doNOTRainbowifyTemplateString = false
var doNOTRainbowifyBigFiles = true
var bigFilesLinesThreshold = 1000
var languageBlacklist: Set<String> = setOf("hocon", "mxml")
var suppressDisabledCheck = false
var suppressBigFileCheck = false
var suppressBlackListCheck = false
var rainbowifyPythonKeywords = false
@Nullable
override fun getState() = this
override fun loadState(state: RainbowSettings) {
copyBean(state, this)
}
companion object {
val instance: RainbowSettings
get() = ApplicationManager.getApplication().getService(RainbowSettings::class.java)
}
}

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.github.izhangzhihao.rainbow.brackets.settings.form.RainbowOptionsPanel">
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.chylex.intellij.coloredbrackets.settings.form.RainbowOptionsPanel">
<grid id="d747" binding="rootPanel" layout-manager="BorderLayout" hgap="0" vgap="0">
<constraints>
<xy x="20" y="20" width="500" height="400"/>

View File

@ -0,0 +1,268 @@
package com.chylex.intellij.coloredbrackets.settings.form
import com.chylex.intellij.coloredbrackets.RainbowHighlighter
import com.chylex.intellij.coloredbrackets.settings.RainbowSettings
import com.intellij.application.options.colors.ColorAndFontOptions
import com.intellij.application.options.colors.ColorAndFontSettingsListener
import com.intellij.application.options.colors.OptionsPanel
import com.intellij.application.options.colors.SchemesPanel
import com.intellij.application.options.colors.TextAttributesDescription
import com.intellij.ide.util.PropertiesComponent
import com.intellij.ui.ColorPanel
import com.intellij.ui.components.JBCheckBox
import com.intellij.ui.treeStructure.Tree
import com.intellij.util.EventDispatcher
import org.jdesktop.swingx.treetable.DefaultMutableTreeTableNode
import java.awt.Color
import java.awt.event.ActionListener
import javax.swing.JLabel
import javax.swing.JPanel
import javax.swing.tree.DefaultMutableTreeNode
import javax.swing.tree.DefaultTreeModel
import javax.swing.tree.TreePath
import javax.swing.tree.TreeSelectionModel
class RainbowOptionsPanel(
private val options: ColorAndFontOptions,
private val schemesProvider: SchemesPanel,
private val category: String,
) : OptionsPanel {
private lateinit var rootPanel: JPanel
private lateinit var optionsTree: Tree
private lateinit var rainbow: JBCheckBox
private lateinit var colorLabel1: JLabel
private lateinit var colorLabel2: JLabel
private lateinit var colorLabel3: JLabel
private lateinit var colorLabel4: JLabel
private lateinit var colorLabel5: JLabel
private val colorLabels: Array<JLabel>
private lateinit var color1: ColorPanel
private lateinit var color2: ColorPanel
private lateinit var color3: ColorPanel
private lateinit var color4: ColorPanel
private lateinit var color5: ColorPanel
private val colors: Array<ColorPanel>
private lateinit var gradientLabel: JLabel
private val properties: PropertiesComponent = PropertiesComponent.getInstance()
private val eventDispatcher: EventDispatcher<ColorAndFontSettingsListener> =
EventDispatcher.create(ColorAndFontSettingsListener::class.java)
init {
colors = arrayOf(color1, color2, color3, color4, color5)
colorLabels = arrayOf(colorLabel1, colorLabel2, colorLabel3, colorLabel4, colorLabel5)
val actionListener = ActionListener {
eventDispatcher.multicaster.settingsChanged()
options.stateChanged()
}
rainbow.addActionListener(actionListener)
for (c in colors) {
c.addActionListener(actionListener)
}
options.addListener(object : ColorAndFontSettingsListener.Abstract() {
override fun settingsChanged() {
if (!schemesProvider.areSchemesLoaded()) return
if (optionsTree.selectedValue != null) {
// update options after global state change
processListValueChanged()
}
}
})
optionsTree.apply {
isRootVisible = false
model = DefaultTreeModel(DefaultMutableTreeTableNode())
selectionModel.selectionMode = TreeSelectionModel.SINGLE_TREE_SELECTION
addTreeSelectionListener {
if (schemesProvider.areSchemesLoaded()) {
processListValueChanged()
}
}
}
}
override fun getPanel(): JPanel = rootPanel
override fun addListener(listener: ColorAndFontSettingsListener) {
eventDispatcher.addListener(listener)
}
override fun updateOptionsList() {
fillOptionsList()
processListValueChanged()
}
private data class DescriptionsNode(val rainbowName: String, val descriptions: List<TextAttributesDescription>) {
override fun toString(): String = rainbowName
}
private fun fillOptionsList() {
val nodes = options.currentDescriptions.asSequence()
.filter { it is TextAttributesDescription && it.group == category }
.map {
val description = it as TextAttributesDescription
val rainbowName = description.toString().split(":")[0]
rainbowName to description
}
.groupBy { it.first }
.map { (rainbowName, descriptions) ->
DefaultMutableTreeNode(
DescriptionsNode(
rainbowName,
descriptions.asSequence().map { it.second }.toList().sortedBy { it.toString() })
)
}
val root = DefaultMutableTreeNode()
for (node in nodes) {
root.add(node)
}
(optionsTree.model as DefaultTreeModel).setRoot(root)
}
private fun processListValueChanged() {
var descriptionsNode = optionsTree.selectedDescriptions
if (descriptionsNode == null) {
properties.getValue(SELECTED_COLOR_OPTION_PROPERTY)?.let { preselected ->
optionsTree.selectOptionByRainbowName(preselected)
descriptionsNode = optionsTree.selectedDescriptions
}
}
descriptionsNode?.run {
properties.setValue(SELECTED_COLOR_OPTION_PROPERTY, rainbowName)
reset(rainbowName, descriptions)
} ?: resetDefault()
}
private fun resetDefault() {
rainbow.isEnabled = false
rainbow.isSelected = false
gradientLabel.text = "Assign each brackets its own color from the spectrum below:"
for (i in 0 until minRange()) {
colors[i].isEnabled = false
colors[i].selectedColor = null
colorLabels[i].isEnabled = false
}
}
private fun reset(rainbowName: String, descriptions: List<TextAttributesDescription>) {
val rainbowOn = RainbowHighlighter.isRainbowEnabled(rainbowName)
rainbow.isEnabled = true
rainbow.isSelected = rainbowOn
gradientLabel.text = "Assign each ${rainbowName.lowercase()} its own color from the spectrum below:"
for (i in 0 until minRange()) {
colors[i].isEnabled = rainbowOn
colorLabels[i].isEnabled = rainbowOn
colors[i].selectedColor = descriptions.indexOfOrNull(i)?.rainbowColor
descriptions.indexOfOrNull(i)?.let { eventDispatcher.multicaster.selectedOptionChanged(it) }
}
}
override fun applyChangesToScheme() {
val scheme = options.selectedScheme
val (rainbowName, descriptions) = optionsTree.selectedDescriptions ?: return
when (rainbowName) {
RainbowHighlighter.NAME_ROUND_BRACKETS,
RainbowHighlighter.NAME_ANGLE_BRACKETS,
RainbowHighlighter.NAME_SQUARE_BRACKETS,
RainbowHighlighter.NAME_SQUIGGLY_BRACKETS,
-> {
RainbowHighlighter.setRainbowEnabled(rainbowName, rainbow.isSelected)
for (i in 0 until minRange()) {
colors[i].selectedColor?.let { color ->
descriptions[i].rainbowColor = color
descriptions[i].apply(scheme)
}
}
}
}
}
private fun minRange() = minOf(RainbowSettings.instance.numberOfColors, 5)
override fun processListOptions(): MutableSet<String> = mutableSetOf(
RainbowHighlighter.NAME_ROUND_BRACKETS,
RainbowHighlighter.NAME_SQUARE_BRACKETS,
RainbowHighlighter.NAME_SQUIGGLY_BRACKETS,
RainbowHighlighter.NAME_ANGLE_BRACKETS
)
override fun showOption(option: String) = Runnable {
optionsTree.selectOptionByRainbowName(option)
}
override fun selectOption(typeToSelect: String) {
optionsTree.selectOptionByType(typeToSelect)
}
companion object {
private const val SELECTED_COLOR_OPTION_PROPERTY = "rainbow.selected.color.option.name"
private var TextAttributesDescription.rainbowColor: Color?
get() = externalForeground
set(value) {
externalForeground = value
}
private val Tree.selectedValue: Any?
get() = (lastSelectedPathComponent as? DefaultMutableTreeNode)?.userObject
private val Tree.selectedDescriptions: DescriptionsNode?
get() = selectedValue as? DescriptionsNode
private fun Tree.findOption(nodeObject: Any, matcher: (Any) -> Boolean): TreePath? {
val model = model as DefaultTreeModel
for (i in 0 until model.getChildCount(nodeObject)) {
val childObject = model.getChild(nodeObject, i)
if (childObject is DefaultMutableTreeNode) {
val data = childObject.userObject
if (matcher(data)) {
return TreePath(model.getPathToRoot(childObject))
}
}
val pathInChild = findOption(childObject, matcher)
if (pathInChild != null) return pathInChild
}
return null
}
private fun Tree.selectOptionByRainbowName(rainbowName: String) {
selectPath(findOption(model.root) { data ->
data is DescriptionsNode
&& rainbowName.isNotBlank()
&& data.rainbowName.contains(rainbowName, ignoreCase = true)
})
}
private fun Tree.selectOptionByType(attributeType: String) {
selectPath(findOption(model.root) { data ->
data is DescriptionsNode && data.descriptions.any { it.type == attributeType }
})
}
private fun Tree.selectPath(path: TreePath?) {
if (path != null) {
selectionPath = path
scrollPathToVisible(path)
}
}
}
}
private fun <E> List<E>.indexOfOrNull(idx: Int): E? = if (idx < this.size) this[idx] else null

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.github.izhangzhihao.rainbow.brackets.settings.form.RainbowSettingsForm">
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.chylex.intellij.coloredbrackets.settings.form.RainbowSettingsForm">
<grid id="27dc6" binding="panel" layout-manager="GridLayoutManager" row-count="2" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
@ -14,7 +14,7 @@
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
<border type="etched" title="Rainbow Brackets"/>
<border type="none"/>
<children>
<component id="2d399" class="javax.swing.JCheckBox" binding="enableRainbow">
<constraints>

View File

@ -0,0 +1,139 @@
package com.chylex.intellij.coloredbrackets.settings.form
import com.chylex.intellij.coloredbrackets.settings.RainbowSettings
import javax.swing.JCheckBox
import javax.swing.JComponent
import javax.swing.JPanel
import javax.swing.JTextField
class RainbowSettingsForm {
private var panel: JPanel? = null
@Suppress("unused")
private var appearancePanel: JPanel? = null
private var enableRainbow: JCheckBox? = null
private var enableRainbowRoundBrackets: JCheckBox? = null
private var enableRainbowSquigglyBrackets: JCheckBox? = null
private var enableRainbowSquareBrackets: JCheckBox? = null
private var enableRainbowAngleBrackets: JCheckBox? = null
private var showRainbowIndentGuides: JCheckBox? = null
private var doNOTRainbowifyBracketsWithoutContent: JCheckBox? = null
private var isDoNOTRainbowifyTheFirstLevel: JCheckBox? = null
private var pressAnyKeyToRemoveTheHighlightingEffects: JCheckBox? = null
private var applyColorsOfRoundForAllBrackets: JCheckBox? = null
private var cycleCountOnAllBrackets: JCheckBox? = null
private var numberOfColors: JTextField? = null
private var languageBlacklist: JTextField? = null
private var disableRainbowIndentsInZenMode: JCheckBox? = null
private var useColorGenerator: JCheckBox? = null
private var rainbowifyTagNameInXML: JCheckBox? = null
private var doNOTRainbowifyTemplateString: JCheckBox? = null
private var doNOTRainbowifyBigFiles: JCheckBox? = null
private var bigFilesLinesThreshold: JTextField? = null
private var rainbowifyPythonKeywords: JCheckBox? = null
private val settings: RainbowSettings = RainbowSettings.instance
fun component(): JComponent? = panel
fun isRainbowEnabled() = enableRainbow?.isSelected
fun isRainbowRoundBracketsEnabled() = enableRainbowRoundBrackets?.isSelected
fun isRainbowSquigglyBracketsEnabled() = enableRainbowSquigglyBrackets?.isSelected
fun isRainbowSquareBracketsEnabled() = enableRainbowSquareBrackets?.isSelected
fun isRainbowAngleBracketsEnabled() = enableRainbowAngleBrackets?.isSelected
fun isShowRainbowIndentGuides() = showRainbowIndentGuides?.isSelected
fun isDoNOTRainbowifyBracketsWithoutContent() = doNOTRainbowifyBracketsWithoutContent?.isSelected
fun isDoNOTRainbowifyTheFirstLevel() = isDoNOTRainbowifyTheFirstLevel?.isSelected
fun pressAnyKeyToRemoveTheHighlightingEffects() = pressAnyKeyToRemoveTheHighlightingEffects?.isSelected
fun applyColorsOfRoundForAllBrackets() = applyColorsOfRoundForAllBrackets?.isSelected
fun cycleCountOnAllBrackets() = cycleCountOnAllBrackets?.isSelected
fun numberOfColors() = numberOfColors?.text?.toIntOrNull()
fun languageBlacklist() =
languageBlacklist?.text?.split(",")?.map { it.trim() }?.filterNot { it.isEmpty() }?.toSet()
fun disableRainbowIndentsInZenMode() = disableRainbowIndentsInZenMode?.isSelected
fun useColorGenerator() = useColorGenerator?.isSelected
fun rainbowifyTagNameInXML() = rainbowifyTagNameInXML?.isSelected
fun doNOTRainbowifyTemplateString() = doNOTRainbowifyTemplateString?.isSelected
fun doNOTRainbowifyBigFiles() = doNOTRainbowifyBigFiles?.isSelected
fun bigFilesLinesThreshold() = bigFilesLinesThreshold?.text?.toIntOrNull()
fun rainbowifyPythonKeywords() = rainbowifyPythonKeywords?.isSelected
val isModified: Boolean
get() = (isRainbowEnabled() != settings.isRainbowEnabled
|| isRainbowAngleBracketsEnabled() != settings.isEnableRainbowAngleBrackets
|| isRainbowRoundBracketsEnabled() != settings.isEnableRainbowRoundBrackets
|| isRainbowSquigglyBracketsEnabled() != settings.isEnableRainbowSquigglyBrackets
|| isRainbowSquareBracketsEnabled() != settings.isEnableRainbowSquareBrackets
|| isShowRainbowIndentGuides() != settings.isShowRainbowIndentGuides
|| isDoNOTRainbowifyBracketsWithoutContent() != settings.isDoNOTRainbowifyBracketsWithoutContent
|| isDoNOTRainbowifyTheFirstLevel() != settings.isDoNOTRainbowifyTheFirstLevel
|| pressAnyKeyToRemoveTheHighlightingEffects() != settings.pressAnyKeyToRemoveTheHighlightingEffects
|| applyColorsOfRoundForAllBrackets() != settings.applyColorsOfRoundForAllBrackets
|| cycleCountOnAllBrackets() != settings.cycleCountOnAllBrackets
|| numberOfColors() != settings.numberOfColors
|| disableRainbowIndentsInZenMode() != settings.disableRainbowIndentsInZenMode
|| useColorGenerator() != settings.useColorGenerator
|| rainbowifyTagNameInXML() != settings.rainbowifyTagNameInXML
|| doNOTRainbowifyTemplateString() != settings.doNOTRainbowifyTemplateString
|| doNOTRainbowifyBigFiles() != settings.doNOTRainbowifyBigFiles
|| bigFilesLinesThreshold() != settings.bigFilesLinesThreshold
|| languageBlacklist() != settings.languageBlacklist
|| rainbowifyPythonKeywords() != settings.rainbowifyPythonKeywords
)
init {
loadSettings()
doNOTRainbowifyBigFiles?.addActionListener { _ -> bigFilesLinesThreshold?.setEnabled(doNOTRainbowifyBigFiles() ?: false) }
}
fun loadSettings() {
enableRainbow?.isSelected = settings.isRainbowEnabled
enableRainbowRoundBrackets?.isSelected = settings.isEnableRainbowRoundBrackets
enableRainbowAngleBrackets?.isSelected = settings.isEnableRainbowAngleBrackets
enableRainbowSquigglyBrackets?.isSelected = settings.isEnableRainbowSquigglyBrackets
enableRainbowSquareBrackets?.isSelected = settings.isEnableRainbowSquareBrackets
showRainbowIndentGuides?.isSelected = settings.isShowRainbowIndentGuides
doNOTRainbowifyBracketsWithoutContent?.isSelected = settings.isDoNOTRainbowifyBracketsWithoutContent
isDoNOTRainbowifyTheFirstLevel?.isSelected = settings.isDoNOTRainbowifyTheFirstLevel
pressAnyKeyToRemoveTheHighlightingEffects?.isSelected = settings.pressAnyKeyToRemoveTheHighlightingEffects
applyColorsOfRoundForAllBrackets?.isSelected = settings.applyColorsOfRoundForAllBrackets
cycleCountOnAllBrackets?.isSelected = settings.cycleCountOnAllBrackets
numberOfColors?.text = settings.numberOfColors.toString()
disableRainbowIndentsInZenMode?.isSelected = settings.disableRainbowIndentsInZenMode
useColorGenerator?.isSelected = settings.useColorGenerator
rainbowifyTagNameInXML?.isSelected = settings.rainbowifyTagNameInXML
doNOTRainbowifyTemplateString?.isSelected = settings.doNOTRainbowifyTemplateString
doNOTRainbowifyBigFiles?.isSelected = settings.doNOTRainbowifyBigFiles
bigFilesLinesThreshold?.text = settings.bigFilesLinesThreshold.toString()
languageBlacklist?.text = settings.languageBlacklist.joinToString(",")
rainbowifyPythonKeywords?.isSelected = settings.rainbowifyPythonKeywords
}
}

View File

@ -0,0 +1,13 @@
package com.chylex.intellij.coloredbrackets.util
import java.awt.Color
fun Color.alphaBlend(background: Color, alpha: Float): Color {
require(alpha in 0.0..1.0) { "alpha(0.0 <= alpha <= 1.0): $alpha" }
val r = (1 - alpha) * background.red + alpha * red
val g = (1 - alpha) * background.green + alpha * green
val b = (1 - alpha) * background.blue + alpha * blue
return Color(r.toInt(), g.toInt(), b.toInt())
}

View File

@ -1,4 +1,4 @@
package com.github.izhangzhihao.rainbow.brackets.util
package com.chylex.intellij.coloredbrackets.util
import com.intellij.openapi.editor.Document

View File

@ -0,0 +1,54 @@
package com.chylex.intellij.coloredbrackets.util
import java.util.concurrent.ConcurrentHashMap
fun <A, R> ((A) -> R).memoize(): (A) -> R = object : (A) -> R {
private val m = MemoizedHandler<((A) -> R), MemoizeKey1<A, R>, R>(this@memoize)
override fun invoke(a: A) = m(MemoizeKey1(a))
}
fun <A, B, R> ((A, B) -> R).memoize(): (A, B) -> R = object : (A, B) -> R {
private val m = MemoizedHandler<((A, B) -> R), MemoizeKey2<A, B, R>, R>(this@memoize)
override fun invoke(a: A, b: B) = m(MemoizeKey2(a, b))
}
fun <A, B, C, R> ((A, B, C) -> R).memoize(): (A, B, C) -> R = object : (A, B, C) -> R {
private val m = MemoizedHandler<((A, B, C) -> R), MemoizeKey3<A, B, C, R>, R>(this@memoize)
override fun invoke(a: A, b: B, c: C) = m(MemoizeKey3(a, b, c))
}
private interface MemoizedCall<in F, out R> {
operator fun invoke(f: F): R
}
private class MemoizedHandler<F, in K : MemoizedCall<F, R>, out R>(val f: F) {
private val m = Platform.newConcurrentMap<K, R>()
operator fun invoke(k: K): R = m[k] ?: run { m.putSafely(k, k(f)) }
}
private data class MemoizeKey1<out A, R>(val a: A) : MemoizedCall<(A) -> R, R> {
override fun invoke(f: (A) -> R) = f(a)
}
private data class MemoizeKey2<out A, out B, R>(val a: A, val b: B) : MemoizedCall<(A, B) -> R, R> {
override fun invoke(f: (A, B) -> R) = f(a, b)
}
private data class MemoizeKey3<out A, out B, out C, R>(val a: A, val b: B, val c: C) : MemoizedCall<(A, B, C) -> R, R> {
override fun invoke(f: (A, B, C) -> R) = f(a, b, c)
}
object Platform {
interface ConcurrentMap<K, V> : MutableMap<K, V> {
fun putSafely(k: K, v: V): V
}
fun <K, V> newConcurrentMap(): ConcurrentMap<K, V> {
val map by lazy { ConcurrentHashMap<K, V>() }
return object : ConcurrentMap<K, V>, MutableMap<K, V> by map {
override fun putSafely(k: K, v: V): V =
map.putIfAbsent(k, v) ?: v
}
}
}

View File

@ -0,0 +1,8 @@
package com.chylex.intellij.coloredbrackets.util
import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiManager
fun VirtualFile.toPsiFile(project: Project): PsiFile? = PsiManager.getInstance(project).findFile(this)

View File

@ -1,4 +1,4 @@
package com.github.izhangzhihao.rainbow.brackets.util
package com.chylex.intellij.coloredbrackets.util
import com.intellij.psi.PsiElement
@ -12,7 +12,8 @@ tailrec fun PsiElement.findPrevSibling(condition: (PsiElement) -> Boolean): PsiE
val prevSibling = prevSibling ?: return null
return if (condition(prevSibling)) {
prevSibling
} else {
}
else {
prevSibling.findPrevSibling(condition)
}
}
@ -21,7 +22,8 @@ tailrec fun PsiElement.findNextSibling(condition: (PsiElement) -> Boolean): PsiE
val nextSibling = nextSibling ?: return null
return if (condition(nextSibling)) {
nextSibling
} else {
}
else {
nextSibling.findNextSibling(condition)
}
}

View File

@ -1,5 +1,4 @@
package com.github.izhangzhihao.rainbow.brackets.util
package com.chylex.intellij.coloredbrackets.util
fun fileExtension(fileName: String) = fileName.substring(fileName.lastIndexOf(".") + 1)

View File

@ -1,4 +1,4 @@
package com.github.izhangzhihao.rainbow.brackets.util
package com.chylex.intellij.coloredbrackets.util
import com.intellij.openapi.editor.colors.TextAttributesKey
import com.intellij.openapi.editor.markup.TextAttributes

View File

@ -0,0 +1,183 @@
package com.chylex.intellij.coloredbrackets.visitor
import com.chylex.intellij.coloredbrackets.bracePairs
import com.chylex.intellij.coloredbrackets.braceTypeSet
import com.chylex.intellij.coloredbrackets.settings.RainbowSettings
import com.intellij.codeInsight.daemon.impl.HighlightVisitor
import com.intellij.lang.BracePair
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.impl.source.tree.LeafPsiElement
import com.intellij.psi.tree.IElementType
class DefaultRainbowVisitor : RainbowHighlightVisitor() {
override fun clone(): HighlightVisitor = DefaultRainbowVisitor()
override fun visit(element: PsiElement) {
val type = (element as? LeafPsiElement)?.elementType ?: return
val settings = RainbowSettings.instance
val processor = Processor(settings)
val matching = processor.filterPairs(type, element) ?: return
val pair =
if (matching.size == 1) {
matching[0]
}
else {
matching.find { processor.isValidBracket(element, it) }
} ?: return
val level = processor.getBracketLevel(element, pair)
if (settings.isDoNOTRainbowifyTheFirstLevel) {
if (level >= 1) {
rainbowPairs(element, pair, level)
}
}
else {
if (level >= 0) {
rainbowPairs(element, pair, level)
}
}
}
private fun rainbowPairs(element: LeafPsiElement, pair: BracePair, level: Int) {
val startElement = element.takeIf { it.elementType == pair.leftBraceType }
val endElement = element.takeIf { it.elementType == pair.rightBraceType }
element.setHighlightInfo(element.parent, level, startElement, endElement)
}
private class Processor(private val settings: RainbowSettings) {
fun getBracketLevel(element: LeafPsiElement, pair: BracePair): Int = iterateBracketParents(element.parent, pair, -1)
private tailrec fun iterateBracketParents(element: PsiElement?, pair: BracePair, count: Int): Int {
if (element == null || element is PsiFile) {
return count
}
var nextCount = count
if (!settings.cycleCountOnAllBrackets) {
if (element.haveBrackets(
{ it.elementType() == pair.leftBraceType },
{ it.elementType() == pair.rightBraceType })
) {
nextCount++
}
}
else {
if (element.haveBrackets(
{ element.language.braceTypeSet.contains(it.elementType()) },
{ element.language.braceTypeSet.contains(it.elementType()) })
) {
nextCount++
}
}
return iterateBracketParents(element.parent, pair, nextCount)
}
private inline fun PsiElement.haveBrackets(
checkLeft: (PsiElement) -> Boolean,
checkRight: (PsiElement) -> Boolean,
): Boolean {
if (this is LeafPsiElement) {
return false
}
var findLeftBracket = false
var findRightBracket = false
var left: PsiElement? = firstChild
var right: PsiElement? = lastChild
while (left != right && (!findLeftBracket || !findRightBracket)) {
val needBreak = left == null || left.nextSibling == right
if (left is LeafPsiElement && checkLeft(left)) {
findLeftBracket = true
}
else {
left = left?.nextSibling
}
if (right is LeafPsiElement && checkRight(right)) {
findRightBracket = true
}
else {
right = right?.prevSibling
}
if (needBreak) {
break
}
}
//For https://github.com/izhangzhihao/intellij-rainbow-brackets/issues/830
if (settings.doNOTRainbowifyTemplateString) {
if (left?.prevSibling?.textMatches("$") == true) return false
}
return findLeftBracket && findRightBracket
}
private fun PsiElement.elementType(): IElementType? {
return (this as? LeafPsiElement)?.elementType
}
fun isValidBracket(element: LeafPsiElement, pair: BracePair): Boolean {
val pairType = when (element.elementType) {
pair.leftBraceType -> pair.rightBraceType
pair.rightBraceType -> pair.leftBraceType
else -> return false
}
return if (pairType == pair.leftBraceType) {
checkBracePair(element, element.parent.firstChild, pairType, PsiElement::getNextSibling)
}
else {
checkBracePair(element, element.parent.lastChild, pairType, PsiElement::getPrevSibling)
}
}
private fun checkBracePair(
brace: PsiElement,
start: PsiElement,
type: IElementType,
next: PsiElement.() -> PsiElement?,
): Boolean {
var element: PsiElement? = start
while (element != null && element != brace) {
if (element is LeafPsiElement && element.elementType == type) {
return true
}
element = element.next()
}
return false
}
fun filterPairs(type: IElementType, element: LeafPsiElement): List<BracePair>? {
val pairs = element.language.bracePairs ?: return null
val filterBraceType = pairs[type.toString()]
return when {
filterBraceType.isNullOrEmpty() -> {
null
}
// https://github.com/izhangzhihao/intellij-rainbow-brackets/issues/198
element.javaClass.simpleName == "OCMacroForeignLeafElement" -> {
null
}
settings.isDoNOTRainbowifyBracketsWithoutContent -> {
filterBraceType
.filterNot { it.leftBraceType == type && element.nextSibling?.elementType() == it.rightBraceType }
.filterNot { it.rightBraceType == type && element.prevSibling?.elementType() == it.leftBraceType }
}
else -> {
filterBraceType
}
}
}
}
}

View File

@ -0,0 +1,42 @@
package com.chylex.intellij.coloredbrackets.visitor
import com.chylex.intellij.coloredbrackets.settings.RainbowSettings
import com.chylex.intellij.coloredbrackets.visitor.XmlRainbowVisitor.Companion.iterateXmlTagParents
import com.chylex.intellij.coloredbrackets.visitor.XmlRainbowVisitor.Companion.xmlParent
import com.intellij.codeInsight.daemon.impl.HighlightVisitor
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.xml.XmlTag
import com.intellij.psi.xml.XmlToken
import com.intellij.psi.xml.XmlTokenType
class PugRainbowVisitor : RainbowHighlightVisitor() {
override fun suitableForFile(file: PsiFile)
: Boolean = super.suitableForFile(file) &&
RainbowSettings.instance.isEnableRainbowAngleBrackets &&
(file.language.id == "Jade" ||
file.viewProvider.allFiles.any { it.language.id == "Jade" } ||
file.name.endsWith(".vue")
)
override fun clone(): HighlightVisitor = PugRainbowVisitor()
override fun visit(element: PsiElement) {
if (element !is XmlToken) {
return
}
if (element.tokenType == XmlTokenType.XML_TAG_NAME) {
val parent = element.parent
if (parent != null && parent is XmlTag) {
parent.level.let { element.setHighlightInfo(element.xmlParent, it, element, null) }
}
}
}
companion object {
private val XmlTag.level: Int
get() = iterateXmlTagParents(parent, 0)
}
}

View File

@ -0,0 +1,102 @@
package com.chylex.intellij.coloredbrackets.visitor
import com.chylex.intellij.coloredbrackets.settings.RainbowSettings
import com.intellij.codeInsight.daemon.impl.HighlightVisitor
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.impl.source.tree.LeafPsiElement
import com.jetbrains.python.PyTokenTypes.BREAK_KEYWORD
import com.jetbrains.python.PyTokenTypes.CLASS_KEYWORD
import com.jetbrains.python.PyTokenTypes.CONTINUE_KEYWORD
import com.jetbrains.python.PyTokenTypes.DEF_KEYWORD
import com.jetbrains.python.PyTokenTypes.ELIF_KEYWORD
import com.jetbrains.python.PyTokenTypes.ELSE_KEYWORD
import com.jetbrains.python.PyTokenTypes.EXCEPT_KEYWORD
import com.jetbrains.python.PyTokenTypes.FINALLY_KEYWORD
import com.jetbrains.python.PyTokenTypes.FOR_KEYWORD
import com.jetbrains.python.PyTokenTypes.IF_KEYWORD
import com.jetbrains.python.PyTokenTypes.RAISE_KEYWORD
import com.jetbrains.python.PyTokenTypes.RETURN_KEYWORD
import com.jetbrains.python.PyTokenTypes.TRY_KEYWORD
import com.jetbrains.python.PyTokenTypes.WHILE_KEYWORD
import com.jetbrains.python.PyTokenTypes.WITH_KEYWORD
import com.jetbrains.python.PyTokenTypes.YIELD_KEYWORD
import com.jetbrains.python.psi.PyStatement
class PythonRainbowVisitor : RainbowHighlightVisitor() {
override fun suitableForFile(file: PsiFile)
: Boolean = super.suitableForFile(file) &&
RainbowSettings.instance.rainbowifyPythonKeywords &&
(file.language.id == "Python" ||
file.viewProvider.allFiles.any { it.language.id == "Python" }
)
override fun clone(): HighlightVisitor = PythonRainbowVisitor()
override fun visit(element: PsiElement) {
val type = (element as? LeafPsiElement)?.elementType ?: return
val exists = statementKeywords.contains(type)
if (exists) {
val level = element.getBracketLevel()
if (RainbowSettings.instance.isDoNOTRainbowifyTheFirstLevel) {
if (level >= 1) {
rainbowPairs(element, level)
}
}
else {
if (level >= 0) {
rainbowPairs(element, level)
}
}
}
}
private fun rainbowPairs(element: LeafPsiElement, level: Int) {
element.setHighlightInfo(element.parent, level, element, element)
}
companion object {
val statementKeywords = setOf(
IF_KEYWORD,
ELSE_KEYWORD,
ELIF_KEYWORD,
BREAK_KEYWORD,
CONTINUE_KEYWORD,
CLASS_KEYWORD,
DEF_KEYWORD,
EXCEPT_KEYWORD,
FINALLY_KEYWORD,
FOR_KEYWORD,
RAISE_KEYWORD,
RETURN_KEYWORD,
TRY_KEYWORD,
WITH_KEYWORD,
WHILE_KEYWORD,
YIELD_KEYWORD,
)
private fun LeafPsiElement.getBracketLevel(): Int =
iterateBracketParents(this, -1)
private tailrec fun iterateBracketParents(
element: PsiElement?,
count: Int,
): Int {
if (element == null || element is PsiFile) {
return count
}
var nextCount = count
if (element is PyStatement) {
nextCount++
}
return iterateBracketParents(
element.parent,
nextCount,
)
}
}
}

View File

@ -0,0 +1,120 @@
package com.chylex.intellij.coloredbrackets.visitor
import com.chylex.intellij.coloredbrackets.RainbowHighlighter.getHighlightInfo
import com.chylex.intellij.coloredbrackets.RainbowInfo
import com.chylex.intellij.coloredbrackets.settings.RainbowSettings
import com.chylex.intellij.coloredbrackets.util.memoizedFileExtension
import com.intellij.codeInsight.daemon.impl.HighlightVisitor
import com.intellij.codeInsight.daemon.impl.analysis.HighlightInfoHolder
import com.intellij.ide.plugins.PluginManagerCore
import com.intellij.openapi.editor.colors.EditorColorsManager
import com.intellij.openapi.extensions.PluginId
import com.intellij.openapi.util.TextRange
import com.intellij.openapi.util.text.StringUtil
import com.intellij.psi.PsiDocumentManager
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import java.awt.Color
abstract class RainbowHighlightVisitor : HighlightVisitor {
private var highlightInfoHolder: HighlightInfoHolder? = null
override fun suitableForFile(file: PsiFile): Boolean {
val settings = RainbowSettings.instance
return settings.isRainbowEnabled &&
checkForBigFile(file) &&
!settings.languageBlacklist.contains(file.fileType.name) &&
!settings.languageBlacklist.contains(memoizedFileExtension(file.name)) &&
fileIsNotHaskellOrIntelliJHaskellPluginNotEnabled(file.fileType.name)
}
final override fun analyze(file: PsiFile, updateWholeFile: Boolean, holder: HighlightInfoHolder, action: Runnable): Boolean {
highlightInfoHolder = holder
onBeforeAnalyze(file, updateWholeFile)
try {
action.run()
} catch (e: Throwable) {
} finally {
onAfterAnalyze()
}
return true
}
protected open fun onBeforeAnalyze(file: PsiFile, updateWholeFile: Boolean) = Unit
protected open fun onAfterAnalyze() {
highlightInfoHolder = null
}
protected fun PsiElement.setHighlightInfo(
parent: PsiElement?,
level: Int,
startElement: PsiElement?,
endElement: PsiElement?,
) {
val holder = highlightInfoHolder ?: return
val globalScheme = EditorColorsManager.getInstance().globalScheme
getHighlightInfo(globalScheme, this, level)
?.also {
holder.add(it)
if (startElement != null || endElement != null) {
val color: Color? = globalScheme.getAttributes(it.forcedTextAttributesKey)?.foregroundColor
color?.let {
parent?.saveRainbowInfo(level, color, startElement, endElement)
}
}
}
}
private fun PsiElement.saveRainbowInfo(
level: Int,
color: Color,
startElement: PsiElement?,
endElement: PsiElement?,
) {
val rainbowInfo = RainbowInfo.RAINBOW_INFO_KEY[this]?.also {
it.level = level
it.color = color
} ?: RainbowInfo(level, color).also { RainbowInfo.RAINBOW_INFO_KEY[this] = it }
startElement?.let { rainbowInfo.startElement = it }
endElement?.let { rainbowInfo.endElement = it }
}
companion object {
private val isIntelliJHaskellEnabled: Boolean by lazy {
PluginManagerCore.getPlugin(
PluginId.getId("intellij.haskell")
)?.isEnabled ?: false
}
fun checkForBigFile(file: PsiFile): Boolean =
!(RainbowSettings.instance.doNOTRainbowifyBigFiles &&
file.getLineCount() > RainbowSettings.instance.bigFilesLinesThreshold)
private fun fileIsNotHaskellOrIntelliJHaskellPluginNotEnabled(fileType: String) =
fileType != "Haskell" || !isIntelliJHaskellEnabled
}
}
fun PsiElement.getLineCount(): Int {
try {
val doc = containingFile?.let { PsiDocumentManager.getInstance(project).getDocument(it) }
if (doc != null) {
val spaceRange = textRange ?: TextRange.EMPTY_RANGE
if (spaceRange.endOffset <= doc.textLength && spaceRange.startOffset < spaceRange.endOffset) {
val startLine = doc.getLineNumber(spaceRange.startOffset)
val endLine = doc.getLineNumber(spaceRange.endOffset - 1)
return endLine - startLine + 1
}
}
return StringUtil.getLineBreakCount(text ?: "") + 1
} catch (e: Throwable) {
return 0
}
}

View File

@ -0,0 +1,16 @@
package com.chylex.intellij.coloredbrackets.visitor
import com.intellij.codeInsight.daemon.impl.HighlightVisitor
import com.intellij.lang.javascript.DialectDetector
import com.intellij.psi.PsiFile
class ReactJSXRainbowVisitor : XmlRainbowVisitor() {
override fun suitableForFile(file: PsiFile): Boolean {
return DialectDetector.isJSX(file)
}
override fun clone(): HighlightVisitor {
return ReactJSXRainbowVisitor()
}
}

View File

@ -0,0 +1,101 @@
package com.chylex.intellij.coloredbrackets.visitor
import com.chylex.intellij.coloredbrackets.settings.RainbowSettings
import com.intellij.codeInsight.daemon.impl.HighlightVisitor
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.xml.XmlFile
import com.intellij.psi.xml.XmlTag
import com.intellij.psi.xml.XmlToken
import com.intellij.psi.xml.XmlTokenType
open class XmlRainbowVisitor : RainbowHighlightVisitor() {
override fun suitableForFile(file: PsiFile)
: Boolean = super.suitableForFile(file) &&
RainbowSettings.instance.isEnableRainbowAngleBrackets &&
(file is XmlFile || file.viewProvider.allFiles.any { it is XmlFile })
override fun clone(): HighlightVisitor = XmlRainbowVisitor()
override fun visit(element: PsiElement) {
if (element !is XmlToken) {
return
}
when (val tokenType = element.tokenType) {
XmlTokenType.XML_DOCTYPE_START,
XmlTokenType.XML_DOCTYPE_END,
XmlTokenType.XML_PI_START,
XmlTokenType.XML_PI_END,
-> {
val startElement = element.takeIf {
tokenType == XmlTokenType.XML_DOCTYPE_START || tokenType == XmlTokenType.XML_PI_START
}
val endElement = element.takeIf {
tokenType == XmlTokenType.XML_DOCTYPE_END || tokenType == XmlTokenType.XML_PI_END
}
element.setHighlightInfo(element.xmlParent, 0, startElement, endElement)
}
XmlTokenType.XML_START_TAG_START,
XmlTokenType.XML_END_TAG_START,
XmlTokenType.XML_TAG_END,
XmlTokenType.XML_EMPTY_ELEMENT_END,
-> {
val startElement = element.takeIf { tokenType == XmlTokenType.XML_START_TAG_START }
val endElement = element.takeIf {
tokenType == XmlTokenType.XML_TAG_END || tokenType == XmlTokenType.XML_EMPTY_ELEMENT_END
}
element.level?.let { element.setHighlightInfo(element.xmlParent, it, startElement, endElement) }
}
XmlTokenType.XML_CDATA_START,
XmlTokenType.XML_CDATA_END,
-> {
val startElement = element.takeIf { tokenType == XmlTokenType.XML_CDATA_START }
val endElement = element.takeIf { tokenType == XmlTokenType.XML_CDATA_END }
element.level?.let { element.setHighlightInfo(element.parent, it + 1, startElement, endElement) }
}
XmlTokenType.XML_TAG_NAME,
XmlTokenType.XML_NAME,
-> {
if (RainbowSettings.instance.rainbowifyTagNameInXML) {
val prevSibling = element.prevSibling
if (prevSibling != null && prevSibling is XmlToken) {
prevSibling.level?.let { element.setHighlightInfo(element.xmlParent, it, element, null) }
}
}
}
}
}
companion object {
val PsiElement.xmlParent: PsiElement?
get() {
var pElement = parent
while (pElement != null && pElement !is XmlTag && pElement !is PsiFile) {
pElement = pElement.parent
}
return pElement
}
tailrec fun iterateXmlTagParents(element: PsiElement?, count: Int): Int {
if (element == null || element is PsiFile) {
return count
}
var nextCount = count
if (element is XmlTag) {
nextCount++
}
return iterateXmlTagParents(element.parent, nextCount)
}
private val XmlToken.level: Int?
get() = iterateXmlTagParents(parent, -1).takeIf { it >= 0 }
}
}

View File

@ -1,77 +0,0 @@
package com.github.izhangzhihao.rainbow.brackets
import com.github.izhangzhihao.rainbow.brackets.provider.BracePairProvider
import com.github.izhangzhihao.rainbow.brackets.util.memoize
import com.intellij.codeInsight.highlighting.BraceMatchingUtil
import com.intellij.lang.*
import com.intellij.psi.tree.IElementType
object BracePairs {
private val providers = LanguageExtension<BracePairProvider>("izhangzhihao.rainbow.brackets.bracePairProvider")
private val bracePairs =
Language.getRegisteredLanguages()
.map { language ->
if (language is CompositeLanguage) {
return@map language.displayName to null
}
val pairs =
LanguageBraceMatching.INSTANCE.forLanguage(language)?.pairs.let {
if (it == null || it.isEmpty()) {
language.associatedFileType
?.let { fileType -> BraceMatchingUtil.getBraceMatcher(fileType, language) as? PairedBraceMatcher }
?.pairs
} else {
it
}
}
val pairsList = providers.forLanguage(language)?.pairs()?.let {
if (pairs != null && pairs.isNotEmpty()) {
it.toMutableSet().apply { addAll(pairs) }
} else {
it
}
} ?: pairs?.toList()
val braceMap: MutableMap<String, MutableList<BracePair>> = mutableMapOf()
val blackSet = providers.forLanguage(language)?.blackList()?.map { it.toString() }?.toSet()
pairsList
?.filter {
if (blackSet != null) {
!blackSet.contains(it.toString())
} else {
true
}
}
?.map { listOf(Pair(it.leftBraceType.toString(), it), Pair(it.rightBraceType.toString(), it)) }
?.flatten()
?.forEach { it ->
val bracePairs = braceMap[it.first]
if (bracePairs == null) {
braceMap[it.first] = mutableListOf(it.second)
} else {
bracePairs.add(it.second)
}
}
language.displayName to braceMap
}
.toMap()
fun getBracePairs(language: Language): MutableMap<String, MutableList<BracePair>>? = bracePairs[language.displayName]
private fun getBraceTypeSetOf(language: Language): Set<IElementType> = getBracePairs(language)?.values?.flatten()?.map { it -> listOf(it.leftBraceType, it.rightBraceType) }?.flatten()?.toSet() ?: emptySet()
val braceTypeSet: (Language) -> Set<IElementType> = { language: Language -> getBraceTypeSetOf(language) }.memoize()
}
inline val Language.bracePairs: MutableMap<String, MutableList<BracePair>>?
get() = BracePairs.getBracePairs(this)
inline val Language.braceTypeSet: Set<IElementType>
get() = BracePairs.braceTypeSet(this)

View File

@ -1,48 +0,0 @@
package com.github.izhangzhihao.rainbow.brackets
import com.intellij.notification.*
import com.intellij.notification.impl.NotificationsManagerImpl
import com.intellij.openapi.Disposable
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.project.Project
import com.intellij.openapi.ui.popup.Balloon
import com.intellij.openapi.wm.WindowManager
import com.intellij.ui.BalloonLayoutData
import com.intellij.ui.awt.RelativePoint
import java.awt.Point
class ApplicationServicePlaceholder : Disposable {
override fun dispose() = Unit
companion object {
val INSTANCE: ApplicationServicePlaceholder = ApplicationManager.getApplication().getService(ApplicationServicePlaceholder::class.java)
}
}
fun createNotification(title: String, content: String, type: NotificationType,
listener: NotificationListener): Notification {
return NotificationGroupManager.getInstance().getNotificationGroup("Rainbow Brackets Notification Group")
.createNotification(title, content, type).setListener(listener)
}
fun showFullNotification(project: Project, notification: Notification) {
val frame = WindowManager.getInstance().getIdeFrame(project)
if (frame == null) {
notification.notify(project)
return
}
val bounds = frame.component.bounds
val target = RelativePoint(frame.component, Point(bounds.x + bounds.width, 20))
try {
val balloon = NotificationsManagerImpl.createBalloon(frame,
notification,
true, // showCallout
true, // hideOnClickOutside
BalloonLayoutData.fullContent(),
ApplicationServicePlaceholder.INSTANCE)
balloon.show(target, Balloon.Position.atLeft)
} catch (e: Exception) {
notification.notify(project)
}
}

Some files were not shown because too many files have changed in this diff Show More