Skip to content

Commit

Permalink
init
Browse files Browse the repository at this point in the history
Signed-off-by: Jack Cherng <[email protected]>
  • Loading branch information
jfcherng committed May 10, 2024
0 parents commit da9bcce
Show file tree
Hide file tree
Showing 16 changed files with 4,693 additions and 0 deletions.
43 changes: 43 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# EditorConfig is awesome: https://EditorConfig.org
root = true

[*]
charset = utf-8
end_of_line = lf
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true

[Makefile]
indent_style = tab

[*.sublime-{settings,commands,keymap,mousemap}]
indent_style = tab
indent_size = 4

[*.sublime-{menu,syntax}]
indent_style = space
indent_size = 2

[*.json]
indent_size = 4

[*.md]
indent_size = 2
trim_trailing_whitespace = false

[*.py]
indent_size = 4

[*.{sh,csh,tcsh,zsh,bash,fish}]
indent_size = 4

[*.{yml,yaml,toml,neon}]
indent_style = space
indent_size = 2

[sublime-package.json]
indent_size = 2

[pyproject.toml]
indent_size = 2
24 changes: 24 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
*.pyright-syntax-test export-ignore
.dependabot export-ignore
.editorconfig export-ignore
.flake8 export-ignore
.gitattributes export-ignore
.github/ export-ignore
.gitignore export-ignore
.gitmodules export-ignore
.style.yapf export-ignore
.travis.yml export-ignore
codecov.yml export-ignore
docs/ export-ignore
Makefile export-ignore
mkdocs.yml export-ignore
mypy.ini export-ignore
pyproject.toml export-ignore
pyrightconfig.json export-ignore
requirements.txt export-ignore
scripts/ export-ignore
stubs/ export-ignore
tests/ export-ignore
tox.ini export-ignore
typings/ export-ignore
unittesting.json export-ignore
12 changes: 12 additions & 0 deletions .github/FUNDING.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# These are supported funding model platforms

github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: https://www.paypal.me/jfcherng/5usd
51 changes: 51 additions & 0 deletions .github/workflows/python.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# This workflow will install Python dependencies, run tests and lint with a variety of Python versions
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions

name: Python

on:
push:
branches:
- '**'
paths:
- '.github/workflows/python.yml'
- '**.py'
- '**.pyi'
- 'Makefile'
pull_request:
branches:
- '**'
paths:
- '.github/workflows/python.yml'
- '**.py'
- '**.pyi'
- 'Makefile'

jobs:
job_lint:
name: ${{ matrix.python-version }}
runs-on: ${{ matrix.os }}

strategy:
matrix:
os: ['ubuntu-latest']
python-version: ['3.8']

steps:
- uses: actions/checkout@v4
with:
submodules: recursive

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}

- name: Install dependencies
run: |
python -m pip install -U uv
make UV_INSTALL_FLAGS="--system" install-dev
- name: Do linting
run: |
make ci-check
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.mypy_cache
.tox
.venv-*/
.venv/
__pycache__
node_modules
1 change: 1 addition & 0 deletions .python-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3.8
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2024 Jack Cherng <[email protected]>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
31 changes: 31 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
UV_INSTALL_FLAGS :=

.PHONY: all
all:

.PHONY: install
install: install-dev

.PHONY: install-dev
install-dev:
uv pip install $(UV_INSTALL_FLAGS) -r requirements-dev.txt

.PHONY: pip-compile
pip-compile:
uv pip compile --upgrade requirements-dev.in -o requirements-dev.txt

.PHONY: ci-check
ci-check:
@echo "========== check: mypy =========="
mypy -p plugin
@echo "========== check: ruff (lint) =========="
ruff check --diff .
@echo "========== check: ruff (format) =========="
ruff format --diff .

.PHONY: ci-fix
ci-fix:
@echo "========== fix: ruff (lint) =========="
ruff check --fix .
@echo "========== fix: ruff (format) =========="
ruff format .
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# ST-RainbowIndent

[![Required ST Build](https://img.shields.io/badge/ST-4114+-orange.svg?style=flat-square&logo=sublime-text)](https://www.sublimetext.com)
[![GitHub Actions](https://img.shields.io/github/actions/workflow/status/jfcherng-sublime/ST-RainbowIndent/python.yml?branch=st4&style=flat-square)](https://github.com/jfcherng-sublime/ST-RainbowIndent/actions)
[![Package Control](https://img.shields.io/packagecontrol/dt/RainbowIndent?style=flat-square)](https://packagecontrol.io/packages/RainbowIndent)
[![GitHub tag (latest by date)](https://img.shields.io/github/v/tag/jfcherng-sublime/ST-RainbowIndent?style=flat-square&logo=github)](https://github.com/jfcherng-sublime/ST-RainbowIndent/tags)
[![Project license](https://img.shields.io/github/license/jfcherng-sublime/ST-RainbowIndent?style=flat-square&logo=github)](https://github.com/jfcherng-sublime/ST-RainbowIndent/blob/st4/LICENSE)
[![GitHub stars](https://img.shields.io/github/stars/jfcherng-sublime/ST-RainbowIndent?style=flat-square&logo=github)](https://github.com/jfcherng-sublime/ST-RainbowIndent/stargazers)
[![Donate to this project using Paypal](https://img.shields.io/badge/paypal-donate-blue.svg?style=flat-square&logo=paypal)](https://www.paypal.me/jfcherng/5usd)

A very naive implementation which is just for fun...

## Installation

Only manually installation is supported.

## Known Issues

- There is no way to draw a region where is nothing there via ST's plugin APIs.
142 changes: 142 additions & 0 deletions plugin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
from __future__ import annotations

import sys
import weakref
from collections import defaultdict
from dataclasses import dataclass
from enum import Enum
from typing import Tuple

import sublime
import sublime_plugin

assert __package__

PLUGIN_NAME = __package__.partition(".")[0]

LEVEL_COLORS = [
"region.redish",
"region.orangish",
"region.yellowish",
"region.greenish",
"region.cyanish",
"region.bluish",
"region.purplish",
"region.pinkish",
]

SIMPLE_REGION = Tuple[int, int]

if sys.version_info >= (3, 11):
from enum import StrEnum
else:

class StrEnum(str, Enum):
__str__ = str.__str__ # type: ignore
__format__ = str.__format__ # type: ignore


def get_regions_key(level: int) -> str:
return f"{PLUGIN_NAME}:{level}"


def get_view_indentation(view: sublime.View) -> IndentInfo:
settings = view.settings()

tab_size = settings.get("tab_size", 4)

if settings.get("translate_tabs_to_spaces", False):
style = IndentStyle.SPACE
else:
style = IndentStyle.TAB

return IndentInfo(tab_size=tab_size, style=style)


@dataclass
class IndentInfo:
tab_size: int
style: IndentStyle

@property
def indent_char(self) -> str:
if self.style is IndentStyle.SPACE:
return " " * self.tab_size
if self.style is IndentStyle.TAB:
return "\t"
raise ValueError(f"Unknown indent style: {self.style}")

@property
def indent_length(self) -> int:
return len(self.indent_char)

@property
def indent_pattern(self) -> str:
return rf"^({self.indent_char})+"


class IndentStyle(StrEnum):
SPACE = "space"
TAB = "tab"


class ViewManager:
__instances: dict[weakref.ref[sublime.View], ViewManager] = {}

def __init__(self, view: sublime.View, *, from_get_instance: bool = False) -> None:
if not from_get_instance:
raise ValueError("Use get_instance() instead.")

self.view = view
self.max_level = -1

@classmethod
def get_instance(cls, view: sublime.View) -> ViewManager:
view_proxy = weakref.proxy(view)
view_ref = weakref.ref(view)
if not cls.__instances.get(view_ref):
cls.__instances[view_ref] = cls(view_proxy, from_get_instance=True)
return cls.__instances[view_ref]

def clear_view(self) -> None:
for level in range(self.max_level + 1):
self.view.erase_regions(get_regions_key(level))

def render_view(self) -> None:
indent_info = get_view_indentation(self.view)

# key = indentation level; value = list of begin point of indentation
level_pts: defaultdict[int, list[int]] = defaultdict(list)
for region in self.view.find_all(indent_info.indent_pattern):
for level in range(region.size() // indent_info.indent_length):
level_pts[level].append(region.a + level * indent_info.indent_length)

for level, begin_pts in level_pts.items():
self.view.add_regions(
get_regions_key(level),
tuple(sublime.Region(pt, pt + indent_info.indent_length) for pt in begin_pts),
scope=LEVEL_COLORS[level % len(LEVEL_COLORS)],
flags=sublime.DRAW_NO_OUTLINE | sublime.HIDE_ON_MINIMAP | sublime.NO_UNDO,
)

self.max_level = max(level_pts.keys(), default=-1)


class RainbowIndent(sublime_plugin.ViewEventListener):
def on_activated_async(self) -> None:
self._work(self.view)

def on_load_async(self) -> None:
self._work(self.view)

def on_modified_async(self) -> None:
self._work(self.view)

def on_revert_async(self) -> None:
self._work(self.view)

@staticmethod
def _work(view: sublime.View) -> None:
vrm = ViewManager.get_instance(view)
vrm.clear_view()
vrm.render_view()
Loading

0 comments on commit da9bcce

Please sign in to comment.