86 Commits

Author SHA1 Message Date
github-actions[bot]
335b3ad36d chore(master): release 0.9.1 2024-07-10 16:21:44 +03:00
9e628239ab docs: update docs lib version & regenerate 2024-07-10 16:18:54 +03:00
d7dd1c4dd7 fix: normal mode transforms 2024-07-10 16:18:54 +03:00
16069971e9 docs: update doc file 2024-07-10 16:18:54 +03:00
9c62c9cd9d refactor: move cache file to .cache dir 2024-07-10 16:18:54 +03:00
33eecfffab refactor: use plugin name 2024-05-07 01:00:38 +03:00
701d373e7a refactor: remove main.lua 2024-05-07 01:00:38 +03:00
1c8fa21578 refactor: file structure 2024-05-07 01:00:38 +03:00
github-actions[bot]
1c39528e7c chore(master): release 0.9.0 2024-05-05 23:06:45 +03:00
972a44cf1e doc: add command desc 2024-05-05 23:02:08 +03:00
ecd4d6bbbc fix: range commands 2024-05-05 23:02:08 +03:00
ea38606a28 fix: sorter init 2024-05-05 23:02:08 +03:00
3362c3c4f1 refactor: clean ups 2024-05-05 23:02:08 +03:00
a9d3ffc2de test: add tests 2024-05-05 23:02:08 +03:00
168552b425 fix: error on select 2024-05-05 23:02:08 +03:00
8e7fe74888 docs: update auto docs 2024-05-05 23:02:08 +03:00
4bbba5d1b3 docs: update readme 2024-05-05 23:02:08 +03:00
eaf84d857d fix: commands 2024-05-05 23:02:08 +03:00
0cb9c1e4ee docs: update readme 2024-05-05 23:02:08 +03:00
8de9125de1 refactor: make telescope dependency optional
lazy load the telescope popup, so that if it's disabled
it won't be attempted
2024-05-05 23:02:08 +03:00
bb177a3360 feat: non-telescope popup 2024-05-05 23:02:08 +03:00
67aef1de42 docs: update readme 2024-05-05 23:02:08 +03:00
github-actions[bot]
b44d58a533 chore(master): release 0.8.0 2024-05-04 11:36:27 +03:00
6863e0e9a6 feat: allow sorter selection in config 2024-05-04 11:33:43 +03:00
733f26fef6 fix: frequency sort 2024-05-04 11:33:43 +03:00
be9c119ee2 fix: user commands 2024-05-04 11:33:43 +03:00
github-actions[bot]
52f26035d2 chore(master): release 0.7.0 2024-05-04 02:29:22 +03:00
f83f2afafd feat: sort telescope by usage frequency 2024-05-04 02:27:10 +03:00
a775191122 docs: update readme 2024-05-04 02:27:10 +03:00
3f684bbdc0 ci: update workflows 2024-05-04 02:27:10 +03:00
646dde56f2 ci: update workflow version 2024-05-04 02:27:10 +03:00
ba385b846b ci: disable workflow concurrency 2024-05-04 02:27:10 +03:00
9393d81815 docs: update readme 2024-05-04 02:27:10 +03:00
github-actions[bot]
e376ac78fc chore(master): release 0.6.0 2024-05-02 03:25:03 +03:00
2fa82f80d6 docs: update readme 2024-05-02 03:19:04 +03:00
ad84766691 chore: cleanup 2024-05-02 03:19:04 +03:00
fbb89b1b44 feat: enable/disable specific replacers 2024-05-02 03:19:04 +03:00
github-actions[bot]
5c0132fe95 chore(master): release 0.5.1 2024-05-01 01:54:53 +03:00
657bda1478 fix: exports 2024-05-01 01:33:05 +03:00
github-actions[bot]
b500db2686 chore(master): release 0.5.0 2024-04-30 15:51:37 +03:00
1fd7e334bc docs: update readme 2024-04-30 15:34:47 +03:00
7ef73f26d1 fix: telescope 2024-04-30 15:29:07 +03:00
Chen Asraf
1f3fc3df09 docs: update demo gif 2024-04-30 15:23:13 +03:00
b31967dd32 docs: update docs
chore: comment out unused code
2024-04-30 15:10:24 +03:00
6f1c92872e test: fix tests 2024-04-30 15:00:54 +03:00
69542d7e1c fix: telescope command 2024-04-30 15:00:54 +03:00
6e409f0a44 feat: add telescope command 2024-04-30 15:00:54 +03:00
dc3d4e6379 docs: update readme 2024-04-30 15:00:54 +03:00
4628b50340 refactor: telescope exports 2024-04-30 15:00:54 +03:00
500edc66e6 feat: add commands
docs: update
2024-04-30 15:00:54 +03:00
758d90b88f docs: add doc strings 2024-04-30 15:00:54 +03:00
a669a998f4 fix: uppercase logic
fix: precommit
2024-04-30 15:00:54 +03:00
c0a354ac79 build: move precommit script to scripts/ dir 2024-04-30 15:00:54 +03:00
b536b832d9 test: fix most tests 2024-04-30 15:00:54 +03:00
fc2de91ac4 ci: add status before fail 2024-04-30 15:00:50 +03:00
a53e0d6d64 fix: word break/transform 2024-04-30 15:00:50 +03:00
bbe802e0b2 build: add precommit 2024-04-30 15:00:50 +03:00
e6735cceeb chore: formatting 2024-04-30 15:00:50 +03:00
b4257a50fe refactor: new logic, file splitting
BREAKING CHANGES: functions, configs have been renamed
2024-04-30 15:00:03 +03:00
Chen Asraf
ded86d9b0f docs: update README.md 2023-05-21 00:24:20 +03:00
Chen Asraf
7518df3364 chore(master): release 0.4.0
chore(master): release 0.4.0
2023-05-18 09:40:11 +03:00
github-actions[bot]
8f980db340 chore(master): release 0.4.0 2023-05-18 06:39:02 +00:00
Chen Asraf
7edc585d15 Merge pull request #14 from chenasraf/develop
Prepare release: v0.3.2
2023-05-18 09:38:08 +03:00
Chen Asraf
9cd27aba2b fix: word edge detection 2023-05-18 09:26:41 +03:00
Chen Asraf
43cc07cde4 fix: words split 2023-05-18 02:41:50 +03:00
Chen Asraf
eb3a7ae843 Merge branch 'master' into develop 2023-05-18 02:23:50 +03:00
Chen Asraf
5765e3d721 feat: visual block mode support 2023-05-18 02:22:11 +03:00
Chen Asraf
bcb5ee3704 test: split into separate files 2023-05-18 01:16:24 +03:00
Chen Asraf
09e4b0986a Merge pull request #12 from chenasraf/release-please--branches--master--components--text-transform.nvim
chore(master): release 0.3.1
2023-05-18 01:06:52 +03:00
github-actions[bot]
e4d1687ab2 chore(master): release 0.3.1 2023-05-17 22:06:31 +00:00
Chen Asraf
d4ef5c0e8c Merge branch 'master' into develop 2023-05-18 01:06:22 +03:00
Chen Asraf
17b3a79374 Merge pull request #11 from chenasraf/develop
prepare release
2023-05-18 01:05:38 +03:00
Chen Asraf
9c1dd7e0e6 test: add more transform test case inputs 2023-05-18 00:57:08 +03:00
Chen Asraf
f79799cd91 fix: into_words dot split
test: add into_words tests
2023-05-18 00:54:30 +03:00
Chen Asraf
63c0b700f7 test: simplify test cases 2023-05-18 00:49:36 +03:00
Chen Asraf
fe617cc2ad test: add transformer tests 2023-05-18 00:41:19 +03:00
Chen Asraf
18ff322f83 docs: added fn comments 2023-05-17 23:30:44 +03:00
Chen Asraf
20fcc6b49c refactor: unify replace logic + prepare replace cursors feat 2023-05-17 23:23:09 +03:00
Chen Asraf
b6401e841f Merge pull request #10 from chenasraf/develop
Prepare release
2023-05-17 20:19:47 +03:00
Chen Asraf
79f9da2467 refactor: move func defs to main.lua and re-export in init.lua 2023-05-17 10:13:15 +03:00
Chen Asraf
4bc66d375b build: fix & run tests on ci 2023-05-17 09:55:01 +03:00
Chen Asraf
baa38828e4 build: remove setup script 2023-05-16 14:52:19 +03:00
Chen Asraf
36651b5aec build: add precommit target 2023-05-16 14:38:16 +03:00
Chen Asraf
52b155f43f docs: Update README.md 2023-05-16 14:38:16 +03:00
Chen Asraf
7f842fbfe3 build: update develop workflow names 2023-05-16 14:38:16 +03:00
Chen Asraf
11ecfb6cc1 docs: update readme 2023-05-16 14:38:16 +03:00
31 changed files with 1824 additions and 551 deletions

View File

@@ -8,3 +8,6 @@ trim_trailing_whitespace = true
[*.md]
trim_trailing_whitespace = false
[Makefile]
indent_style = tab

View File

@@ -1,23 +1,24 @@
name: Release
name: Dev Release
on:
push:
branches: [develop]
pull_request:
branches: [develop]
types: [opened, synchronize]
concurrency:
group: github.head_ref
cancel-in-progress: true
# concurrency:
# group: github.head_ref
# cancel-in-progress: true
jobs:
lint:
runs-on: ubuntu-latest
name: lint
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: JohnnyMorganz/stylua-action@v2
- uses: JohnnyMorganz/stylua-action@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
version: latest
@@ -27,7 +28,7 @@ jobs:
runs-on: ubuntu-latest
name: documentation
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
fetch-depth: 2
@@ -41,19 +42,53 @@ jobs:
run: make documentation-ci
- name: check docs diff
run: exit $(git status --porcelain doc | wc -l | tr -d " ")
release:
name: release
if: ${{ github.ref == 'refs/heads/develop' }}
permissions: write-all
run: |
git status doc
ex=$(git status --porcelain doc | wc -l | tr -d " ")
if [[ $ex -ne 0 ]]; then git diff doc; fi
exit $ex
tests:
needs:
- lint
- documentation
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
timeout-minutes: 2
strategy:
matrix:
neovim_version: ['v0.7.2', 'v0.8.3', 'v0.9.0', 'nightly']
- uses: google-github-actions/release-please-action@v3
steps:
- uses: actions/checkout@v4
- run: date +%F > todays-date
- name: restore cache for today's nightly.
uses: actions/cache@v4
with:
path: _neovim
key: ${{ runner.os }}-x64-${{ hashFiles('todays-date') }}
- name: setup neovim
uses: rhysd/action-setup-vim@v1
with:
neovim: true
version: ${{ matrix.neovim_version }}
- name: run tests
run: make test-ci
release:
name: dev-release
if: ${{ github.ref == 'refs/heads/develop' }}
permissions: write-all
needs:
- tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: google-github-actions/release-please-action@v4
id: release
with:
release-type: simple

View File

@@ -4,20 +4,21 @@ on:
push:
branches: [master]
pull_request:
branches: [master]
types: [opened, synchronize]
concurrency:
group: github.head_ref
cancel-in-progress: true
# concurrency:
# group: github.head_ref
# cancel-in-progress: true
jobs:
lint:
runs-on: ubuntu-latest
name: lint
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: JohnnyMorganz/stylua-action@v2
- uses: JohnnyMorganz/stylua-action@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
version: latest
@@ -27,7 +28,7 @@ jobs:
runs-on: ubuntu-latest
name: documentation
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
fetch-depth: 2
@@ -41,50 +42,53 @@ jobs:
run: make documentation-ci
- name: check docs diff
run: exit $(git status --porcelain doc | wc -l | tr -d " ")
run: |
git status doc
ex=$(git status --porcelain doc | wc -l | tr -d " ")
if [[ $ex -ne 0 ]]; then git diff doc; fi
exit $ex
# tests:
# needs:
# - lint
# - documentation
# runs-on: ubuntu-latest
# timeout-minutes: 2
# strategy:
# matrix:
# neovim_version: ['v0.7.2', 'v0.8.3', 'v0.9.0', 'nightly']
tests:
needs:
- lint
- documentation
runs-on: ubuntu-latest
timeout-minutes: 2
strategy:
matrix:
neovim_version: ['v0.7.2', 'v0.8.3', 'v0.9.0', 'nightly']
# steps:
# - uses: actions/checkout@v3
steps:
- uses: actions/checkout@v4
# - run: date +%F > todays-date
- run: date +%F > todays-date
# - name: restore cache for today's nightly.
# uses: actions/cache@v3
# with:
# path: _neovim
# key: ${{ runner.os }}-x64-${{ hashFiles('todays-date') }}
- name: restore cache for today's nightly.
uses: actions/cache@v4
with:
path: _neovim
key: ${{ runner.os }}-x64-${{ hashFiles('todays-date') }}
# - name: setup neovim
# uses: rhysd/action-setup-vim@v1
# with:
# neovim: true
# version: ${{ matrix.neovim_version }}
- name: setup neovim
uses: rhysd/action-setup-vim@v1
with:
neovim: true
version: ${{ matrix.neovim_version }}
# - name: run tests
# run: make test-ci
- name: run tests
run: make test-ci
release:
name: release
if: ${{ github.ref == 'refs/heads/master' }}
permissions: write-all
needs:
- lint
- documentation
- tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: google-github-actions/release-please-action@v3
- uses: google-github-actions/release-please-action@v4
id: release
with:
release-type: simple

View File

@@ -1,5 +1,98 @@
# Changelog
## [0.9.1](https://github.com/chenasraf/text-transform.nvim/compare/v0.9.0...v0.9.1) (2024-07-10)
### Bug Fixes
* normal mode transforms ([d7dd1c4](https://github.com/chenasraf/text-transform.nvim/commit/d7dd1c4dd7e4ccd2da1458e5fb9a6653ad4e5e17))
## [0.9.0](https://github.com/chenasraf/text-transform.nvim/compare/v0.8.0...v0.9.0) (2024-05-05)
### Features
* non-telescope popup ([bb177a3](https://github.com/chenasraf/text-transform.nvim/commit/bb177a3360372f3fd40c82da71a13f1db59d3d9e))
### Bug Fixes
* commands ([eaf84d8](https://github.com/chenasraf/text-transform.nvim/commit/eaf84d857d860828cbb5af96887a24c2ab99f8e8))
* error on select ([168552b](https://github.com/chenasraf/text-transform.nvim/commit/168552b4259672fe918b1f3baeed787548e8fd03))
* range commands ([ecd4d6b](https://github.com/chenasraf/text-transform.nvim/commit/ecd4d6bbbcd45231f2e8ea00961f076ff56dd1e7))
* sorter init ([ea38606](https://github.com/chenasraf/text-transform.nvim/commit/ea38606a28f2308a37b301ebb4c56e2bd584b459))
## [0.8.0](https://github.com/chenasraf/text-transform.nvim/compare/v0.7.0...v0.8.0) (2024-05-04)
### Features
* allow sorter selection in config ([6863e0e](https://github.com/chenasraf/text-transform.nvim/commit/6863e0e9a66b5592fbba2aea7a07760465844cf7))
### Bug Fixes
* frequency sort ([733f26f](https://github.com/chenasraf/text-transform.nvim/commit/733f26fef61b050ae8f62582e3f9c03268f8e835))
* user commands ([be9c119](https://github.com/chenasraf/text-transform.nvim/commit/be9c119ee23f1e93a5bd537c01f55e11d2607180))
## [0.7.0](https://github.com/chenasraf/text-transform.nvim/compare/v0.6.0...v0.7.0) (2024-05-03)
### Features
* sort telescope by usage frequency ([f83f2af](https://github.com/chenasraf/text-transform.nvim/commit/f83f2afafdfbbcb986ca1c6d3db062cd3b964c62))
## [0.6.0](https://github.com/chenasraf/text-transform.nvim/compare/v0.5.1...v0.6.0) (2024-05-02)
### Features
* enable/disable specific replacers ([fbb89b1](https://github.com/chenasraf/text-transform.nvim/commit/fbb89b1b44dfb9b57cd241b7c574665cce7dab10))
## [0.5.1](https://github.com/chenasraf/text-transform.nvim/compare/v0.5.0...v0.5.1) (2024-04-30)
### Bug Fixes
* exports ([657bda1](https://github.com/chenasraf/text-transform.nvim/commit/657bda14789b907911157c4f34e8a088c1e00d15))
## [0.5.0](https://github.com/chenasraf/text-transform.nvim/compare/v0.4.0...v0.5.0) (2024-04-30)
### Features
* add commands ([500edc6](https://github.com/chenasraf/text-transform.nvim/commit/500edc66e6ac0a649108749ad290f2a370cf4219))
* add telescope command ([6e409f0](https://github.com/chenasraf/text-transform.nvim/commit/6e409f0a44e50b3ef65f426e36b7824ee2d95d7a))
### Bug Fixes
* precommit ([a669a99](https://github.com/chenasraf/text-transform.nvim/commit/a669a998f47de6c33e9667d40ce3a219d60af69b))
* telescope ([7ef73f2](https://github.com/chenasraf/text-transform.nvim/commit/7ef73f26d13f72454d0388bdca4541f7f4aa78e2))
* telescope command ([69542d7](https://github.com/chenasraf/text-transform.nvim/commit/69542d7e1c6c88832611fb3c0f1d46102c133bd1))
* uppercase logic ([a669a99](https://github.com/chenasraf/text-transform.nvim/commit/a669a998f47de6c33e9667d40ce3a219d60af69b))
* word break/transform ([a53e0d6](https://github.com/chenasraf/text-transform.nvim/commit/a53e0d6d64051d5fea6d102529c403e4cbd0cce2))
## [0.4.0](https://github.com/chenasraf/text-transform.nvim/compare/v0.3.1...v0.4.0) (2023-05-18)
### Features
* visual block mode support ([5765e3d](https://github.com/chenasraf/text-transform.nvim/commit/5765e3d72109c473b4f9ab8839d01f8c983d0dc2))
### Bug Fixes
* word edge detection ([9cd27ab](https://github.com/chenasraf/text-transform.nvim/commit/9cd27aba2b74b072d57edadecdd63646d84a82cf))
* words split ([43cc07c](https://github.com/chenasraf/text-transform.nvim/commit/43cc07cde43e8a4caf6a9c7471f6e3e69e67c242))
## [0.3.1](https://github.com/chenasraf/text-transform.nvim/compare/v0.3.0...v0.3.1) (2023-05-17)
### Bug Fixes
* into_words dot split ([f79799c](https://github.com/chenasraf/text-transform.nvim/commit/f79799cd912436e48ee2bcde7a5d98042337f8ae))
## [0.3.0](https://github.com/chenasraf/text-transform.nvim/compare/v0.2.1...v0.3.0) (2023-05-16)

View File

@@ -9,4 +9,7 @@ community_bridge: # Replace with a single Community Bridge project-name e.g., cl
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.com/cgi-bin/webscr?cmd=_donations&business=TSH3C3ABGQM22&currency_code=ILS&source=url"]
custom:
[
'https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=TSH3C3ABGQM22&currency_code=ILS&source=url',
]

View File

@@ -14,6 +14,10 @@ test:
deps:
@mkdir -p deps
git clone --depth 1 https://github.com/echasnovski/mini.nvim deps/mini.nvim
git clone --depth 1 https://github.com/nvim-lua/plenary.nvim deps/plenary.nvim
git clone --depth 1 https://github.com/nvim-telescope/telescope.nvim deps/telescope.nvim
echo "#!/usr/bin/env bash\n\nmake precommit" > .git/hooks/pre-commit
chmod +x .git/hooks/pre-commit
# installs deps before running tests, useful for the CI.
test-ci: deps test
@@ -29,6 +33,9 @@ documentation-ci: deps documentation
lint:
stylua .
# setup
setup:
./scripts/setup.sh
# precommit
precommit:
./scripts/precommit.sh
clean:
rm -rf deps

220
README.md
View File

@@ -8,14 +8,23 @@
<div align="center">
![Demonstration](https://github.com/chenasraf/text-transform.nvim/assets/167217/e73f0e27-d72d-4aa6-bfa7-6f691aba9713)
![Demo](https://github.com/chenasraf/text-transform.nvim/assets/167217/20c0106e-2c3b-4dd5-894e-23b313d8bc6b)
</div>
## ⚡️ Features
Transform the current word or selection between multiple case types. Need to easily replace myVar
with my_var or vice versa? This plugin is for you!
Transform the current word or selection between multiple case types. Need to easily replace `myVar`
with `my_var` or vice versa? This plugin is for you!
- Works on current word in **Normal Mode**
- Will replace the current word selectable by <kbd>ciw</kbd>
- Works on selection in **Visual Mode**
- Will replace only inside the selection
- Works on column selections in **Visual Block Mode**
- Will detect if the block is a single column or multiple columns
- If it's a single column, will replace the word under each cursor
- If it's a selection with length, will replace only inside the selection ranges
| Transformation | Example Inputs | Output |
| -------------- | --------------------------- | -------- |
@@ -27,73 +36,55 @@ with my_var or vice versa? This plugin is for you!
| `Title Case` | `my_var`, `my-var`, `MyVar` | `My Var` |
| `CONST_CASE` | `my_var`, `my-var`, `MyVar` | `MY_VAR` |
## 📋 Installation
## 🔽 Installation
<div align="center">
<table>
<thead>
<tr>
<th>Package manager</th>
<th>Snippet</th>
</tr>
</thead>
<tbody>
<tr>
<td>
[wbthomason/packer.nvim](https://github.com/wbthomason/packer.nvim)
</td>
<td>
### [Lazy](https://github.com/folke/lazy.nvim)
```lua
-- stable version
use { "chenasraf/text-transform.nvim", tag = "*" }
-- dev version
use { "chenasraf/text-transform.nvim" }
require("lazy").setup({
"chenasraf/text-transform.nvim",
-- stable version
version = "*", -- or: tag = "stable"
-- dev version
-- branch = "develop",
-- Optional - for Telescope popup
dependencies = { 'nvim-telescope/telescope.nvim', 'nvim-lua/plenary.nvim' }
})
```
</td>
</tr>
<tr>
<td>
[junegunn/vim-plug](https://github.com/junegunn/vim-plug)
</td>
<td>
### [Packer](https://github.com/wbthomason/packer.nvim)
```lua
-- stable version
Plug "chenasraf/text-transform.nvim", { "tag": "*" }
-- dev version
Plug "chenasraf/text-transform.nvim"
use { "chenasraf/text-transform.nvim",
-- stable version
tag = "stable",
-- dev version
-- branch = "develop",
-- Optional - for Telescope popup
requires = { 'nvim-telescope/telescope.nvim', 'nvim-lua/plenary.nvim' }
}
```
</td>
</tr>
<tr>
<td>
### [Plug](https://github.com/junegunn/vim-plug)
[folke/lazy.nvim](https://github.com/folke/lazy.nvim)
```vim
" Dependencies - optional for Telescope popup
Plug 'nvim-telescope/telescope.nvim'
Plug 'nvim-lua/plenary.nvim'
</td>
<td>
```lua
-- stable version
require("lazy").setup({{ "chenasraf/text-transform.nvim", version = "*" }})
-- dev version
require("lazy").setup({ "chenasraf/text-transform.nvim" })
" stable version
Plug 'chenasraf/text-transform.nvim', { 'tag': 'stable' }
" dev version
Plug 'chenasraf/text-transform.nvim', { 'branch': 'develop' }
```
</td>
</tr>
</tbody>
</table>
</div>
If you decide not to use Telescope, you can ignore the dependencies. In that case, be sure to change
your config with `popup_type = 'select'` so that TextTransform never tries to load Telescope.
## ☄ Getting started
It falls back to `vim.ui.select()` instead, which may or may not still be Telescope behind the
scenes, or something else; depending on your setup.
## 🚀 Getting started
To get started, [install](#-installation) the plugin via your favorite package manager.
@@ -112,42 +103,113 @@ To get started, [install](#-installation) the plugin via your favorite package m
1. Select the desired transform and you're done!
## ⚙ Configuration
## ⚙ Configuration
<details>
<summary>Click to unfold the full list of options with their default values</summary>
> **Note**: The options are also available in Neovim by calling `:h TextTransform.options`
> **Note**: The options are also available in Neovim by calling `:h text-transform.options`
The following are the default options when none are configured by the user.
To merge any new config into the default, you can override only the keys you need, and leave the
rest to use the defaults.
```lua
require("text-transform").setup({
-- Prints useful logs about what event are triggered, and reasons actions are executed.
--- Prints information about internals of the plugin. Very verbose, only useful for debugging.
debug = false,
-- Keymap to trigger the transform.
--- Keymap configurations
keymap = {
-- Normal mode keymap.
["n"] = "<Leader>~",
-- Visual mode keymap.
["v"] = "<Leader>~",
--- Keymap to open the telescope popup. Set to `false` or `nil` to disable keymapping
--- You can always customize your own keymapping manually.
telescope_popup = {
--- Opens the popup in normal mode
["n"] = "<Leader>~",
--- Opens the popup in visual/visual block modes
["v"] = "<Leader>~",
},
},
---
--- Configurations for the text-transform replacers
--- Keys indicate the replacer name, and the value is a table with the following options:
---
--- - `enabled` (boolean): Enable or disable the replacer - disabled replacers do not show up in the popup.
replacers = {
camel_case = { enabled = true },
const_case = { enabled = true },
dot_case = { enabled = true },
kebab_case = { enabled = true },
pascal_case = { enabled = true },
snake_case = { enabled = true },
title_case = { enabled = true },
},
--- Sort the replacers in the popup.
--- Possible values: 'frequency', 'name'
sort_by = "frequency",
--- The popup type to show.
--- Possible values: 'telescope', 'select'
popup_type = 'telescope'
})
```
## 📝 Commands
The following commands are available for your use in your own mappings or for reference.
| Command | Description |
| ---------------- | ---------------------------------------------------------------------------------------------------------------------- |
| `:TextTransform` | Pop up a either a Telescope window or a selection popup, depending on the `popup_type` config. |
| `:TtTelescope` | Pop up a Telescope window with all the transformers, which will directly act on the selected text or highlighted word. |
| `:TtSelect` | Pop up a selection popup with all the transformers, which will directly act on the selected text or highlighted word. |
| `:TtCamel` | Replace selection/word with `camelCase`. |
| `:TtSnake` | Replace selection/word with `snake_case`. |
| `:TtPascal` | Replace selection/word with `PascalCase`. |
| `:TtConst` | Replace selection/word with `CONST_CASE`. |
| `:TtDot` | Replace selection/word with `dot.case`. |
| `:TtKebab` | Replace selection/word with `kebab-case`. |
| `:TtTitle` | Replace selection/word with `Title Case`. |
## ⌨️⌨️ Keymaps
You can use the setup options to customize the default keymaps used to trigger the Telescope Popup.
To disable these automated mappings, pass `nil` or `false` to the containing table (e.g.
`telescope_popup`) or to the keys themselves.
```lua
-- Disable entirely
require("text-transform").setup({
keymap = {
telescope_popup = nil,
},
})
-- Disable just one keymap
require("text-transform").setup({
keymap = {
telescope_popup = {
["v"] = nil,
},
},
})
```
</details>
You can also create custom mappings to specific case conversions or to the Telescope popup yourself.
## 🧰 Commands
```lua
-- Trigger telescope popup
vim.keymap.set("n", "<leader>~~", ":TtTelescope", { silent = true, desc = "Transform Text" })
Use the following as example, you can mix &amp; match the different replacement functions with the
desired transform function.
-- Trigger case converters directly
vim.keymap.set({ "n", "v" }, "<leader>Ccc", ":TtCamel", { silent = true, desc = "To camelCase" })
vim.keymap.set({ "n", "v" }, "<leader>Csn", ":TtSnake", { silent = true, desc = "To snake_case" })
vim.keymap.set({ "n", "v" }, "<leader>Cpa", ":TtPascal", { silent = true, desc = "To PascalCase" })
vim.keymap.set({ "n", "v" }, "<leader>Cco", ":TtConst", { silent = true, desc = "To CONST_CASE" })
vim.keymap.set({ "n", "v" }, "<leader>Cdo", ":TtDot", { silent = true, desc = "To dot.case" })
vim.keymap.set({ "n", "v" }, "<leader>Cke", ":TtKebab", { silent = true, desc = "To kebab-case" })
vim.keymap.set({ "n", "v" }, "<leader>Ctt", ":TtTitle", { silent = true, desc = "To Title Case" })
```
Normally you wouldn't need to call this, as you would just use the keymap you used in `setup()`.
| Command | Description |
| ---------------------------------------------------------------- | -------------------------------------------------- |
| `:lua TextTransform.replace_word(TextTransform.camel_case)` | Replaces selected word with camelCase version. |
| `:lua TextTransform.replace_selection(TextTransform.snake_case)` | Replaces visual selection with snake_case version. |
## ⌨ Contributing
## 💁🏻 Contributing
I am developing this package on my free time, so any support, whether code, issues, or just stars is
very helpful to sustaining its life. If you are feeling incredibly generous and would like to donate

View File

@@ -1,2 +1,23 @@
TextTransform.options text-transform.txt /*TextTransform.options*
TextTransform.config text-transform.txt /*TextTransform.config*
TextTransform.get_visual_selection_details() text-transform.txt /*TextTransform.get_visual_selection_details()*
TextTransform.merge() text-transform.txt /*TextTransform.merge()*
TextTransform.replace_columns() text-transform.txt /*TextTransform.replace_columns()*
TextTransform.replace_range() text-transform.txt /*TextTransform.replace_range()*
TextTransform.replace_selection() text-transform.txt /*TextTransform.replace_selection()*
TextTransform.replace_word() text-transform.txt /*TextTransform.replace_word()*
TextTransform.restore_positions() text-transform.txt /*TextTransform.restore_positions()*
TextTransform.save_positions() text-transform.txt /*TextTransform.save_positions()*
TextTransform.select_popup() text-transform.txt /*TextTransform.select_popup()*
TextTransform.setup() text-transform.txt /*TextTransform.setup()*
TextTransform.show_popup() text-transform.txt /*TextTransform.show_popup()*
TextTransform.telescope_popup() text-transform.txt /*TextTransform.telescope_popup()*
TextTransform.to_camel_case() text-transform.txt /*TextTransform.to_camel_case()*
TextTransform.to_const_case() text-transform.txt /*TextTransform.to_const_case()*
TextTransform.to_dot_case() text-transform.txt /*TextTransform.to_dot_case()*
TextTransform.to_kebab_case() text-transform.txt /*TextTransform.to_kebab_case()*
TextTransform.to_pascal_case() text-transform.txt /*TextTransform.to_pascal_case()*
TextTransform.to_snake_case() text-transform.txt /*TextTransform.to_snake_case()*
TextTransform.to_title_case() text-transform.txt /*TextTransform.to_title_case()*
TextTransform.to_words() text-transform.txt /*TextTransform.to_words()*
TextTransform.transform_words() text-transform.txt /*TextTransform.transform_words()*
find_word_boundaries() text-transform.txt /*find_word_boundaries()*

View File

@@ -1,35 +1,277 @@
==============================================================================
------------------------------------------------------------------------------
*TextTransform.options*
`TextTransform.options`
*TextTransform.config*
`TextTransform.config`
Your plugin configuration with its default values.
Default values:
>
TextTransform.options = {
-- Prints useful logs about what event are triggered, and reasons actions are executed.
>lua
TextTransform.config = {
--- Prints information about internals of the plugin. Very verbose, only useful for debugging.
debug = false,
-- Keymap to trigger the transform.
--- Keymap configurations
keymap = {
-- Normal mode keymap.
["n"] = "<Leader>~",
-- Visual mode keymap.
["v"] = "<Leader>~",
--- Keymap to open the telescope popup. Set to `false` or `nil` to disable keymapping
--- You can always customize your own keymapping manually.
telescope_popup = {
--- Opens the popup in normal mode
["n"] = "<Leader>~",
--- Opens the popup in visual/visual block modes
["v"] = "<Leader>~",
},
},
---
--- Configurations for the text-transform replacers
--- Keys indicate the replacer name, and the value is a table with the following options:
---
--- - `enabled` (boolean): Enable or disable the replacer - disabled replacers do not show up in the popup.
replacers = {
camel_case = { enabled = true },
const_case = { enabled = true },
dot_case = { enabled = true },
kebab_case = { enabled = true },
pascal_case = { enabled = true },
snake_case = { enabled = true },
title_case = { enabled = true },
},
--- Sort the replacers in the popup.
--- Possible values: 'frequency', 'name'
sort_by = "frequency",
--- The popup type to show.
--- Possible values: 'telescope', 'select'
popup_type = "telescope",
}
<
------------------------------------------------------------------------------
*TextTransform.setup()*
`TextTransform.setup`({options})
Define your text-transform setup.
Parameters~
Parameters ~
{options} `(table)` Module config table. See |TextTransform.options|.
Usage~
Usage ~
`require("text-transform").setup()` (add `{}` with your |TextTransform.options| table)
==============================================================================
------------------------------------------------------------------------------
*find_word_boundaries()*
`find_word_boundaries`({line}, {start_col})
Finds the boundaries of the surrounding word around `start_col` within `line`.
Parameters ~
{line} `(number)`
{start_col} `(number)`
Return ~
`(number)` start_col, number end_col
------------------------------------------------------------------------------
*TextTransform.replace_range()*
`TextTransform.replace_range`({start_line}, {start_col}, {end_line}, {end_col}, {transform_name})
Replace the range between the given positions with the given transform.
Acts on the lines between the given positions, replacing the text between the given columns.
Parameters ~
{start_line} `(number)` The starting line
{start_col} `(number)` The starting column
{end_line} `(number)` The ending line
{end_col} `(number)` The ending column
{transform_name} `(string)` The transformer name
------------------------------------------------------------------------------
*TextTransform.replace_word()*
`TextTransform.replace_word`({transform_name}, {position})
Replace the word under the cursor with the given transform.
If `position` is provided, replace the word under the given position.
Otherwise, attempts to find the word under the cursor.
Parameters ~
{transform_name} `(string)` The transformer name
{position} `(table|nil)` A table containing the position of the word to replace
------------------------------------------------------------------------------
*TextTransform.replace_columns()*
`TextTransform.replace_columns`({transform_name})
Replaces each column in visual block mode selection with the given transform.
Assumes that the each selection is 1 character and operates on the whole word under each cursor.
Parameters ~
{transform_name} `(string)` The transformer name
------------------------------------------------------------------------------
*TextTransform.replace_selection()*
`TextTransform.replace_selection`({transform_name})
Replaces a selection with the given transform. This function attempts to infer the replacement
type based on the cursor positiono and visual selections, and passes information to relevant
range replacement functions.
Parameters ~
{transform_name} `(string)` The transformer name
------------------------------------------------------------------------------
*TextTransform.get_visual_selection_details()*
`TextTransform.get_visual_selection_details`()
Takes the saved positions and translates them into individual visual ranges, regardless of how
the original selection was performed.
This allows to treat all ranges equally and allows to work on each selection without knowing
the full information around the selection logic.
==============================================================================
------------------------------------------------------------------------------
*TextTransform.save_positions()*
`TextTransform.save_positions`()
Save the current cursor position, mode, and visual selection ranges
------------------------------------------------------------------------------
*TextTransform.restore_positions()*
`TextTransform.restore_positions`({positions})
Restore the cursor position, mode, and visual selection ranges saved using `save_position()`,
or a given modified state, if passed as the first argument
==============================================================================
------------------------------------------------------------------------------
*TextTransform.to_words()*
`TextTransform.to_words`({string})
Splits a string into words.
Parameters ~
{string} `(string)`
Return ~
`(table)`
------------------------------------------------------------------------------
*TextTransform.transform_words()*
`TextTransform.transform_words`({words}, {with_word_cb}, {separator})
Transforms a table of strings into a string using a callback and separator.
The callback is called with the word, the index, and the table of words.
The separator is added between each word.
Parameters ~
{words} `(string|table)` string or table of strings
{with_word_cb} `(function)` (word: string, index: number, words: table) -> string
{separator} `(string|nil)` (optional)
Return ~
`(string)`
------------------------------------------------------------------------------
*TextTransform.to_camel_case()*
`TextTransform.to_camel_case`({string})
Transforms a string into camelCase.
Parameters ~
{string} `(string)`
Return ~
`(string)`
------------------------------------------------------------------------------
*TextTransform.to_snake_case()*
`TextTransform.to_snake_case`({string})
Transfroms a string into snake_case.
Parameters ~
{string} `(any)`
Return ~
`(string)`
------------------------------------------------------------------------------
*TextTransform.to_pascal_case()*
`TextTransform.to_pascal_case`({string})
Transforms a string into PascalCase.
Parameters ~
{string} `(string)`
Return ~
`(string)`
------------------------------------------------------------------------------
*TextTransform.to_title_case()*
`TextTransform.to_title_case`({string})
Transforms a string into Title Case.
Parameters ~
{string} `(string)`
Return ~
`(string)`
------------------------------------------------------------------------------
*TextTransform.to_kebab_case()*
`TextTransform.to_kebab_case`({string})
Transforms a string into kebab-case.
Parameters ~
{string} `(string)`
Return ~
`(string)`
------------------------------------------------------------------------------
*TextTransform.to_dot_case()*
`TextTransform.to_dot_case`({string})
Transforms a string into dot.case.
Parameters ~
{string} `(string)`
Return ~
`(string)`
------------------------------------------------------------------------------
*TextTransform.to_const_case()*
`TextTransform.to_const_case`({string})
Transforms a string into CONSTANT_CASE.
Parameters ~
{string} `(string)`
Return ~
`(string)`
==============================================================================
------------------------------------------------------------------------------
*TextTransform.show_popup()*
`TextTransform.show_popup`()
Pops up a selection menu, containing the available case transformers.
When a transformer is selected, the cursor position/range/columns will be used to replace the
words around the cursor or inside the selection.
The cursor positions/ranges are saved before opening the menu and restored once a selection is
made.
==============================================================================
------------------------------------------------------------------------------
*TextTransform.select_popup()*
`TextTransform.select_popup`()
Pops up a selection menu, containing the available case transformers.
When a transformer is selected, the cursor position/range/columns will be used to replace the
words around the cursor or inside the selection.
The cursor positions/ranges are saved before opening the menu and restored once a selection is
made.
==============================================================================
------------------------------------------------------------------------------
*TextTransform.telescope_popup()*
`TextTransform.telescope_popup`()
Pops up a telescope menu, containing the available case transformers.
When a transformer is selected, the cursor position/range/columns will be used to replace the
words around the cursor or inside the selection.
The cursor positions/ranges are saved before opening the menu and restored once a selection is
made.
==============================================================================
------------------------------------------------------------------------------
*TextTransform.merge()*
`TextTransform.merge`({t1}, {t2})
Merges two tables into one. Same as `vim.tbl_extend("keep", t1, t2)`.
Mutates the first table.
TODO accept multiple tables to merge
Parameters ~
{t1} `(table)`
{t2} `(table)`
Return ~
`(table)`
vim:tw=78:ts=8:noet:ft=help:norl:

View File

@@ -0,0 +1,71 @@
local D = require("text-transform.utils.debug")
local util = require("text-transform.utils")
local state = require("text-transform.state")
local replacers = require("text-transform.replacers")
local popup = require("text-transform.popup")
local common = require("text-transform.popup.common")
local TextTransform = {}
function TextTransform.init_commands()
local map = {
TtCamel = "camel_case",
TtConst = "const_case",
TtDot = "dot_case",
TtKebab = "kebab_case",
TtPascal = "pascal_case",
TtSnake = "snake_case",
TtTitle = "title_case",
}
local cmdopts = { range = true, force = true }
local opts = function(desc)
return util.merge(cmdopts, { desc = desc })
end
for cmd, transformer_name in pairs(map) do
local item
for _, t in ipairs(common.items) do
if t.value == transformer_name then
item = t.label
break
end
end
vim.api.nvim_create_user_command(cmd, function()
state.save_positions()
replacers.replace_selection(transformer_name)
vim.schedule(function()
state.restore_positions()
end)
end, opts("Change to " .. item))
end
-- specific popups
vim.api.nvim_create_user_command("TtTelescope", function()
local telescope = require("text-transform.popup.telescope")
telescope.telescope_popup()
end, opts("Change Case with Telescope"))
vim.api.nvim_create_user_command("TtSelect", function()
local select = require("text-transform.popup.select")
select.select_popup()
end, opts("Change Case with Select"))
-- auto popup by config
vim.api.nvim_create_user_command("TextTransform", popup.show_popup, opts("Change Case"))
end
function TextTransform.init_keymaps()
local keymaps = _G.TextTransform.config.keymap
D.log("init_keymaps", "Initializing keymaps, config %s", vim.inspect(_G.TextTransform))
if keymaps.telescope_popup then
local keys = keymaps.telescope_popup
if keys.n then
vim.keymap.set("n", keys.n, popup.show_popup, { silent = true, desc = "Change Case" })
end
if keys.v then
vim.keymap.set("v", keys.v, popup.show_popup, { silent = true, desc = "Change Case" })
end
end
end
return TextTransform

View File

@@ -1,19 +1,63 @@
local commands = require("text-transform.commands")
local D = require("text-transform.utils.debug")
local utils = require("text-transform.utils")
local TextTransform = {}
local function ensure_config()
-- when the config is not set to the global object, we set it
if _G.TextTransform.config == nil then
_G.TextTransform.config = TextTransform.config
end
end
local function init()
ensure_config()
local o = TextTransform.config
D.log("config", "Initializing TextTransform with %s", vim.inspect(o))
commands.init_commands()
commands.init_keymaps()
end
--- Your plugin configuration with its default values.
---
--- Default values:
---@eval return MiniDoc.afterlines_to_code(MiniDoc.current.eval_section)
TextTransform.options = {
-- Prints useful logs about what event are triggered, and reasons actions are executed.
TextTransform.config = {
--- Prints information about internals of the plugin. Very verbose, only useful for debugging.
debug = false,
-- Keymap to trigger the transform.
--- Keymap configurations
keymap = {
-- Normal mode keymap.
["n"] = "<Leader>~",
-- Visual mode keymap.
["v"] = "<Leader>~",
--- Keymap to open the telescope popup. Set to `false` or `nil` to disable keymapping
--- You can always customize your own keymapping manually.
telescope_popup = {
--- Opens the popup in normal mode
["n"] = "<Leader>~",
--- Opens the popup in visual/visual block modes
["v"] = "<Leader>~",
},
},
---
--- Configurations for the text-transform replacers
--- Keys indicate the replacer name, and the value is a table with the following options:
---
--- - `enabled` (boolean): Enable or disable the replacer - disabled replacers do not show up in the popup.
replacers = {
camel_case = { enabled = true },
const_case = { enabled = true },
dot_case = { enabled = true },
kebab_case = { enabled = true },
pascal_case = { enabled = true },
snake_case = { enabled = true },
title_case = { enabled = true },
},
--- Sort the replacers in the popup.
--- Possible values: 'frequency', 'name'
sort_by = "frequency",
--- The popup type to show.
--- Possible values: 'telescope', 'select'
popup_type = "telescope",
}
--- Define your text-transform setup.
@@ -24,39 +68,17 @@ TextTransform.options = {
function TextTransform.setup(options)
options = options or {}
TextTransform.options = vim.tbl_deep_extend("keep", options, TextTransform.options)
TextTransform.config = utils.merge(TextTransform.config, options)
local map = {
["&camelCase"] = "TextTransform.camel_case",
["&snake_case"] = "TextTransform.snake_case",
["&PascalCase"] = "TextTransform.pascal_case",
["&kebab-case"] = "TextTransform.kebab_case",
["&dot\\.case"] = "TextTransform.dot_case",
["&Title\\ Case"] = "TextTransform.title_case",
["C&ONST_CASE"] = "TextTransform.const_case",
}
for k, v in pairs(map) do
vim.cmd("amenu TransformsWord." .. k .. " :lua TextTransform.replace_word(" .. v .. ")<CR>")
vim.cmd(
"amenu TransformsSelection." .. k .. " :lua TextTransform.replace_selection(" .. v .. ")<CR>"
)
if vim.api.nvim_get_vvar("vim_did_enter") == 0 then
vim.defer_fn(function()
init()
end, 0)
else
init()
end
vim.keymap.set(
"n",
TextTransform.options.keymap.n,
"<cmd>popup TransformsWord<CR>",
{ silent = true }
)
vim.keymap.set(
"v",
TextTransform.options.keymap.v,
"<cmd>popup TransformsSelection<CR>",
{ silent = true }
)
return TextTransform.options
return TextTransform.config
end
return TextTransform

View File

@@ -1,41 +1,23 @@
local M = require("text-transform.main")
local utils = require("text-transform.utils")
local tt = require("text-transform.transformers")
local replacers = require("text-transform.replacers")
local state = require("text-transform.state")
local popup = require("text-transform.popup")
local TextTransform = {}
-- Toggle the plugin by calling the `enable`/`disable` methods respectively.
function TextTransform.toggle()
-- when the config is not set to the global object, we set it
if _G.TextTransform.config == nil then
_G.TextTransform.config = require("text-transform.config").options
end
_G.TextTransform.state = M.toggle()
end
-- starts TextTransform and set internal functions and state.
function TextTransform.enable()
if _G.TextTransform.config == nil then
_G.TextTransform.config = require("text-transform.config").options
end
local state = M.enable()
if state ~= nil then
_G.TextTransform.state = state
end
return state
end
-- disables TextTransform and reset internal functions and state.
function TextTransform.disable()
_G.TextTransform.state = M.disable()
end
-- setup TextTransform options and merge them with user provided ones.
function TextTransform.setup(opts)
_G.TextTransform.config = require("text-transform.config").setup(opts)
end
_G.TextTransform = TextTransform
local function merge(table)
TextTransform = utils.merge(TextTransform, table)
end
merge(tt)
merge(replacers)
merge(state)
merge(popup)
_G.TextTransform = TextTransform
return _G.TextTransform

View File

@@ -1,49 +0,0 @@
local D = require("text-transform.util.debug")
-- internal methods
local TextTransform = {}
-- state
local S = {
-- Boolean determining if the plugin is enabled or not.
enabled = false,
}
---Toggle the plugin by calling the `enable`/`disable` methods respectively.
---@private
function TextTransform.toggle()
if S.enabled then
return TextTransform.disable()
end
return TextTransform.enable()
end
---Initializes the plugin.
---@private
function TextTransform.enable()
if S.enabled then
return S
end
S.enabled = true
return S
end
---Disables the plugin and reset the internal state.
---@private
function TextTransform.disable()
if not S.enabled then
return S
end
-- reset the state
S = {
enabled = false,
}
return S
end
return TextTransform

View File

@@ -0,0 +1,67 @@
local D = require("text-transform.utils.debug")
local state = require("text-transform.state")
local replacers = require("text-transform.replacers")
local popup_common = {}
popup_common.items = {
{ label = "camelCase", value = "camel_case" },
{ label = "snake_case", value = "snake_case" },
{ label = "PascalCase", value = "pascal_case" },
{ label = "kebab-case", value = "kebab_case" },
{ label = "dot.case", value = "dot_case" },
{ label = "Title Case", value = "title_case" },
{ label = "CONST_CASE", value = "const_case" },
}
popup_common.default_frequency = {
camel_case = 1,
snake_case = 1,
pascal_case = 1,
kebab_case = 1,
dot_case = 1,
title_case = 1,
const_case = 1,
}
local cache_dir = vim.fn.stdpath("cache")
local frequency_file = cache_dir .. "/text-transform-frequency.json"
local frequency
function popup_common.load_frequency()
if frequency then
return frequency
end
if vim.fn.filereadable(frequency_file) == 0 then
frequency = popup_common.default_frequency
vim.fn.writefile({ vim.fn.json_encode(frequency) }, frequency_file)
else
frequency = vim.fn.json_decode(vim.fn.readfile(frequency_file))
end
D.log("telescope", "frequency loaded: %s", vim.inspect(frequency))
return frequency
end
function popup_common.inc_frequency(name)
frequency[name] = (frequency[name] or 0) + 1
D.log("telescope", "new frequency: %s %d", name, frequency[name])
vim.fn.writefile({ vim.fn.json_encode(frequency) }, frequency_file)
end
function popup_common.entry_maker(entry)
return {
value = entry.value,
display = entry.label,
ordinal = entry.label,
frequency = frequency[entry.value] or 1,
}
end
function popup_common.select(selection)
vim.schedule(function()
replacers.replace_selection(selection.value)
state.restore_positions()
end)
end
return popup_common

View File

@@ -0,0 +1,20 @@
local TextTransform = {}
--- Pops up a selection menu, containing the available case transformers.
--- When a transformer is selected, the cursor position/range/columns will be used to replace the
--- words around the cursor or inside the selection.
---
--- The cursor positions/ranges are saved before opening the menu and restored once a selection is
--- made.
function TextTransform.show_popup()
local config = _G.TextTransform.config
if config.popup_type == "telescope" then
local telescope = require("text-transform.popup.telescope")
telescope.telescope_popup()
else
local select = require("text-transform.popup.select")
select.select_popup()
end
end
return TextTransform

View File

@@ -0,0 +1,30 @@
local common = require("text-transform.popup.common")
local state = require("text-transform.state")
local TextTransform = {}
--- Pops up a selection menu, containing the available case transformers.
--- When a transformer is selected, the cursor position/range/columns will be used to replace the
--- words around the cursor or inside the selection.
---
--- The cursor positions/ranges are saved before opening the menu and restored once a selection is
--- made.
function TextTransform.select_popup()
common.load_frequency()
state.save_positions()
vim.ui.select(common.items, {
prompt = "Change Case",
format_item = function(item)
return item.label
end,
}, function(choice)
if not choice then
return
end
local item = common.entry_maker(choice)
common.select(item)
end)
end
return TextTransform

View File

@@ -0,0 +1,104 @@
local common = require("text-transform.popup.common")
local D = require("text-transform.utils.debug")
local state = require("text-transform.state")
local pickers = require("telescope.pickers")
local finders = require("telescope.finders")
local telescope_conf = require("telescope.config").values
local actions = require("telescope.actions")
local action_state = require("telescope.actions.state")
local dropdown = require("telescope.themes").get_dropdown({})
local Sorter = require("telescope.sorters").Sorter
local TextTransform = {}
local frequency_sorter = Sorter:new({
---@diagnostic disable-next-line: unused-local
scoring_function = function(self, prompt, line)
local generic_sorter = telescope_conf.generic_sorter()
generic_sorter:init()
local entry
for _, item in ipairs(common.items) do
if item.label == line then
entry = common.entry_maker(item)
break
end
end
D.log("frequency_sorter", "entry %s", vim.inspect(entry))
D.log("frequency_sorter", "prompt %s line %s", prompt, line)
-- Basic filtering based on prompt matching, non-matching items score below 0 to exclude them
local basic_score = (generic_sorter:scoring_function(prompt, line) or 1)
D.log("frequency_sorter", "%s basic_score: %s", entry.value, basic_score)
if basic_score < 0 then
return basic_score
end
-- D.log("frequency_sorter", "entry: %s", vim.inspect(entry))
-- D.log("frequency_sorter", "prompt: %s", prompt)
-- Calculate score based on frequency, higher frequency should have lower score
local freq_score = (entry.frequency or 1) * 10
D.log("frequency_sorter", "freq_score: %s", freq_score)
local final_score = 999999999 - freq_score + basic_score
D.log("frequency_sorter", "%s final_score: %s", line, final_score)
-- Combine scores, with frequency having the primary influence if present
return final_score
end,
})
local generic_sorter = telescope_conf.generic_sorter()
local sorter_map = {
frequency = frequency_sorter,
name = generic_sorter,
}
--- Pops up a telescope menu, containing the available case transformers.
--- When a transformer is selected, the cursor position/range/columns will be used to replace the
--- words around the cursor or inside the selection.
---
--- The cursor positions/ranges are saved before opening the menu and restored once a selection is
--- made.
function TextTransform.telescope_popup()
state.save_positions()
local filtered = {}
local config = _G.TextTransform.config
local sorter = sorter_map[config.sort_by] or generic_sorter
if config.sort_by == "frequency" then
common.load_frequency()
end
for _, item in ipairs(common.items) do
if config.replacers[item.value] and not config.replacers[item.value].enabled then
goto continue
end
table.insert(filtered, item)
::continue::
end
local picker = pickers.new(dropdown, {
prompt_title = "Change Case",
finder = finders.new_table({
results = common.items,
entry_maker = common.entry_maker,
}),
sorter = sorter,
attach_mappings = function(prompt_bufnr)
actions.select_default:replace(function()
local selection = action_state.get_selected_entry()
common.inc_frequency(selection.value)
actions.close(prompt_bufnr)
common.select(selection)
end)
return true
end,
})
vim.schedule(function()
picker:find()
end)
end
return TextTransform

View File

@@ -0,0 +1,233 @@
local D = require("text-transform.utils.debug")
local S = require("text-transform.state")
local t = require("text-transform.transformers")
local TextTransform = {}
--- Finds the boundaries of the surrounding word around `start_col` within `line`.
---@param line number
---@param start_col number
---@return number start_col, number end_col
local function find_word_boundaries(line, start_col)
local line_text = vim.fn.getline(line)
-- dashes, underscores, and periods are considered part of a word
local word_pat = "[A-Za-z0-9_.\\-]"
local non_word_pat = "[^A-Za-z0-9_.\\-]"
-- TODO handle searching backwards
local word_start_col = vim.fn.match(line_text:sub(start_col), word_pat) + start_col
local word_end_col = vim.fn.match(line_text:sub(word_start_col), non_word_pat)
+ word_start_col
- 1
D.log(
"find_word_boundaries",
"Found word boundaries: %s",
vim.inspect({ word_start_col, word_end_col })
)
D.log("find_word_boundaries", "Word text: %s", line_text:sub(word_start_col, word_end_col))
D.log("find_word_boundaries", "Line text: %s", line_text)
return word_start_col, word_end_col
end
--- Replace the range between the given positions with the given transform.
--- Acts on the lines between the given positions, replacing the text between the given columns.
---
---@param start_line number The starting line
---@param start_col number The starting column
---@param end_line number The ending line
---@param end_col number The ending column
---@param transform_name string The transformer name
function TextTransform.replace_range(start_line, start_col, end_line, end_col, transform_name)
D.log("replace_range", "Replacing range with %s", transform_name)
local transform = t["to_" .. transform_name]
local lines = vim.fn.getline(start_line, end_line) ---@type any
local transformed = {}
if #lines == 1 then
local line = lines[1]
local before = line:sub(0, start_col - 1)
local fixed = transform(line:sub(start_col, end_col))
local after = line:sub(end_col + 1)
table.insert(transformed, before .. fixed .. after)
else
for i, line in ipairs(lines) do
if i == 1 then
table.insert(transformed, line:sub(1, start_col - 1) .. transform(line:sub(start_col)))
elseif i == #lines then
table.insert(transformed, transform(line:sub(1, end_col)) .. line:sub(end_col + 1))
else
table.insert(transformed, transform(line))
end
end
end
vim.fn.setline(start_line, transformed)
end
--- Replace the word under the cursor with the given transform.
--- If `position` is provided, replace the word under the given position.
--- Otherwise, attempts to find the word under the cursor.
---
---@param transform_name string The transformer name
---@param position table|nil A table containing the position of the word to replace
function TextTransform.replace_word(transform_name, position)
D.log("replace_word", "Replacing word with %s", transform_name)
local word, line, col, start_col, end_col
if not position then
word = vim.fn.expand("<cword>")
else
_, line, col = unpack(position)
start_col, end_col = find_word_boundaries(line, col)
word = vim.fn.getline(line):sub(start_col, end_col)
end
D.log("replace_word", "Found word %s", word)
D.log("replace_word", "Using transformer %s", transform_name)
local transformer = t["to_" .. transform_name]
local transformed = transformer(word)
D.log("replace_word", "New value %s", transformed)
if not position then
vim.cmd("normal ciw" .. transformed)
else
TextTransform.replace_range(line, start_col, line, end_col, transform_name)
end
end
--- Replaces each column in visual block mode selection with the given transform.
--- Assumes that the each selection is 1 character and operates on the whole word under each cursor.
---
---@param transform_name string The transformer name
function TextTransform.replace_columns(transform_name)
local selections = TextTransform.get_visual_selection_details()
D.log("replace_columns", "Replacing columns with %s", transform_name)
for _, sel in ipairs(selections) do
TextTransform.replace_word(transform_name, { 0, sel.start_line, sel.start_col, 0 })
end
end
--- Replaces a selection with the given transform. This function attempts to infer the replacement
--- type based on the cursor positiono and visual selections, and passes information to relevant
--- range replacement functions.
---
---@param transform_name string The transformer name
function TextTransform.replace_selection(transform_name)
D.log("replace_selection", "Replacing selection with %s", transform_name)
-- determine if cursor is a 1-width column across multiple lines or a normal selection
-- local start_line, start_col, end_line, end_col = unpack(vim.fn.getpos("'<"))
local selections = TextTransform.get_visual_selection_details()
D.log("replace_selection", "Selections: %s", vim.inspect(selections))
local is_multiline = #selections > 1
local is_column = is_multiline and selections[1].start_col == selections[#selections].end_col
local is_single_cursor = not is_multiline
and not is_column
and selections
and selections[1]
and selections[1].start_col == selections[1].end_col
D.log(
"replace_selection",
"is_multiline: %s, is_column: %s, is_word: %s",
is_multiline,
is_column,
is_single_cursor
)
if is_single_cursor then
TextTransform.replace_word(transform_name)
elseif is_column then
TextTransform.replace_columns(transform_name)
else
for _, sel in pairs(selections) do
TextTransform.replace_range(
sel.start_line,
sel.start_col,
sel.end_line,
sel.end_col,
transform_name
)
end
end
end
--- Takes the saved positions and translates them into individual visual ranges, regardless of how
--- the original selection was performed.
---
--- This allows to treat all ranges equally and allows to work on each selection without knowing
--- the full information around the selection logic.
function TextTransform.get_visual_selection_details()
if not S.state.positions.pos then
D.log("get_visual_selection_details", "No positions saved")
return {}
end
D.log(
"get_visual_selection_details",
"Getting visual selection details - mode: %s, is_visual: %s, is_block: %s",
S.state.mode,
S.is_visual_mode(),
S.is_block_visual_mode()
)
-- Get the start and end positions of the selection
local start_pos = S.state.positions.visual_start
local end_pos = S.state.positions.visual_end
local start_line, start_col = start_pos[2], start_pos[3]
local end_line, end_col = end_pos[2], end_pos[3]
-- Check if currently in visual mode; if not, return the cursor position
if
not S.is_visual_mode()
and not S.is_block_visual_mode()
and not S.has_range(start_pos, end_pos)
then
D.log("get_visual_selection_details", "Returning single cursor position: " .. vim.inspect(S))
local pos = S.state.positions.pos
return {
{
start_line = pos[2],
end_line = pos[2],
start_col = pos[3],
end_col = pos[3],
},
}
end
-- Swap if selection is made upwards or backwards
if start_line > end_line or (start_line == end_line and start_col > end_col) then
start_line, end_line = end_line, start_line
start_col, end_col = end_col, start_col
end
-- If it's block visual mode, return table for each row
if S.is_block_visual_mode() or S.has_range(start_pos, end_pos) then
local block_selection = {}
for line = start_line, end_line do
if start_col == end_col then
-- find the word surrounding the position
start_col, _ = find_word_boundaries(line, start_col)
end
table.insert(block_selection, {
start_line = line,
end_line = line,
start_col = start_col,
end_col = start_col,
})
end
D.log(
"get_visual_selection_details",
"Returning block selection: %s",
vim.inspect(block_selection)
)
return block_selection
else
-- Normal visual mode, return single table entry
D.log("get_visual_selection_details", "Returning normal selection")
return {
{
start_line = start_line,
end_line = end_line,
start_col = start_col,
end_col = end_col,
},
}
end
end
return TextTransform

View File

@@ -0,0 +1,126 @@
local D = require("text-transform.utils.debug")
-- methods
local TextTransform = {
-- The current state of the plugin
state = {
-- A table containing cursor position and visual selection details,
-- saved using `save_position()` and can be restored using `restore_positions()`
--@type {buf: number, mode: string, pos: table, visual_start: table, visual_end: table}
positions = {},
},
}
local function get_mode_type(mode)
-- classify mode as either visual, line, block or normal
local mode_map = {
["v"] = "visual",
["V"] = "line",
["\22"] = "block",
}
return mode_map[mode] or "normal"
end
function TextTransform.has_range(visual_start, visual_end)
return visual_start and visual_end and visual_start[2] ~= visual_end[2]
end
local function capture_part(start_sel, end_sel, return_type)
local l, sel
if return_type == "start" then
l = math.min(start_sel[2], end_sel[2])
sel = start_sel
else
l = math.max(start_sel[2], end_sel[2])
sel = end_sel
end
return { sel[1], l, sel[3], sel[4] }
end
function TextTransform.is_block_visual_mode()
return TextTransform.state.positions.mode == "block"
-- return vim.fn.mode() == "V" or vim.fn.mode() == "\22"
end
function TextTransform.is_visual_mode()
return TextTransform.state.positions.mode == "visual"
-- return vim.fn.mode() == 'v'
end
--- Save the current cursor position, mode, and visual selection ranges
function TextTransform.save_positions()
local buf = vim.api.nvim_get_current_buf()
local mode_info = vim.api.nvim_get_mode()
local mode = get_mode_type(mode_info.mode)
local pos = vim.fn.getcurpos()
-- leave mode, required to get the positions - they only register on mode leave
-- in case of visual mode
local esc = vim.api.nvim_replace_termcodes("<esc>", true, false, true)
vim.api.nvim_feedkeys(esc, "x", true)
local visual_start = vim.fn.getpos("'<")
local visual_end = vim.fn.getpos("'>")
D.log("save_positions", "Saved mode %s, cursor %s", mode, vim.inspect(pos))
if mode == "visual" or mode == "line" or mode == "block" then
if TextTransform.has_range(visual_start, visual_end) then -- for ranges
D.log(
"save_positions",
"Visual range, mode is %s, %s",
mode,
vim.inspect({ visual_start, visual_end })
)
-- Adjust the positions to correctly capture the entire block
visual_start = capture_part(visual_start, visual_end, "start")
visual_end = capture_part(visual_start, visual_end, "end")
end
D.log(
"state",
"Saved visual mode %s, cursor %s",
mode,
vim.inspect({ visual_start, visual_end })
)
end
local positions = {
buf = buf,
mode = mode,
pos = pos,
visual_start = visual_start,
visual_end = visual_end,
}
D.log("save_positions", "State: %s", vim.inspect(positions))
TextTransform.state.positions = positions
return positions
end
--- Restore the cursor position, mode, and visual selection ranges saved using `save_position()`,
--- or a given modified state, if passed as the first argument
function TextTransform.restore_positions(positions)
positions = positions or TextTransform.state.positions
vim.api.nvim_set_current_buf(positions.buf)
vim.fn.setpos(".", positions.pos)
D.log(
"restore_positions",
"Restored mode %s, cursor %s",
positions.mode,
vim.inspect(positions.pos)
)
-- Attempt to restore visual mode accurately
if
(positions.mode == "visual" or positions.mode == "block")
and positions.visual_start
and positions.visual_end
then
vim.fn.setpos("'<", positions.visual_start)
vim.fn.setpos("'>", positions.visual_end)
local command = "normal! gv"
vim.cmd(command)
D.log("restore_positions", [[Restored visual mode %s using "%s"]], positions.mode, command)
end
TextTransform.state.positions = {}
end
return TextTransform

View File

@@ -0,0 +1,151 @@
local D = require("text-transform.utils.debug")
-- local utils = require("text-transform.utils")
local TextTransform = {}
TextTransform.WORD_BOUNDRY = "[%_%-%s%.]"
--- Splits a string into words.
---@param string string
---@return table
function TextTransform.to_words(string)
local words = {}
local word = ""
local last_is_upper = false
local last_is_digit = false
for i = 1, #string do
local char = string:sub(i, i)
if char:match(TextTransform.WORD_BOUNDRY) then
if word ~= "" then
table.insert(words, word:lower())
end
word = ""
else
if
(char:match("%d") and not last_is_digit)
or (char:match("%u") and not last_is_upper and word ~= "")
or (char:match("%l") and last_is_digit)
then
if word ~= "" then
table.insert(words, word:lower())
word = ""
end
end
-- Update flags based on current character type
if char:match("%d") then
last_is_digit = true
last_is_upper = false
elseif char:match("%u") then
last_is_upper = true
last_is_digit = false
else -- Lowercase or any non-digit/non-uppercase
last_is_upper = false
last_is_digit = false
end
-- Append current character to the current word
word = word .. char
end
-- D.log("to_words", "i %d char %s word %s words %s", i, char, word, utils.dump(words))
end
if word ~= "" then
table.insert(words, word:lower())
end
D.log("to_words", "words %s", vim.inspect(words))
return words
end
--- Transforms a table of strings into a string using a callback and separator.
--- The callback is called with the word, the index, and the table of words.
--- The separator is added between each word.
---
---@param words string|table string or table of strings
---@param with_word_cb function (word: string, index: number, words: table) -> string
---@param separator string|nil (optional)
---@return string
function TextTransform.transform_words(words, with_word_cb, separator)
if type(words) ~= "table" then
words = TextTransform.to_words(words)
end
local out = ""
for i, word in ipairs(words) do
local new_word = with_word_cb(word, i, word)
if separator and i > 1 then
new_word = separator .. new_word
end
out = out .. new_word
D.log("transform_words", "word %s (%d) new_word %s out %s", word, i, new_word, out)
end
return out
end
--- Transforms a string into camelCase.
---@param string string
---@return string
function TextTransform.to_camel_case(string)
return TextTransform.transform_words(string, function(word, i)
if i == 1 then
return word:lower()
end
return word:sub(1, 1):upper() .. word:sub(2):lower()
end)
end
--- Transfroms a string into snake_case.
---@param string any
---@return string
function TextTransform.to_snake_case(string)
return TextTransform.transform_words(string, function(word, i)
if i == 1 then
return word:lower()
end
return word:lower()
end, "_")
end
--- Transforms a string into PascalCase.
---@param string string
---@return string
function TextTransform.to_pascal_case(string)
local cc = TextTransform.to_camel_case(string)
return cc:sub(1, 1):upper() .. cc:sub(2)
end
--- Transforms a string into Title Case.
---@param string string
---@return string
function TextTransform.to_title_case(string)
return TextTransform.transform_words(string, function(word)
return word:sub(1, 1):upper() .. word:sub(2):lower()
end, " ")
end
--- Transforms a string into kebab-case.
---@param string string
---@return string
function TextTransform.to_kebab_case(string)
return TextTransform.transform_words(string, function(word)
return word:lower()
end, "-")
end
--- Transforms a string into dot.case.
---@param string string
---@return string
function TextTransform.to_dot_case(string)
return TextTransform.transform_words(string, function(word)
return word:lower()
end, ".")
end
--- Transforms a string into CONSTANT_CASE.
---@param string string
---@return string
function TextTransform.to_const_case(string)
return TextTransform.transform_words(string, function(word)
return word:upper()
end, "_")
end
return TextTransform

View File

@@ -1,13 +1,13 @@
local D = {}
---prints only if debug is true.
---
---@param scope string: the scope from where this function is called.
---@param str string: the formatted string.
---@param ... any: the arguments of the formatted string.
---@private
local function is_debug()
return _G.TextTransform ~= nil
and _G.TextTransform.config ~= nil
and _G.TextTransform.config.debug
end
function D.log(scope, str, ...)
if _G.TextTransform.config ~= nil and not _G.TextTransform.config.debug then
if not is_debug() then
return
end
@@ -20,22 +20,17 @@ function D.log(scope, str, ...)
print(
string.format(
"[text-transform:%s %s in %s] > %s",
"%s [text-transform:%s in %s] > %s",
os.date("%H:%M:%S"),
line,
scope,
line,
string.format(str, ...)
)
)
end
---prints the table if debug is true.
---
---@param table table: the table to print.
---@param indent number?: the default indent value, starts at 0.
---@private
function D.tprint(table, indent)
if _G.TextTransform.config ~= nil and not _G.TextTransform.config.debug then
if not is_debug() then
return
end

View File

@@ -0,0 +1,19 @@
local TextTransform = {}
--- Merges two tables into one. Same as `vim.tbl_extend("keep", t1, t2)`.
--- Mutates the first table.
---
--- TODO accept multiple tables to merge
---
---@param t1 table
---@param t2 table
---@return table
function TextTransform.merge(t1, t2)
return vim.tbl_extend("force", t1, t2)
end
function TextTransform.has_range(visual_start, visual_end)
return visual_start and visual_end and visual_start[2] ~= visual_end[2]
end
return TextTransform

View File

@@ -4,185 +4,3 @@ if _G.TextTransformLoaded then
end
_G.TextTransformLoaded = true
function TextTransform.into_words(str)
local words = {}
local word = ""
local previous_is_upper = false
for i = 1, #str do
local char = str:sub(i, i)
-- split on uppercase letters
if char:match("%u") and not previous_is_upper then
if word ~= "" then
table.insert(words, word)
end
previous_is_upper = true
word = char
-- split on underscores, hyphens, and spaces
elseif char:match("[%_%-%s]") then
if word ~= "" then
table.insert(words, word)
previous_is_upper = false
end
word = ""
else
word = word .. char
previous_is_upper = char:match("%u")
end
end
if word ~= "" then
table.insert(words, word)
previous_is_upper = false
end
return words
end
function TextTransform.camel_case(string)
local words = TextTransform.into_words(string)
local camel_case = ""
for i, word in ipairs(words) do
if i == 1 then
camel_case = camel_case .. word:lower()
else
camel_case = camel_case .. word:sub(1, 1):upper() .. word:sub(2):lower()
end
end
return camel_case
end
function TextTransform.snake_case(string)
local words = TextTransform.into_words(string)
local snake_case = ""
for i, word in ipairs(words) do
if i == 1 then
snake_case = snake_case .. word:lower()
else
snake_case = snake_case .. "_" .. word:lower()
end
end
return snake_case
end
function TextTransform.pascal_case(string)
local words = TextTransform.into_words(string)
local pascal_case = ""
for _, word in ipairs(words) do
pascal_case = pascal_case .. word:sub(1, 1):upper() .. word:sub(2):lower()
end
return pascal_case
end
function TextTransform.kebab_case(string)
local words = TextTransform.into_words(string)
local kebab_case = ""
for i, word in ipairs(words) do
if i == 1 then
kebab_case = kebab_case .. word:lower()
else
kebab_case = kebab_case .. "-" .. word:lower()
end
end
return kebab_case
end
function TextTransform.dot_case(string)
local words = TextTransform.into_words(string)
local dot_case = ""
for i, word in ipairs(words) do
if i == 1 then
dot_case = dot_case .. word:lower()
else
dot_case = dot_case .. "." .. word:lower()
end
end
return dot_case
end
function TextTransform.title_case(string)
local words = TextTransform.into_words(string)
local title_case = ""
for i, word in ipairs(words) do
title_case = title_case .. word:sub(1, 1):upper() .. word:sub(2):lower()
if i ~= #words then
title_case = title_case .. " "
end
end
return title_case
end
function TextTransform.const_case(string)
local words = TextTransform.into_words(string)
local const_case = ""
for i, word in ipairs(words) do
if i == 1 then
const_case = const_case .. word:upper()
else
const_case = const_case .. "_" .. word:upper()
end
end
return const_case
end
function TextTransform.replace_selection(transform)
-- get the current visual selection, and transform the line, only replacing the selected text itself
local _, start_line, start_col = unpack(vim.fn.getpos("'<"))
local _, end_line, end_col = unpack(vim.fn.getpos("'>"))
-- print(vim.inspect(vim.fn.getpos("'<")), vim.inspect(vim.fn.getpos("'>")),
-- start_line, start_col, end_line, end_col)
local lines = vim.fn.getline(start_line, end_line)
-- print(vim.inspect(lines))
-- transform all included lines
local transformed = ""
if #lines == 1 then
transformed = lines[1]:sub(1, start_col - 1)
.. transform(lines[1]:sub(start_col, end_col))
.. lines[1]:sub(end_col + 1)
else
transformed = lines[1]:sub(1, start_col - 1) .. transform(lines[1]:sub(start_col)) .. "\n"
for i = 2, #lines - 1 do
transformed = transformed .. transform(lines[i]) .. "\n"
end
transformed = transformed
.. transform(lines[#lines]:sub(1, end_col))
.. lines[#lines]:sub(end_col + 1)
end
-- replace the lines with the transformed lines
vim.fn.setline(start_line, transformed)
for i = start_line + 1, end_line do
vim.fn.setline(i, "")
end
-- move the cursor to the end of the transformed text
vim.fn.cursor(end_line, end_col)
end
function TextTransform.replace_word(transform)
local word = vim.fn.expand("<cword>")
local transformed = transform(word)
vim.cmd("normal ciw" .. transformed)
end
local should_test = false
if should_test then
local map = {
["CamelCase"] = TextTransform.camel_case,
["SnakeCase"] = TextTransform.snake_case,
["PascalCase"] = TextTransform.pascal_case,
["KebabCase"] = TextTransform.kebab_case,
["DotCase"] = TextTransform.dot_case,
["TitleCase"] = TextTransform.title_case,
["ConstCase"] = TextTransform.title_case,
}
for k, tst in pairs(map) do
print(k .. ": " .. "hello_world" .. " => " .. tst("hello_world"))
print(k .. ": " .. "HELLO_WORLD" .. " => " .. tst("HELLO_WORLD"))
print(k .. ": " .. "HelloWorld" .. " => " .. tst("HelloWorld"))
print(k .. ": " .. "Hello-World" .. " => " .. tst("Hello-World"))
end
end

View File

@@ -6,6 +6,8 @@ if #vim.api.nvim_list_uis() == 0 then
-- Add 'mini.nvim' to 'runtimepath' to be able to use 'mini.test'
-- Assumed that 'mini.nvim' is stored in 'deps/mini.nvim'
vim.cmd("set rtp+=deps/mini.nvim")
vim.cmd("set rtp+=deps/telescope.nvim")
vim.cmd("set rtp+=deps/plenary.nvim")
-- Set up 'mini.test'
require("mini.test").setup()

26
scripts/precommit.sh Executable file
View File

@@ -0,0 +1,26 @@
#!/usr/bin/env bash
# shellcheck disable=SC2181,SC2086
# Stylua check
diffs=$(stylua --check --output-format=json .)
# Exit code != 0 means there are changes (files are not formatted)
if [[ "$?" -ne 0 ]]; then
# Get filenames of diffs
filelist="$(echo "$diffs" | jq -r '.file')"
# Format & add to git
stylua $filelist
git add $filelist
fi
# Run lints
make lint
# Run tests
# make test
# Generate docs & add to git
make documentation
git add doc

View File

@@ -1,79 +0,0 @@
#!/bin/bash
USAGE="\033[0;37m[INFO] - usage: USERNAME=my-github-username PLUGIN_NAME=my-awesome-plugin REPOSITORY_NAME=my-awesome-plugin.nvim make setup\n\033[0m"
echo -e "$USAGE"
if [[ -z "$USERNAME" ]]; then
echo -e "\t> No USERNAME provided, what's your GitHub/GitLab username?"
read -r USERNAME
fi
if [[ -z "$REPOSITORY_NAME" ]]; then
REPOSITORY_NAME=$(basename -s .git "$(git config --get remote.origin.url)")
read -rp $'\t> No REPOSITORY_NAME provided, is \033[1;32m'"$REPOSITORY_NAME"$'\033[0m good? [Y/n]\n' yn
case $yn in
[Yy]* );;
[Nn]* )
echo -e "\t> Enter your repository name"
read -r REPOSITORY_NAME
;;
* )
echo -e "$USAGE"
exit 1;;
esac
fi
if [[ -z "$PLUGIN_NAME" ]]; then
DEFAULT_REPOSITORY_NAME=$(echo "$REPOSITORY_NAME" | sed -e "s/\.nvim//")
read -rp $'\t> No PLUGIN_NAME provided, defaulting to \033[1;32m'"$DEFAULT_REPOSITORY_NAME"$'\033[0m, continue? [Y/n]\n' yn
case $yn in
[Yy]* )
PLUGIN_NAME=$DEFAULT_REPOSITORY_NAME
;;
[Nn]* )
echo -e "\t> Enter your plugin name"
read -r PLUGIN_NAME
;;
* )
echo -e "$USAGE"
exit 1;;
esac
fi
echo -e "Username: \033[1;32m$USERNAME\033[0m\nRepository: \033[1;32m$REPOSITORY_NAME\033[0m\nPlugin: \033[1;32m$PLUGIN_NAME\033[0m\n\n\tRenaming placeholder files..."
rm -rf doc
mv plugin/your-plugin-name.lua "plugin/$PLUGIN_NAME.lua"
mv lua/your-plugin-name "lua/$PLUGIN_NAME"
mv README_TEMPLATE.md README.md
echo -e "\tReplacing placeholder names..."
PASCAL_CASE_PLUGIN_NAME=$(echo "$PLUGIN_NAME" | perl -pe 's/(^|-)./uc($&)/ge;s/-//g')
grep -rl "YourPluginName" .github/ plugin/ tests/ lua/ | xargs sed -i "" -e "s/YourPluginName/$PASCAL_CASE_PLUGIN_NAME/g"
grep -rl "your-plugin-name" README.md .github/ plugin/ tests/ lua/ | xargs sed -i "" -e "s/your-plugin-name/$PLUGIN_NAME/g"
grep -rl "YOUR_GITHUB_USERNAME" README.md .github/ | xargs sed -i "" -e "s/YOUR_GITHUB_USERNAME/$USERNAME/g"
grep -rl "YOUR_REPOSITORY_NAME" README.md .github/ | xargs sed -i "" -e "s/YOUR_REPOSITORY_NAME/$REPOSITORY_NAME/g"
echo -e "\n\033[1;32mOK.\033[0m"
echo -e "\tFetching dependencies (tests and documentation generator)..."
make deps
echo -e "\n\033[1;32mOK.\033[0m"
echo -e "\tGenerating docs..."
make documentation
echo -e "\n\033[1;32mOK.\033[0m"
echo -e "\tRunning tests..."
make test
echo -e "\n\033[1;32mOK.\033[0m"

View File

@@ -1,3 +1,4 @@
local MiniTest = require("mini.test")
-- partially imported from https://github.com/echasnovski/mini.nvim
local Helpers = {}
@@ -162,4 +163,9 @@ Helpers.new_child_neovim = function()
return child
end
function Helpers.init_plugin(child, config)
config = config or ""
child.lua([[require('text-transform').setup(]] .. config .. [[)]])
end
return Helpers

View File

@@ -1,4 +1,5 @@
local helpers = dofile("tests/helpers.lua")
local MiniTest = require("mini.test")
-- See https://github.com/echasnovski/mini.nvim/blob/main/lua/mini/test.lua for more documentation
@@ -31,28 +32,47 @@ T["setup()"]["sets exposed methods and default options value"] = function()
-- global object that holds your plugin information
eq_type_global(child, "_G.TextTransform", "table")
-- public methods
eq_type_global(child, "_G.TextTransform.toggle", "function")
eq_type_global(child, "_G.TextTransform.disable", "function")
eq_type_global(child, "_G.TextTransform.enable", "function")
-- config
eq_type_global(child, "_G.TextTransform.config", "table")
-- assert the value, and the type
eq_config(child, "debug", false)
eq_type_config(child, "debug", "boolean")
eq_config(child, "debug", false)
eq_type_config(child, "keymap", "table")
eq_type_config(child, "keymap.telescope_popup", "table")
eq_type_config(child, "keymap.telescope_popup.v", "string")
eq_config(child, "keymap.telescope_popup.v", "<Leader>~")
eq_type_config(child, "keymap.telescope_popup.n", "string")
eq_config(child, "keymap.telescope_popup.n", "<Leader>~")
end
T["setup()"]["overrides default values"] = function()
child.lua([[require('text-transform').setup({
helpers.init_plugin(
child,
[[{
-- write all the options with a value different than the default ones
debug = true,
})]])
keymap = {
["v"] = "<leader>c",
["n"] = "<leader>c",
},
}]]
)
-- assert the value, and the type
eq_config(child, "debug", true)
eq_type_config(child, "debug", "boolean")
eq_config(child, "debug", true)
eq_type_config(child, "keymap", "table")
eq_type_config(child, "keymap.v", "string")
eq_config(child, "keymap.v", "<leader>c")
eq_type_config(child, "keymap.n", "string")
eq_config(child, "keymap.n", "<leader>c")
end
return T

63
tests/test_popups.lua Normal file
View File

@@ -0,0 +1,63 @@
local helpers = dofile("tests/helpers.lua")
local MiniTest = require("mini.test")
-- See https://github.com/echasnovski/mini.nvim/blob/main/lua/mini/test.lua for more documentation
local child = helpers.new_child_neovim()
local eq_global, eq_config, eq_state =
helpers.expect.global_equality, helpers.expect.config_equality, helpers.expect.state_equality
local eq_type_global, eq_type_config, eq_type_state =
helpers.expect.global_type_equality,
helpers.expect.config_type_equality,
helpers.expect.state_type_equality
local T = MiniTest.new_set({
hooks = {
-- This will be executed before every (even nested) case
pre_case = function()
-- Restart child process with custom 'init.lua' script
child.restart({ "-u", "scripts/minimal_init.lua" })
end,
-- This will be executed one after all tests from this set are finished
post_once = child.stop,
},
})
-- Tests related to the `setup` method.
T["popups"] = MiniTest.new_set()
T["popups"]["exposes show_popup"] = function()
helpers.init_plugin(child)
eq_type_global(child, "_G.TextTransform", "table")
eq_type_global(child, "_G.TextTransform.show_popup", "function")
eq_type_global(child, "_G.TextTransform.telescope_popup", "nil")
end
T["popups"]["telescope exposes telescope_popup"] = function()
helpers.init_plugin(child)
eq_type_global(child, "_G.TextTransform", "table")
eq_type_global(child, "_G.TextTransform.telescope_popup", "nil")
child.lua([[Telescope = require('text-transform.popup.telescope')]])
eq_type_global(child, "Telescope.telescope_popup", "function")
end
T["popups"]["select exposes select_popup"] = function()
helpers.init_plugin(child)
eq_type_global(child, "_G.TextTransform", "table")
eq_type_global(child, "_G.TextTransform.select_popup", "nil")
child.lua([[Select = require('text-transform.popup.select')]])
eq_type_global(child, "Select.select_popup", "function")
end
return T

69
tests/test_to_words.lua Normal file
View File

@@ -0,0 +1,69 @@
local helpers = dofile("tests/helpers.lua")
local MiniTest = require("mini.test")
-- See https://github.com/echasnovski/mini.nvim/blob/main/lua/mini/test.lua for more documentation
local child = helpers.new_child_neovim()
local eq_global, eq_config, eq_state =
helpers.expect.global_equality, helpers.expect.config_equality, helpers.expect.state_equality
local eq_type_global, eq_type_config, eq_type_state =
helpers.expect.global_type_equality,
helpers.expect.config_type_equality,
helpers.expect.state_type_equality
local T = MiniTest.new_set({
hooks = {
-- This will be executed before every (even nested) case
pre_case = function()
-- Restart child process with custom 'init.lua' script
child.restart({ "-u", "scripts/minimal_init.lua" })
end,
-- This will be executed one after all tests from this set are finished
post_once = child.stop,
},
})
local function test_string(child, str)
helpers.init_plugin(child)
child.lua([[result = require('text-transform').to_words("]] .. str .. [[")]])
end
T["to_words()"] = MiniTest.new_set()
T["to_words()"]["should split normal spaced words"] = function()
test_string(child, "hello world")
eq_type_global(child, "result", "table")
eq_global(child, "result", { "hello", "world" })
end
T["to_words()"]["should split camel case strings"] = function()
test_string(child, "helloWorld")
eq_type_global(child, "result", "table")
eq_global(child, "result", { "hello", "world" })
end
T["to_words()"]["should split dot case strings"] = function()
test_string(child, "hello.world")
eq_type_global(child, "result", "table")
eq_global(child, "result", { "hello", "world" })
end
T["to_words()"]["should split const case strings"] = function()
test_string(child, "HELLO_WORLD")
eq_type_global(child, "result", "table")
eq_global(child, "result", { "hello", "world" })
end
T["to_words()"]["should treat numbers as words"] = function()
test_string(child, "helloWorld123")
eq_type_global(child, "result", "table")
eq_global(child, "result", { "hello", "world", "123" })
end
T["to_words()"]["should trim trailing/leading"] = function()
test_string(child, " hello world ")
eq_type_global(child, "result", "table")
eq_global(child, "result", { "hello", "world" })
end
return T

107
tests/test_transforms.lua Normal file
View File

@@ -0,0 +1,107 @@
local helpers = dofile("tests/helpers.lua")
-- See https://github.com/echasnovski/mini.nvim/blob/main/lua/mini/test.lua for more documentation
local child = helpers.new_child_neovim()
local eq_global, eq_config, eq_state =
helpers.expect.global_equality, helpers.expect.config_equality, helpers.expect.state_equality
local eq_type_global, eq_type_config, eq_type_state =
helpers.expect.global_type_equality,
helpers.expect.config_type_equality,
helpers.expect.state_type_equality
local T = MiniTest.new_set({
hooks = {
-- This will be executed before every (even nested) case
pre_case = function()
-- Restart child process with custom 'init.lua' script
child.restart({ "-u", "scripts/minimal_init.lua" })
end,
-- This will be executed one after all tests from this set are finished
post_once = child.stop,
},
})
local function make_transform_test(fn_name, input, expected)
return function()
child.lua([[require('text-transform').setup()]])
child.lua([[result = require('text-transform').to_]] .. fn_name .. '("' .. input .. '")')
eq_global(child, "result", expected)
end
end
local map = {
["camel_case"] = {
{ "hello_world", "helloWorld" },
{ "hello world", "helloWorld" },
{ "hello-world", "helloWorld" },
{ "hello.world", "helloWorld" },
{ "hello", "hello" },
{ "helloWorld123", "helloWorld123" },
{ "HELLO_WORLD", "helloWorld" },
},
["snake_case"] = {
{ "helloWorld", "hello_world" },
{ "hello world", "hello_world" },
{ "hello-world", "hello_world" },
{ "hello.world", "hello_world" },
{ "hello", "hello" },
{ "helloWorld123", "hello_world_123" },
{ "HELLO_WORLD", "hello_world" },
},
["pascal_case"] = {
{ "hello_world", "HelloWorld" },
{ "hello world", "HelloWorld" },
{ "hello-world", "HelloWorld" },
{ "hello.world", "HelloWorld" },
{ "hello", "Hello" },
{ "helloWorld123", "HelloWorld123" },
{ "HELLO_WORLD", "HelloWorld" },
},
["kebab_case"] = {
{ "helloWorld", "hello-world" },
{ "hello world", "hello-world" },
{ "hello-world", "hello-world" },
{ "hello.world", "hello-world" },
{ "hello", "hello" },
{ "helloWorld123", "hello-world-123" },
{ "HELLO_WORLD", "hello-world" },
},
["dot_case"] = {
{ "helloWorld", "hello.world" },
{ "hello world", "hello.world" },
{ "hello-world", "hello.world" },
{ "hello.world", "hello.world" },
{ "hello", "hello" },
{ "helloWorld123", "hello.world.123" },
{ "HELLO_WORLD", "hello.world" },
},
["const_case"] = {
{ "helloWorld", "HELLO_WORLD" },
{ "hello world", "HELLO_WORLD" },
{ "hello-world", "HELLO_WORLD" },
{ "hello.world", "HELLO_WORLD" },
{ "hello", "HELLO" },
{ "helloWorld123", "HELLO_WORLD_123" },
{ "HELLO_WORLD", "HELLO_WORLD" },
},
["title_case"] = {
{ "helloWorld", "Hello World" },
{ "hello world", "Hello World" },
{ "hello-world", "Hello World" },
{ "hello.world", "Hello World" },
{ "hello", "Hello" },
{ "helloWorld123", "Hello World 123" },
{ "HELLO_WORLD", "Hello World" },
},
}
for fn_name, cases in pairs(map) do
T[fn_name .. "()"] = MiniTest.new_set()
for _, case in ipairs(cases) do
local input, output = unpack(case)
T[fn_name .. "()"]["input: " .. input] = make_transform_test(fn_name, input, output)
end
end
return T