Compare commits
10 Commits
main
...
c9b3a33797
Author | SHA1 | Date | |
---|---|---|---|
c9b3a33797
|
|||
31a40b63cb
|
|||
aa7b057910
|
|||
dd63c37354
|
|||
4cab3548c3
|
|||
882c5d0eae
|
|||
a4a83d9ab3
|
|||
91eb77cd2c
|
|||
db76359837
|
|||
983350f63c
|
1
.gitattributes
vendored
@@ -1 +0,0 @@
|
||||
* text=auto eol=lf
|
128
.github/CODE_OF_CONDUCT.md
vendored
Normal file
@@ -0,0 +1,128 @@
|
||||
# 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.
|
69
.github/CONTRIBUTING.md
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
## 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. [](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
Normal file
@@ -0,0 +1,2 @@
|
||||
open_collective: intellij-rainbow-brackets
|
||||
custom: https://github.com/izhangzhihao/intellij-rainbow-brackets#support-us
|
2
.gitignore
vendored
@@ -1,5 +1,7 @@
|
||||
/.idea/*
|
||||
!/.idea/icon*.png
|
||||
!/.idea/runConfigurations
|
||||
|
||||
/.gradle/
|
||||
/build/
|
||||
/libs/
|
||||
|
24
.idea/runConfigurations/Run_CLion.xml
generated
@@ -1,24 +0,0 @@
|
||||
<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>
|
24
.idea/runConfigurations/Run_IDEA.xml
generated
@@ -1,24 +0,0 @@
|
||||
<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>
|
24
.idea/runConfigurations/Run_Rider.xml
generated
@@ -1,24 +0,0 @@
|
||||
<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>
|
24
.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CommitMessageInspectionProfile">
|
||||
<profile version="1.0">
|
||||
<inspection_tool class="GrazieCommit" enabled="true" level="TYPO" enabled_by_default="true"/>
|
||||
</profile>
|
||||
</component>
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git"/>
|
||||
</component>
|
||||
<component name="IssueNavigationConfiguration">
|
||||
<option name="links">
|
||||
<list>
|
||||
<IssueNavigationLink>
|
||||
<option name="issueRegexp" value="#(\d+)"/>
|
||||
<option name="linkRegexp" value="https://github.com//izhangzhihao/intellij-rainbow-brackets/issues/$1"/>
|
||||
</IssueNavigationLink>
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git"/>
|
||||
</component>
|
||||
</project>
|
742
CHANGELOG.md
Normal file
@@ -0,0 +1,742 @@
|
||||
## 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>
|
303
README.md
@@ -1,12 +1,297 @@
|
||||
# Colored Brackets
|
||||
|
||||
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.
|
||||
<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>
|
||||
|
||||
## Key Changes
|
||||
<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>
|
||||
|
||||
- Support for C# (Rider)
|
||||
- Support for C++ (Rider, CLion, CLion Nova)
|
||||
- Support for Settings Sync
|
||||
- Improved highlighting performance
|
||||
- Increased default setting for maximum line count from 1K to 100K
|
||||
- Fixed service initialization warnings reported by 2024.2+
|
||||
<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://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)
|
||||
|
||||
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
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
* Scala
|
||||
|
||||

|
||||
|
||||
* Clojure
|
||||
|
||||

|
||||
|
||||
* Kotlin
|
||||
|
||||

|
||||
|
||||
* HTML/XML
|
||||
|
||||

|
||||
|
||||
* 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.
|
||||
|
||||

|
||||
|
||||
<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.
|
||||
|
||||

|
||||
|
||||
* Rainbow indent guide lines
|
||||
|
||||

|
||||
|
||||
* looking forward to your screenshots(PR welcome!)
|
||||
|
||||
## Customize colors
|
||||
|
||||
<kbd>Settings/Preferences</kbd> > <kbd>Editor</kbd> > <kbd>Color Scheme</kbd> > <kbd>Rainbow Brackets</kbd>:
|
||||
|
||||

|
||||
|
||||
## 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="{"luminosity": "light","hue": "blue"}"/>
|
||||
</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)
|
||||
|
141
build.gradle.kts
@@ -1,54 +1,51 @@
|
||||
@file:Suppress("ConvertLambdaToReference")
|
||||
@file:Suppress("SpellCheckingInspection")
|
||||
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
import kotlin.io.path.Path
|
||||
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
id("org.jetbrains.intellij")
|
||||
kotlin("jvm") version "1.8.0"
|
||||
id("org.jetbrains.intellij") version "1.15.0"
|
||||
}
|
||||
|
||||
group = "com.chylex.intellij.coloredbrackets"
|
||||
version = "1.2.0"
|
||||
group = "com.chylex.intellij.rainbowbrackets"
|
||||
version = "6.26-chylex-2"
|
||||
|
||||
allprojects {
|
||||
apply(plugin = "org.jetbrains.kotlin.jvm")
|
||||
apply(plugin = "org.jetbrains.intellij")
|
||||
val ideVersion = "2023.2.2"
|
||||
|
||||
idea {
|
||||
module {
|
||||
excludeDirs.add(file("gradle"))
|
||||
excludeDirs.add(file("libs"))
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
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)
|
||||
val clion: Configuration by configurations.creating
|
||||
val rider: Configuration by configurations.creating
|
||||
|
||||
dependencies {
|
||||
clion("com.jetbrains.intellij.clion:clion:$ideVersion")
|
||||
rider("com.jetbrains.intellij.rider:riderRD:$ideVersion")
|
||||
|
||||
compileOnly(fileTree("libs"))
|
||||
|
||||
testImplementation("org.junit.jupiter:junit-jupiter:5.9.2")
|
||||
testImplementation("io.kotest:kotest-assertions-core:5.5.5")
|
||||
}
|
||||
|
||||
kotlin {
|
||||
jvmToolchain(17)
|
||||
}
|
||||
|
||||
tasks.withType<KotlinCompile> {
|
||||
kotlinOptions.freeCompilerArgs = listOf(
|
||||
"-Xjvm-default=all"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
subprojects {
|
||||
tasks.buildSearchableOptions {
|
||||
enabled = false
|
||||
}
|
||||
}
|
||||
|
||||
idea {
|
||||
module {
|
||||
excludeDirs.add(file("gradle"))
|
||||
}
|
||||
}
|
||||
|
||||
intellij {
|
||||
type.set("IU")
|
||||
version.set(ideVersion)
|
||||
updateSinceUntilBuild.set(false)
|
||||
|
||||
plugins.set(
|
||||
listOf(
|
||||
@@ -62,68 +59,52 @@ intellij {
|
||||
"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
|
||||
"Dart:232.8660.129", // https://plugins.jetbrains.com/plugin/6351-dart/versions/stable
|
||||
"Pythonid:232.9921.47", // https://plugins.jetbrains.com/plugin/631-python/versions
|
||||
"com.jetbrains.php:232.9921.55", // https://plugins.jetbrains.com/plugin/6610-php/versions
|
||||
"com.jetbrains.sh:232.8660.88", // https://plugins.jetbrains.com/plugin/13122-shell-script/versions
|
||||
"org.intellij.scala:2023.2.23", // https://plugins.jetbrains.com/plugin/1347-scala/versions
|
||||
"org.jetbrains.plugins.go-template:232.9921.89", // https://plugins.jetbrains.com/plugin/10581-go-template/versions
|
||||
"org.jetbrains.plugins.ruby:232.9921.47", // https://plugins.jetbrains.com/plugin/1293-ruby/versions
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
dependencies {
|
||||
testImplementation("junit:junit:4.13.2")
|
||||
testImplementation("io.kotest:kotest-assertions-core:5.8.0") {
|
||||
exclude(group = "org.jetbrains.kotlin")
|
||||
}
|
||||
}
|
||||
|
||||
tasks.patchPluginXml {
|
||||
sinceBuild.set("233")
|
||||
sinceBuild.set("232")
|
||||
}
|
||||
|
||||
tasks.test {
|
||||
useJUnit()
|
||||
useJUnitPlatform()
|
||||
}
|
||||
|
||||
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"),
|
||||
tasks.withType<KotlinCompile> {
|
||||
kotlinOptions.freeCompilerArgs = listOf(
|
||||
"-Xjvm-default=all"
|
||||
)
|
||||
}
|
||||
|
||||
val jarFiles = zipTree(outputs.files.singleFile)
|
||||
fun createDownloadIdeTask(name: String, dependency: Configuration, configuration: CopySpec.() -> Unit): Sync {
|
||||
return tasks.create<Sync>(name) {
|
||||
group = "ides"
|
||||
outputs.upToDateWhen { false }
|
||||
|
||||
for (expectedPath in expectedPaths) {
|
||||
val found = jarFiles.find { it.toPath().endsWith(expectedPath) }
|
||||
checkNotNull(found) { "Expected path not found: $expectedPath" }
|
||||
from(dependency.map(::zipTree), configuration)
|
||||
into(file("libs/${dependency.name}"))
|
||||
}
|
||||
}
|
||||
|
||||
val downloadIdeClion = createDownloadIdeTask("downloadIdeClion", clion) {
|
||||
include("plugins/cidr-base-plugin/**")
|
||||
}
|
||||
|
||||
val downloadIdeRider = createDownloadIdeTask("downloadIdeRider", rider) {
|
||||
include("lib/app.jar")
|
||||
}
|
||||
|
||||
tasks.create<Sync>("downloadExtraIdes") {
|
||||
group = "ides"
|
||||
|
||||
dependsOn(downloadIdeClion)
|
||||
dependsOn(downloadIdeRider)
|
||||
}
|
||||
|
1
clion/.gitignore
vendored
@@ -1 +0,0 @@
|
||||
/build/
|
@@ -1,13 +0,0 @@
|
||||
intellij {
|
||||
type.set("CL")
|
||||
|
||||
plugins.set(listOf(
|
||||
// Built-in
|
||||
"cidr-base-plugin",
|
||||
//"org.jetbrains.plugins.clion.radler" // Only in 2024.1 or newer. Worked around by only including the .xml file, and taking the implementation from Rider.
|
||||
))
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(rootProject)
|
||||
}
|
@@ -1,8 +0,0 @@
|
||||
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))
|
||||
}
|
@@ -1,10 +0,0 @@
|
||||
<idea-plugin>
|
||||
<extensions defaultExtensionNs="com.chylex.coloredbrackets">
|
||||
<bracePairProvider language="C++"
|
||||
implementationClass="com.chylex.intellij.coloredbrackets.provider.CppBracePairProvider" />
|
||||
</extensions>
|
||||
|
||||
<extensions defaultExtensionNs="com.intellij">
|
||||
<highlightVisitor implementation="com.chylex.intellij.coloredbrackets.visitor.CppRainbowVisitor" />
|
||||
</extensions>
|
||||
</idea-plugin>
|
@@ -1,6 +0,0 @@
|
||||
<idea-plugin>
|
||||
<extensions defaultExtensionNs="com.chylex.coloredbrackets">
|
||||
<bracePairProvider language="ObjectiveC"
|
||||
implementationClass="com.chylex.intellij.coloredbrackets.provider.OCBracePairProvider" />
|
||||
</extensions>
|
||||
</idea-plugin>
|
@@ -1 +1,2 @@
|
||||
org.gradle.parallel=true
|
||||
kotlin.stdlib.default.dependency=false
|
||||
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
14
gradlew
vendored
@@ -145,7 +145,7 @@ 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
|
||||
# shellcheck disable=SC3045
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
@@ -153,7 +153,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
# shellcheck disable=SC3045
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
@@ -202,11 +202,11 @@ 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.
|
||||
# Collect all arguments for the java command;
|
||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||
# shell script including quotes and variable substitutions, so put them in
|
||||
# double quotes to make sure that they get re-expanded; and
|
||||
# * put everything else in single quotes, so that it's not re-expanded.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
|
1
rider/.gitignore
vendored
@@ -1 +0,0 @@
|
||||
/build/
|
@@ -1,7 +0,0 @@
|
||||
intellij {
|
||||
type.set("RD")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(rootProject)
|
||||
}
|
@@ -1,13 +0,0 @@
|
||||
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),
|
||||
)
|
||||
}
|
@@ -1,13 +0,0 @@
|
||||
package com.chylex.intellij.coloredbrackets.provider
|
||||
|
||||
import com.intellij.lang.BracePair
|
||||
import com.jetbrains.rider.cpp.fileType.lexer.CppTokenTypes
|
||||
|
||||
class CppBracePairProvider : BracePairProvider {
|
||||
override fun pairs(): List<BracePair> = listOf(
|
||||
BracePair(CppTokenTypes.LPAR, CppTokenTypes.RPAR, false),
|
||||
BracePair(CppTokenTypes.LBRACE, CppTokenTypes.RBRACE, false),
|
||||
BracePair(CppTokenTypes.LBRACKET, CppTokenTypes.RBRACKET, false),
|
||||
BracePair(CppTokenTypes.LT, CppTokenTypes.GT, false),
|
||||
)
|
||||
}
|
@@ -1,26 +0,0 @@
|
||||
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.tree.IElementType
|
||||
import com.jetbrains.rider.languages.fileTypes.csharp.kotoparser.lexer.CSharpTokenType
|
||||
import com.jetbrains.rider.languages.fileTypes.csharp.psi.CSharpDummyNode
|
||||
|
||||
class CSharpRainbowVisitor : ReSharperRainbowVisitor("C#") {
|
||||
|
||||
override fun suitableForFile(file: PsiFile): Boolean {
|
||||
return super.suitableForFile(file) && RainbowSettings.instance.isEnableRainbowAngleBrackets
|
||||
}
|
||||
|
||||
override fun isAllowedElementType(type: IElementType): Boolean {
|
||||
return type == CSharpTokenType.LPARENTH || type == CSharpTokenType.RPARENTH
|
||||
}
|
||||
|
||||
override fun PsiElement.isDummyNode(): Boolean {
|
||||
return this is CSharpDummyNode
|
||||
}
|
||||
|
||||
override fun clone(): HighlightVisitor = CSharpRainbowVisitor()
|
||||
}
|
@@ -1,20 +0,0 @@
|
||||
package com.chylex.intellij.coloredbrackets.visitor
|
||||
|
||||
import com.intellij.codeInsight.daemon.impl.HighlightVisitor
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.tree.IElementType
|
||||
import com.jetbrains.rider.cpp.fileType.lexer.CppTokenTypes
|
||||
import com.jetbrains.rider.cpp.fileType.psi.CppDummyNode
|
||||
|
||||
class CppRainbowVisitor : ReSharperRainbowVisitor("C++") {
|
||||
|
||||
override fun clone(): HighlightVisitor = CppRainbowVisitor()
|
||||
|
||||
override fun isAllowedElementType(type: IElementType): Boolean {
|
||||
return type == CppTokenTypes.LPAR || type == CppTokenTypes.RPAR
|
||||
}
|
||||
|
||||
override fun PsiElement.isDummyNode(): Boolean {
|
||||
return this is CppDummyNode
|
||||
}
|
||||
}
|
@@ -1,120 +0,0 @@
|
||||
package com.chylex.intellij.coloredbrackets.visitor
|
||||
|
||||
import com.chylex.intellij.coloredbrackets.bracePairs
|
||||
import com.chylex.intellij.coloredbrackets.settings.RainbowSettings
|
||||
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.intellij.psi.util.elementType
|
||||
|
||||
abstract class ReSharperRainbowVisitor(private val languageId: String) : RainbowHighlightVisitor() {
|
||||
|
||||
override fun suitableForFile(file: PsiFile): Boolean {
|
||||
return super.suitableForFile(file) && (file.language.id == languageId || file.viewProvider.allFiles.any { it.language.id == languageId })
|
||||
}
|
||||
|
||||
protected abstract fun isAllowedElementType(type: IElementType): Boolean
|
||||
|
||||
final override fun visit(element: PsiElement) {
|
||||
val type = (element as? LeafPsiElement)?.elementType?.takeIf(::isAllowedElementType) ?: return
|
||||
val pair = type.language.bracePairs?.getValue(type.toString())?.singleOrNull() ?: return
|
||||
|
||||
val settings = RainbowSettings.instance
|
||||
|
||||
if (settings.isDoNOTRainbowifyBracketsWithoutContent) {
|
||||
if (pair.leftBraceType == type && element.nextSibling?.elementType == pair.rightBraceType) {
|
||||
return
|
||||
}
|
||||
|
||||
if (pair.rightBraceType == type && element.prevSibling?.elementType == pair.leftBraceType) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
val level = element.getBracketLevel(pair, type)
|
||||
|
||||
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 fun LeafPsiElement.getBracketLevel(pair: BracePair, type: IElementType): Int {
|
||||
return 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) {
|
||||
if (type == pair.leftBraceType && element.elementType == pair.rightBraceType) {
|
||||
nextCount--
|
||||
}
|
||||
|
||||
if (type == pair.rightBraceType && element.elementType == pair.leftBraceType) {
|
||||
nextCount--
|
||||
}
|
||||
|
||||
if (type == element.elementType) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract fun PsiElement.isDummyNode(): Boolean
|
||||
|
||||
private tailrec fun PsiElement?.iterForNextDummyNode(): PsiElement? {
|
||||
return when {
|
||||
this == null -> null
|
||||
this.isDummyNode() -> this
|
||||
this.nextSibling == null -> null
|
||||
else -> this.nextSibling.iterForNextDummyNode()
|
||||
}
|
||||
}
|
||||
|
||||
private tailrec fun PsiElement?.iterForPreDummyNode(): PsiElement? {
|
||||
return when {
|
||||
this == null -> null
|
||||
this.isDummyNode() -> this
|
||||
this.prevSibling == null -> null
|
||||
else -> this.prevSibling.iterForPreDummyNode()
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,10 +0,0 @@
|
||||
<idea-plugin>
|
||||
<extensions defaultExtensionNs="com.chylex.coloredbrackets">
|
||||
<bracePairProvider language="C++"
|
||||
implementationClass="com.chylex.intellij.coloredbrackets.provider.CppBracePairProvider" />
|
||||
</extensions>
|
||||
|
||||
<extensions defaultExtensionNs="com.intellij">
|
||||
<highlightVisitor implementation="com.chylex.intellij.coloredbrackets.visitor.CppRainbowVisitor" />
|
||||
</extensions>
|
||||
</idea-plugin>
|
@@ -1,5 +0,0 @@
|
||||
<idea-plugin>
|
||||
<extensions defaultExtensionNs="com.intellij">
|
||||
<annotator language="C#" implementationClass="com.chylex.intellij.coloredbrackets.annotator.RainbowAnnotator" />
|
||||
</extensions>
|
||||
</idea-plugin>
|
@@ -1,10 +0,0 @@
|
||||
<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>
|
BIN
screenshots/alipay.jpg
Normal file
After Width: | Height: | Size: 34 KiB |
BIN
screenshots/customize-colors.png
Normal file
After Width: | Height: | Size: 22 KiB |
1
screenshots/jetbrains.svg
Normal file
@@ -0,0 +1 @@
|
||||
<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>
|
After Width: | Height: | Size: 4.4 KiB |
BIN
screenshots/wechat.jpg
Normal file
After Width: | Height: | Size: 39 KiB |
BIN
screenshots/with-Clojure.png
Normal file
After Width: | Height: | Size: 123 KiB |
BIN
screenshots/with-HTML.png
Normal file
After Width: | Height: | Size: 60 KiB |
BIN
screenshots/with-java.png
Normal file
After Width: | Height: | Size: 43 KiB |
BIN
screenshots/with-kotlin.png
Normal file
After Width: | Height: | Size: 64 KiB |
BIN
screenshots/with-material-theme-ui.png
Normal file
After Width: | Height: | Size: 29 KiB |
BIN
screenshots/with-scala.png
Normal file
After Width: | Height: | Size: 102 KiB |
@@ -1,11 +1,8 @@
|
||||
rootProject.name = "ColoredBrackets"
|
||||
rootProject.name = "RainbowBrackets"
|
||||
|
||||
pluginManagement {
|
||||
plugins {
|
||||
kotlin("jvm") version "1.9.21"
|
||||
id("org.jetbrains.intellij") version "1.17.4"
|
||||
repositories {
|
||||
maven(url = "https://oss.sonatype.org/content/repositories/snapshots/")
|
||||
gradlePluginPortal()
|
||||
}
|
||||
}
|
||||
|
||||
include("clion")
|
||||
include("rider")
|
||||
|
@@ -1,88 +0,0 @@
|
||||
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.id to braceMap
|
||||
}
|
||||
.toMap()
|
||||
}
|
||||
|
||||
fun getBracePairs(language: Language): MutableMap<String, MutableList<BracePair>>? = bracePairs.value[language.id]
|
||||
|
||||
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)
|
@@ -1,228 +0,0 @@
|
||||
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())
|
||||
}
|
||||
}
|
@@ -1,40 +0,0 @@
|
||||
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")
|
||||
}
|
||||
}
|
@@ -1,100 +0,0 @@
|
||||
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.Ref
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.ui.EditorNotificationPanel
|
||||
import com.intellij.ui.EditorNotificationProvider
|
||||
import com.intellij.ui.EditorNotifications
|
||||
import com.intellij.ui.HyperlinkLabel
|
||||
import java.util.function.Function
|
||||
import javax.swing.JComponent
|
||||
|
||||
class RainbowifyBanner : EditorNotificationProvider {
|
||||
override fun collectNotificationData(project: Project, file: VirtualFile): Function<in FileEditor, out JComponent?> {
|
||||
return Function { createNotificationPanel(project, file) }
|
||||
}
|
||||
|
||||
private fun createNotificationPanel(project: Project, file: VirtualFile): 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
|
||||
}
|
||||
|
||||
private fun EditorNotificationPanel.createComponentActionLabel(labelText: String, callback: (HyperlinkLabel) -> Unit) {
|
||||
val label: Ref<HyperlinkLabel> = Ref.create()
|
||||
label.set(createActionLabel(labelText) {
|
||||
callback(label.get())
|
||||
})
|
||||
}
|
||||
}
|
@@ -1,18 +0,0 @@
|
||||
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"))
|
||||
)
|
||||
}
|
@@ -1,104 +0,0 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,38 +0,0 @@
|
||||
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
|
||||
}
|
||||
|
||||
}
|
@@ -1,60 +0,0 @@
|
||||
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
|
||||
}
|
||||
|
||||
}
|
@@ -1,97 +0,0 @@
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -1,30 +0,0 @@
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,123 +0,0 @@
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,108 +0,0 @@
|
||||
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)
|
||||
}
|
@@ -1,22 +0,0 @@
|
||||
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))
|
||||
}
|
||||
}
|
@@ -1,8 +0,0 @@
|
||||
package com.chylex.intellij.coloredbrackets.color
|
||||
|
||||
enum class Luminosity {
|
||||
random,
|
||||
bright,
|
||||
light,
|
||||
dark
|
||||
}
|
@@ -1,156 +0,0 @@
|
||||
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()
|
||||
}
|
@@ -1,269 +0,0 @@
|
||||
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) }
|
||||
|
||||
}
|
||||
}
|
@@ -1,340 +0,0 @@
|
||||
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.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.text.CharArrayUtil
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList
|
||||
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,
|
||||
private val myEditor: Editor,
|
||||
private val myFile: PsiFile,
|
||||
) : TextEditorHighlightingPass(project, myEditor.document, false), DumbAware {
|
||||
|
||||
@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 = IntArrayList()
|
||||
val indents = IntArrayList()
|
||||
|
||||
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.isEmpty && curIndent <= indents.peekInt(0)) {
|
||||
ProgressManager.checkCanceled()
|
||||
val level = indents.popInt()
|
||||
val startLine = lines.popInt()
|
||||
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.isEmpty) {
|
||||
ProgressManager.checkCanceled()
|
||||
val level = indents.popInt()
|
||||
val startLine = lines.popInt()
|
||||
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 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 = 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
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,31 +0,0 @@
|
||||
package com.chylex.intellij.coloredbrackets.indents
|
||||
|
||||
import com.intellij.codeHighlighting.Pass
|
||||
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.editor.impl.ImaginaryEditor
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiFile
|
||||
|
||||
class RainbowIndentsPassFactory :
|
||||
TextEditorHighlightingPassFactoryRegistrar, TextEditorHighlightingPassFactory {
|
||||
|
||||
override fun createHighlightingPass(file: PsiFile, editor: Editor): RainbowIndentsPass? {
|
||||
return when (editor) {
|
||||
is ImaginaryEditor -> null
|
||||
else -> RainbowIndentsPass(file.project, editor, file)
|
||||
}
|
||||
}
|
||||
|
||||
override fun registerHighlightingPassFactory(registrar: TextEditorHighlightingPassRegistrar, project: Project) {
|
||||
registrar.registerTextEditorHighlightingPass(
|
||||
this,
|
||||
TextEditorHighlightingPassRegistrar.Anchor.LAST,
|
||||
Pass.LAST_PASS,
|
||||
false,
|
||||
false
|
||||
)
|
||||
}
|
||||
}
|
@@ -1,12 +0,0 @@
|
||||
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) }
|
||||
}
|
||||
}
|
@@ -1,14 +0,0 @@
|
||||
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)
|
||||
)
|
||||
}
|
@@ -1,12 +0,0 @@
|
||||
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)
|
||||
)
|
||||
}
|
@@ -1,13 +0,0 @@
|
||||
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)
|
||||
)
|
||||
}
|
@@ -1,59 +0,0 @@
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -1,72 +0,0 @@
|
||||
package com.chylex.intellij.coloredbrackets.settings
|
||||
|
||||
import com.chylex.intellij.coloredbrackets.settings.form.RainbowSettingsForm
|
||||
import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer
|
||||
import com.intellij.openapi.options.ConfigurationException
|
||||
import com.intellij.openapi.options.SearchableConfigurable
|
||||
import com.intellij.openapi.project.ProjectManager
|
||||
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
|
||||
|
||||
ProjectManager.getInstanceIfCreated()?.openProjects?.forEach {
|
||||
DaemonCodeAnalyzer.getInstance(it).restart()
|
||||
}
|
||||
}
|
||||
|
||||
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"
|
||||
}
|
||||
}
|
@@ -1,61 +0,0 @@
|
||||
package com.chylex.intellij.coloredbrackets.settings
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.components.PersistentStateComponent
|
||||
import com.intellij.openapi.components.SettingsCategory
|
||||
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")], category = SettingsCategory.UI)
|
||||
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 = 100_000
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
@@ -1,268 +0,0 @@
|
||||
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
|
@@ -1,139 +0,0 @@
|
||||
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
|
||||
}
|
||||
}
|
@@ -1,13 +0,0 @@
|
||||
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())
|
||||
}
|
@@ -1,54 +0,0 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,8 +0,0 @@
|
||||
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)
|
@@ -1,217 +0,0 @@
|
||||
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
|
||||
import it.unimi.dsi.fastutil.objects.Object2ByteOpenHashMap
|
||||
|
||||
class DefaultRainbowVisitor : RainbowHighlightVisitor() {
|
||||
|
||||
override fun clone(): HighlightVisitor = DefaultRainbowVisitor()
|
||||
|
||||
private var processor: Processor? = null
|
||||
|
||||
override fun onBeforeAnalyze(file: PsiFile, updateWholeFile: Boolean) {
|
||||
processor = Processor(RainbowSettings.instance)
|
||||
}
|
||||
|
||||
override fun visit(element: PsiElement) {
|
||||
val type = (element as? LeafPsiElement)?.elementType ?: return
|
||||
|
||||
val processor = processor!!
|
||||
val matching = processor.filterPairs(type, element) ?: return
|
||||
|
||||
val pair = when (matching.size) {
|
||||
1 -> matching[0]
|
||||
else -> matching.find { processor.isValidBracket(element, it) } ?: return
|
||||
}
|
||||
|
||||
val level = processor.getBracketLevel(element, pair)
|
||||
if (processor.settings.isDoNOTRainbowifyTheFirstLevel) {
|
||||
if (level >= 1) {
|
||||
rainbowPairs(element, pair, level)
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (level >= 0) {
|
||||
rainbowPairs(element, pair, level)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAfterAnalyze() {
|
||||
processor = null
|
||||
}
|
||||
|
||||
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(val settings: RainbowSettings) {
|
||||
|
||||
private companion object {
|
||||
private const val CACHE_MISS = (-1).toByte()
|
||||
}
|
||||
|
||||
private data class HasBracketsCacheKey(val element: PsiElement, val pair: BracePair) {
|
||||
private val hashCode = (31 * System.identityHashCode(element)) + System.identityHashCode(pair)
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
return other is HasBracketsCacheKey && element === other.element && pair === other.pair
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return hashCode
|
||||
}
|
||||
}
|
||||
|
||||
private val hasBracketsCache = Object2ByteOpenHashMap<HasBracketsCacheKey>().apply { defaultReturnValue(CACHE_MISS) }
|
||||
|
||||
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.hasBrackets(
|
||||
pair,
|
||||
{ it.elementType == pair.leftBraceType },
|
||||
{ it.elementType == pair.rightBraceType })
|
||||
) {
|
||||
nextCount++
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (element.hasBrackets(
|
||||
pair,
|
||||
{ element.language.braceTypeSet.contains(it.elementType) },
|
||||
{ element.language.braceTypeSet.contains(it.elementType) })
|
||||
) {
|
||||
nextCount++
|
||||
}
|
||||
}
|
||||
|
||||
return iterateBracketParents(element.parent, pair, nextCount)
|
||||
}
|
||||
|
||||
private inline fun PsiElement.hasBrackets(
|
||||
pair: BracePair,
|
||||
checkLeft: (LeafPsiElement) -> Boolean,
|
||||
checkRight: (LeafPsiElement) -> Boolean,
|
||||
): Boolean {
|
||||
if (this is LeafPsiElement) {
|
||||
return false
|
||||
}
|
||||
|
||||
val cacheKey = HasBracketsCacheKey(this, pair)
|
||||
val cacheValue = hasBracketsCache.getByte(cacheKey)
|
||||
if (cacheValue != CACHE_MISS) {
|
||||
return cacheValue == 1.toByte()
|
||||
}
|
||||
|
||||
val hasBrackets = run {
|
||||
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 && left?.prevSibling?.textMatches("$") == true) {
|
||||
false
|
||||
}
|
||||
else {
|
||||
findLeftBracket && findRightBracket
|
||||
}
|
||||
}
|
||||
|
||||
hasBracketsCache.put(cacheKey, if (hasBrackets) 1.toByte() else 0.toByte())
|
||||
return hasBrackets
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
private fun PsiElement.elementType(): IElementType? {
|
||||
return (this as? LeafPsiElement)?.elementType
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,42 +0,0 @@
|
||||
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)
|
||||
}
|
||||
}
|
@@ -1,102 +0,0 @@
|
||||
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,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,119 +0,0 @@
|
||||
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()
|
||||
} 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
|
||||
}
|
||||
}
|
@@ -1,16 +0,0 @@
|
||||
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()
|
||||
}
|
||||
}
|
@@ -1,101 +0,0 @@
|
||||
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 }
|
||||
}
|
||||
}
|
@@ -0,0 +1,82 @@
|
||||
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.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
|
||||
|
||||
object BracePairs {
|
||||
|
||||
private val providers = LanguageExtension<BracePairProvider>("com.chylex.intellij.rainbowbrackets.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)
|
@@ -0,0 +1,217 @@
|
||||
package com.github.izhangzhihao.rainbow.brackets
|
||||
|
||||
import com.github.izhangzhihao.rainbow.brackets.settings.RainbowSettings
|
||||
import com.github.izhangzhihao.rainbow.brackets.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
|
||||
import java.util.UUID
|
||||
|
||||
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 = RainbowSettings.instance
|
||||
|
||||
private val roundBracketsRainbowColorKeys: Array<TextAttributesKey> =
|
||||
createRainbowAttributesKeys(KEY_ROUND_BRACKETS, settings.numberOfColors)
|
||||
private val squareBracketsRainbowColorKeys: Array<TextAttributesKey> =
|
||||
createRainbowAttributesKeys(KEY_SQUARE_BRACKETS, settings.numberOfColors)
|
||||
private val squigglyBracketsRainbowColorKeys: Array<TextAttributesKey> =
|
||||
createRainbowAttributesKeys(KEY_SQUIGGLY_BRACKETS, settings.numberOfColors)
|
||||
private val angleBracketsRainbowColorKeys: Array<TextAttributesKey> =
|
||||
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
|
||||
NAME_SQUARE_BRACKETS -> squareBracketsRainbowColorKeys
|
||||
NAME_SQUIGGLY_BRACKETS -> squigglyBracketsRainbowColorKeys
|
||||
NAME_ANGLE_BRACKETS -> angleBracketsRainbowColorKeys
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("UNUSED_PARAMETER") // we use parameter as cache key
|
||||
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.github.izhangzhihao.rainbow.brackets.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())
|
||||
}
|
||||
}
|
@@ -0,0 +1,40 @@
|
||||
package com.github.izhangzhihao.rainbow.brackets
|
||||
|
||||
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")
|
||||
}
|
||||
}
|
@@ -0,0 +1,93 @@
|
||||
package com.github.izhangzhihao.rainbow.brackets
|
||||
|
||||
import com.github.izhangzhihao.rainbow.brackets.settings.RainbowConfigurable
|
||||
import com.github.izhangzhihao.rainbow.brackets.settings.RainbowSettings
|
||||
import com.github.izhangzhihao.rainbow.brackets.util.memoizedFileExtension
|
||||
import com.github.izhangzhihao.rainbow.brackets.util.toPsiFile
|
||||
import com.github.izhangzhihao.rainbow.brackets.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? {
|
||||
|
||||
if (!RainbowSettings.instance.isRainbowEnabled) {
|
||||
if (RainbowSettings.instance.suppressDisabledCheck) return null
|
||||
return EditorNotificationPanel().apply {
|
||||
text("Rainbow Brackets is now disabled")
|
||||
icon(AllIcons.General.GearPlain)
|
||||
createComponentActionLabel("got it, don't show again") {
|
||||
RainbowSettings.instance.suppressDisabledCheck = true
|
||||
EditorNotifications.getInstance(project).updateAllNotifications()
|
||||
}
|
||||
|
||||
createComponentActionLabel("enable Rainbow Brackets") {
|
||||
RainbowSettings.instance.isRainbowEnabled = true
|
||||
EditorNotifications.getInstance(project).updateAllNotifications()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val psiFile = file.toPsiFile(project)
|
||||
if (psiFile != null && !checkForBigFile(psiFile)) {
|
||||
if (RainbowSettings.instance.suppressBigFileCheck) return null
|
||||
return EditorNotificationPanel().apply {
|
||||
text("Rainbowify is disabled for files > " + RainbowSettings.instance.bigFilesLinesThreshold + " lines")
|
||||
icon(AllIcons.General.InspectionsEye)
|
||||
createComponentActionLabel("got it, don't show again") {
|
||||
RainbowSettings.instance.suppressBigFileCheck = true
|
||||
EditorNotifications.getInstance(project).updateAllNotifications()
|
||||
}
|
||||
|
||||
createComponentActionLabel("open settings") {
|
||||
ShowSettingsUtilImpl.showSettingsDialog(project, RainbowConfigurable.ID, "")
|
||||
EditorNotifications.getInstance(project).updateAllNotifications()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
RainbowSettings.instance.languageBlacklist.contains(file.fileType.name) ||
|
||||
RainbowSettings.instance.languageBlacklist.contains(memoizedFileExtension(file.name))
|
||||
) {
|
||||
if (RainbowSettings.instance.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") {
|
||||
RainbowSettings.instance.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())
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,18 @@
|
||||
package com.github.izhangzhihao.rainbow.brackets
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||
import com.fasterxml.jackson.module.kotlin.readValue
|
||||
import com.github.izhangzhihao.rainbow.brackets.color.Luminosity
|
||||
import com.github.izhangzhihao.rainbow.brackets.color.fromString
|
||||
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.github.izhangzhihao.rainbow.brackets.color.randomColor(
|
||||
fromString(ops.getOrDefault("hue", "random")),
|
||||
Luminosity.valueOf(ops.getOrDefault("luminosity", "random"))
|
||||
)
|
||||
}
|
@@ -0,0 +1,96 @@
|
||||
package com.github.izhangzhihao.rainbow.brackets.action
|
||||
|
||||
import com.github.izhangzhihao.rainbow.brackets.RainbowInfo
|
||||
import com.intellij.codeInsight.highlighting.HighlightManager
|
||||
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 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
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,36 @@
|
||||
package com.github.izhangzhihao.rainbow.brackets.action
|
||||
|
||||
import com.github.izhangzhihao.rainbow.brackets.RainbowInfo
|
||||
import com.github.izhangzhihao.rainbow.brackets.settings.RainbowSettings
|
||||
import com.github.izhangzhihao.rainbow.brackets.util.alphaBlend
|
||||
import com.github.izhangzhihao.rainbow.brackets.util.create
|
||||
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.*
|
||||
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,53 @@
|
||||
package com.github.izhangzhihao.rainbow.brackets.action
|
||||
|
||||
import com.github.izhangzhihao.rainbow.brackets.RainbowInfo
|
||||
import com.github.izhangzhihao.rainbow.brackets.settings.RainbowSettings
|
||||
import com.github.izhangzhihao.rainbow.brackets.util.alphaBlend
|
||||
import com.github.izhangzhihao.rainbow.brackets.util.create
|
||||
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.*
|
||||
|
||||
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
|
||||
if (startOffset > 0) {
|
||||
highlightManager.addRangeHighlight(this,
|
||||
0,
|
||||
startOffset,
|
||||
attributes, //create("ScopeOutsideHighlightingRestrainAction", attributes),
|
||||
false, //hideByTextChange
|
||||
RainbowSettings.instance.pressAnyKeyToRemoveTheHighlightingEffects, //hideByAnyKey
|
||||
highlighters)
|
||||
}
|
||||
|
||||
val endOffset = rainbowInfo.endOffset
|
||||
val lastOffset = document.textLength
|
||||
if (endOffset < lastOffset) {
|
||||
highlightManager.addRangeHighlight(this,
|
||||
endOffset,
|
||||
lastOffset,
|
||||
attributes, //create("ScopeOutsideHighlightingRestrainAction", attributes),
|
||||
false, //hideByTextChange
|
||||
RainbowSettings.instance.pressAnyKeyToRemoveTheHighlightingEffects, //hideByAnyKey
|
||||
highlighters)
|
||||
}
|
||||
|
||||
return highlighters
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,78 @@
|
||||
package com.github.izhangzhihao.rainbow.brackets.annotator
|
||||
|
||||
import com.github.izhangzhihao.rainbow.brackets.RainbowHighlighter
|
||||
import com.github.izhangzhihao.rainbow.brackets.RainbowInfo
|
||||
import com.github.izhangzhihao.rainbow.brackets.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.*
|
||||
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.github.izhangzhihao.rainbow.brackets.util.create(
|
||||
"rainbow-kotlin-label",
|
||||
TextAttributes(it, null, null, EffectType.BOXED, Font.PLAIN)
|
||||
))
|
||||
.create()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,31 @@
|
||||
package com.github.izhangzhihao.rainbow.brackets.annotator
|
||||
|
||||
import com.github.izhangzhihao.rainbow.brackets.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.github.izhangzhihao.rainbow.brackets.util.create(
|
||||
"rainbow-kotlin-arrow",
|
||||
TextAttributes(it, null, null, EffectType.BOXED, Font.PLAIN)
|
||||
)
|
||||
)
|
||||
.create()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,115 @@
|
||||
package com.github.izhangzhihao.rainbow.brackets.annotator
|
||||
|
||||
import com.github.izhangzhihao.rainbow.brackets.RainbowHighlighter.NAME_ANGLE_BRACKETS
|
||||
import com.github.izhangzhihao.rainbow.brackets.RainbowHighlighter.NAME_ROUND_BRACKETS
|
||||
import com.github.izhangzhihao.rainbow.brackets.RainbowHighlighter.NAME_SQUARE_BRACKETS
|
||||
import com.github.izhangzhihao.rainbow.brackets.RainbowHighlighter.NAME_SQUIGGLY_BRACKETS
|
||||
import com.github.izhangzhihao.rainbow.brackets.RainbowHighlighter.getRainbowColorByLevel
|
||||
import com.github.izhangzhihao.rainbow.brackets.annotator.RainbowUtils.annotateUtil
|
||||
import com.github.izhangzhihao.rainbow.brackets.annotator.RainbowUtils.settings
|
||||
import com.github.izhangzhihao.rainbow.brackets.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) {
|
||||
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 = 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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,108 @@
|
||||
package com.github.izhangzhihao.rainbow.brackets.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)
|
||||
}
|
@@ -0,0 +1,23 @@
|
||||
package com.github.izhangzhihao.rainbow.brackets.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))
|
||||
}
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
package com.github.izhangzhihao.rainbow.brackets.color
|
||||
|
||||
enum class Luminosity {
|
||||
random,
|
||||
bright,
|
||||
light,
|
||||
dark
|
||||
}
|
@@ -0,0 +1,153 @@
|
||||
package com.github.izhangzhihao.rainbow.brackets.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()
|
||||
}
|
@@ -0,0 +1,258 @@
|
||||
package com.github.izhangzhihao.rainbow.brackets.indents
|
||||
|
||||
import com.github.izhangzhihao.rainbow.brackets.RainbowInfo
|
||||
import com.github.izhangzhihao.rainbow.brackets.util.*
|
||||
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
|
||||
|
||||
/** 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 = Math.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) }
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,365 @@
|
||||
package com.github.izhangzhihao.rainbow.brackets.indents
|
||||
|
||||
import com.github.izhangzhihao.rainbow.brackets.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.Language
|
||||
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.*
|
||||
|
||||
/** 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<Language, TokenSet> = HashMap()
|
||||
val lineIndents: IntArray // 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 {
|
||||
lineIndents = IntArray(document.lineCount)
|
||||
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]
|
||||
if (comments == null) {
|
||||
val definition = LanguageParserDefinitions.INSTANCE.forLanguage(language)
|
||||
if (definition != null) {
|
||||
comments = definition.commentTokens
|
||||
}
|
||||
if (comments == null) {
|
||||
return false
|
||||
} else {
|
||||
myComments[language] = 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
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,24 @@
|
||||
package com.github.izhangzhihao.rainbow.brackets.indents
|
||||
|
||||
import com.intellij.codeHighlighting.*
|
||||
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
|
||||
)
|
||||
}
|
||||
}
|
@@ -0,0 +1,12 @@
|
||||
package com.github.izhangzhihao.rainbow.brackets.listener
|
||||
|
||||
import com.github.izhangzhihao.rainbow.brackets.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) }
|
||||
}
|
||||
}
|
@@ -1,4 +1,4 @@
|
||||
package com.chylex.intellij.coloredbrackets.provider
|
||||
package com.github.izhangzhihao.rainbow.brackets.provider
|
||||
|
||||
import com.intellij.lang.BracePair
|
||||
|
@@ -0,0 +1,13 @@
|
||||
package com.github.izhangzhihao.rainbow.brackets.provider
|
||||
|
||||
import com.intellij.lang.BracePair
|
||||
import com.jetbrains.rider.ideaInterop.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),
|
||||
)
|
||||
}
|
@@ -1,4 +1,4 @@
|
||||
package com.chylex.intellij.coloredbrackets.provider
|
||||
package com.github.izhangzhihao.rainbow.brackets.provider
|
||||
|
||||
import com.intellij.lang.BracePair
|
||||
import com.jetbrains.lang.dart.DartTokenTypes.GT
|