richless 0.2.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- richless-0.2.1/.gitignore +207 -0
- richless-0.2.1/LICENSE +21 -0
- richless-0.2.1/PKG-INFO +356 -0
- richless-0.2.1/README.md +344 -0
- richless-0.2.1/pyproject.toml +25 -0
- richless-0.2.1/richless-init.sh +128 -0
- richless-0.2.1/richless.py +191 -0
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[codz]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
share/python-wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
.installed.cfg
|
|
26
|
+
*.egg
|
|
27
|
+
MANIFEST
|
|
28
|
+
|
|
29
|
+
# PyInstaller
|
|
30
|
+
# Usually these files are written by a python script from a template
|
|
31
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
32
|
+
*.manifest
|
|
33
|
+
*.spec
|
|
34
|
+
|
|
35
|
+
# Installer logs
|
|
36
|
+
pip-log.txt
|
|
37
|
+
pip-delete-this-directory.txt
|
|
38
|
+
|
|
39
|
+
# Unit test / coverage reports
|
|
40
|
+
htmlcov/
|
|
41
|
+
.tox/
|
|
42
|
+
.nox/
|
|
43
|
+
.coverage
|
|
44
|
+
.coverage.*
|
|
45
|
+
.cache
|
|
46
|
+
nosetests.xml
|
|
47
|
+
coverage.xml
|
|
48
|
+
*.cover
|
|
49
|
+
*.py.cover
|
|
50
|
+
.hypothesis/
|
|
51
|
+
.pytest_cache/
|
|
52
|
+
cover/
|
|
53
|
+
|
|
54
|
+
# Translations
|
|
55
|
+
*.mo
|
|
56
|
+
*.pot
|
|
57
|
+
|
|
58
|
+
# Django stuff:
|
|
59
|
+
*.log
|
|
60
|
+
local_settings.py
|
|
61
|
+
db.sqlite3
|
|
62
|
+
db.sqlite3-journal
|
|
63
|
+
|
|
64
|
+
# Flask stuff:
|
|
65
|
+
instance/
|
|
66
|
+
.webassets-cache
|
|
67
|
+
|
|
68
|
+
# Scrapy stuff:
|
|
69
|
+
.scrapy
|
|
70
|
+
|
|
71
|
+
# Sphinx documentation
|
|
72
|
+
docs/_build/
|
|
73
|
+
|
|
74
|
+
# PyBuilder
|
|
75
|
+
.pybuilder/
|
|
76
|
+
target/
|
|
77
|
+
|
|
78
|
+
# Jupyter Notebook
|
|
79
|
+
.ipynb_checkpoints
|
|
80
|
+
|
|
81
|
+
# IPython
|
|
82
|
+
profile_default/
|
|
83
|
+
ipython_config.py
|
|
84
|
+
|
|
85
|
+
# pyenv
|
|
86
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
87
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
88
|
+
# .python-version
|
|
89
|
+
|
|
90
|
+
# pipenv
|
|
91
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
92
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
93
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
94
|
+
# install all needed dependencies.
|
|
95
|
+
#Pipfile.lock
|
|
96
|
+
|
|
97
|
+
# UV
|
|
98
|
+
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
|
99
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
100
|
+
# commonly ignored for libraries.
|
|
101
|
+
#uv.lock
|
|
102
|
+
|
|
103
|
+
# poetry
|
|
104
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
105
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
106
|
+
# commonly ignored for libraries.
|
|
107
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
108
|
+
#poetry.lock
|
|
109
|
+
#poetry.toml
|
|
110
|
+
|
|
111
|
+
# pdm
|
|
112
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
113
|
+
# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
|
|
114
|
+
# https://pdm-project.org/en/latest/usage/project/#working-with-version-control
|
|
115
|
+
#pdm.lock
|
|
116
|
+
#pdm.toml
|
|
117
|
+
.pdm-python
|
|
118
|
+
.pdm-build/
|
|
119
|
+
|
|
120
|
+
# pixi
|
|
121
|
+
# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
|
|
122
|
+
#pixi.lock
|
|
123
|
+
# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
|
|
124
|
+
# in the .venv directory. It is recommended not to include this directory in version control.
|
|
125
|
+
.pixi
|
|
126
|
+
|
|
127
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
128
|
+
__pypackages__/
|
|
129
|
+
|
|
130
|
+
# Celery stuff
|
|
131
|
+
celerybeat-schedule
|
|
132
|
+
celerybeat.pid
|
|
133
|
+
|
|
134
|
+
# SageMath parsed files
|
|
135
|
+
*.sage.py
|
|
136
|
+
|
|
137
|
+
# Environments
|
|
138
|
+
.env
|
|
139
|
+
.envrc
|
|
140
|
+
.venv
|
|
141
|
+
env/
|
|
142
|
+
venv/
|
|
143
|
+
ENV/
|
|
144
|
+
env.bak/
|
|
145
|
+
venv.bak/
|
|
146
|
+
|
|
147
|
+
# Spyder project settings
|
|
148
|
+
.spyderproject
|
|
149
|
+
.spyproject
|
|
150
|
+
|
|
151
|
+
# Rope project settings
|
|
152
|
+
.ropeproject
|
|
153
|
+
|
|
154
|
+
# mkdocs documentation
|
|
155
|
+
/site
|
|
156
|
+
|
|
157
|
+
# mypy
|
|
158
|
+
.mypy_cache/
|
|
159
|
+
.dmypy.json
|
|
160
|
+
dmypy.json
|
|
161
|
+
|
|
162
|
+
# Pyre type checker
|
|
163
|
+
.pyre/
|
|
164
|
+
|
|
165
|
+
# pytype static type analyzer
|
|
166
|
+
.pytype/
|
|
167
|
+
|
|
168
|
+
# Cython debug symbols
|
|
169
|
+
cython_debug/
|
|
170
|
+
|
|
171
|
+
# PyCharm
|
|
172
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
173
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
174
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
175
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
176
|
+
#.idea/
|
|
177
|
+
|
|
178
|
+
# Abstra
|
|
179
|
+
# Abstra is an AI-powered process automation framework.
|
|
180
|
+
# Ignore directories containing user credentials, local state, and settings.
|
|
181
|
+
# Learn more at https://abstra.io/docs
|
|
182
|
+
.abstra/
|
|
183
|
+
|
|
184
|
+
# Visual Studio Code
|
|
185
|
+
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
|
|
186
|
+
# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
|
|
187
|
+
# and can be added to the global gitignore or merged into this file. However, if you prefer,
|
|
188
|
+
# you could uncomment the following to ignore the entire vscode folder
|
|
189
|
+
# .vscode/
|
|
190
|
+
|
|
191
|
+
# Ruff stuff:
|
|
192
|
+
.ruff_cache/
|
|
193
|
+
|
|
194
|
+
# PyPI configuration file
|
|
195
|
+
.pypirc
|
|
196
|
+
|
|
197
|
+
# Cursor
|
|
198
|
+
# Cursor is an AI-powered code editor. `.cursorignore` specifies files/directories to
|
|
199
|
+
# exclude from AI features like autocomplete and code analysis. Recommended for sensitive data
|
|
200
|
+
# refer to https://docs.cursor.com/context/ignore-files
|
|
201
|
+
.cursorignore
|
|
202
|
+
.cursorindexingignore
|
|
203
|
+
|
|
204
|
+
# Marimo
|
|
205
|
+
marimo/_static/
|
|
206
|
+
marimo/_lsp/
|
|
207
|
+
__marimo__/
|
richless-0.2.1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 DavidJBianco
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
richless-0.2.1/PKG-INFO
ADDED
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: richless
|
|
3
|
+
Version: 0.2.1
|
|
4
|
+
Summary: LESSOPEN filter for Markdown rendering and syntax highlighting with less
|
|
5
|
+
License-File: LICENSE
|
|
6
|
+
Requires-Python: >=3.12
|
|
7
|
+
Requires-Dist: pygments>=2.0.0
|
|
8
|
+
Requires-Dist: rich>=13.8.0
|
|
9
|
+
Provides-Extra: dev
|
|
10
|
+
Requires-Dist: pytest>=7.0.0; extra == 'dev'
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
|
|
13
|
+
# richless
|
|
14
|
+
|
|
15
|
+
A LESSOPEN filter for automatically rendering Markdown files and syntax highlighting source code when using `less`. View your Markdown documents with beautiful formatting and programming language files with syntax highlighting directly in the terminal, without changing how you use `less`.
|
|
16
|
+
|
|
17
|
+
## Quick Start
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
# 1. Install richless
|
|
21
|
+
git clone <repository-url>
|
|
22
|
+
cd richless
|
|
23
|
+
uv tool install .
|
|
24
|
+
|
|
25
|
+
# 2. Copy init script and add to your ~/.bashrc or ~/.zshrc
|
|
26
|
+
cp richless-init.sh ~/.richless-init.sh
|
|
27
|
+
echo 'source ~/.richless-init.sh' >> ~/.bashrc # or ~/.zshrc
|
|
28
|
+
|
|
29
|
+
# 3. Reload your shell
|
|
30
|
+
source ~/.bashrc # or ~/.zshrc
|
|
31
|
+
|
|
32
|
+
# 4. Try it!
|
|
33
|
+
less README.md # View rendered Markdown
|
|
34
|
+
less richless.py # View syntax-highlighted Python
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Features
|
|
38
|
+
|
|
39
|
+
- **Seamless Integration**: Works transparently with `less` via LESSOPEN
|
|
40
|
+
- **Automatic Markdown Rendering**: Recognizes `.md` and `.markdown` files automatically and renders them beautifully
|
|
41
|
+
- **Rich Terminal Formatting**: Beautiful rendering with headers, lists, code blocks, tables, and more
|
|
42
|
+
- **Data Format Highlighting**: Syntax highlighting for JSON, JSONL, YAML, and XML files with automatic detection
|
|
43
|
+
- **Code Highlighting**: Syntax highlighting for 500+ programming languages (Python, JavaScript, Go, Rust, and more)
|
|
44
|
+
- **Works with Wildcards**: `less *.md` or `less *.py` just works
|
|
45
|
+
- **Correct Filenames**: Shows actual filenames in less, not temporary files
|
|
46
|
+
- **Powered by rich and Pygments**: Leverages [rich](https://github.com/Textualize/rich) for beautiful terminal output and [Pygments](https://pygments.org/) for syntax highlighting
|
|
47
|
+
|
|
48
|
+
## Installation
|
|
49
|
+
|
|
50
|
+
### Prerequisites
|
|
51
|
+
|
|
52
|
+
- **Python 3.12+**
|
|
53
|
+
- **[uv](https://github.com/astral-sh/uv)** - Modern Python package manager
|
|
54
|
+
```bash
|
|
55
|
+
# Install uv if you don't have it
|
|
56
|
+
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
57
|
+
```
|
|
58
|
+
- **less pager** (standard on macOS and most Linux distributions)
|
|
59
|
+
|
|
60
|
+
### Step 1: Install richless
|
|
61
|
+
|
|
62
|
+
Install richless in an isolated virtualenv using uv's tool install feature:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
# Clone or download this repository
|
|
66
|
+
git clone <repository-url>
|
|
67
|
+
cd richless
|
|
68
|
+
|
|
69
|
+
# Install richless as a uv tool
|
|
70
|
+
uv tool install .
|
|
71
|
+
|
|
72
|
+
# Or install directly from git (once published)
|
|
73
|
+
# uv tool install git+https://github.com/yourusername/richless
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
This installs `richless` and all its dependencies in an isolated virtualenv, making the `richless` command available system-wide without polluting your project environments.
|
|
77
|
+
|
|
78
|
+
**Note:** If you see a warning about PATH, run:
|
|
79
|
+
```bash
|
|
80
|
+
export PATH="$HOME/.local/bin:$PATH"
|
|
81
|
+
# Add this line to your ~/.bashrc or ~/.zshrc to make it permanent
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Step 2: Configure Shell Integration
|
|
85
|
+
|
|
86
|
+
You have two configuration options depending on your needs:
|
|
87
|
+
|
|
88
|
+
#### Option 1: Basic LESSOPEN (Minimal setup)
|
|
89
|
+
|
|
90
|
+
If you prefer not to use the shell wrapper, you can manually set two environment variables. This is a lighter-weight alternative but lacks some features.
|
|
91
|
+
|
|
92
|
+
**Setup:**
|
|
93
|
+
|
|
94
|
+
Add these lines to your `~/.bashrc`, `~/.zshrc`, or `~/.profile`:
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
export LESSOPEN="|richless %s"
|
|
98
|
+
export LESS="-R"
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Then reload your shell:
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
source ~/.bashrc # or source ~/.zshrc
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
**What you get:**
|
|
108
|
+
- ✅ Automatic Markdown rendering when you run `less file.md`
|
|
109
|
+
- ✅ Syntax highlighting for programming language source files (Python, JavaScript, etc.)
|
|
110
|
+
- ✅ Works with wildcards: `less *.md` or `less *.py`
|
|
111
|
+
- ✅ Fast and lightweight
|
|
112
|
+
|
|
113
|
+
**Limitations:**
|
|
114
|
+
- ❌ Doesn't work with piped input: `cat file.md | less` won't render
|
|
115
|
+
- ❌ Can't force markdown on non-.md files through `less`
|
|
116
|
+
|
|
117
|
+
#### Option 2: Transparent Wrapper (Recommended - used by Quick Start)
|
|
118
|
+
|
|
119
|
+
This is the recommended setup and what Quick Start uses. It provides a smart wrapper around `less` that handles edge cases like piped input and forcing markdown on arbitrary files.
|
|
120
|
+
|
|
121
|
+
**Setup:**
|
|
122
|
+
|
|
123
|
+
1. Copy the shell integration script to your home directory:
|
|
124
|
+
```bash
|
|
125
|
+
# From the richless project directory
|
|
126
|
+
cp richless-init.sh ~/.richless-init.sh
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
2. Source it in your shell configuration:
|
|
130
|
+
```bash
|
|
131
|
+
# Add this line to ~/.bashrc, ~/.zshrc, or ~/.profile
|
|
132
|
+
source ~/.richless-init.sh
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
3. Reload your shell:
|
|
136
|
+
```bash
|
|
137
|
+
source ~/.bashrc # or source ~/.zshrc
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
**What you get:**
|
|
141
|
+
- ✅ Everything from Option 1, plus:
|
|
142
|
+
- ✅ Piped input works: `cat file.md | less` renders markdown
|
|
143
|
+
- ✅ Force markdown flag: `less --md document.txt` forces rendering
|
|
144
|
+
- ✅ Auto-detection: Intelligently detects markdown in piped content
|
|
145
|
+
- ✅ Backward compatible: Acts like normal `less` when not needed
|
|
146
|
+
|
|
147
|
+
**Note:** The shell integration file is compatible with sh, bash, and zsh.
|
|
148
|
+
|
|
149
|
+
## Usage
|
|
150
|
+
|
|
151
|
+
Once configured, just use `less` normally! Markdown files will be automatically rendered, and source code files will be syntax highlighted.
|
|
152
|
+
|
|
153
|
+
### Basic Usage (Both Options)
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
# View a Markdown file (automatically rendered)
|
|
157
|
+
less README.md
|
|
158
|
+
|
|
159
|
+
# View multiple Markdown files
|
|
160
|
+
less *.md
|
|
161
|
+
|
|
162
|
+
# View with wildcards
|
|
163
|
+
less docs/**/*.md
|
|
164
|
+
|
|
165
|
+
# Source code files get syntax highlighting automatically
|
|
166
|
+
less script.py # Python
|
|
167
|
+
less app.js # JavaScript
|
|
168
|
+
less main.go # Go
|
|
169
|
+
less config.json # JSON
|
|
170
|
+
less styles.css # CSS
|
|
171
|
+
|
|
172
|
+
# All standard less options work
|
|
173
|
+
less -N README.md # Show line numbers
|
|
174
|
+
less -i script.py # Case-insensitive search
|
|
175
|
+
less +50 README.md # Start at line 50
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### Additional Features (Option 2 / Quick Start)
|
|
179
|
+
|
|
180
|
+
If you followed the Quick Start or are using Option 2, you also get these features:
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
# Force Markdown rendering on non-.md files
|
|
184
|
+
less --md document.txt
|
|
185
|
+
less -m notes.txt
|
|
186
|
+
|
|
187
|
+
# Piped input works!
|
|
188
|
+
cat file.md | less
|
|
189
|
+
echo "# Hello\n**World**" | less --md
|
|
190
|
+
|
|
191
|
+
# Pipe from other commands
|
|
192
|
+
curl https://example.com/README.md | less --md
|
|
193
|
+
grep -A 50 "## Section" doc.md | less
|
|
194
|
+
|
|
195
|
+
# Auto-detection: if piped content looks like markdown, it renders automatically
|
|
196
|
+
cat file.md | less # Detects markdown syntax and renders
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### Direct richless Usage
|
|
200
|
+
|
|
201
|
+
You can also call `richless` directly if needed:
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
# Render and pipe to less
|
|
205
|
+
richless document.md | less -R
|
|
206
|
+
|
|
207
|
+
# Force markdown rendering
|
|
208
|
+
richless --md document.txt | less -R
|
|
209
|
+
|
|
210
|
+
# Read from stdin
|
|
211
|
+
cat file.md | richless --md - | less -R
|
|
212
|
+
echo "# Test" | richless --md - | less -R
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### Standard less Commands
|
|
216
|
+
|
|
217
|
+
All standard `less` commands work normally inside the pager:
|
|
218
|
+
|
|
219
|
+
- `/pattern` - Search forward
|
|
220
|
+
- `?pattern` - Search backward
|
|
221
|
+
- `n` / `N` - Next/previous match
|
|
222
|
+
- `g` / `G` - Go to start/end
|
|
223
|
+
- `q` - Quit
|
|
224
|
+
- `h` - Help
|
|
225
|
+
|
|
226
|
+
## How It Works
|
|
227
|
+
|
|
228
|
+
**Basic Mode (Option 1):**
|
|
229
|
+
1. When you run `less file.md`, the `LESSOPEN` environment variable tells less to run `richless file.md` first
|
|
230
|
+
2. `richless` detects the `.md` extension and uses the `rich` library to render the Markdown
|
|
231
|
+
3. `rich` renders the Markdown to beautifully formatted ANSI text with proper table support
|
|
232
|
+
4. The formatted output is piped to `less` for viewing
|
|
233
|
+
5. For programming language source files (`.py`, `.js`, `.java`, etc.), `rich` automatically provides syntax highlighting using Pygments
|
|
234
|
+
|
|
235
|
+
**Transparent Wrapper (Option 2):**
|
|
236
|
+
- The shell function intercepts calls to `less` before they execute
|
|
237
|
+
- For regular files, it passes through to the basic LESSOPEN mechanism
|
|
238
|
+
- For piped input or when `--md` is specified, it saves the content to a temp file and renders it
|
|
239
|
+
- Auto-detection checks piped content for markdown patterns (headers, lists, links, etc.)
|
|
240
|
+
|
|
241
|
+
## Troubleshooting
|
|
242
|
+
|
|
243
|
+
### "richless: command not found"
|
|
244
|
+
|
|
245
|
+
Make sure `~/.local/bin` is in your PATH:
|
|
246
|
+
```bash
|
|
247
|
+
export PATH="$HOME/.local/bin:$PATH"
|
|
248
|
+
# Add this to your ~/.bashrc or ~/.zshrc
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
### Piped input doesn't work
|
|
252
|
+
|
|
253
|
+
If you're using Option 1 (Basic LESSOPEN), piped input won't work. Either:
|
|
254
|
+
- Switch to Option 2 / Quick Start for piped input support
|
|
255
|
+
- Or use: `cat file.md | richless --md - | less -R`
|
|
256
|
+
|
|
257
|
+
### Colors don't show up
|
|
258
|
+
|
|
259
|
+
If you're using Option 1 (manual setup), make sure you have the `-R` flag set:
|
|
260
|
+
```bash
|
|
261
|
+
export LESS="-R"
|
|
262
|
+
```
|
|
263
|
+
The init script (Option 2 / Quick Start) sets this automatically.
|
|
264
|
+
|
|
265
|
+
### Shell function not loading
|
|
266
|
+
|
|
267
|
+
Make sure you're sourcing the init script, not executing it:
|
|
268
|
+
```bash
|
|
269
|
+
source ~/.richless-init.sh # Correct
|
|
270
|
+
./richless-init.sh # Wrong - this won't define the function in your shell
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
### After sourcing, "less file.md" shows an error
|
|
274
|
+
|
|
275
|
+
Make sure you've reinstalled richless after any updates:
|
|
276
|
+
```bash
|
|
277
|
+
uv tool uninstall richless
|
|
278
|
+
uv tool install .
|
|
279
|
+
source ~/.richless-init.sh # Re-source to pick up changes
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
## Updating
|
|
283
|
+
|
|
284
|
+
```bash
|
|
285
|
+
# Update to the latest version
|
|
286
|
+
cd /path/to/richless
|
|
287
|
+
git pull # Get latest changes
|
|
288
|
+
uv tool upgrade richless
|
|
289
|
+
|
|
290
|
+
# Re-source the init script to pick up any changes
|
|
291
|
+
source ~/.richless-init.sh
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
## Uninstalling
|
|
295
|
+
|
|
296
|
+
```bash
|
|
297
|
+
# Remove the tool
|
|
298
|
+
uv tool uninstall richless
|
|
299
|
+
|
|
300
|
+
# Remove from your shell config (~/.bashrc or ~/.zshrc)
|
|
301
|
+
# Delete or comment out the source line:
|
|
302
|
+
# source ~/.richless-init.sh
|
|
303
|
+
|
|
304
|
+
# Remove the init script
|
|
305
|
+
rm ~/.richless-init.sh
|
|
306
|
+
|
|
307
|
+
# If using Option 1 instead, remove these lines:
|
|
308
|
+
# export LESSOPEN="|richless %s"
|
|
309
|
+
# export LESS="-R"
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
## Development
|
|
313
|
+
|
|
314
|
+
```bash
|
|
315
|
+
# Clone the repository
|
|
316
|
+
git clone <repository-url>
|
|
317
|
+
cd richless
|
|
318
|
+
|
|
319
|
+
# Install dependencies
|
|
320
|
+
uv sync
|
|
321
|
+
|
|
322
|
+
# Install in development mode
|
|
323
|
+
uv tool install --editable .
|
|
324
|
+
|
|
325
|
+
# Make changes, then reinstall
|
|
326
|
+
uv tool install --editable . --force
|
|
327
|
+
|
|
328
|
+
# Test it
|
|
329
|
+
less README.md
|
|
330
|
+
|
|
331
|
+
# Run tests
|
|
332
|
+
uv run pytest tests/test_richless.py -v
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
## Dependencies
|
|
336
|
+
|
|
337
|
+
- [rich](https://github.com/Textualize/rich) - Python library for rich terminal output, Markdown rendering, and syntax highlighting
|
|
338
|
+
- [Pygments](https://pygments.org/) - Syntax highlighting library
|
|
339
|
+
- Python 3.12+
|
|
340
|
+
- uv - Modern Python package manager
|
|
341
|
+
|
|
342
|
+
## Comparison to Alternatives
|
|
343
|
+
|
|
344
|
+
**vs. glow / mdcat / bat:**
|
|
345
|
+
- richless integrates directly with `less`, so you use your familiar pager commands
|
|
346
|
+
- Works transparently - no need to remember a different command
|
|
347
|
+
- Supports all standard `less` features (search, navigation, etc.)
|
|
348
|
+
|
|
349
|
+
**vs. vimpager:**
|
|
350
|
+
- Lighter weight, doesn't require Vim
|
|
351
|
+
- Uses rich library's excellent Markdown rendering with full table support
|
|
352
|
+
- Simple LESSOPEN integration
|
|
353
|
+
|
|
354
|
+
## License
|
|
355
|
+
|
|
356
|
+
See LICENSE file for details.
|
richless-0.2.1/README.md
ADDED
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
# richless
|
|
2
|
+
|
|
3
|
+
A LESSOPEN filter for automatically rendering Markdown files and syntax highlighting source code when using `less`. View your Markdown documents with beautiful formatting and programming language files with syntax highlighting directly in the terminal, without changing how you use `less`.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# 1. Install richless
|
|
9
|
+
git clone <repository-url>
|
|
10
|
+
cd richless
|
|
11
|
+
uv tool install .
|
|
12
|
+
|
|
13
|
+
# 2. Copy init script and add to your ~/.bashrc or ~/.zshrc
|
|
14
|
+
cp richless-init.sh ~/.richless-init.sh
|
|
15
|
+
echo 'source ~/.richless-init.sh' >> ~/.bashrc # or ~/.zshrc
|
|
16
|
+
|
|
17
|
+
# 3. Reload your shell
|
|
18
|
+
source ~/.bashrc # or ~/.zshrc
|
|
19
|
+
|
|
20
|
+
# 4. Try it!
|
|
21
|
+
less README.md # View rendered Markdown
|
|
22
|
+
less richless.py # View syntax-highlighted Python
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Features
|
|
26
|
+
|
|
27
|
+
- **Seamless Integration**: Works transparently with `less` via LESSOPEN
|
|
28
|
+
- **Automatic Markdown Rendering**: Recognizes `.md` and `.markdown` files automatically and renders them beautifully
|
|
29
|
+
- **Rich Terminal Formatting**: Beautiful rendering with headers, lists, code blocks, tables, and more
|
|
30
|
+
- **Data Format Highlighting**: Syntax highlighting for JSON, JSONL, YAML, and XML files with automatic detection
|
|
31
|
+
- **Code Highlighting**: Syntax highlighting for 500+ programming languages (Python, JavaScript, Go, Rust, and more)
|
|
32
|
+
- **Works with Wildcards**: `less *.md` or `less *.py` just works
|
|
33
|
+
- **Correct Filenames**: Shows actual filenames in less, not temporary files
|
|
34
|
+
- **Powered by rich and Pygments**: Leverages [rich](https://github.com/Textualize/rich) for beautiful terminal output and [Pygments](https://pygments.org/) for syntax highlighting
|
|
35
|
+
|
|
36
|
+
## Installation
|
|
37
|
+
|
|
38
|
+
### Prerequisites
|
|
39
|
+
|
|
40
|
+
- **Python 3.12+**
|
|
41
|
+
- **[uv](https://github.com/astral-sh/uv)** - Modern Python package manager
|
|
42
|
+
```bash
|
|
43
|
+
# Install uv if you don't have it
|
|
44
|
+
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
45
|
+
```
|
|
46
|
+
- **less pager** (standard on macOS and most Linux distributions)
|
|
47
|
+
|
|
48
|
+
### Step 1: Install richless
|
|
49
|
+
|
|
50
|
+
Install richless in an isolated virtualenv using uv's tool install feature:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
# Clone or download this repository
|
|
54
|
+
git clone <repository-url>
|
|
55
|
+
cd richless
|
|
56
|
+
|
|
57
|
+
# Install richless as a uv tool
|
|
58
|
+
uv tool install .
|
|
59
|
+
|
|
60
|
+
# Or install directly from git (once published)
|
|
61
|
+
# uv tool install git+https://github.com/yourusername/richless
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
This installs `richless` and all its dependencies in an isolated virtualenv, making the `richless` command available system-wide without polluting your project environments.
|
|
65
|
+
|
|
66
|
+
**Note:** If you see a warning about PATH, run:
|
|
67
|
+
```bash
|
|
68
|
+
export PATH="$HOME/.local/bin:$PATH"
|
|
69
|
+
# Add this line to your ~/.bashrc or ~/.zshrc to make it permanent
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Step 2: Configure Shell Integration
|
|
73
|
+
|
|
74
|
+
You have two configuration options depending on your needs:
|
|
75
|
+
|
|
76
|
+
#### Option 1: Basic LESSOPEN (Minimal setup)
|
|
77
|
+
|
|
78
|
+
If you prefer not to use the shell wrapper, you can manually set two environment variables. This is a lighter-weight alternative but lacks some features.
|
|
79
|
+
|
|
80
|
+
**Setup:**
|
|
81
|
+
|
|
82
|
+
Add these lines to your `~/.bashrc`, `~/.zshrc`, or `~/.profile`:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
export LESSOPEN="|richless %s"
|
|
86
|
+
export LESS="-R"
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Then reload your shell:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
source ~/.bashrc # or source ~/.zshrc
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
**What you get:**
|
|
96
|
+
- ✅ Automatic Markdown rendering when you run `less file.md`
|
|
97
|
+
- ✅ Syntax highlighting for programming language source files (Python, JavaScript, etc.)
|
|
98
|
+
- ✅ Works with wildcards: `less *.md` or `less *.py`
|
|
99
|
+
- ✅ Fast and lightweight
|
|
100
|
+
|
|
101
|
+
**Limitations:**
|
|
102
|
+
- ❌ Doesn't work with piped input: `cat file.md | less` won't render
|
|
103
|
+
- ❌ Can't force markdown on non-.md files through `less`
|
|
104
|
+
|
|
105
|
+
#### Option 2: Transparent Wrapper (Recommended - used by Quick Start)
|
|
106
|
+
|
|
107
|
+
This is the recommended setup and what Quick Start uses. It provides a smart wrapper around `less` that handles edge cases like piped input and forcing markdown on arbitrary files.
|
|
108
|
+
|
|
109
|
+
**Setup:**
|
|
110
|
+
|
|
111
|
+
1. Copy the shell integration script to your home directory:
|
|
112
|
+
```bash
|
|
113
|
+
# From the richless project directory
|
|
114
|
+
cp richless-init.sh ~/.richless-init.sh
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
2. Source it in your shell configuration:
|
|
118
|
+
```bash
|
|
119
|
+
# Add this line to ~/.bashrc, ~/.zshrc, or ~/.profile
|
|
120
|
+
source ~/.richless-init.sh
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
3. Reload your shell:
|
|
124
|
+
```bash
|
|
125
|
+
source ~/.bashrc # or source ~/.zshrc
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
**What you get:**
|
|
129
|
+
- ✅ Everything from Option 1, plus:
|
|
130
|
+
- ✅ Piped input works: `cat file.md | less` renders markdown
|
|
131
|
+
- ✅ Force markdown flag: `less --md document.txt` forces rendering
|
|
132
|
+
- ✅ Auto-detection: Intelligently detects markdown in piped content
|
|
133
|
+
- ✅ Backward compatible: Acts like normal `less` when not needed
|
|
134
|
+
|
|
135
|
+
**Note:** The shell integration file is compatible with sh, bash, and zsh.
|
|
136
|
+
|
|
137
|
+
## Usage
|
|
138
|
+
|
|
139
|
+
Once configured, just use `less` normally! Markdown files will be automatically rendered, and source code files will be syntax highlighted.
|
|
140
|
+
|
|
141
|
+
### Basic Usage (Both Options)
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
# View a Markdown file (automatically rendered)
|
|
145
|
+
less README.md
|
|
146
|
+
|
|
147
|
+
# View multiple Markdown files
|
|
148
|
+
less *.md
|
|
149
|
+
|
|
150
|
+
# View with wildcards
|
|
151
|
+
less docs/**/*.md
|
|
152
|
+
|
|
153
|
+
# Source code files get syntax highlighting automatically
|
|
154
|
+
less script.py # Python
|
|
155
|
+
less app.js # JavaScript
|
|
156
|
+
less main.go # Go
|
|
157
|
+
less config.json # JSON
|
|
158
|
+
less styles.css # CSS
|
|
159
|
+
|
|
160
|
+
# All standard less options work
|
|
161
|
+
less -N README.md # Show line numbers
|
|
162
|
+
less -i script.py # Case-insensitive search
|
|
163
|
+
less +50 README.md # Start at line 50
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Additional Features (Option 2 / Quick Start)
|
|
167
|
+
|
|
168
|
+
If you followed the Quick Start or are using Option 2, you also get these features:
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
# Force Markdown rendering on non-.md files
|
|
172
|
+
less --md document.txt
|
|
173
|
+
less -m notes.txt
|
|
174
|
+
|
|
175
|
+
# Piped input works!
|
|
176
|
+
cat file.md | less
|
|
177
|
+
echo "# Hello\n**World**" | less --md
|
|
178
|
+
|
|
179
|
+
# Pipe from other commands
|
|
180
|
+
curl https://example.com/README.md | less --md
|
|
181
|
+
grep -A 50 "## Section" doc.md | less
|
|
182
|
+
|
|
183
|
+
# Auto-detection: if piped content looks like markdown, it renders automatically
|
|
184
|
+
cat file.md | less # Detects markdown syntax and renders
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### Direct richless Usage
|
|
188
|
+
|
|
189
|
+
You can also call `richless` directly if needed:
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
# Render and pipe to less
|
|
193
|
+
richless document.md | less -R
|
|
194
|
+
|
|
195
|
+
# Force markdown rendering
|
|
196
|
+
richless --md document.txt | less -R
|
|
197
|
+
|
|
198
|
+
# Read from stdin
|
|
199
|
+
cat file.md | richless --md - | less -R
|
|
200
|
+
echo "# Test" | richless --md - | less -R
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### Standard less Commands
|
|
204
|
+
|
|
205
|
+
All standard `less` commands work normally inside the pager:
|
|
206
|
+
|
|
207
|
+
- `/pattern` - Search forward
|
|
208
|
+
- `?pattern` - Search backward
|
|
209
|
+
- `n` / `N` - Next/previous match
|
|
210
|
+
- `g` / `G` - Go to start/end
|
|
211
|
+
- `q` - Quit
|
|
212
|
+
- `h` - Help
|
|
213
|
+
|
|
214
|
+
## How It Works
|
|
215
|
+
|
|
216
|
+
**Basic Mode (Option 1):**
|
|
217
|
+
1. When you run `less file.md`, the `LESSOPEN` environment variable tells less to run `richless file.md` first
|
|
218
|
+
2. `richless` detects the `.md` extension and uses the `rich` library to render the Markdown
|
|
219
|
+
3. `rich` renders the Markdown to beautifully formatted ANSI text with proper table support
|
|
220
|
+
4. The formatted output is piped to `less` for viewing
|
|
221
|
+
5. For programming language source files (`.py`, `.js`, `.java`, etc.), `rich` automatically provides syntax highlighting using Pygments
|
|
222
|
+
|
|
223
|
+
**Transparent Wrapper (Option 2):**
|
|
224
|
+
- The shell function intercepts calls to `less` before they execute
|
|
225
|
+
- For regular files, it passes through to the basic LESSOPEN mechanism
|
|
226
|
+
- For piped input or when `--md` is specified, it saves the content to a temp file and renders it
|
|
227
|
+
- Auto-detection checks piped content for markdown patterns (headers, lists, links, etc.)
|
|
228
|
+
|
|
229
|
+
## Troubleshooting
|
|
230
|
+
|
|
231
|
+
### "richless: command not found"
|
|
232
|
+
|
|
233
|
+
Make sure `~/.local/bin` is in your PATH:
|
|
234
|
+
```bash
|
|
235
|
+
export PATH="$HOME/.local/bin:$PATH"
|
|
236
|
+
# Add this to your ~/.bashrc or ~/.zshrc
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### Piped input doesn't work
|
|
240
|
+
|
|
241
|
+
If you're using Option 1 (Basic LESSOPEN), piped input won't work. Either:
|
|
242
|
+
- Switch to Option 2 / Quick Start for piped input support
|
|
243
|
+
- Or use: `cat file.md | richless --md - | less -R`
|
|
244
|
+
|
|
245
|
+
### Colors don't show up
|
|
246
|
+
|
|
247
|
+
If you're using Option 1 (manual setup), make sure you have the `-R` flag set:
|
|
248
|
+
```bash
|
|
249
|
+
export LESS="-R"
|
|
250
|
+
```
|
|
251
|
+
The init script (Option 2 / Quick Start) sets this automatically.
|
|
252
|
+
|
|
253
|
+
### Shell function not loading
|
|
254
|
+
|
|
255
|
+
Make sure you're sourcing the init script, not executing it:
|
|
256
|
+
```bash
|
|
257
|
+
source ~/.richless-init.sh # Correct
|
|
258
|
+
./richless-init.sh # Wrong - this won't define the function in your shell
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### After sourcing, "less file.md" shows an error
|
|
262
|
+
|
|
263
|
+
Make sure you've reinstalled richless after any updates:
|
|
264
|
+
```bash
|
|
265
|
+
uv tool uninstall richless
|
|
266
|
+
uv tool install .
|
|
267
|
+
source ~/.richless-init.sh # Re-source to pick up changes
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
## Updating
|
|
271
|
+
|
|
272
|
+
```bash
|
|
273
|
+
# Update to the latest version
|
|
274
|
+
cd /path/to/richless
|
|
275
|
+
git pull # Get latest changes
|
|
276
|
+
uv tool upgrade richless
|
|
277
|
+
|
|
278
|
+
# Re-source the init script to pick up any changes
|
|
279
|
+
source ~/.richless-init.sh
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
## Uninstalling
|
|
283
|
+
|
|
284
|
+
```bash
|
|
285
|
+
# Remove the tool
|
|
286
|
+
uv tool uninstall richless
|
|
287
|
+
|
|
288
|
+
# Remove from your shell config (~/.bashrc or ~/.zshrc)
|
|
289
|
+
# Delete or comment out the source line:
|
|
290
|
+
# source ~/.richless-init.sh
|
|
291
|
+
|
|
292
|
+
# Remove the init script
|
|
293
|
+
rm ~/.richless-init.sh
|
|
294
|
+
|
|
295
|
+
# If using Option 1 instead, remove these lines:
|
|
296
|
+
# export LESSOPEN="|richless %s"
|
|
297
|
+
# export LESS="-R"
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
## Development
|
|
301
|
+
|
|
302
|
+
```bash
|
|
303
|
+
# Clone the repository
|
|
304
|
+
git clone <repository-url>
|
|
305
|
+
cd richless
|
|
306
|
+
|
|
307
|
+
# Install dependencies
|
|
308
|
+
uv sync
|
|
309
|
+
|
|
310
|
+
# Install in development mode
|
|
311
|
+
uv tool install --editable .
|
|
312
|
+
|
|
313
|
+
# Make changes, then reinstall
|
|
314
|
+
uv tool install --editable . --force
|
|
315
|
+
|
|
316
|
+
# Test it
|
|
317
|
+
less README.md
|
|
318
|
+
|
|
319
|
+
# Run tests
|
|
320
|
+
uv run pytest tests/test_richless.py -v
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
## Dependencies
|
|
324
|
+
|
|
325
|
+
- [rich](https://github.com/Textualize/rich) - Python library for rich terminal output, Markdown rendering, and syntax highlighting
|
|
326
|
+
- [Pygments](https://pygments.org/) - Syntax highlighting library
|
|
327
|
+
- Python 3.12+
|
|
328
|
+
- uv - Modern Python package manager
|
|
329
|
+
|
|
330
|
+
## Comparison to Alternatives
|
|
331
|
+
|
|
332
|
+
**vs. glow / mdcat / bat:**
|
|
333
|
+
- richless integrates directly with `less`, so you use your familiar pager commands
|
|
334
|
+
- Works transparently - no need to remember a different command
|
|
335
|
+
- Supports all standard `less` features (search, navigation, etc.)
|
|
336
|
+
|
|
337
|
+
**vs. vimpager:**
|
|
338
|
+
- Lighter weight, doesn't require Vim
|
|
339
|
+
- Uses rich library's excellent Markdown rendering with full table support
|
|
340
|
+
- Simple LESSOPEN integration
|
|
341
|
+
|
|
342
|
+
## License
|
|
343
|
+
|
|
344
|
+
See LICENSE file for details.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "richless"
|
|
3
|
+
version = "0.2.1"
|
|
4
|
+
description = "LESSOPEN filter for Markdown rendering and syntax highlighting with less"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.12"
|
|
7
|
+
dependencies = [
|
|
8
|
+
"rich>=13.8.0",
|
|
9
|
+
"pygments>=2.0.0",
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
[project.optional-dependencies]
|
|
13
|
+
dev = [
|
|
14
|
+
"pytest>=7.0.0",
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
[project.scripts]
|
|
18
|
+
richless = "richless:main"
|
|
19
|
+
|
|
20
|
+
[build-system]
|
|
21
|
+
requires = ["hatchling"]
|
|
22
|
+
build-backend = "hatchling.build"
|
|
23
|
+
|
|
24
|
+
[tool.hatch.build.targets.sdist]
|
|
25
|
+
include = ["richless.py", "richless-init.sh", "LICENSE", "README.md"]
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# richless-init.sh
|
|
3
|
+
#
|
|
4
|
+
# Shell integration for richless - transparent Markdown rendering in less
|
|
5
|
+
#
|
|
6
|
+
# Usage: Add this line to your ~/.bashrc, ~/.zshrc, or ~/.profile:
|
|
7
|
+
# source /path/to/richless-init.sh
|
|
8
|
+
#
|
|
9
|
+
# This will override the 'less' command to automatically render Markdown files
|
|
10
|
+
# and support additional features like piped input and force-markdown mode.
|
|
11
|
+
#
|
|
12
|
+
# Compatible with: sh, bash, zsh
|
|
13
|
+
|
|
14
|
+
# Only initialize if richless is available
|
|
15
|
+
if ! command -v richless >/dev/null 2>&1; then
|
|
16
|
+
echo "Warning: richless command not found. Please install richless first." >&2
|
|
17
|
+
return 1 2>/dev/null || exit 1
|
|
18
|
+
fi
|
|
19
|
+
|
|
20
|
+
# Configure LESSOPEN for automatic Markdown detection
|
|
21
|
+
export LESSOPEN="|richless %s"
|
|
22
|
+
export LESS="${LESS:--R}" # Add -R flag if LESS not already set, otherwise preserve existing
|
|
23
|
+
|
|
24
|
+
# Transparent wrapper function for less
|
|
25
|
+
# This handles cases where LESSOPEN doesn't work (piped input, force markdown)
|
|
26
|
+
less() {
|
|
27
|
+
# Check if stdin is a pipe (not a terminal)
|
|
28
|
+
if [ ! -t 0 ]; then
|
|
29
|
+
# Reading from pipe - check for --md flag (must be exact match, not substring)
|
|
30
|
+
local force_markdown=0
|
|
31
|
+
for arg in "$@"; do
|
|
32
|
+
case "$arg" in
|
|
33
|
+
--md|-m) force_markdown=1; break ;;
|
|
34
|
+
esac
|
|
35
|
+
done
|
|
36
|
+
|
|
37
|
+
# Save piped input to temp file
|
|
38
|
+
local tmpfile
|
|
39
|
+
tmpfile=$(mktemp "${TMPDIR:-/tmp}/richless.XXXXXX") || return 1
|
|
40
|
+
cat > "$tmpfile"
|
|
41
|
+
|
|
42
|
+
# Collect non --md/--m arguments for less
|
|
43
|
+
local clean_args=""
|
|
44
|
+
for arg in "$@"; do
|
|
45
|
+
case "$arg" in
|
|
46
|
+
--md|-m)
|
|
47
|
+
# Skip our custom flag
|
|
48
|
+
;;
|
|
49
|
+
*)
|
|
50
|
+
clean_args="${clean_args} ${arg}"
|
|
51
|
+
;;
|
|
52
|
+
esac
|
|
53
|
+
done
|
|
54
|
+
|
|
55
|
+
# Check if we should render as markdown
|
|
56
|
+
if [ $force_markdown -eq 1 ]; then
|
|
57
|
+
richless --md "$tmpfile" | command less -R ${clean_args}
|
|
58
|
+
else
|
|
59
|
+
# Check for YAML/JSON first - these should use syntax highlighting, not markdown
|
|
60
|
+
first_line=$(head -1 "$tmpfile" 2>/dev/null)
|
|
61
|
+
if printf '%s\n' "$first_line" | grep -qE '^---$|^%YAML|^[[:space:]]*[{[]'; then
|
|
62
|
+
# Looks like YAML or JSON - use syntax highlighting
|
|
63
|
+
richless "$tmpfile" | command less -R ${clean_args}
|
|
64
|
+
# Check for YAML with comments: first non-comment line has key: pattern
|
|
65
|
+
elif grep -m1 -vE '^[[:space:]]*#|^[[:space:]]*$' "$tmpfile" 2>/dev/null | grep -qE '^[a-zA-Z_][a-zA-Z0-9_-]*:'; then
|
|
66
|
+
# Looks like YAML with comments - use syntax highlighting
|
|
67
|
+
richless "$tmpfile" | command less -R ${clean_args}
|
|
68
|
+
# Auto-detect: check if piped content looks like markdown
|
|
69
|
+
elif grep -qE '^#{1,6} |^\* |^- |^[0-9]+\. |^\[.*\]\(.*\)|^```|\*\*.*\*\*|^>|^\||^-{3,}|^={3,}' "$tmpfile" 2>/dev/null; then
|
|
70
|
+
richless --md "$tmpfile" | command less -R ${clean_args}
|
|
71
|
+
else
|
|
72
|
+
command less -R ${clean_args} "$tmpfile"
|
|
73
|
+
fi
|
|
74
|
+
fi
|
|
75
|
+
|
|
76
|
+
# Clean up temp file
|
|
77
|
+
rm -f "$tmpfile"
|
|
78
|
+
else
|
|
79
|
+
# No pipe - check for --md flag among arguments (must be exact match, not substring)
|
|
80
|
+
local force_markdown=0
|
|
81
|
+
local has_md_flag=0
|
|
82
|
+
|
|
83
|
+
for arg in "$@"; do
|
|
84
|
+
case "$arg" in
|
|
85
|
+
--md|-m) has_md_flag=1; break ;;
|
|
86
|
+
esac
|
|
87
|
+
done
|
|
88
|
+
|
|
89
|
+
if [ $has_md_flag -eq 1 ]; then
|
|
90
|
+
# Build arrays for files and options
|
|
91
|
+
local files=""
|
|
92
|
+
local opts=""
|
|
93
|
+
|
|
94
|
+
for arg in "$@"; do
|
|
95
|
+
case "$arg" in
|
|
96
|
+
--md|-m)
|
|
97
|
+
force_markdown=1
|
|
98
|
+
;;
|
|
99
|
+
-*)
|
|
100
|
+
opts="${opts} ${arg}"
|
|
101
|
+
;;
|
|
102
|
+
*)
|
|
103
|
+
files="${files} ${arg}"
|
|
104
|
+
;;
|
|
105
|
+
esac
|
|
106
|
+
done
|
|
107
|
+
|
|
108
|
+
# Render each file with markdown
|
|
109
|
+
for file in ${files}; do
|
|
110
|
+
if [ -n "$file" ]; then
|
|
111
|
+
richless --md "$file" | command less -R ${opts}
|
|
112
|
+
fi
|
|
113
|
+
done
|
|
114
|
+
else
|
|
115
|
+
# Normal less operation - let LESSOPEN handle it
|
|
116
|
+
command less "$@"
|
|
117
|
+
fi
|
|
118
|
+
fi
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
# Export the function so it's available in subshells (bash only)
|
|
122
|
+
# Note: zsh doesn't support 'export -f', but functions are automatically available in subshells
|
|
123
|
+
if [ -n "$BASH_VERSION" ]; then
|
|
124
|
+
export -f less 2>/dev/null || true
|
|
125
|
+
fi
|
|
126
|
+
|
|
127
|
+
# Suppress any function output
|
|
128
|
+
: # null command to ensure clean sourcing
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
richless - A LESSOPEN filter for Markdown rendering and syntax highlighting.
|
|
4
|
+
|
|
5
|
+
This utility works as a preprocessor for 'less', automatically rendering
|
|
6
|
+
Markdown files and syntax highlighting code using the rich library.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import argparse
|
|
10
|
+
import os
|
|
11
|
+
import re
|
|
12
|
+
import shutil
|
|
13
|
+
import sys
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from rich.console import Console
|
|
16
|
+
from rich.markdown import Markdown
|
|
17
|
+
from rich.syntax import Syntax
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def is_markdown_file(filepath: str) -> bool:
|
|
21
|
+
"""Check if the file has a Markdown extension."""
|
|
22
|
+
ext = Path(filepath).suffix.lower()
|
|
23
|
+
return ext in ['.md', '.markdown']
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def detect_syntax_from_content(content: str) -> str:
|
|
27
|
+
"""Detect file type from content when extension is unknown."""
|
|
28
|
+
if not content:
|
|
29
|
+
return "text"
|
|
30
|
+
|
|
31
|
+
lines = content.split('\n', 20) # Check first 20 lines
|
|
32
|
+
first_line = lines[0].strip() if lines else ''
|
|
33
|
+
|
|
34
|
+
# YAML detection: starts with --- or %YAML
|
|
35
|
+
if first_line == '---' or first_line.startswith('%YAML'):
|
|
36
|
+
return "yaml"
|
|
37
|
+
|
|
38
|
+
# JSON detection: starts with { or [
|
|
39
|
+
if first_line.startswith('{') or first_line.startswith('['):
|
|
40
|
+
return "json"
|
|
41
|
+
|
|
42
|
+
# Shebang detection
|
|
43
|
+
if first_line.startswith('#!'):
|
|
44
|
+
if 'python' in first_line:
|
|
45
|
+
return "python"
|
|
46
|
+
elif 'bash' in first_line or '/sh' in first_line:
|
|
47
|
+
return "bash"
|
|
48
|
+
elif 'node' in first_line:
|
|
49
|
+
return "javascript"
|
|
50
|
+
elif 'ruby' in first_line:
|
|
51
|
+
return "ruby"
|
|
52
|
+
elif 'perl' in first_line:
|
|
53
|
+
return "perl"
|
|
54
|
+
|
|
55
|
+
# XML/HTML detection
|
|
56
|
+
if first_line.startswith('<?xml') or first_line.startswith('<!DOCTYPE'):
|
|
57
|
+
return "xml"
|
|
58
|
+
|
|
59
|
+
# YAML detection: look for key: value patterns (possibly after # comments)
|
|
60
|
+
# Skip comment lines and look for YAML structure
|
|
61
|
+
for line in lines:
|
|
62
|
+
stripped = line.strip()
|
|
63
|
+
# Skip empty lines and comments
|
|
64
|
+
if not stripped or stripped.startswith('#'):
|
|
65
|
+
continue
|
|
66
|
+
# Check for YAML key: value pattern (word followed by colon)
|
|
67
|
+
if re.match(r'^[a-zA-Z_][a-zA-Z0-9_-]*:\s*', stripped):
|
|
68
|
+
return "yaml"
|
|
69
|
+
# If first non-comment line doesn't look like YAML, stop checking
|
|
70
|
+
break
|
|
71
|
+
|
|
72
|
+
return "text"
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def get_terminal_width() -> int:
|
|
76
|
+
"""Get terminal width, even when stdout is piped."""
|
|
77
|
+
# Try stderr since stdout is piped through LESSOPEN
|
|
78
|
+
try:
|
|
79
|
+
return os.get_terminal_size(sys.stderr.fileno()).columns
|
|
80
|
+
except (OSError, ValueError):
|
|
81
|
+
pass
|
|
82
|
+
# Fall back to shutil (checks COLUMNS env var, then defaults to 80)
|
|
83
|
+
return shutil.get_terminal_size().columns
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def render_markdown(content: str) -> None:
|
|
87
|
+
"""Render Markdown content using rich."""
|
|
88
|
+
width = get_terminal_width()
|
|
89
|
+
console = Console(force_terminal=True, width=width)
|
|
90
|
+
md = Markdown(content)
|
|
91
|
+
console.print(md)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def render_syntax(filepath: str, content: str) -> None:
|
|
95
|
+
"""Render code with syntax highlighting using rich."""
|
|
96
|
+
# Determine lexer from file extension
|
|
97
|
+
path = Path(filepath)
|
|
98
|
+
ext = path.suffix.lstrip('.')
|
|
99
|
+
|
|
100
|
+
# Map non-standard extensions to their lexer names
|
|
101
|
+
ext_map = {
|
|
102
|
+
'jsonl': 'json', # JSON Lines uses JSON syntax
|
|
103
|
+
}
|
|
104
|
+
if ext in ext_map:
|
|
105
|
+
ext = ext_map[ext]
|
|
106
|
+
|
|
107
|
+
# If no recognizable extension, try to detect from content
|
|
108
|
+
# Temp files from shell wrapper are named richless.XXXXXX (random suffix)
|
|
109
|
+
if not ext or path.stem == 'richless':
|
|
110
|
+
ext = detect_syntax_from_content(content)
|
|
111
|
+
|
|
112
|
+
# Calculate width needed to avoid truncating long lines
|
|
113
|
+
# This allows 'less' to handle horizontal scrolling
|
|
114
|
+
lines = content.splitlines()
|
|
115
|
+
max_line_length = max((len(line) for line in lines), default=80)
|
|
116
|
+
width = max(max_line_length + 1, 80)
|
|
117
|
+
|
|
118
|
+
# Create console with width to accommodate longest line
|
|
119
|
+
console = Console(force_terminal=True, width=width)
|
|
120
|
+
|
|
121
|
+
# Create Syntax object - use default background to avoid padding
|
|
122
|
+
syntax = Syntax(content, ext or "text", theme="monokai", line_numbers=False,
|
|
123
|
+
background_color="default")
|
|
124
|
+
console.print(syntax)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def main():
|
|
128
|
+
"""Main entry point for richless."""
|
|
129
|
+
parser = argparse.ArgumentParser(
|
|
130
|
+
description='LESSOPEN filter for Markdown rendering and syntax highlighting',
|
|
131
|
+
add_help=True,
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
parser.add_argument('file',
|
|
135
|
+
help='File to process (use "-" for stdin)')
|
|
136
|
+
parser.add_argument('--md', '--markdown',
|
|
137
|
+
dest='force_markdown',
|
|
138
|
+
action='store_true',
|
|
139
|
+
help='Force Markdown rendering even for non-.md files')
|
|
140
|
+
|
|
141
|
+
args = parser.parse_args()
|
|
142
|
+
|
|
143
|
+
# Strip whitespace from filename (less adds leading space via LESSOPEN)
|
|
144
|
+
filepath = args.file.strip()
|
|
145
|
+
|
|
146
|
+
# Handle stdin input
|
|
147
|
+
input_file = filepath
|
|
148
|
+
temp_file = None
|
|
149
|
+
content = None
|
|
150
|
+
|
|
151
|
+
try:
|
|
152
|
+
if filepath == '-' or filepath == '/dev/stdin':
|
|
153
|
+
# Read from stdin
|
|
154
|
+
content = sys.stdin.read()
|
|
155
|
+
input_file = 'stdin.md' if args.force_markdown else 'stdin.txt'
|
|
156
|
+
else:
|
|
157
|
+
# Read from file
|
|
158
|
+
with open(filepath, 'r', encoding='utf-8') as f:
|
|
159
|
+
content = f.read()
|
|
160
|
+
input_file = filepath
|
|
161
|
+
|
|
162
|
+
# Determine if we should render as markdown
|
|
163
|
+
is_markdown = args.force_markdown or is_markdown_file(input_file)
|
|
164
|
+
|
|
165
|
+
if is_markdown:
|
|
166
|
+
render_markdown(content)
|
|
167
|
+
else:
|
|
168
|
+
# Syntax highlighting for code files
|
|
169
|
+
render_syntax(input_file, content)
|
|
170
|
+
|
|
171
|
+
return 0
|
|
172
|
+
|
|
173
|
+
except FileNotFoundError:
|
|
174
|
+
print(f"richless: File not found: {args.file}", file=sys.stderr)
|
|
175
|
+
return 1
|
|
176
|
+
except Exception as e:
|
|
177
|
+
print(f"richless: Error: {e}", file=sys.stderr)
|
|
178
|
+
# Fall back to plain output
|
|
179
|
+
try:
|
|
180
|
+
if content:
|
|
181
|
+
print(content, end='')
|
|
182
|
+
elif filepath not in ['-', '/dev/stdin']:
|
|
183
|
+
with open(filepath, 'r', encoding='utf-8') as f:
|
|
184
|
+
print(f.read(), end='')
|
|
185
|
+
return 0
|
|
186
|
+
except Exception:
|
|
187
|
+
return 1
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
if __name__ == "__main__":
|
|
191
|
+
sys.exit(main())
|