├── .github └── ISSUE_TEMPLATE │ └── bug_report.md ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── bin └── vale-ai │ ├── .vale_default.ini │ ├── vale │ ├── vale-linux │ ├── vale-macos │ └── vale.exe ├── composer.json ├── composer.lock ├── config └── config.php ├── docs ├── _index.md ├── customization │ ├── _index.md │ ├── configure-style-ruleset.md │ └── publishing-and-configuring-styles.md ├── getting-started │ ├── _index.md │ └── installation.md └── usage │ ├── _index.md │ ├── evaluating-the-results.md │ └── linting.md ├── resources └── styles │ ├── vale │ ├── Annotations.yml │ ├── Editorializing.yml │ ├── Hedging.yml │ ├── Litotes.yml │ ├── Redundancy.yml │ └── Uncomparables.yml │ └── write-good │ ├── Cliches.yml │ ├── E-Prime.yml │ ├── Illusions.yml │ ├── Passive.yml │ ├── README.md │ ├── So.yml │ ├── ThereIs.yml │ ├── TooWordy.yml │ ├── Weasel.yml │ └── meta.json └── src ├── Console └── Commands │ ├── LintTranslationCommand.php │ ├── LintViewCommand.php │ └── LinterCommand.php ├── Exceptions └── LinterException.php ├── LaravelProseLinterFacade.php ├── LaravelProseLinterServiceProvider.php ├── Linter ├── LintingHint.php ├── LintingResult.php ├── TranslationLinter.php ├── Vale.php └── ViewLinter.php └── Styles ├── StyleInterface.php ├── Vale.php └── WriteGood.php /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help improve the package 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **Executed command** 14 | - Which artisan command was executed? Please provide all parameters and options. 15 | 16 | **CLI output** 17 | Paste the CLI output here. If possible, try to execute the command again with the `--verbose` flag. 18 | 19 | **Expected behavior** 20 | A clear and concise description of what you expected to happen. 21 | 22 | **Screenshots and further data** 23 | If applicable, add screenshots to help explain your problem. A short summary of your directory structure (views or translations) or, if applicable, the specific translation file or template that failed, is helpful too. 24 | 25 | **System** 26 | - OS or Server Version 27 | - PHP Version 28 | - Laravel Version 29 | - Package version 30 | 31 | **Additional context** 32 | Add any other context about the problem here. 33 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to `laravel-prose-linter` will be documented in this file 4 | 5 | ## 1.0.0 - 201X-XX-XX 6 | 7 | - initial release 8 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Contributions are **welcome** and will be fully **credited**. 4 | 5 | Please read and understand the contribution guide before creating an issue or pull request. 6 | 7 | ## Etiquette 8 | 9 | This project is open source, and as such, the maintainers give their free time to build and maintain the source code 10 | held within. They make the code freely available in the hope that it will be of use to other developers. It would be 11 | extremely unfair for them to suffer abuse or anger for their hard work. 12 | 13 | Please be considerate towards maintainers when raising issues or presenting pull requests. Let's show the 14 | world that developers are civilized and selfless people. 15 | 16 | It's the duty of the maintainer to ensure that all submissions to the project are of sufficient 17 | quality to benefit the project. Many developers have different skillsets, strengths, and weaknesses. Respect the maintainer's decision, and do not be upset or abusive if your submission is not used. 18 | 19 | ## Viability 20 | 21 | When requesting or submitting new features, first consider whether it might be useful to others. Open 22 | source projects are used by many developers, who may have entirely different needs to your own. Think about 23 | whether or not your feature is likely to be used by other users of the project. 24 | 25 | ## Procedure 26 | 27 | Before filing an issue: 28 | 29 | - Attempt to replicate the problem, to ensure that it wasn't a coincidental incident. 30 | - Check to make sure your feature suggestion isn't already present within the project. 31 | - Check the pull requests tab to ensure that the bug doesn't have a fix in progress. 32 | - Check the pull requests tab to ensure that the feature isn't already in progress. 33 | 34 | Before submitting a pull request: 35 | 36 | - Check the codebase to ensure that your feature doesn't already exist. 37 | - Check the pull requests to ensure that another person hasn't already submitted the feature or fix. 38 | 39 | ## Requirements 40 | 41 | If the project maintainer has any additional requirements, you will find them listed here. 42 | 43 | - **[PSR-2 Coding Standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)** - The easiest way to apply the conventions is to install [PHP Code Sniffer](https://pear.php.net/package/PHP_CodeSniffer). 44 | 45 | - **Add tests!** - Your patch won't be accepted if it doesn't have tests. 46 | 47 | - **Document any change in behaviour** - Make sure the `README.md` and any other relevant documentation are kept up-to-date. 48 | 49 | - **Consider our release cycle** - We try to follow [SemVer v2.0.0](https://semver.org/). Randomly breaking public APIs is not an option. 50 | 51 | - **One pull request per feature** - If you want to do more than one thing, send multiple pull requests. 52 | 53 | - **Send coherent history** - Make sure each individual commit in your pull request is meaningful. If you had to make multiple intermediate commits while developing, please [squash them](https://www.git-scm.com/book/en/v2/Git-Tools-Rewriting-History#Changing-Multiple-Commit-Messages) before submitting. 54 | 55 | **Happy coding**! 56 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Diana Scharf 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. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![](https://beyondco.de/img/docs/laravel-prose-linter/laravel-prose-linter.png) 2 | 3 | # Laravel Prose Linter 4 | 5 | [![Latest Version on Packagist](https://img.shields.io/packagist/v/beyondcode/laravel-prose-linter.svg?style=flat-square)](https://packagist.org/packages/beyondcode/laravel-prose-linter) 6 | [![Total Downloads](https://img.shields.io/packagist/dt/beyondcode/laravel-prose-linter.svg?style=flat-square)](https://packagist.org/packages/beyondcode/laravel-prose-linter) 7 | 8 | 9 | Syntax-aware proofreading for your Laravel application. 10 | 11 | The Laravel Prose Linter helps you to polish the texts of your Laravel application. Let it check your translations and even your blade templates for typos, slang and get suggestions for a better writing style depending on which prose style you choose. 12 | 13 | ## Documentation 14 | 15 | For installation instructions and usage and examples, please take a look at the [official documentation](https://beyondco.de/docs/laravel-prose-linter/). 16 | 17 | ### System Requirements 18 | This package requires PHP 8.0 or higher and Laravel 8 or higher. 19 | 20 | ### Security 21 | 22 | If you discover any security related issues, please email support@beyondco.de instead of using the issue tracker. 23 | 24 | ## Credits 25 | 26 | - [Diana Scharf](https://github.com/mechelon) 27 | - [Marcel Pociot](https://github.com/mpociot) 28 | - [All Contributors](../../contributors) 29 | 30 | ## License 31 | 32 | The MIT License (MIT). Please see [License File](LICENSE.md) for more information. 33 | -------------------------------------------------------------------------------- /bin/vale-ai/.vale_default.ini: -------------------------------------------------------------------------------- 1 | 2 | StylesPath = styles 3 | 4 | [*.{md,html}] 5 | BasedOnStyles = write-good 6 | -------------------------------------------------------------------------------- /bin/vale-ai/vale: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beyondcode/laravel-prose-linter/5b14a8436fde8b2d56ddb181577c5098595e2c5c/bin/vale-ai/vale -------------------------------------------------------------------------------- /bin/vale-ai/vale-linux: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beyondcode/laravel-prose-linter/5b14a8436fde8b2d56ddb181577c5098595e2c5c/bin/vale-ai/vale-linux -------------------------------------------------------------------------------- /bin/vale-ai/vale-macos: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beyondcode/laravel-prose-linter/5b14a8436fde8b2d56ddb181577c5098595e2c5c/bin/vale-ai/vale-macos -------------------------------------------------------------------------------- /bin/vale-ai/vale.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beyondcode/laravel-prose-linter/5b14a8436fde8b2d56ddb181577c5098595e2c5c/bin/vale-ai/vale.exe -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "beyondcode/laravel-prose-linter", 3 | "description": "Syntax-aware proofreading for your Laravel application.", 4 | "keywords": [ 5 | "beyondcode", 6 | "laravel", 7 | "vale", 8 | "linter", 9 | "prose linting", 10 | "laravel-prose-linter" 11 | ], 12 | "homepage": "https://beyondco.de/docs/laravel-prose-linter/", 13 | "license": "MIT", 14 | "type": "library", 15 | "authors": [ 16 | { 17 | "name": "Diana Scharf", 18 | "email": "diana@beyondco.de", 19 | "role": "Developer" 20 | } 21 | ], 22 | "require": { 23 | "php": "^8.0", 24 | "illuminate/support": "^8.0|^9.0|^10.0", 25 | "symfony/finder": "^4.0|^5.0|^6.0" 26 | }, 27 | "require-dev": { 28 | "orchestra/testbench": "^6.0|^7.0|^8.0", 29 | "phpunit/phpunit": "^9.0|^10.0" 30 | }, 31 | "autoload": { 32 | "psr-4": { 33 | "Beyondcode\\LaravelProseLinter\\": "src" 34 | } 35 | }, 36 | "autoload-dev": { 37 | "psr-4": { 38 | "Beyondcode\\LaravelProseLinter\\Tests\\": "tests" 39 | } 40 | }, 41 | "scripts": { 42 | "test": "vendor/bin/phpunit", 43 | "test-coverage": "vendor/bin/phpunit --coverage-html coverage" 44 | 45 | }, 46 | "config": { 47 | "sort-packages": true 48 | }, 49 | "extra": { 50 | "laravel": { 51 | "providers": [ 52 | "Beyondcode\\LaravelProseLinter\\LaravelProseLinterServiceProvider" 53 | ] 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /config/config.php: -------------------------------------------------------------------------------- 1 | [ 8 | \Beyondcode\LaravelProseLinter\Styles\WriteGood::class, 9 | \Beyondcode\LaravelProseLinter\Styles\Vale::class, 10 | ], 11 | 12 | ]; 13 | -------------------------------------------------------------------------------- /docs/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | packageName: Laravel Prose Linter 3 | githubUrl: https://github.com/beyondcode/laravel-prose-linter 4 | --- 5 | # Laravel Prose Linter 6 | 7 | AI-based proofreading for your Laravel application. 8 | 9 | The Laravel Prose Linter helps you to polish the texts of your Laravel application. Let it check your translations and even your blade templates for typos, slang and get suggestions for a better writing style depending on which prose style you choose. 10 | 11 | We recommend to take a quick glimpse at the [errata-ai/vale](https://docs.errata.ai/vale/about) package to learn what prose linting is all about. -------------------------------------------------------------------------------- /docs/customization/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Customization 3 | order: 3 4 | --- -------------------------------------------------------------------------------- /docs/customization/configure-style-ruleset.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Configure style ruleset 3 | order: 1 4 | --- 5 | 6 | # Customization 7 | 8 | ## Configure style ruleset 9 | 10 | A set of linting rules is called a *style*. The Laravel Prose Linter comes pre-defined with two styles: WriteGood and the default Vale style. 11 | 12 | You can customize the styles by publishing the package config as described in [Installation](docs/laravel-prose-linter/getting-started/installation) and have a look at the `config/linter.php` file: 13 | 14 | ```php 15 | /* 16 | * Customize the Vale styles used by the linter. 17 | */ 18 | return [ 19 | 'styles' => [ 20 | \Beyondcode\LaravelProseLinter\Styles\WriteGood::class, 21 | \Beyondcode\LaravelProseLinter\Styles\Vale::class 22 | ] 23 | 24 | ]; 25 | ``` 26 | 27 | There has to be at least one style applied for the linter to work. When you use multiple styles, please consider that some of the linting rules can conflict with each other. -------------------------------------------------------------------------------- /docs/customization/publishing-and-configuring-styles.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Publishing and configuring styles 3 | order: 2 4 | --- 5 | 6 | # Publishing and configuring styles 7 | 8 | Let's assume you want to use a style guide that implements the »Google Developer Documentation Style Guide« because you have documentation text in some blade templates. 9 | 10 | First you've got to publish the linting styles as well as the linting configuration like explained in [Installation](docs/laravel-prose-linter/getting-started/installation). To integrate this style into your application, copy the `Google` directory of the [styles' repository](https://github.com/errata-ai/Google) to `resources/lang/vendor/laravel-prose-linter`. 11 | 12 | Next up, create a class for your style in your Laravel app that implements the `Beyondcode\LaravelProseLinter\Styles\StyleInterface`: 13 | 14 | ```php 15 | namespace App\Library\LaravelProseLinter; 16 | 17 | use Beyondcode\LaravelProseLinter\Styles\StyleInterface; 18 | 19 | class GoogleDeveloperDocumentationStyle implements StyleInterface 20 | { 21 | 22 | public static function getStyleDirectoryName(): string 23 | { 24 | return 'Google'; 25 | } 26 | 27 | } 28 | ``` 29 | 30 | The interface implements the method `getStyleDirectoryName()` that has to return the name of the directory which contains the styles' linting rules, in this case it's `Google`. 31 | 32 | To include the style in your linting, add it to the `styles` array in the `config/linter.php` file: 33 | 34 | ```php 35 | /* 36 | * Customize the Vale styles used by the linter. 37 | */ 38 | return [ 39 | 'styles' => [ 40 | App\Library\LaravelProseLinter\GoogleDeveloperDocumentationStyle::class 41 | ] 42 | 43 | ]; 44 | ``` 45 | 46 | Lint a blade template or translation of your choice to see the results of this style: 47 | 48 | ```bash 49 | ~ php artisan lint:blade docs 50 | Linting single blade template with key 'docs'. 51 | 🗣 Start linting ... 52 | 1/1 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100% 53 | +---------+------+----------+------------------------------------------------------------------------------------------------+----------+---------------------------+ 54 | | Key | Line | Position | Message | Severity | Condition | 55 | +---------+------+----------+------------------------------------------------------------------------------------------------+----------+---------------------------+ 56 | | preview | 11 | 41 | 'Package Development' should use sentence-style capitalization. | warning | Google.Headings | 57 | | preview | 75 | 141 | In general, don't use an ellipsis. | warning | Google.Ellipses | 58 | | preview | 78 | 144 | Don't use exclamation points in text. | error | Google.Exclamation | 59 | [...] 60 | | preview | 282 | 164 | Don't put a period at the end of a heading. | warning | Google.HeadingPunctuation | 61 | +---------+------+----------+------------------------------------------------------------------------------------------------+----------+---------------------------+ 62 | 45 linting hints were found. 63 | Applied styles: GoogleDeveloperDocumentationStyle 64 | 🏁 Finished linting in 0.22 seconds. 65 | ``` -------------------------------------------------------------------------------- /docs/getting-started/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Getting started 3 | order: 1 4 | --- -------------------------------------------------------------------------------- /docs/getting-started/installation.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Installation 3 | order: 1 4 | --- 5 | 6 | Syntax-aware proofreading for your Laravel application. 7 | 8 | The Laravel Prose Linter helps you to polish the texts of your Laravel application. Let it check your translations and even your blade templates for typos, slang and get suggestions for a better writing style depending on which prose style you choose. 9 | 10 | We recommend to take a quick glimpse at the [errata-ai/vale](https://docs.errata.ai/vale/about) package to learn what prose linting is all about. 11 | 12 | # Installation 13 | 14 | ## System Requirements 15 | This package requires PHP 8.0 or higher. 16 | 17 | You can install the package via composer: 18 | 19 | ```bash 20 | ~ composer require beyondcode/laravel-prose-linter 21 | ``` 22 | 23 | If you want to customize the styles used by the linter (see **here**), publish the config and the style assets: 24 | 25 | ```bash 26 | ~ php artisan vendor:publish --tag=linting-config 27 | ~ php artisan vendor:publish --tag=linting-styles 28 | ``` 29 | 30 | 31 | With that, you're ready to lint! 32 | ```bash 33 | ~ php artisan lint:translation auth validation 34 | 🗣 Start linting ... 35 | 2/2 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100% 36 | +---------------------------------+------+----------+--------------------------------------------------------------------+----------+---------------------+ 37 | | Key. | Line | Position | Message | Severity | Condition | 38 | +---------------------------------+------+----------+--------------------------------------------------------------------+----------+---------------------+ 39 | | auth.throttle | 1 | 5 | 'many' is a weasel word! | warning | write-good.Weasel | 40 | | validation.accepted | 1 | 21 | 'be accepted' may be passive voice. Use active voice if you can. | warning | write-good.Passive | 41 | [...] 42 | +---------------------------------+------+----------+--------------------------------------------------------------------+----------+---------------------+ 43 | 17 linting hints were found. 44 | Applied styles: WriteGood, Vale 45 | 🏁 Finished linting in 8 seconds. 46 | ``` 47 | -------------------------------------------------------------------------------- /docs/usage/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Usage 3 | order: 2 4 | --- -------------------------------------------------------------------------------- /docs/usage/evaluating-the-results.md: -------------------------------------------------------------------------------- 1 | # Evaluating the results 2 | 3 | ## CLI 4 | 5 | The linter outputs every hint with a minimum level of ``suggestion``. 6 | 7 | The linting results of blade templates and translations are both printed as a table in the CLI and may look like this: 8 | 9 | ```bash 10 | ~ php artisan lint:translation auth validation 11 | 🗣 Start linting ... 12 | 2/2 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100% 13 | +---------------------------------+------+----------+--------------------------------------------------------------------+----------+---------------------+ 14 | | Key. | Line | Position | Message | Severity | Condition | 15 | +---------------------------------+------+----------+--------------------------------------------------------------------+----------+---------------------+ 16 | | auth.throttle | 1 | 5 | 'many' is a weasel word! | warning | write-good.Weasel | 17 | | validation.accepted | 1 | 21 | 'be accepted' may be passive voice. Use active voice if you can. | warning | write-good.Passive | 18 | [...] 19 | +---------------------------------+------+----------+--------------------------------------------------------------------+----------+---------------------+ 20 | 17 linting hints were found. 21 | Applied styles: WriteGood, Vale 22 | 🏁 Finished linting in 8 seconds. 23 | ``` 24 | 25 | The result gives you a short overview which translations or blade templates were linted, how long linting took and which styles were applied. The table lists all linting hints with translation or template key and a position to find the respective sentence or word in the text. 26 | 27 | ## JSON 28 | 29 | If you provide the `--json` flag in the command as stated above, the results file can be found in your applications `storage` folder. It will look a bit like this: 30 | 31 | ```bash 32 | [ 33 | [ 34 | "auth.throttle", 35 | 1, 36 | 5, 37 | "'many' is a weasel word!", 38 | "warning", 39 | "write-good.Weasel" 40 | ] 41 | ] 42 | ``` 43 | 44 | The order of the array elements is the same as in the CLI output: Translation or template key, line, position, linting hint message, severity and the condition of the library that produced the hint. -------------------------------------------------------------------------------- /docs/usage/linting.md: -------------------------------------------------------------------------------- 1 | # Usage 2 | 3 | The package includes a default configuration of the linter based on the Vale and the WriteGood styles, so that you can start linting right away! 4 | 5 | The linter supports two different modes: translation linting and blade template linting. 6 | 7 | ## Linting 8 | 9 | ### Blade templates 10 | 11 | To lint all blade templates of your project, call the following command from your project root: 12 | 13 | ```bash 14 | ~ php artisan lint:blade 15 | ``` 16 | 17 | Linting all templates of your app can take a while, so it will ask you for confirmation before it starts. 18 | 19 | As an alternative, you can lint either a single blade template by providing the template key just like you would inside laravel: 20 | 21 | ```bash 22 | ~ php artisan lint:blade auth.login 23 | ``` 24 | 25 | Or you exclude one or several view directories by using the `--exclude` option: 26 | 27 | ```bash 28 | ~ php artisan lint:blade --exclude=auth,vendor 29 | ``` 30 | 31 | ### Translations 32 | 33 | If you want to lint all translations of your application, open up your CLI in the Laravel project root and execute the following command: 34 | 35 | ```bash 36 | ~ php artisan lint:translation 37 | ``` 38 | 39 | This can be quite time-consuming for big applications, so if you want to restrict the linting to a certain namespace*, provide it as a parameter: 40 | 41 | ```bash 42 | ~ php artisan lint:translation auth 43 | ``` 44 | 45 | If you want to lint the translations of multiple namespaces, add them as further parameters: 46 | 47 | ```bash 48 | ~ php artisan lint:translation auth passwords pagination 49 | ``` 50 | 51 | * A translation namespace in Laravel is the name file of the file before the file extension where the translation array is returned. For example: The translations of the `passwords` namespace are located in `resources/lang/en/passwords.php` . 52 | 53 | ### Output 54 | 55 | Instead of evaluating the results in the CLI as a table you for further processing or storing by appending a `--json` flag to the command: 56 | 57 | ```bash 58 | ~ php artisan lint:translation --json 59 | ``` 60 | 61 | The result file will be saved in the `storage` folder of your application. -------------------------------------------------------------------------------- /resources/styles/vale/Annotations.yml: -------------------------------------------------------------------------------- 1 | message: "'%s' left in text" 2 | extends: existence 3 | ignorecase: false 4 | level: suggestion 5 | tokens: 6 | - XXX 7 | - FIXME 8 | - TODO 9 | - NOTE 10 | -------------------------------------------------------------------------------- /resources/styles/vale/Editorializing.yml: -------------------------------------------------------------------------------- 1 | extends: existence 2 | message: "Consider removing '%s'" 3 | ignorecase: true 4 | level: warning 5 | tokens: 6 | - actually 7 | - aptly 8 | - are a number 9 | - basically 10 | - clearly 11 | - completely 12 | - essentially 13 | - exceedingly 14 | - excellent 15 | - extremely 16 | - fairly 17 | - fortunately 18 | - huge 19 | - interestingly 20 | - is a number 21 | - it is important to realize 22 | - it should be noted that 23 | - largely 24 | - mostly 25 | - notably 26 | - note that 27 | - obviously 28 | - of course 29 | - quite 30 | - relatively 31 | - remarkably 32 | - several 33 | - significantly 34 | - substantially 35 | - surprisingly 36 | - tiny 37 | - totally 38 | - tragically 39 | - unfortunately 40 | - untimely 41 | - various 42 | - vast 43 | - very 44 | - without a doubt 45 | -------------------------------------------------------------------------------- /resources/styles/vale/Hedging.yml: -------------------------------------------------------------------------------- 1 | extends: existence 2 | message: "Consider removing '%s'" 3 | ignorecase: true 4 | level: warning 5 | tokens: 6 | - appear to be 7 | - arguably 8 | - as far as I know 9 | - could 10 | - from my point of view 11 | - hopefully 12 | - I believe 13 | - I doubt 14 | - I think 15 | - in general 16 | - In my opinion 17 | - likely 18 | - might 19 | - more or less 20 | - perhaps 21 | - possibly 22 | - presumably 23 | - probably 24 | - seem(s)? 25 | - somewhat 26 | - to my knowledge 27 | - unlikely 28 | - usually 29 | -------------------------------------------------------------------------------- /resources/styles/vale/Litotes.yml: -------------------------------------------------------------------------------- 1 | extends: substitution 2 | message: Consider using '%s' instead of '%s' 3 | ignorecase: true 4 | level: warning 5 | swap: 6 | no minor: major 7 | no ordinary: extraordinary 8 | no(?:t)? small: large 9 | not (?:a)? bad: good 10 | not accept(?:ed)?: rejected 11 | not allow(?:ed)?: prevented 12 | not certain: uncertain 13 | not consider(?:ed)?: ignored 14 | not fail: passed 15 | not have: lack(s) 16 | not many: few 17 | not old enough: too young 18 | not remember: forgot 19 | not succeed: failed 20 | not the brightest: dumb 21 | not the same: different 22 | not unaware: aware 23 | not unclean: clean 24 | not unfamiliar: familiar 25 | not unlike: like 26 | not unpleasant: pleasant 27 | not useless: useful 28 | not wrong: right 29 | -------------------------------------------------------------------------------- /resources/styles/vale/Redundancy.yml: -------------------------------------------------------------------------------- 1 | extends: existence 2 | message: "'%s' is redundant" 3 | ignorecase: true 4 | level: error 5 | tokens: 6 | - (?:added|extra) bonus 7 | - (?:advance|prior) notice 8 | - (?:gather|assemble|attach|blend|mix|combine|collaborate|connect|cooperate) together 9 | - (?:terrible|horrible) tragedy 10 | - \d{1,2} ?a\.?m\.? in the morning 11 | - \d{1,2} ?p\.?m\.? at night 12 | - \d{1,2} ?p\.?m\.? in the afternoon 13 | - \d{1,2} ?p\.?m\.? in the evening 14 | - ABM missile 15 | - ABM missiles 16 | - ABS braking system 17 | - absolutely (?:necessary|essential) 18 | - ACT test 19 | - already existing 20 | - armed gunman 21 | - artificial prosthesis 22 | - ATM machine 23 | - bald-headed 24 | - brief (?:moment|summary) 25 | - CD disc 26 | - cease and desist 27 | - circle around 28 | - close proximity 29 | - closed fist 30 | - completely (?:destroyed|eliminate|engulfed|filled|surround) 31 | - could possibly 32 | - CPI Index 33 | - current trend(s)? 34 | - end result 35 | - enter in 36 | - erode away 37 | - false pretense 38 | - final result 39 | - free gift 40 | - GPS system 41 | - GUI interface 42 | - HIV virus 43 | - inner feelings 44 | - ISBN number 45 | - join together 46 | - LCD display 47 | - local resident(s)? 48 | - natural instinct 49 | - new invention 50 | - null and void 51 | - past history 52 | - PDF format 53 | - PIN number 54 | - please RSVP 55 | - RAM memory 56 | - RAS syndrome 57 | - RIP in peace 58 | - safe haven 59 | - SALT talks 60 | - SAT test 61 | - shout loudly 62 | - surrounded on all sides 63 | - undergraduate student 64 | - unexpected surprise 65 | - unintended mistake 66 | - UPC code(s)? 67 | - violent explosion 68 | -------------------------------------------------------------------------------- /resources/styles/vale/Uncomparables.yml: -------------------------------------------------------------------------------- 1 | extends: existence 2 | message: "'%s' is not comparable" 3 | ignorecase: true 4 | level: error 5 | raw: 6 | - \b(?:absolutely|most|more|less|least|very|quite|largely|extremely|increasingly|kind of|mildy|hardly|greatly|sort of)\b\s* 7 | tokens: 8 | - absolute 9 | - adequate 10 | - complete 11 | - correct 12 | - certain 13 | - devoid 14 | - entire 15 | - 'false' 16 | - fatal 17 | - favorite 18 | - final 19 | - ideal 20 | - impossible 21 | - inevitable 22 | - infinite 23 | - irrevocable 24 | - main 25 | - manifest 26 | - only 27 | - paramount 28 | - perfect 29 | - perpetual 30 | - possible 31 | - preferable 32 | - principal 33 | - singular 34 | - stationary 35 | - sufficient 36 | - 'true' 37 | - unanimous 38 | - unavoidable 39 | - unbroken 40 | - uniform 41 | - unique 42 | - universal 43 | - void 44 | - whole 45 | -------------------------------------------------------------------------------- /resources/styles/write-good/Cliches.yml: -------------------------------------------------------------------------------- 1 | extends: existence 2 | message: "Try to avoid using clichés like '%s'." 3 | ignorecase: true 4 | level: warning 5 | tokens: 6 | - a chip off the old block 7 | - a clean slate 8 | - a dark and stormy night 9 | - a far cry 10 | - a fine kettle of fish 11 | - a loose cannon 12 | - a penny saved is a penny earned 13 | - a tough row to hoe 14 | - a word to the wise 15 | - ace in the hole 16 | - acid test 17 | - add insult to injury 18 | - against all odds 19 | - air your dirty laundry 20 | - all fun and games 21 | - all in a day's work 22 | - all talk, no action 23 | - all thumbs 24 | - all your eggs in one basket 25 | - all's fair in love and war 26 | - all's well that ends well 27 | - almighty dollar 28 | - American as apple pie 29 | - an axe to grind 30 | - another day, another dollar 31 | - armed to the teeth 32 | - as luck would have it 33 | - as old as time 34 | - as the crow flies 35 | - at loose ends 36 | - at my wits end 37 | - avoid like the plague 38 | - babe in the woods 39 | - back against the wall 40 | - back in the saddle 41 | - back to square one 42 | - back to the drawing board 43 | - bad to the bone 44 | - badge of honor 45 | - bald faced liar 46 | - ballpark figure 47 | - banging your head against a brick wall 48 | - baptism by fire 49 | - barking up the wrong tree 50 | - bat out of hell 51 | - be all and end all 52 | - beat a dead horse 53 | - beat around the bush 54 | - been there, done that 55 | - beggars can't be choosers 56 | - behind the eight ball 57 | - bend over backwards 58 | - benefit of the doubt 59 | - bent out of shape 60 | - best thing since sliced bread 61 | - bet your bottom dollar 62 | - better half 63 | - better late than never 64 | - better mousetrap 65 | - better safe than sorry 66 | - between a rock and a hard place 67 | - beyond the pale 68 | - bide your time 69 | - big as life 70 | - big cheese 71 | - big fish in a small pond 72 | - big man on campus 73 | - bigger they are the harder they fall 74 | - bird in the hand 75 | - bird's eye view 76 | - birds and the bees 77 | - birds of a feather flock together 78 | - bit the hand that feeds you 79 | - bite the bullet 80 | - bite the dust 81 | - bitten off more than he can chew 82 | - black as coal 83 | - black as pitch 84 | - black as the ace of spades 85 | - blast from the past 86 | - bleeding heart 87 | - blessing in disguise 88 | - blind ambition 89 | - blind as a bat 90 | - blind leading the blind 91 | - blood is thicker than water 92 | - blood sweat and tears 93 | - blow off steam 94 | - blow your own horn 95 | - blushing bride 96 | - boils down to 97 | - bolt from the blue 98 | - bone to pick 99 | - bored stiff 100 | - bored to tears 101 | - bottomless pit 102 | - boys will be boys 103 | - bright and early 104 | - brings home the bacon 105 | - broad across the beam 106 | - broken record 107 | - brought back to reality 108 | - bull by the horns 109 | - bull in a china shop 110 | - burn the midnight oil 111 | - burning question 112 | - burning the candle at both ends 113 | - burst your bubble 114 | - bury the hatchet 115 | - busy as a bee 116 | - by hook or by crook 117 | - call a spade a spade 118 | - called onto the carpet 119 | - calm before the storm 120 | - can of worms 121 | - can't cut the mustard 122 | - can't hold a candle to 123 | - case of mistaken identity 124 | - cat got your tongue 125 | - cat's meow 126 | - caught in the crossfire 127 | - caught red-handed 128 | - checkered past 129 | - chomping at the bit 130 | - cleanliness is next to godliness 131 | - clear as a bell 132 | - clear as mud 133 | - close to the vest 134 | - cock and bull story 135 | - cold shoulder 136 | - come hell or high water 137 | - cool as a cucumber 138 | - cool, calm, and collected 139 | - cost a king's ransom 140 | - count your blessings 141 | - crack of dawn 142 | - crash course 143 | - creature comforts 144 | - cross that bridge when you come to it 145 | - crushing blow 146 | - cry like a baby 147 | - cry me a river 148 | - cry over spilt milk 149 | - crystal clear 150 | - curiosity killed the cat 151 | - cut and dried 152 | - cut through the red tape 153 | - cut to the chase 154 | - cute as a bugs ear 155 | - cute as a button 156 | - cute as a puppy 157 | - cuts to the quick 158 | - dark before the dawn 159 | - day in, day out 160 | - dead as a doornail 161 | - devil is in the details 162 | - dime a dozen 163 | - divide and conquer 164 | - dog and pony show 165 | - dog days 166 | - dog eat dog 167 | - dog tired 168 | - don't burn your bridges 169 | - don't count your chickens 170 | - don't look a gift horse in the mouth 171 | - don't rock the boat 172 | - don't step on anyone's toes 173 | - don't take any wooden nickels 174 | - down and out 175 | - down at the heels 176 | - down in the dumps 177 | - down the hatch 178 | - down to earth 179 | - draw the line 180 | - dressed to kill 181 | - dressed to the nines 182 | - drives me up the wall 183 | - dull as dishwater 184 | - dyed in the wool 185 | - eagle eye 186 | - ear to the ground 187 | - early bird catches the worm 188 | - easier said than done 189 | - easy as pie 190 | - eat your heart out 191 | - eat your words 192 | - eleventh hour 193 | - even the playing field 194 | - every dog has its day 195 | - every fiber of my being 196 | - everything but the kitchen sink 197 | - eye for an eye 198 | - face the music 199 | - facts of life 200 | - fair weather friend 201 | - fall by the wayside 202 | - fan the flames 203 | - feast or famine 204 | - feather your nest 205 | - feathered friends 206 | - few and far between 207 | - fifteen minutes of fame 208 | - filthy vermin 209 | - fine kettle of fish 210 | - fish out of water 211 | - fishing for a compliment 212 | - fit as a fiddle 213 | - fit the bill 214 | - fit to be tied 215 | - flash in the pan 216 | - flat as a pancake 217 | - flip your lid 218 | - flog a dead horse 219 | - fly by night 220 | - fly the coop 221 | - follow your heart 222 | - for all intents and purposes 223 | - for the birds 224 | - for what it's worth 225 | - force of nature 226 | - force to be reckoned with 227 | - forgive and forget 228 | - fox in the henhouse 229 | - free and easy 230 | - free as a bird 231 | - fresh as a daisy 232 | - full steam ahead 233 | - fun in the sun 234 | - garbage in, garbage out 235 | - gentle as a lamb 236 | - get a kick out of 237 | - get a leg up 238 | - get down and dirty 239 | - get the lead out 240 | - get to the bottom of 241 | - get your feet wet 242 | - gets my goat 243 | - gilding the lily 244 | - give and take 245 | - go against the grain 246 | - go at it tooth and nail 247 | - go for broke 248 | - go him one better 249 | - go the extra mile 250 | - go with the flow 251 | - goes without saying 252 | - good as gold 253 | - good deed for the day 254 | - good things come to those who wait 255 | - good time was had by all 256 | - good times were had by all 257 | - greased lightning 258 | - greek to me 259 | - green thumb 260 | - green-eyed monster 261 | - grist for the mill 262 | - growing like a weed 263 | - hair of the dog 264 | - hand to mouth 265 | - happy as a clam 266 | - happy as a lark 267 | - hasn't a clue 268 | - have a nice day 269 | - have high hopes 270 | - have the last laugh 271 | - haven't got a row to hoe 272 | - head honcho 273 | - head over heels 274 | - hear a pin drop 275 | - heard it through the grapevine 276 | - heart's content 277 | - heavy as lead 278 | - hem and haw 279 | - high and dry 280 | - high and mighty 281 | - high as a kite 282 | - hit paydirt 283 | - hold your head up high 284 | - hold your horses 285 | - hold your own 286 | - hold your tongue 287 | - honest as the day is long 288 | - horns of a dilemma 289 | - horse of a different color 290 | - hot under the collar 291 | - hour of need 292 | - I beg to differ 293 | - icing on the cake 294 | - if the shoe fits 295 | - if the shoe were on the other foot 296 | - in a jam 297 | - in a jiffy 298 | - in a nutshell 299 | - in a pig's eye 300 | - in a pinch 301 | - in a word 302 | - in hot water 303 | - in the gutter 304 | - in the nick of time 305 | - in the thick of it 306 | - in your dreams 307 | - it ain't over till the fat lady sings 308 | - it goes without saying 309 | - it takes all kinds 310 | - it takes one to know one 311 | - it's a small world 312 | - it's only a matter of time 313 | - ivory tower 314 | - Jack of all trades 315 | - jockey for position 316 | - jog your memory 317 | - joined at the hip 318 | - judge a book by its cover 319 | - jump down your throat 320 | - jump in with both feet 321 | - jump on the bandwagon 322 | - jump the gun 323 | - jump to conclusions 324 | - just a hop, skip, and a jump 325 | - just the ticket 326 | - justice is blind 327 | - keep a stiff upper lip 328 | - keep an eye on 329 | - keep it simple, stupid 330 | - keep the home fires burning 331 | - keep up with the Joneses 332 | - keep your chin up 333 | - keep your fingers crossed 334 | - kick the bucket 335 | - kick up your heels 336 | - kick your feet up 337 | - kid in a candy store 338 | - kill two birds with one stone 339 | - kiss of death 340 | - knock it out of the park 341 | - knock on wood 342 | - knock your socks off 343 | - know him from Adam 344 | - know the ropes 345 | - know the score 346 | - knuckle down 347 | - knuckle sandwich 348 | - knuckle under 349 | - labor of love 350 | - ladder of success 351 | - land on your feet 352 | - lap of luxury 353 | - last but not least 354 | - last hurrah 355 | - last-ditch effort 356 | - law of the jungle 357 | - law of the land 358 | - lay down the law 359 | - leaps and bounds 360 | - let sleeping dogs lie 361 | - let the cat out of the bag 362 | - let the good times roll 363 | - let your hair down 364 | - let's talk turkey 365 | - letter perfect 366 | - lick your wounds 367 | - lies like a rug 368 | - life's a bitch 369 | - life's a grind 370 | - light at the end of the tunnel 371 | - lighter than a feather 372 | - lighter than air 373 | - like clockwork 374 | - like father like son 375 | - like taking candy from a baby 376 | - like there's no tomorrow 377 | - lion's share 378 | - live and learn 379 | - live and let live 380 | - long and short of it 381 | - long lost love 382 | - look before you leap 383 | - look down your nose 384 | - look what the cat dragged in 385 | - looking a gift horse in the mouth 386 | - looks like death warmed over 387 | - loose cannon 388 | - lose your head 389 | - lose your temper 390 | - loud as a horn 391 | - lounge lizard 392 | - loved and lost 393 | - low man on the totem pole 394 | - luck of the draw 395 | - luck of the Irish 396 | - make hay while the sun shines 397 | - make money hand over fist 398 | - make my day 399 | - make the best of a bad situation 400 | - make the best of it 401 | - make your blood boil 402 | - man of few words 403 | - man's best friend 404 | - mark my words 405 | - meaningful dialogue 406 | - missed the boat on that one 407 | - moment in the sun 408 | - moment of glory 409 | - moment of truth 410 | - money to burn 411 | - more power to you 412 | - more than one way to skin a cat 413 | - movers and shakers 414 | - moving experience 415 | - naked as a jaybird 416 | - naked truth 417 | - neat as a pin 418 | - needle in a haystack 419 | - needless to say 420 | - neither here nor there 421 | - never look back 422 | - never say never 423 | - nip and tuck 424 | - nip it in the bud 425 | - no guts, no glory 426 | - no love lost 427 | - no pain, no gain 428 | - no skin off my back 429 | - no stone unturned 430 | - no time like the present 431 | - no use crying over spilled milk 432 | - nose to the grindstone 433 | - not a hope in hell 434 | - not a minute's peace 435 | - not in my backyard 436 | - not playing with a full deck 437 | - not the end of the world 438 | - not written in stone 439 | - nothing to sneeze at 440 | - nothing ventured nothing gained 441 | - now we're cooking 442 | - off the top of my head 443 | - off the wagon 444 | - off the wall 445 | - old hat 446 | - older and wiser 447 | - older than dirt 448 | - older than Methuselah 449 | - on a roll 450 | - on cloud nine 451 | - on pins and needles 452 | - on the bandwagon 453 | - on the money 454 | - on the nose 455 | - on the rocks 456 | - on the spot 457 | - on the tip of my tongue 458 | - on the wagon 459 | - on thin ice 460 | - once bitten, twice shy 461 | - one bad apple doesn't spoil the bushel 462 | - one born every minute 463 | - one brick short 464 | - one foot in the grave 465 | - one in a million 466 | - one red cent 467 | - only game in town 468 | - open a can of worms 469 | - open and shut case 470 | - open the flood gates 471 | - opportunity doesn't knock twice 472 | - out of pocket 473 | - out of sight, out of mind 474 | - out of the frying pan into the fire 475 | - out of the woods 476 | - out on a limb 477 | - over a barrel 478 | - over the hump 479 | - pain and suffering 480 | - pain in the 481 | - panic button 482 | - par for the course 483 | - part and parcel 484 | - party pooper 485 | - pass the buck 486 | - patience is a virtue 487 | - pay through the nose 488 | - penny pincher 489 | - perfect storm 490 | - pig in a poke 491 | - pile it on 492 | - pillar of the community 493 | - pin your hopes on 494 | - pitter patter of little feet 495 | - plain as day 496 | - plain as the nose on your face 497 | - play by the rules 498 | - play your cards right 499 | - playing the field 500 | - playing with fire 501 | - pleased as punch 502 | - plenty of fish in the sea 503 | - point with pride 504 | - poor as a church mouse 505 | - pot calling the kettle black 506 | - pretty as a picture 507 | - pull a fast one 508 | - pull your punches 509 | - pulling your leg 510 | - pure as the driven snow 511 | - put it in a nutshell 512 | - put one over on you 513 | - put the cart before the horse 514 | - put the pedal to the metal 515 | - put your best foot forward 516 | - put your foot down 517 | - quick as a bunny 518 | - quick as a lick 519 | - quick as a wink 520 | - quick as lightning 521 | - quiet as a dormouse 522 | - rags to riches 523 | - raining buckets 524 | - raining cats and dogs 525 | - rank and file 526 | - rat race 527 | - reap what you sow 528 | - red as a beet 529 | - red herring 530 | - reinvent the wheel 531 | - rich and famous 532 | - rings a bell 533 | - ripe old age 534 | - ripped me off 535 | - rise and shine 536 | - road to hell is paved with good intentions 537 | - rob Peter to pay Paul 538 | - roll over in the grave 539 | - rub the wrong way 540 | - ruled the roost 541 | - running in circles 542 | - sad but true 543 | - sadder but wiser 544 | - salt of the earth 545 | - scared stiff 546 | - scared to death 547 | - sealed with a kiss 548 | - second to none 549 | - see eye to eye 550 | - seen the light 551 | - seize the day 552 | - set the record straight 553 | - set the world on fire 554 | - set your teeth on edge 555 | - sharp as a tack 556 | - shoot for the moon 557 | - shoot the breeze 558 | - shot in the dark 559 | - shoulder to the wheel 560 | - sick as a dog 561 | - sigh of relief 562 | - signed, sealed, and delivered 563 | - sink or swim 564 | - six of one, half a dozen of another 565 | - skating on thin ice 566 | - slept like a log 567 | - slinging mud 568 | - slippery as an eel 569 | - slow as molasses 570 | - smart as a whip 571 | - smooth as a baby's bottom 572 | - sneaking suspicion 573 | - snug as a bug in a rug 574 | - sow wild oats 575 | - spare the rod, spoil the child 576 | - speak of the devil 577 | - spilled the beans 578 | - spinning your wheels 579 | - spitting image of 580 | - spoke with relish 581 | - spread like wildfire 582 | - spring to life 583 | - squeaky wheel gets the grease 584 | - stands out like a sore thumb 585 | - start from scratch 586 | - stick in the mud 587 | - still waters run deep 588 | - stitch in time 589 | - stop and smell the roses 590 | - straight as an arrow 591 | - straw that broke the camel's back 592 | - strong as an ox 593 | - stubborn as a mule 594 | - stuff that dreams are made of 595 | - stuffed shirt 596 | - sweating blood 597 | - sweating bullets 598 | - take a load off 599 | - take one for the team 600 | - take the bait 601 | - take the bull by the horns 602 | - take the plunge 603 | - takes one to know one 604 | - takes two to tango 605 | - the more the merrier 606 | - the real deal 607 | - the real McCoy 608 | - the red carpet treatment 609 | - the same old story 610 | - there is no accounting for taste 611 | - thick as a brick 612 | - thick as thieves 613 | - thin as a rail 614 | - think outside of the box 615 | - third time's the charm 616 | - this day and age 617 | - this hurts me worse than it hurts you 618 | - this point in time 619 | - three sheets to the wind 620 | - through thick and thin 621 | - throw in the towel 622 | - tie one on 623 | - tighter than a drum 624 | - time and time again 625 | - time is of the essence 626 | - tip of the iceberg 627 | - tired but happy 628 | - to coin a phrase 629 | - to each his own 630 | - to make a long story short 631 | - to the best of my knowledge 632 | - toe the line 633 | - tongue in cheek 634 | - too good to be true 635 | - too hot to handle 636 | - too numerous to mention 637 | - touch with a ten foot pole 638 | - tough as nails 639 | - trial and error 640 | - trials and tribulations 641 | - tried and true 642 | - trip down memory lane 643 | - twist of fate 644 | - two cents worth 645 | - two peas in a pod 646 | - ugly as sin 647 | - under the counter 648 | - under the gun 649 | - under the same roof 650 | - under the weather 651 | - until the cows come home 652 | - unvarnished truth 653 | - up the creek 654 | - uphill battle 655 | - upper crust 656 | - upset the applecart 657 | - vain attempt 658 | - vain effort 659 | - vanquish the enemy 660 | - vested interest 661 | - waiting for the other shoe to drop 662 | - wakeup call 663 | - warm welcome 664 | - watch your p's and q's 665 | - watch your tongue 666 | - watching the clock 667 | - water under the bridge 668 | - weather the storm 669 | - weed them out 670 | - week of Sundays 671 | - went belly up 672 | - wet behind the ears 673 | - what goes around comes around 674 | - what you see is what you get 675 | - when it rains, it pours 676 | - when push comes to shove 677 | - when the cat's away 678 | - when the going gets tough, the tough get going 679 | - white as a sheet 680 | - whole ball of wax 681 | - whole hog 682 | - whole nine yards 683 | - wild goose chase 684 | - will wonders never cease? 685 | - wisdom of the ages 686 | - wise as an owl 687 | - wolf at the door 688 | - words fail me 689 | - work like a dog 690 | - world weary 691 | - worst nightmare 692 | - worth its weight in gold 693 | - wrong side of the bed 694 | - yanking your chain 695 | - yappy as a dog 696 | - years young 697 | - you are what you eat 698 | - you can run but you can't hide 699 | - you only live once 700 | - you're the boss 701 | - young and foolish 702 | - young and vibrant 703 | -------------------------------------------------------------------------------- /resources/styles/write-good/E-Prime.yml: -------------------------------------------------------------------------------- 1 | extends: existence 2 | message: "Try to avoid using '%s'." 3 | ignorecase: true 4 | level: suggestion 5 | tokens: 6 | - am 7 | - are 8 | - aren't 9 | - be 10 | - been 11 | - being 12 | - he's 13 | - here's 14 | - here's 15 | - how's 16 | - i'm 17 | - is 18 | - isn't 19 | - it's 20 | - she's 21 | - that's 22 | - there's 23 | - they're 24 | - was 25 | - wasn't 26 | - we're 27 | - were 28 | - weren't 29 | - what's 30 | - where's 31 | - who's 32 | - you're 33 | -------------------------------------------------------------------------------- /resources/styles/write-good/Illusions.yml: -------------------------------------------------------------------------------- 1 | extends: repetition 2 | message: "'%s' is repeated!" 3 | level: warning 4 | alpha: true 5 | action: 6 | name: edit 7 | params: 8 | - truncate 9 | - " " 10 | tokens: 11 | - '[^\s]+' 12 | -------------------------------------------------------------------------------- /resources/styles/write-good/Passive.yml: -------------------------------------------------------------------------------- 1 | extends: existence 2 | message: "'%s' may be passive voice. Use active voice if you can." 3 | ignorecase: true 4 | level: warning 5 | raw: 6 | - \b(am|are|were|being|is|been|was|be)\b\s* 7 | tokens: 8 | - '[\w]+ed' 9 | - awoken 10 | - beat 11 | - become 12 | - been 13 | - begun 14 | - bent 15 | - beset 16 | - bet 17 | - bid 18 | - bidden 19 | - bitten 20 | - bled 21 | - blown 22 | - born 23 | - bought 24 | - bound 25 | - bred 26 | - broadcast 27 | - broken 28 | - brought 29 | - built 30 | - burnt 31 | - burst 32 | - cast 33 | - caught 34 | - chosen 35 | - clung 36 | - come 37 | - cost 38 | - crept 39 | - cut 40 | - dealt 41 | - dived 42 | - done 43 | - drawn 44 | - dreamt 45 | - driven 46 | - drunk 47 | - dug 48 | - eaten 49 | - fallen 50 | - fed 51 | - felt 52 | - fit 53 | - fled 54 | - flown 55 | - flung 56 | - forbidden 57 | - foregone 58 | - forgiven 59 | - forgotten 60 | - forsaken 61 | - fought 62 | - found 63 | - frozen 64 | - given 65 | - gone 66 | - gotten 67 | - ground 68 | - grown 69 | - heard 70 | - held 71 | - hidden 72 | - hit 73 | - hung 74 | - hurt 75 | - kept 76 | - knelt 77 | - knit 78 | - known 79 | - laid 80 | - lain 81 | - leapt 82 | - learnt 83 | - led 84 | - left 85 | - lent 86 | - let 87 | - lighted 88 | - lost 89 | - made 90 | - meant 91 | - met 92 | - misspelt 93 | - mistaken 94 | - mown 95 | - overcome 96 | - overdone 97 | - overtaken 98 | - overthrown 99 | - paid 100 | - pled 101 | - proven 102 | - put 103 | - quit 104 | - read 105 | - rid 106 | - ridden 107 | - risen 108 | - run 109 | - rung 110 | - said 111 | - sat 112 | - sawn 113 | - seen 114 | - sent 115 | - set 116 | - sewn 117 | - shaken 118 | - shaven 119 | - shed 120 | - shod 121 | - shone 122 | - shorn 123 | - shot 124 | - shown 125 | - shrunk 126 | - shut 127 | - slain 128 | - slept 129 | - slid 130 | - slit 131 | - slung 132 | - smitten 133 | - sold 134 | - sought 135 | - sown 136 | - sped 137 | - spent 138 | - spilt 139 | - spit 140 | - split 141 | - spoken 142 | - spread 143 | - sprung 144 | - spun 145 | - stolen 146 | - stood 147 | - stridden 148 | - striven 149 | - struck 150 | - strung 151 | - stuck 152 | - stung 153 | - stunk 154 | - sung 155 | - sunk 156 | - swept 157 | - swollen 158 | - sworn 159 | - swum 160 | - swung 161 | - taken 162 | - taught 163 | - thought 164 | - thrived 165 | - thrown 166 | - thrust 167 | - told 168 | - torn 169 | - trodden 170 | - understood 171 | - upheld 172 | - upset 173 | - wed 174 | - wept 175 | - withheld 176 | - withstood 177 | - woken 178 | - won 179 | - worn 180 | - wound 181 | - woven 182 | - written 183 | - wrung 184 | -------------------------------------------------------------------------------- /resources/styles/write-good/README.md: -------------------------------------------------------------------------------- 1 | Based on [write-good](https://github.com/btford/write-good). 2 | 3 | > Naive linter for English prose for developers who can't write good and wanna learn to do other stuff good too. 4 | 5 | ``` 6 | The MIT License (MIT) 7 | 8 | Copyright (c) 2014 Brian Ford 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | ``` 28 | -------------------------------------------------------------------------------- /resources/styles/write-good/So.yml: -------------------------------------------------------------------------------- 1 | extends: existence 2 | message: "Don't start a sentence with '%s'." 3 | level: error 4 | raw: 5 | - '(?:[;-]\s)so[\s,]|\bSo[\s,]' 6 | -------------------------------------------------------------------------------- /resources/styles/write-good/ThereIs.yml: -------------------------------------------------------------------------------- 1 | extends: existence 2 | message: "Don't start a sentence with '%s'." 3 | ignorecase: false 4 | level: error 5 | raw: 6 | - '(?:[;-]\s)There\s(is|are)|\bThere\s(is|are)\b' 7 | -------------------------------------------------------------------------------- /resources/styles/write-good/TooWordy.yml: -------------------------------------------------------------------------------- 1 | extends: existence 2 | message: "'%s' is too wordy." 3 | ignorecase: true 4 | level: warning 5 | tokens: 6 | - a number of 7 | - abundance 8 | - accede to 9 | - accelerate 10 | - accentuate 11 | - accompany 12 | - accomplish 13 | - accorded 14 | - accrue 15 | - acquiesce 16 | - acquire 17 | - additional 18 | - adjacent to 19 | - adjustment 20 | - admissible 21 | - advantageous 22 | - adversely impact 23 | - advise 24 | - aforementioned 25 | - aggregate 26 | - aircraft 27 | - all of 28 | - all things considered 29 | - alleviate 30 | - allocate 31 | - along the lines of 32 | - already existing 33 | - alternatively 34 | - amazing 35 | - ameliorate 36 | - anticipate 37 | - apparent 38 | - appreciable 39 | - as a matter of fact 40 | - as a means of 41 | - as far as I'm concerned 42 | - as of yet 43 | - as to 44 | - as yet 45 | - ascertain 46 | - assistance 47 | - at the present time 48 | - at this time 49 | - attain 50 | - attributable to 51 | - authorize 52 | - because of the fact that 53 | - belated 54 | - benefit from 55 | - bestow 56 | - by means of 57 | - by virtue of 58 | - by virtue of the fact that 59 | - cease 60 | - close proximity 61 | - commence 62 | - comply with 63 | - concerning 64 | - consequently 65 | - consolidate 66 | - constitutes 67 | - demonstrate 68 | - depart 69 | - designate 70 | - discontinue 71 | - due to the fact that 72 | - each and every 73 | - economical 74 | - eliminate 75 | - elucidate 76 | - employ 77 | - endeavor 78 | - enumerate 79 | - equitable 80 | - equivalent 81 | - evaluate 82 | - evidenced 83 | - exclusively 84 | - expedite 85 | - expend 86 | - expiration 87 | - facilitate 88 | - factual evidence 89 | - feasible 90 | - finalize 91 | - first and foremost 92 | - for all intents and purposes 93 | - for the most part 94 | - for the purpose of 95 | - forfeit 96 | - formulate 97 | - have a tendency to 98 | - honest truth 99 | - however 100 | - if and when 101 | - impacted 102 | - implement 103 | - in a manner of speaking 104 | - in a timely manner 105 | - in a very real sense 106 | - in accordance with 107 | - in addition 108 | - in all likelihood 109 | - in an effort to 110 | - in between 111 | - in excess of 112 | - in lieu of 113 | - in light of the fact that 114 | - in many cases 115 | - in my opinion 116 | - in order to 117 | - in regard to 118 | - in some instances 119 | - in terms of 120 | - in the case of 121 | - in the event that 122 | - in the final analysis 123 | - in the nature of 124 | - in the near future 125 | - in the process of 126 | - inception 127 | - incumbent upon 128 | - indicate 129 | - indication 130 | - initiate 131 | - irregardless 132 | - is applicable to 133 | - is authorized to 134 | - is responsible for 135 | - it is 136 | - it is essential 137 | - it seems that 138 | - it was 139 | - magnitude 140 | - maximum 141 | - methodology 142 | - minimize 143 | - minimum 144 | - modify 145 | - monitor 146 | - multiple 147 | - necessitate 148 | - nevertheless 149 | - not certain 150 | - not many 151 | - not often 152 | - not unless 153 | - not unlike 154 | - notwithstanding 155 | - null and void 156 | - numerous 157 | - objective 158 | - obligate 159 | - obtain 160 | - on the contrary 161 | - on the other hand 162 | - one particular 163 | - optimum 164 | - overall 165 | - owing to the fact that 166 | - participate 167 | - particulars 168 | - pass away 169 | - pertaining to 170 | - point in time 171 | - portion 172 | - possess 173 | - preclude 174 | - previously 175 | - prior to 176 | - prioritize 177 | - procure 178 | - proficiency 179 | - provided that 180 | - purchase 181 | - put simply 182 | - readily apparent 183 | - refer back 184 | - regarding 185 | - relocate 186 | - remainder 187 | - remuneration 188 | - requirement 189 | - reside 190 | - residence 191 | - retain 192 | - satisfy 193 | - shall 194 | - should you wish 195 | - similar to 196 | - solicit 197 | - span across 198 | - strategize 199 | - subsequent 200 | - substantial 201 | - successfully complete 202 | - sufficient 203 | - terminate 204 | - the month of 205 | - the point I am trying to make 206 | - therefore 207 | - time period 208 | - took advantage of 209 | - transmit 210 | - transpire 211 | - type of 212 | - until such time as 213 | - utilization 214 | - utilize 215 | - validate 216 | - various different 217 | - what I mean to say is 218 | - whether or not 219 | - with respect to 220 | - with the exception of 221 | - witnessed 222 | -------------------------------------------------------------------------------- /resources/styles/write-good/Weasel.yml: -------------------------------------------------------------------------------- 1 | extends: existence 2 | message: "'%s' is a weasel word!" 3 | ignorecase: true 4 | level: warning 5 | tokens: 6 | - absolutely 7 | - accidentally 8 | - additionally 9 | - allegedly 10 | - alternatively 11 | - angrily 12 | - anxiously 13 | - approximately 14 | - awkwardly 15 | - badly 16 | - barely 17 | - beautifully 18 | - blindly 19 | - boldly 20 | - bravely 21 | - brightly 22 | - briskly 23 | - bristly 24 | - bubbly 25 | - busily 26 | - calmly 27 | - carefully 28 | - carelessly 29 | - cautiously 30 | - cheerfully 31 | - clearly 32 | - closely 33 | - coldly 34 | - completely 35 | - consequently 36 | - correctly 37 | - courageously 38 | - crinkly 39 | - cruelly 40 | - crumbly 41 | - cuddly 42 | - currently 43 | - daily 44 | - daringly 45 | - deadly 46 | - definitely 47 | - deliberately 48 | - doubtfully 49 | - dumbly 50 | - eagerly 51 | - early 52 | - easily 53 | - elegantly 54 | - enormously 55 | - enthusiastically 56 | - equally 57 | - especially 58 | - eventually 59 | - exactly 60 | - exceedingly 61 | - exclusively 62 | - extremely 63 | - fairly 64 | - faithfully 65 | - fatally 66 | - fiercely 67 | - finally 68 | - fondly 69 | - few 70 | - foolishly 71 | - fortunately 72 | - frankly 73 | - frantically 74 | - generously 75 | - gently 76 | - giggly 77 | - gladly 78 | - gracefully 79 | - greedily 80 | - happily 81 | - hardly 82 | - hastily 83 | - healthily 84 | - heartily 85 | - helpfully 86 | - honestly 87 | - hourly 88 | - hungrily 89 | - hurriedly 90 | - immediately 91 | - impatiently 92 | - inadequately 93 | - ingeniously 94 | - innocently 95 | - inquisitively 96 | - interestingly 97 | - irritably 98 | - jiggly 99 | - joyously 100 | - justly 101 | - kindly 102 | - largely 103 | - lately 104 | - lazily 105 | - likely 106 | - literally 107 | - lonely 108 | - loosely 109 | - loudly 110 | - loudly 111 | - luckily 112 | - madly 113 | - many 114 | - mentally 115 | - mildly 116 | - monthly 117 | - mortally 118 | - mostly 119 | - mysteriously 120 | - neatly 121 | - nervously 122 | - nightly 123 | - noisily 124 | - normally 125 | - obediently 126 | - occasionally 127 | - only 128 | - openly 129 | - painfully 130 | - particularly 131 | - patiently 132 | - perfectly 133 | - politely 134 | - poorly 135 | - powerfully 136 | - presumably 137 | - previously 138 | - promptly 139 | - punctually 140 | - quarterly 141 | - quickly 142 | - quietly 143 | - rapidly 144 | - rarely 145 | - really 146 | - recently 147 | - recklessly 148 | - regularly 149 | - remarkably 150 | - relatively 151 | - reluctantly 152 | - repeatedly 153 | - rightfully 154 | - roughly 155 | - rudely 156 | - sadly 157 | - safely 158 | - selfishly 159 | - sensibly 160 | - seriously 161 | - sharply 162 | - shortly 163 | - shyly 164 | - significantly 165 | - silently 166 | - simply 167 | - sleepily 168 | - slowly 169 | - smartly 170 | - smelly 171 | - smoothly 172 | - softly 173 | - solemnly 174 | - sparkly 175 | - speedily 176 | - stealthily 177 | - sternly 178 | - stupidly 179 | - substantially 180 | - successfully 181 | - suddenly 182 | - surprisingly 183 | - suspiciously 184 | - swiftly 185 | - tenderly 186 | - tensely 187 | - thoughtfully 188 | - tightly 189 | - timely 190 | - truthfully 191 | - unexpectedly 192 | - unfortunately 193 | - usually 194 | - very 195 | - victoriously 196 | - violently 197 | - vivaciously 198 | - warmly 199 | - waverly 200 | - weakly 201 | - wearily 202 | - weekly 203 | - wildly 204 | - wisely 205 | - worldly 206 | - wrinkly 207 | - yearly 208 | -------------------------------------------------------------------------------- /resources/styles/write-good/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "feed": "https://github.com/errata-ai/write-good/releases.atom", 3 | "vale_version": ">=1.0.0" 4 | } 5 | -------------------------------------------------------------------------------- /src/Console/Commands/LintTranslationCommand.php: -------------------------------------------------------------------------------- 1 | argument('namespace'); 21 | $outputAsJson = $this->option('json') ? true : false; 22 | $verbose = $this->option('verbose'); 23 | 24 | $namespacesToLint = empty($namespaces) ? $translationLinter->getTranslationFiles() : $namespaces; 25 | $totalNamespacesToLint = count($namespacesToLint); 26 | 27 | $this->info('🗣 Start linting ...'); 28 | $startTime = microtime(true); 29 | 30 | // create progress bar 31 | $progressBar = $this->output->createProgressBar($totalNamespacesToLint); 32 | 33 | $results = []; 34 | foreach ($namespacesToLint as $namespaceToLint) { 35 | try { 36 | $results[] = $translationLinter->lintNamespace($namespaceToLint); 37 | $progressBar->advance(); 38 | } catch (Exception $exception) { 39 | $this->warn("($namespaceToLint) Unexpected error."); 40 | if ($verbose) { 41 | $this->line($exception->getMessage()); 42 | } 43 | } 44 | } 45 | 46 | $tableResults = Arr::flatten($results, 2); 47 | 48 | $lintingDuration = round(microtime(true) - $startTime, 2); 49 | $progressBar->finish(); 50 | 51 | $this->finishLintingOutput($tableResults, $outputAsJson, $lintingDuration); 52 | 53 | return 0; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/Console/Commands/LintViewCommand.php: -------------------------------------------------------------------------------- 1 | argument('bladeTemplate'); 20 | $directoriesToExclude = $this->option('exclude') !== null ? explode(',', $this->option('exclude')) : []; 21 | 22 | $outputAsJson = $this->option('json') ? true : false; 23 | $verbose = $this->option('verbose') ? true : false; 24 | 25 | if ($singleBladeTemplate === null && empty($directoriesToExclude)) { 26 | if (! $this->confirm('Are you sure you want to lint all blade files in your application?')) { 27 | $this->line('❌ Linting aborted.'); 28 | 29 | return 1; 30 | } 31 | } elseif ($singleBladeTemplate !== null && ! empty($directoriesToExclude)) { 32 | $this->error('Invalid parameters. Please provide either a single template key to lint or directories to exclude or no further options to lint all blade templates.'); 33 | 34 | return 2; 35 | } 36 | 37 | // collect blade files to lint 38 | $templatesToLint = []; 39 | if ($singleBladeTemplate !== null) { 40 | $this->line("Linting single blade template with key '$singleBladeTemplate'."); 41 | $templatesToLint[] = $singleBladeTemplate; 42 | $totalFilesToLint = count($templatesToLint); 43 | } else { 44 | $templatesToLint = $viewLinter->readBladeKeys($directoriesToExclude); 45 | $totalFilesToLint = count($templatesToLint); 46 | 47 | $message = 'Linting all blade templates'; 48 | 49 | if (! empty($directoriesToExclude)) { 50 | $message .= ', excluding: '.implode(', ', $directoriesToExclude); 51 | } 52 | $this->line($message); 53 | 54 | $this->line("Found $totalFilesToLint blade files."); 55 | } 56 | 57 | $this->info('🗣 Start linting ...'); 58 | $startTime = microtime(true); 59 | 60 | // create progress bar 61 | $progressBar = $this->output->createProgressBar($totalFilesToLint); 62 | 63 | $results = []; 64 | foreach ($templatesToLint as $templateToLint) { 65 | try { 66 | $progressBar->advance(); 67 | 68 | $filePath = $viewLinter->createLintableCopy($templateToLint); 69 | 70 | $result = $viewLinter->lintFile($filePath, $templateToLint); 71 | if ($result === null) { 72 | continue; 73 | } 74 | $results = array_merge($results, $result); 75 | } catch (Exception $exception) { 76 | $this->warn("($templateToLint) Unexpected error."); 77 | if ($verbose) { 78 | $this->line($exception->getMessage()); 79 | } 80 | } finally { 81 | $viewLinter->deleteLintableCopy(); 82 | } 83 | } 84 | 85 | $lintingDuration = round(microtime(true) - $startTime, 2); 86 | $progressBar->finish(); 87 | 88 | $this->finishLintingOutput($results, $outputAsJson, $lintingDuration); 89 | 90 | return 0; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/Console/Commands/LinterCommand.php: -------------------------------------------------------------------------------- 1 | info(PHP_EOL); 19 | 20 | $totalHints = count($results); 21 | 22 | if ($totalHints > 0) { 23 | if ($outputAsJson) { 24 | $filePath = storage_path('linting_blade_result_'.date('Y-m-d-H-i-s').'.json'); 25 | File::put($filePath, json_encode($results, JSON_UNESCAPED_SLASHES)); 26 | 27 | $this->warn("$totalHints linting hints were found."); 28 | $this->warn('For detail, check results in file'); 29 | $this->warn($filePath); 30 | } else { 31 | $this->table( 32 | ['Key', 'Line', 'Position', 'Message', 'Severity', 'Condition'], 33 | $results 34 | ); 35 | $this->warn("$totalHints linting hints were found."); 36 | } 37 | } else { 38 | $this->info('✅ No errors, warnings or suggestions found.'); 39 | } 40 | 41 | $this->info( 42 | 'Applied styles: '. 43 | collect(config('linter.styles')) 44 | ->map(function ($style) { 45 | return Str::afterLast($style, '\\'); 46 | }) 47 | ->implode(', ') 48 | ); 49 | 50 | $this->info("🏁 Finished linting in $lintingDuration seconds."); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Exceptions/LinterException.php: -------------------------------------------------------------------------------- 1 | result = LintingResult::fromJsonOutput($textKey, $output); 24 | 25 | return $e; 26 | } 27 | 28 | /** 29 | * @return LintingResult 30 | */ 31 | public function getResult(): LintingResult 32 | { 33 | return $this->result; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/LaravelProseLinterFacade.php: -------------------------------------------------------------------------------- 1 | app->runningInConsole()) { 17 | $this->publishes([ 18 | __DIR__.'/../config/config.php' => config_path('linter.php'), 19 | ], 'linting-config'); 20 | 21 | // Publish the style files 22 | $this->publishes([ 23 | __DIR__.'/../resources/styles' => resource_path('laravel-prose-linter'), 24 | ], 'linting-styles'); 25 | 26 | // Register package commands 27 | $this->commands([ 28 | LintTranslationCommand::class, 29 | LintViewCommand::class, 30 | ]); 31 | } 32 | } 33 | 34 | /** 35 | * Register the application services. 36 | */ 37 | public function register() 38 | { 39 | // Automatically apply the package configuration 40 | $this->mergeConfigFrom(__DIR__.'/../config/config.php', 'linter'); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Linter/LintingHint.php: -------------------------------------------------------------------------------- 1 | libraryCheck = $libraryCheck; 42 | $this->line = $line; 43 | $this->position = $position; 44 | $this->message = $message; 45 | $this->severity = $severity; 46 | } 47 | 48 | /** 49 | * @param array $result 50 | * @return LintingHint 51 | */ 52 | public static function fromJson(array $result): LintingHint 53 | { 54 | $hintData = $result; 55 | 56 | return new LintingHint( 57 | $hintData['Check'], 58 | $hintData['Line'], 59 | $hintData['Span'][0], 60 | $hintData['Message'], 61 | $hintData['Severity'] 62 | ); 63 | } 64 | 65 | /** 66 | * @return array 67 | */ 68 | public function toArray(): array 69 | { 70 | return [ 71 | 'line' => $this->line, 72 | 'position' => $this->position, 73 | 'message' => $this->message, 74 | 'severity' => $this->severity, 75 | 'libraryCheck' => $this->libraryCheck, 76 | ]; 77 | } 78 | 79 | /** 80 | * @return array 81 | */ 82 | public function toFlatArray(): array 83 | { 84 | return [ 85 | $this->line, 86 | $this->position, 87 | $this->message, 88 | $this->severity, 89 | $this->libraryCheck, 90 | ]; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/Linter/LintingResult.php: -------------------------------------------------------------------------------- 1 | textIdentifier = $textIdentifier; 27 | 28 | foreach ($results[array_key_first($results)] as $hint) { 29 | $lintingResult->hints[] = LintingHint::fromJson($hint); 30 | } 31 | 32 | return $lintingResult; 33 | } 34 | 35 | /** 36 | * @return string 37 | */ 38 | public function getTextIdentifier(): string 39 | { 40 | return $this->textIdentifier; 41 | } 42 | 43 | /** 44 | * @return LintingHint[] 45 | */ 46 | public function getHints(): array 47 | { 48 | return $this->hints; 49 | } 50 | 51 | /** 52 | * @return array 53 | */ 54 | public function toArray(): array 55 | { 56 | $hints = []; 57 | foreach ($this->getHints() as $hint) { 58 | $hints[] = array_merge( 59 | [$this->getTextIdentifier()], 60 | $hint->toFlatArray()); 61 | } 62 | 63 | return $hints; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/Linter/TranslationLinter.php: -------------------------------------------------------------------------------- 1 | rewind(); 29 | 30 | while ($it->valid()) { 31 | if (! $it->isDot()) { 32 | $translationFiles->add($it->key()); 33 | } 34 | 35 | $it->next(); 36 | } 37 | // extract namespaces 38 | $namespaces = $translationFiles->map(function ($file) { 39 | if (Str::startsWith($file, '.')) { 40 | return false; 41 | } 42 | 43 | $fileName = Str::afterLast($file, "lang{$this->directorySeparator}en{$this->directorySeparator}"); 44 | 45 | return Str::before($fileName, '.php'); 46 | }); 47 | 48 | return $namespaces->toArray(); 49 | } 50 | 51 | /** 52 | * @param string $namespace 53 | * @return array|string 54 | */ 55 | public function readTranslationArray(string $namespace): array|string 56 | { 57 | // TODO flatten, e.g. validation 58 | return __($namespace); 59 | } 60 | 61 | /** 62 | * @param string $namespace 63 | * @return array 64 | * 65 | * @throws LinterException 66 | */ 67 | public function lintNamespace(string $namespace): array 68 | { 69 | $translations = $this->readTranslationArray($namespace); 70 | 71 | if (! is_array($translations)) { 72 | throw new LinterException('No translations found.'); 73 | } 74 | 75 | $this->lintingResults = []; 76 | try { 77 | $this->lintTranslationArray($translations, $namespace); 78 | } catch (ProcessFailedException $processFailedException) { 79 | // toDo 80 | } 81 | 82 | return $this->lintingResults; 83 | } 84 | 85 | /** 86 | * @throws LinterException 87 | */ 88 | private function lintTranslationArray($translations, $parentKey = null) 89 | { 90 | foreach ($translations as $translationKey => $translationText) { 91 | if (is_array($translationText)) { 92 | $fullKey = $this->translationKey($translationKey, $parentKey); 93 | $this->lintTranslationArray($translationText, $fullKey); 94 | continue; 95 | } 96 | 97 | $result = $this->lintString( 98 | $translationText, 99 | $this->translationKey($translationKey, $parentKey) 100 | ); 101 | 102 | if ($result === null) { 103 | continue; 104 | } 105 | 106 | $this->lintingResults[] = $result; 107 | } 108 | } 109 | 110 | private function translationKey($translationKey, $parentKey = null): string 111 | { 112 | return $parentKey ? $parentKey.'.'.$translationKey : $translationKey; 113 | } 114 | 115 | /** 116 | * @param string $translationKey 117 | * @param string $translationText 118 | * @return array|null 119 | * 120 | * @throws LinterException 121 | */ 122 | public function lintSingleTranslation(string $translationKey, string $translationText): array|null 123 | { 124 | return $this->lintString($translationText, $translationKey); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/Linter/Vale.php: -------------------------------------------------------------------------------- 1 | valePath = __DIR__.'/../../bin/vale-ai'; 39 | $this->resolveValeExecutable(); 40 | $this->writeValeIni(); 41 | $this->handleFileSystem(); 42 | } 43 | 44 | /** 45 | * @throws LinterException 46 | */ 47 | private function resolveValeExecutable() 48 | { 49 | $this->valeExecutable = match (PHP_OS_FAMILY) { 50 | 'Darwin' => './vale-macos ', 51 | 'Windows' => 'vale.exe ', 52 | 'Linux' => './vale-linux ', 53 | default => throw new LinterException('Operating system is not supported: '.PHP_OS_FAMILY), 54 | }; 55 | } 56 | 57 | private function handleFileSystem() 58 | { 59 | if (PHP_OS_FAMILY === 'Windows') { 60 | $this->directorySeparator = '\\'; 61 | } else { 62 | $this->directorySeparator = '/'; 63 | } 64 | } 65 | 66 | /** 67 | * @param string $textToLint 68 | * @param string|null $textIdentifier 69 | * @return array|null 70 | * 71 | * @throws LinterException 72 | */ 73 | public function lintString(string $textToLint, ?string $textIdentifier = null): array|null 74 | { 75 | $process = Process::fromShellCommandline( 76 | $this->valeExecutable.' --output=JSON --ext=".md" "'.$textToLint.'"' 77 | ); 78 | $process->setWorkingDirectory($this->valePath); 79 | $process->run(); 80 | 81 | if (! $process->isSuccessful()) { 82 | throw new ProcessFailedException($process); 83 | } 84 | 85 | $result = json_decode($process->getOutput(), true); 86 | 87 | if (! empty($result)) { 88 | return LintingResult::fromJsonOutput($textIdentifier ?? 'Text', $result)->toArray(); 89 | } 90 | if (! is_array($result)) { 91 | throw new LinterException('Invalid vale output: '.print_r($process->getOutput(), true)); 92 | } 93 | 94 | return null; 95 | } 96 | 97 | /** 98 | * @param $filePath 99 | * @param $textIdentifier 100 | * @return array|null 101 | * 102 | * @throws Exception 103 | */ 104 | public function lintFile($filePath, $textIdentifier): array|null 105 | { 106 | $process = Process::fromShellCommandline( 107 | $this->valeExecutable.' --output=JSON '.$filePath 108 | ); 109 | 110 | $process->setWorkingDirectory($this->valePath); 111 | 112 | $process->run(); 113 | 114 | $result = json_decode($process->getOutput(), true); 115 | 116 | if (! empty($result)) { 117 | return LintingResult::fromJsonOutput($textIdentifier ?? 'Text', $result)->toArray(); 118 | } 119 | if (! is_array($result)) { 120 | throw new Exception('Invalid vale output: '.print_r($process->getOutput(), true)); 121 | } 122 | 123 | return null; 124 | } 125 | 126 | /** 127 | * Build .vale.ini dynamically based on the configuration. 128 | * 129 | * @throws Exception 130 | */ 131 | protected function getAppliedStyles(): string 132 | { 133 | $configuredStyles = config('linter.styles', [\Beyondcode\LaravelProseLinter\Styles\Vale::class]); 134 | 135 | if (count($configuredStyles) == 0) { 136 | throw new Exception('No styles defined. Please check your config (linter.styles)!'); 137 | } 138 | 139 | $styles = []; 140 | foreach ($configuredStyles as $configuredStyle) { 141 | $styleClass = new $configuredStyle(); 142 | $styles[] = $styleClass->getStyleDirectoryName(); 143 | } 144 | 145 | return implode(',', $styles); 146 | } 147 | 148 | private function writeStyles() 149 | { 150 | $stylePath = $this->valePath.'/styles'; 151 | 152 | // clear temporary vale style directory 153 | File::deleteDirectory($stylePath); 154 | 155 | // copy resources from application styles if existing 156 | if (File::exists(resource_path('laravel-prose-linter'))) { 157 | File::copyDirectory( 158 | resource_path('laravel-prose-linter'), 159 | $stylePath 160 | ); 161 | } else { 162 | // copy resources from default 163 | File::copyDirectory(__DIR__.'/../../resources/styles', $stylePath); 164 | } 165 | } 166 | 167 | /** 168 | * Create .vale.ini during runtime. 169 | * 170 | * @throws Exception 171 | */ 172 | public function writeValeIni() 173 | { 174 | $appliedStyles = $this->getAppliedStyles(); 175 | 176 | $this->writeStyles(); 177 | 178 | $valeIni = " 179 | StylesPath = styles 180 | MinAlertLevel = suggestion 181 | 182 | [*.{html,md}] 183 | BasedOnStyles = {$appliedStyles} 184 | "; 185 | File::put($this->valePath.'/.vale.ini', $valeIni); 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /src/Linter/ViewLinter.php: -------------------------------------------------------------------------------- 1 | ignoreDotFiles(true) 20 | ->in(resource_path('views')) 21 | ->exclude($excludedDirectories) 22 | ->name('*.blade.php'); 23 | 24 | $bladeTemplateKeys = new Collection(); 25 | 26 | /** @var SplFileInfo $viewFile */ 27 | foreach ($viewFinder as $viewFile) { 28 | $relativePath = $viewFile->getRelativePath(); 29 | if (! empty($relativePath)) { 30 | $relativePath = $relativePath.'.'; 31 | } 32 | $viewName = $relativePath.$viewFile->getBasename('.blade.php'); 33 | 34 | $bladeTemplateKeys->add(str_replace('/', '.', $viewName)); 35 | } 36 | 37 | return $bladeTemplateKeys; 38 | } 39 | 40 | public function deleteLintableCopy() 41 | { 42 | File::deleteDirectory($this->valePath.'/tmp'); 43 | } 44 | 45 | /** 46 | * @param $templateKey 47 | * @return string 48 | */ 49 | public function createLintableCopy($templateKey): string 50 | { 51 | File::makeDirectory($this->valePath.'/tmp'); 52 | 53 | // copy a view to the tmp 54 | $viewPath = view($templateKey)->getPath(); 55 | 56 | $templateCopyPath = $this->valePath."/tmp/{$templateKey}.blade.html"; 57 | File::copy($viewPath, $templateCopyPath); 58 | 59 | return $templateCopyPath; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/Styles/StyleInterface.php: -------------------------------------------------------------------------------- 1 |