diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml new file mode 100644 index 0000000..9ddb094 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -0,0 +1,50 @@ +name: Bug Report +description: Create a new issue to file a bug report +title: 🐛  +labels: + - bug +body: + - type: input + id: emacs-version + attributes: + label: What version of Emacs are you running? + description: Use `M-x emacs-version` to get it + validations: + required: true + - type: input + id: languagetool-version + attributes: + label: What version of LanguageTool are you running? + description: Run `languagetool` and check it in about + validations: + required: true + - type: input + id: package-version + attributes: + label: What version of this package are you running? + description: You can find it in the header comments of your `.el` files + validations: + required: true + - type: textarea + id: current-behaviour + attributes: + label: What is the current behaviour? + description: Write a concise description of what you're experiencing. + placeholder: Tell us what you see! + render: markdown + - type: textarea + id: expected-behaviour + attributes: + label: What is you expect to happen? + description: Write a concise description of what you expected to happen. + placeholder: Tell us what you want! + render: markdown + - type: textarea + attributes: + label: What do you do to get this bug? + description: Write the steps to reproduce the behavior. + placeholder: | + 1. With this config... + 2. Run '...' + 3. See error... + render: markdown diff --git a/.github/workflows/byte-compile.yml b/.github/workflows/byte-compile.yml index 9a912bd..e76104c 100644 --- a/.github/workflows/byte-compile.yml +++ b/.github/workflows/byte-compile.yml @@ -11,12 +11,20 @@ on: jobs: build: runs-on: ubuntu-latest + strategy: + matrix: + emacs_version: + - 27.1 + - 27.2 + - snapshot steps: - - uses: actions/checkout@v2 + - name: Checkout Repository + uses: actions/checkout@v2 - name: Install emacs - run: sudo apt install emacs - - name: Install packages - run: | - emacs --batch --eval "(progn (require 'package) (add-to-list 'package-archives '(\"melpa\" . \"http://melpa.org/packages/\") t) (package-initialize) (package-refresh-contents) (package-install 'request))" - - name: Byte-compile the code - run: make + uses: purcell/setup-emacs@master + with: + version: ${{ matrix.emacs_version }} + - name: Install required packages and Byte-compile the code + run: make ci + env: + LOAD_PATH: "-f package-initialize -L ." diff --git a/.gitignore b/.gitignore index 206569d..d04bf56 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,6 @@ # Undo-tree save-files *.~undo-tree + +# Environment variables +env.mk diff --git a/ChangeLog.adoc b/ChangeLog.adoc deleted file mode 100644 index 130ce54..0000000 --- a/ChangeLog.adoc +++ /dev/null @@ -1,64 +0,0 @@ -= Change Log -Joar Buitago -v0.4.3, 2020-01-21 - -All notable changes to this project will be documented in this file. - -The format is based on -link:https://keepachangelog.com/en/1.0.0/[Keep a Changelog], -and this project adheres to -link:https://semver.org/spec/v2.0.0.html[Semantic Versioning]. - - - -== [Unreleased] - -== [0.4.3] -=== Fixed -* Error when putting `~` on LanguageTool jar path. (Fix #7) - -== [0.4.2] -=== Fixed -* Error when checking with classes on. (Fix #3) - -== [0.4.1] -=== Fixed -* Support for using class files directly. (Fix #3) - -== [0.4.0] -=== Added -* LanguageTool Server Mode has a delay of two secconds before checking - the buffer when idle. (Close #2) -* LanguageTool Server Mode better support for external servers. - -=== Changed -* `languagetool-clear-buffer` now recheck the buffer when LanguageTool - Server Mode is active -* LanguageTool Server Mode check every time it attemps to first - connect to a server. - -== [0.3.0] -=== Added -* Support to LanguageTool HTTP server mode, and correction in real time. - -== [0.2.0] -=== Added -* Support for different error colourful faces: misspelling, grammar and style. - -=== Fixed -* Jagged minibuffer message - -== [0.1.2] -=== Fixed -* Correction fails if language is set. - -== [0.1.1] -=== Fixed -* Redundant code for obtaining the java arguments. - -== [0.1.0] -=== Added -* Communication with LanguageTool. -* Functions for the user to use. -* Visual overlay to mark corrections. -* Correction through minibuffer. diff --git a/ChangeLog.org b/ChangeLog.org new file mode 100644 index 0000000..be45a13 --- /dev/null +++ b/ChangeLog.org @@ -0,0 +1,88 @@ +* Change Log + +All notable changes to this project will appear in this file. + +The format follows [[https://keepachangelog.com/en/1.0.0/][Keep a Changelog]], and this project adheres to [[https://semver.org/spec/v2.0.0.html][Semantic +Versioning]]. + + + +** [Unreleased] + +** [1.0.0] +*** Added +- ~languagetool-set-language~ has its own history and autocompletion. +- Customizable alist with all the available language codes and names. +- Customizable alist with the issue faces and names. +- New and better documentation in ReadMe file. +- ~languagetool-correction-language~ is buffer local and safe variable. + +*** Changed +- ~languagetool-default-language~ is now ~languagetool-correction-language~. +- ~languagetool-language-tool-jar~ is now ~languagetool-console-command~. +- ~languagetool-language-tool-arguments~ is now ~languagetool-console-arguments~. +- ~langaugetool-server-language-tool-jar~ is now ~languagetool-server-command~. +- ~languagetool-check~ recheck the buffer in LanguageTool Server Mode. +- ~languagetool-clear-buffer~ is now ~languagetool-clear-suggestions~. + +*** Removed +- LanguageTool Server Mode only checks when idle after 3 secconds by default. +- ~languagetool-language-tool-class~ is no longer necessary for class detection. +- ~languagetool-server-delayed-commands~ is no longer available. +- ~languagetool-clear-buffer~ no recheck the buffer when LanguageTool Server + Mode is active. + +*** Fixed +- If using LanguageTool classes, fails invoking LanguageTool. +- Customizable support, now ~customize~ recognize and build correctly the + interface for package variables. + +** [0.4.3] +*** Fixed +- Error when putting `~` on LanguageTool jar path. (Fix #7) + +** [0.4.2] +*** Fixed +- Error when checking with classes on. (Fix #3) + +** [0.4.1] +*** Fixed +- Support for using class files directly. (Fix #3) + +** [0.4.0] +*** Added +- LanguageTool Server Mode has a delay of two secconds before checking the + buffer when idle. (Close #2) +- LanguageTool Server Mode better support for external servers. + +*** Changed +- ~languagetool-clear-buffer~ now recheck the buffer when LanguageTool Server + Mode is active. +- LanguageTool Server Mode check every time it attemps to first connect to a + server. + +** [0.3.0] +*** Added +- Support to LanguageTool HTTP server mode, and correction in real time. + +** [0.2.0] +*** Added +- Support for different error colourful faces: misspelling, grammar and style. + +*** Fixed +- Jagged minibuffer message + +** [0.1.2] +*** Fixed +- Correction fails when language sets. + +** [0.1.1] +*** Fixed +- Redundant code for obtaining the java arguments. + +** [0.1.0] +*** Added +- Communication with LanguageTool. +- Functions for the user to use. +- Visual overlay to mark corrections. +- Correction through minibuffer. diff --git a/License.adoc b/License.org similarity index 85% rename from License.adoc rename to License.org index 622171e..ff77574 100644 --- a/License.adoc +++ b/License.org @@ -1,4 +1,4 @@ -= GNU GENERAL PUBLIC LICENSE +* GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 @@ -8,7 +8,7 @@ Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. -== Preamble +** Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. @@ -73,9 +73,9 @@ assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. -== TERMS AND CONDITIONS +** TERMS AND CONDITIONS -=== 0. Definitions. +*** 0. Definitions. "This License" refers to version 3 of the GNU General Public License. @@ -115,7 +115,7 @@ work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. -=== 1. Source Code. +*** 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of @@ -156,7 +156,7 @@ regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. -=== 2. Basic Permissions. +*** 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated @@ -181,7 +181,7 @@ Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. -=== 3. Protecting Users' Legal Rights From Anti-Circumvention Law. +*** 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article @@ -197,7 +197,7 @@ operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. -=== 4. Conveying Verbatim Copies. +*** 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and @@ -210,31 +210,30 @@ recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. -=== 5. Conveying Modified Source Versions. +*** 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: -[loweralpha] -. The work must carry prominent notices stating that you modified it, - and giving a relevant date. -. The work must carry prominent notices stating that it is released - under this License and any conditions added under section 7. This - requirement modifies the requirement in section 4 to "keep intact - all notices". -. You must license the entire work, as a whole, under this License to - anyone who comes into possession of a copy. This License will - therefore apply, along with any applicable section 7 additional - terms, to the whole of the work, and all its parts, regardless of - how they are packaged. This License gives no permission to license - the work in any other way, but it does not invalidate such - permission if you have separately received it. -. If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your work - need not make them do so. +a. The work must carry prominent notices stating that you modified it, + and giving a relevant date. +b. The work must carry prominent notices stating that it is released + under this License and any conditions added under section 7. This + requirement modifies the requirement in section 4 to "keep intact + all notices". +c. You must license the entire work, as a whole, under this License to + anyone who comes into possession of a copy. This License will + therefore apply, along with any applicable section 7 additional + terms, to the whole of the work, and all its parts, regardless of + how they are packaged. This License gives no permission to license + the work in any other way, but it does not invalidate such + permission if you have separately received it. +d. If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your work + need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, @@ -246,50 +245,49 @@ beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. -=== 6. Conveying Non-Source Forms. +*** 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: -[loweralpha] -. Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium customarily - used for software interchange. -. Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a written - offer, valid for at least three years and valid for as long as you - offer spare parts or customer support for that product model, to - give anyone who possesses the object code either (1) a copy of the - Corresponding Source for all the software in the product that is - covered by this License, on a durable physical medium customarily - used for software interchange, for a price no more than your - reasonable cost of physically performing this conveying of source, - or (2) access to copy the Corresponding Source from a network server - at no charge. -. Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This alternative - is allowed only occasionally and noncommercially, and only if you - received the object code with such an offer, in accord with - subsection 6b. -. Convey the object code by offering access from a designated place - (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) that - supports equivalent copying facilities, provided you maintain clear - directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. -. Convey the object code using peer-to-peer transmission, provided you - inform other peers where the object code and Corresponding Source of - the work are being offered to the general public at no charge under - subsection 6d. +a. Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium customarily + used for software interchange. +b. Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a written + offer, valid for at least three years and valid for as long as you + offer spare parts or customer support for that product model, to + give anyone who possesses the object code either (1) a copy of the + Corresponding Source for all the software in the product that is + covered by this License, on a durable physical medium customarily + used for software interchange, for a price no more than your + reasonable cost of physically performing this conveying of source, + or (2) access to copy the Corresponding Source from a network server + at no charge. +c. Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This alternative + is allowed only occasionally and noncommercially, and only if you + received the object code with such an offer, in accord with + subsection 6b. +d. Convey the object code by offering access from a designated place + (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) that + supports equivalent copying facilities, provided you maintain clear + directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. +e. Convey the object code using peer-to-peer transmission, provided you + inform other peers where the object code and Corresponding Source of + the work are being offered to the general public at no charge under + subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be @@ -343,7 +341,7 @@ documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. -=== 7. Additional Terms. +*** 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. @@ -365,24 +363,23 @@ Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: -[loweralpha] -. Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or -. Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or -. Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or -. Limiting the use for publicity purposes of names of licensors or - authors of the material; or -. Declining to grant rights under trademark law for use of some trade - names, trademarks, or service marks; or -. Requiring indemnification of licensors and authors of that material - by anyone who conveys the material (or modified versions of it) with - contractual assumptions of liability to the recipient, for any - liability that these contractual assumptions directly impose on - those licensors and authors. +a. Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or +b. Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or +c. Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or +d. Limiting the use for publicity purposes of names of licensors or + authors of the material; or +e. Declining to grant rights under trademark law for use of some trade + names, trademarks, or service marks; or +f. Requiring indemnification of licensors and authors of that material + by anyone who conveys the material (or modified versions of it) with + contractual assumptions of liability to the recipient, for any + liability that these contractual assumptions directly impose on + those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you @@ -403,7 +400,7 @@ Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. -=== 8. Termination. +*** 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or @@ -431,7 +428,7 @@ this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. -=== 9. Acceptance Not Required for Having Copies. +*** 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work @@ -442,7 +439,7 @@ modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. -=== 10. Automatic Licensing of Downstream Recipients. +*** 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and @@ -467,7 +464,7 @@ rights granted under this License, and you may not initiate litigation any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. -=== 11. Patents. +*** 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The @@ -536,7 +533,7 @@ Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. -=== 12. No Surrender of Others' Freedom. +*** 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not @@ -549,7 +546,7 @@ from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. -=== 13. Use with the GNU Affero General Public License. +*** 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed @@ -560,7 +557,7 @@ but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. -=== 14. Revised Versions of this License. +*** 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions @@ -586,7 +583,7 @@ permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. -=== 15. Disclaimer of Warranty. +*** 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT @@ -598,7 +595,7 @@ PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. -=== 16. Limitation of Liability. +*** 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR @@ -610,7 +607,7 @@ LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. -=== 17. Interpretation of Sections 15 and 16. +*** 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, @@ -621,7 +618,7 @@ copy of the Program in return for a fee. END OF TERMS AND CONDITIONS -=== How to Apply These Terms to Your New Programs +*** How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it @@ -633,7 +630,7 @@ attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. ----- +#+BEGIN_SRC org Copyright (C) @@ -649,7 +646,7 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ----- +#+END_SRC Also add information on how to contact you by electronic and paper mail. @@ -657,12 +654,12 @@ mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: ----- +#+BEGIN_SRC org Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. ----- +#+END_SRC The hypothetical commands \`show w' and \`show c' should show the appropriate parts of the General Public License. Of course, your diff --git a/ReadMe.adoc b/ReadMe.adoc deleted file mode 100644 index 741208e..0000000 --- a/ReadMe.adoc +++ /dev/null @@ -1,179 +0,0 @@ -= languagetool.el -Joar Buitago -v0.4.3, 2020-01-21 - -[.text-center] --- -link:https://melpa.org/\#/languagetool[image:https://melpa.org/packages/languagetool-badge.svg[MELPA]] -link:https://stable.melpa.org/\#/languagetool[image:https://stable.melpa.org/packages/languagetool-badge.svg[MELPA Stable]] -image:https://github.com/PillFall/Emacs-LanguageTool.el/workflows/build/badge.svg[build] --- - -Use LanguageTool as your Emacs grammar, spelling and styling -corrector. - -.Package in action. Text from https://languagetool.org/ -image::https://i.stack.imgur.com/1oqM7.png[Package in action] - -This package was based on -link:https://github.com/mhayashi1120/Emacs-langtool/[mhayashi1120 -Emacs-Langtool], it's intended to make a pipeline through Emacs and -parse JSON output, making it incompatible with the original version, -that's why this package is not a forked version and it's made from -scratch. - -LanguageTool is an open source proof-reading tool built in Java which -supports more than 30 languages. - - - -== Installation - -=== Obtaining the prerequisites - -The first thing you need is *Java*, so download and install it. - -You also need to download -*link:https://languagetool.org/download/[LanguageTool]* from its -official page. - -After download, extract the contents in a folder you want -(e.g. `~/.languagetool/`). - - -=== Configuring the package - -Next you need to set the package configuration. First we add the path -to the `languagetool-commandline.jar` file in your `.emacs` file like: - -[source, lisp] ----- -(setq languagetool-language-tool-jar - "~/.languagetool/languagetool-commandline.jar") ----- - -Or via customize. It's highly encouraged to use full path instead. - -If you are going to use the LanguageTool HTTP Server instead, you need -to add to your `.emacs` file: - -[source, lisp] ----- -(setq languagetool-server-language-tool-jar - "~/.languagetool/languagetool-server.jar") -(languagetool-server-start) ----- - -It's recommended to add a Java flag to process the files in UTF-8, so -you should add to your `.emacs`: - -[source, lisp] ----- -(setq languagetool-java-arguments '("-Dfile.encoding=UTF-8")) ----- - -And finally you should tell which language are you using by default in -your `.emacs` file like: - -[source, lisp] ----- -(setq languagetool-default-language "en-GB") ----- - -You can change this latter using `languagetool-set-language` function. - - - -== Usage - -There are 5 commands than can be used directly and bound to a key if -desired. - -* `languagetool-check` To check the active region or the buffer. -* `languagetool-clear-buffer` To delete all the buffer highlights. -* `languagetool-correct-at-point` To correct the warning at point. -* `languagetool-correct-buffer` To correct the whole buffer. -* `languagetool-set-language` To change correction language. - -For this package the suggested keybinding is: - -[source, lisp] ----- -(global-set-key (kbd "C-c l c") 'languagetool-check) -(global-set-key (kbd "C-c l d") 'languagetool-clear-buffer) -(global-set-key (kbd "C-c l p") 'languagetool-correct-at-point) -(global-set-key (kbd "C-c l b") 'languagetool-correct-buffer) -(global-set-key (kbd "C-c l l") 'languagetool-set-language) ----- - -Once you are correcting with build in functions it will pop-up a -correction buffer at the minibuffer, you only need to choose. - -Or you can use a LanguageTool HTTP Server with -`languagetool-server-mode`. In this mode the function -`languagetool-check` will be undefined in the current buffer to -prevent checking with another tool. `languagetool-clear-buffer` -clears and recheck the buffer in case of needed. The other functions -works as usual. - - - -== Customizable Variables - -For this package there are a few customizable variables that should be -set in order to take control of LanguageTool suggestions. - -* `languagetool-java-bin` Sets the path to the JAVA executable, if it - is in `PATH` only it's name is needed. Set to `"java"` by default. -* `languagetool-java-arguments` If you need to pass arguments to JAVA - set this variable. It must be a list of strings, one string for - each argument without spaces. -+ -e.g. -+ -[source,lisp] ----- -(setq languagetool-java-arguments '("-flag1" "-flag2")) -;; or -(setq languagetool-java-arguments '("-flag" "argumentforflag") ----- -* `languagetool-language-tool-class` Tell the package that the package - will use the class files directly. Set to `nil` by default. -* `languagetool-language-tool-jar` Sets the path of the LanguageTool - command line jar file. It's recommended to put the full path. -* `languagetool-language-tool-arguments` Sets arguments to pass to - LanguageTool. It must be a list of strings, one string for each - argument without spaces. -+ -e.g. -+ -[source,lisp] ----- -(setq languagetool-language-tool-arguments '("-flag1" "-flag2")) -;; or -(setq languagetool-language-tool-arguments '("-flag" "argumentforflag") ----- -* `languagetool-server-language-tool-jar` Sets the path of the - LanguageTool Server jar file. It's recommended to put the full - path. -* `languagetool-default-language` Sets the LanguageTool correction - language. It can be changed via command with - `languagetool-set-language`. -* `languagetool-mother-tongue` If set LanguageTool behaviour change to - correct using `languagetool-default-language` but being aware of - false friends. -* `languagetool-disabled-rules` Tells LanguageTool to ignore these - rules when checking text. -* `languagetool-local-disabled-rules` As `languagetool-disabled-rules` - tells LanguageTool to ignore these rules when checking text but it's - buffer local, so if you want you can set it in your local variables - comment. -* `languagetool-server-url` Tells LanguageTool Server mode to send - POST request to this url. Set to `"http://localhost"` by default. -* `languagetool-server-port` Tells LanguageTool Server mode to send - Post request to this port. Set to `8081` by default. -* `languagetool-server-delayed-commands` A list of hooks for sending a - request to the server. -* `languegetool-server-request-delay` Controls the number og seconts - while idle before this package sends a new request to the - LanguegeTool Server. diff --git a/ReadMe.org b/ReadMe.org new file mode 100644 index 0000000..a9b197c --- /dev/null +++ b/ReadMe.org @@ -0,0 +1,203 @@ +#+BEGIN_HTML +

+ languagetool.el logo +

+

languagetool.el

+

+ languagetool.el in action +

+
+

+ + MELPA Version + + + MELPA Stable Version + + + CI Status + +

+#+END_HTML + +** Introduction + +LanguageTool is a free and open-source multilingual grammar, style, and spell +checker with support for more than 30 languages. So, why not use it as your +Emacs grammar and spell check tool. That is what this package is for. + +*languagetool.el* is a tool written for Emacs keeping in mind integrity and +display. In a way that you can see all the issues which LanguageTool generates +for your text and which kind of issue is, by following a colour key. + +LanguageTool standalone version comes with tree different executables written in +Java: + +- The first is the /GUI/, to use LanguageTool directly. +- The other is the /Command Line Interface/, to check text in this way. +- And the final one is the /Server/, to create an HTTP server to check text via + requests. + +*languagetool.el* creates a wrapper for two of these three tools, the /CLI/ +(~console~ module) and the /Server/ (~server~ module). + + + +** Installation + +*** Obtaining the prerequisites + +As LanguageTool is code in /Java/, you need to install that on your system. +LanguageTool requires Java 8 or later to work. + +You'll need (obviously) LanguageTool itself, you can download it in this [[https://languagetool.org/download/][link]] +or, if available, with your package manager. + +If you download LanguageTool Zip file, extract the contents in a folder you +want; for example ~/home/pillfall/.languagetool/~, if you use LanguageTool with +other tools could be a great option. + +In case you use your package manager, find out if it download the class files or +the standalone executable and where it put them, this is useful for later +configuration. For example /ArchLinux/ download the class files and puts them in +the ~/usr/share/languagetool~ and ~/usr/share/java/languagetool~ directories. + +*** Obtaining the package + +You can install the package using the MELPA repository or via ~use-package~. + +In case you use the latter and get the LanguageTool Zip, one default +configuration, enabling all current features, would be: + +#+BEGIN_SRC elisp +(use-package languagetool + :ensure t + :defer t + :commands (languagetool-check + languagetool-clear-suggestions + languagetool-correct-at-point + languagetool-correct-buffer + languagetool-set-language + languagetool-server-mode + languagetool-server-start + languagetool-server-stop) + :config + (setq languagetool-java-arguments '("-Dfile.encoding=UTF-8") + languagetool-console-command "~/.languagetool/languagetool-commandline.jar" + languagetool-server-command "~/.languagetool/languagetool-server.jar")) +#+END_SRC + +If you want to install it from source, you can clone this repository and do all +the configuration manually (create a ~package.el~, generate ~autoloads.el~, +etc.). Right now, there are no installation steps following this approach, so +you'll need to know what you are doing, but there is a handy makefile which byte +compiles all the elisp files. + +*** Configuring the package + +To get this package to work, you'll need to configure some variables. + +First you must configure Java to accept files in UTF-8, for that purpose, set +~languagetool-java-arguments~ using /customize/ interface or with elisp like +follows: + +#+BEGIN_SRC elisp +(setq languagetool-console-command '("-Dfile.encoding=UTF-8")) +#+END_SRC + +Then you need to tell the package how do you plan to call LanguageTool. + +If you download, extracted and plan to use the LanguageTool Zip, set +~languagetool-console-command~ and ~languagetool-server-command~ using +/customize/ interface or with elisp like follows: + +#+BEGIN_SRC elisp +(setq languagetool-console-command "~/.languagetool/languagetool-commandline.jar" + languagetool-server-command "~/.languagetool/languagetool-server.jar") +#+END_SRC + +If you are going to use a package manager, and it download the class files, you +will call LanguageTool from its class, so set ~languagetool-java-arguments~, +~languagetool-console-command~ and ~languagetool-server-command~ using +/customize/ interface or with elisp like follows: + +#+BEGIN_SRC elisp +(setq languagetool-java-arguments '("-Dfile.encoding=UTF-8" + "-cp" "/usr/share/languagetool:/usr/share/java/languagetool/*") + languagetool-console-command "org.languagetool.commandline.Main" + languagetool-server-command "org.languagetool.server.HTTPServer") +#+END_SRC + +By default, the correction language is ~auto~ and it is buffer local, meaning +that you could check in different languages different files, if you want to +change the language, you could use [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Specifying-File-Variables.html][local file variables]] to define the language +to use in that buffer, setting the variable ~languagetool-correction-language~, +or call ~languagetool-set-language~. + +If you want to know more customization options you can find those at the +/customize/ interface. + + + +** Quick Usage + +When you end customizing the packages (faces, languages, etc.). You can now +start checking your text. So, you can use either ~console~ mode or ~server~ +mode. + +*** ~console~ Mode +:PROPERTIES: +:CUSTOM_ID: console-mode +:END: + +In this mode, when you start checking, the first thing you need to do is call +~languagetool-check~. This will invoke LanguageTool in the current region, if +any, and then highlight all the suggestions made by the tool. If there is no +region, the whole available portion of the buffer will check. + +This function is synchronous. Therefore, it blocks Emacs until LanguageTool done +with your text. This is the right behaviour, as LanguageTool is a bit slow +checking text in this mode, so it prevents you from changing the text while +checking. + +After LanguageTool highlights all its suggestions, now you can correct your +text, then put your cursor on the underlined word and call +~languagetool-correct-at-point~, this will pop up +a transient minibuffer with all the suggestions, choose the one fits your needs, +and you are ready to go. There is also a buffer wide correction function, called +~languagetool-correct-buffer~, you can call it if you want to check all the +buffer, suggestion by suggestion. + +If you finish, and don't want to see any more suggestions, call +~languagetool-clear-suggestions~ and all the highlighting will disappear. + +*** ~server~ Mode + +In this mode, you first start having a running the server. To initialize it, you +can call ~languagetool-server-start~, then you'll have a running server attached +to Emacs (If you close Emacs, it's over). This server starts to listen in port +~8081~ by default. You can change it by setting ~languagetool-server-port~ to +another value. + +If you are going to use a server with another configuration, like servers not +located in your localhost, you must set ~languagetool-server-host~ and +~languagetool-server-port~ to whatever adjust your needs. These variables play +in the communication to the LanguageTool HTTP API. + +After your server is running, you can toggle on the ~languagetool-server-mode~. +LanguageTool then starts to highlight all its suggestions in the available +portion of the buffer. You use the same method as in [[#console-mode][~console~ mode]] to correct +your text. + +If you finish, just toggle off the ~languagetool-server-mode~, it will take all +the LanguageTool suggestions with itself. diff --git a/languagetool-console.el b/languagetool-console.el new file mode 100644 index 0000000..3a4444c --- /dev/null +++ b/languagetool-console.el @@ -0,0 +1,243 @@ +;;; languagetool-console.el --- LanguageTool Console commands -*- lexical-binding: t; -*- + +;; Copyright (C) 2020-2022 Joar Buitrago + +;; Author: Joar Buitrago +;; Keywords: grammar text docs tools convenience checker +;; URL: https://github.com/PillFall/Emacs-LanguageTool.el +;; Version: 1.0.0 +;; Package-Requires: ((emacs "27.0") (request "0.3.2")) + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . + +;;; Commentary: + +;; LanguageTool commands and variables to use LanguageTool via +;; languagetool-commandline.jar or org.languagetool.commandline.Main. + +;;; Code: + +(require 'json) +(require 'languagetool-core) +(require 'languagetool-issue) +(require 'languagetool-java) + +;; Group definition: + +(defgroup languagetool-console nil + "LanguageTool command line parser and checking" + :tag "Console" + :prefix "languagetool-console-" + :group 'languagetool) + +;; Variable definitions: + +(defcustom languagetool-console-command nil + "LanguageTool Command Line path or class. + +When using LanguageTool you should set this variable to either +the path or the class to call LanguageTool." + :group 'languagetool-console + :type '(choice + file + string)) + +(defcustom languagetool-console-arguments nil + "LanguageTool Command Line extra arguments. + +More info at http://wiki.languagetool.org/command-line-options." + :group 'languagetool-console + :type '(choice + (const nil) + (repeat string))) + +(defvar languagetool-console-output-buffer-name "*LanguageTool Output*" + "LanguageTool Console output buffer for debugging. + +This buffer is also used for parsing the output from LanguageTool +Command Line.") + +(defvar-local languagetool-console-output-parsed nil + "LanguageTool Console last parsed output from working on current buffer.") + +;;; Function definitions: + +(defun languagetool-console-class-p () + "Return nil if `languagetool-console-command' is not a Java class." + (let ((regex (rx + line-start + (zero-or-more + (group + (in alpha ?_ ?$) + (zero-or-more + (in alnum ?_ ?$)) + ?.)) + (in alpha ?_ ?$) + (zero-or-more + (in alnum ?_ ?$)) + line-end))) + (string-match-p regex languagetool-console-command))) + +(defun languagetool-console-command-exists-p () + "Return t is `languagetool-console-command' can be used or exists. + +Also sets `languagetool-console-command' to a full path if needed +for this package to work." + (or (languagetool-console-class-p) + (when (file-readable-p (file-truename languagetool-console-command)) + (setq languagetool-console-command (file-truename languagetool-console-command)) + t))) + +(defun languagetool-console-parse-arguments () + "Return the LanguageTool Command Line arguments as a list." + (unless (listp languagetool-console-arguments) + (error "LanguageTool Console Arguments must be a list of strings")) + + (let ((arguments nil)) + + ;; Appends LanguageTool Console Command + (unless (languagetool-console-class-p) + (setq arguments (append arguments (list "-jar")))) + (setq arguments (append arguments (list languagetool-console-command))) + + ;; Appends the LanguageTool arguments + (setq arguments (append arguments languagetool-console-arguments)) + + ;; Appends the common arguments + (setq arguments (append arguments + (list "--encoding" "utf8") + (list "--json"))) + + ;; Appends the correction language information + (if (string= languagetool-correction-language "auto") + (setq arguments (append arguments (list "--autoDetect"))) + (setq arguments (append arguments (list "--language" languagetool-correction-language)))) + + ;; Appends the mother tongue information + (when (stringp languagetool-mother-tongue) + (setq arguments (append arguments (list "--mothertongue" languagetool-mother-tongue)))) + + ;; Appends the disabled rules + (let ((rules "")) + ;; Global disabled rules + (dolist (rule languagetool-disabled-rules) + (if (string= rules "") + (setq rules (concat rules rule)) + (setq rules (concat rules "," rule)))) + ;; Local disabled rules + (dolist (rule languagetool-local-disabled-rules) + (if (string= rules "") + (setq rules (concat rules rule)) + (setq rules (concat rules "," rule)))) + (unless (string= rules "") + (setq arguments (append arguments (list "--disable" rules))))) + arguments)) + +(defun languagetool-console-write-debug-info (text) + "Write debug info in `languagetool-console-output-buffer-name'. + +The argument TEXT is the region passed to LanguageTool for +checking." + (insert (propertize " ----- LanguageTool Command:" 'face 'font-lock-warning-face) + "\n\n") + (insert languagetool-java-bin " " + (mapconcat (lambda (x) (format "%s" x)) (append + (languagetool-console-parse-arguments) + (languagetool-java-parse-arguments)) " ") + "\n\n\n\n") + (insert (propertize " ----- LanguageTool Text:" 'face 'font-lock-warning-face) + "\n\n") + (insert text "\n\n\n\n") + (insert (propertize " ----- LanguageTool Output:" 'face 'font-lock-warning-face) + "\n\n")) + +(defun languagetool-console-invoke-command-region (begin end) + "Invoke LanguageTool passing the current region to STDIN. + +The region is delimited by BEGIN and END." + (languagetool-core-clear-buffer) + (unless (executable-find languagetool-java-bin) + (error "Java could not be found")) + (unless languagetool-console-command + (error "LanguageTool Console Command is empty")) + (unless (languagetool-console-command-exists-p) + (error "LanguageTool Console Command could not be found")) + + (save-excursion + (let ((status 0) + (buffer (get-buffer-create languagetool-console-output-buffer-name)) + (text (buffer-substring-no-properties begin end)) + (json-parsed nil)) + (with-current-buffer buffer + (erase-buffer) + (languagetool-console-write-debug-info text)) + (setq status + (apply #'call-process-region begin end + languagetool-java-bin + nil + languagetool-console-output-buffer-name + nil + (append + (languagetool-java-parse-arguments) + (languagetool-console-parse-arguments)))) + (when (/= status 0) + (error "LanguageTool returned with status %d" status)) + (with-current-buffer buffer + (widen) + (goto-char (point-max)) + (backward-sexp) + (setq json-parsed (json-read))) + (setq languagetool-console-output-parsed json-parsed))) + (pop-mark)) + +(defun languagetool-console-check (begin end) + "Show LanguageTool Console suggestions in the buffer. + +This function checks for the region delimited by BEGIN and END." + (languagetool-console-invoke-command-region begin end) + (if (languagetool-console-matches-exists-p) + (progn + (message (substitute-command-keys "LangugeTool finished. +Use \\[languagetool-correct-buffer] to correct the buffer.")) + (languagetool-console-highlight-matches begin) + (run-hooks 'languagetool-error-exists-hook)) + (progn + (message "LanguageTool finished. +Found no errors.") + (run-hooks 'languagetool-no-error-hook)))) + +(defun languagetool-console-matches-exists-p () + "Return t if issues where found by LanguageTool or nil otherwise." + (/= 0 (length (cdr (assoc 'matches languagetool-console-output-parsed))))) + +(defun languagetool-console-highlight-matches (begin) + "Highlight issues in the buffer. + +BEGIN defines the start of the current region." + (let ((corrections (cdr (assoc 'matches languagetool-console-output-parsed))) + (correction nil)) + (dotimes (index (length corrections)) + (setq correction (aref corrections index)) + (let ((offset (cdr (assoc 'offset correction))) + (size (cdr (assoc 'length correction)))) + (languagetool-issue-create-overlay + (+ begin offset) (+ begin offset size) + correction)))) + (setq languagetool-core-hint-timer + (run-with-idle-timer languagetool-hint-idle-delay t + languagetool-hint-function))) + +(provide 'languagetool-console) + +;;; languagetool-console.el ends here diff --git a/languagetool-core.el b/languagetool-core.el new file mode 100644 index 0000000..530e2e2 --- /dev/null +++ b/languagetool-core.el @@ -0,0 +1,216 @@ +;;; languagetool-core.el --- LanguageTool Core module -*- lexical-binding: t; -*- + +;; Copyright (C) 2020-2022 Joar Buitrago + +;; Author: Joar Buitrago +;; Keywords: grammar text docs tools convenience checker +;; URL: https://github.com/PillFall/Emacs-LanguageTool.el +;; Version: 1.0.0 +;; Package-Requires: ((emacs "27.0") (request "0.3.2")) + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . + +;;; Commentary: + +;; LanguageTool Core functions and packages + +;;; Code: + +;; Variable definitions: + + +(defcustom languagetool-correction-language "auto" + "LanguageTool correction and checking language. + +This is a string which indicate the language LanguageTool assume +the text is written in or \"auto\" for automatic calculation." + :group 'languagetool + :local t + :safe #'languagetool-core-safe-language + :type '(choice + string + (const "auto"))) + +(defcustom languagetool-mother-tongue nil + "Your mother tongue for being aware of false friends. + +As in some languages two differents word can look or sound +similar, but differ in meaning. + +For example in English you have the word \"abnegation\", that is +translated to Polish as \"poświęcenie\". But there is a word in +Polish \"abnegacja\" which means slovenliness or untidiness, +which can be mistranslated." + :group 'languagetool + :type '(choice + (const nil) + string)) + +(defcustom languagetool-core-languages + '(("auto" . "Automatic Detection") + ("ar" . "Arabic") + ("ast-ES" . "Asturian") + ("be-BY" . "Belarusian") + ("br-FR" . "Breton") + ("ca-ES" . "Catalan") + ("ca-ES-valencia" . "Catalan (Valencian)") + ("zh-CN" . "Chinese") + ("da-DK" . "Danish") + ("nl" . "Dutch") + ("nl-BE" . "Dutch (Belgium)") + ("en" . "English") + ("en-AU" . "English (Australian)") + ("en-CA" . "English (Canadian)") + ("en-GB" . "English (GB)") + ("en-NZ" . "English (New Zealand)") + ("en-ZA" . "English (South African)") + ("en-US" . "English (US)") + ("eo" . "Esperanto") + ("fr" . "French") + ("gl-ES" . "Galician") + ("de" . "German") + ("de-AT" . "German (Austria)") + ("de-DE" . "German (Germany)") + ("de-CH" . "German (Swiss)") + ("el-GR" . "Greek") + ("ga-IE" . "Irish") + ("it" . "Italian") + ("ja-JP" . "Japanese") + ("km-KH" . "Khmer") + ("nb" . "Norwegian (Bokmål)") + ("no" . "Norwegian (Bokmål)") + ("fa" . "Persian") + ("pl-PL" . "Polish") + ("pt" . "Portuguese") + ("pt-AO" . "Portuguese (Angola preAO)") + ("pt-BR" . "Portuguese (Brazil)") + ("pt-MZ" . "Portuguese (Moçambique preAO)") + ("pt-PT" . "Portuguese (Portugal)") + ("ro-RO" . "Romanian") + ("ru-RU" . "Russian") + ("de-DE-x-simple-language" . "Simple German") + ("sk-SK" . "Slovak") + ("sl-SI" . "Slovenian") + ("es" . "Spanish") + ("es-AR" . "Spanish (voseo)") + ("sv" . "Swedish") + ("tl-PH" . "Tagalog") + ("ta-IN" . "Tamil") + ("uk-UA" . "Ukrainian")) + "LanguageTool available languages for correction. + +Each element is a cons-cell with the form (CODE . NAME)." + :group 'languagetool + :type '(alist + :key-type (string :tag "Code") + :value-type (string :tag "Name"))) + +(defvar-local languagetool-correction-language-history nil + "Buffer local LanguageTool correction language history.") + +(defcustom languagetool-disabled-rules nil + "LanguageTool global disabled rules." + :group 'languagetool + :type '(choice + (const nil) + (repeat string))) + + +(defcustom languagetool-local-disabled-rules nil + "LanguageTool buffer local disabled rules." + :group 'languagetool + :local t + :type '(choice + (const nil) + (repeat string))) + +(defcustom languagetool-hint-function + 'languagetool-core-hint-default-function + "Display error information in the minibuffer. + +The function must search for overlays at point. You must pass the +function symbol. + +A example hint function: +\(defun hint-function () + \"Hint display function.\" + (dolist (ov (overlays-at (point))) + (when (overlay-get ov 'languagetool-message) + (unless (current-message) + (message + \"%s%s\" (overlay-get ov 'languagetool-short-message) + (if (/= 0 + (length (overlay-get ov 'languagetool-replacements))) + (concat + \" -> (\" + (mapconcat + #'identity (languagetool-core-get-replacements ov) \", \") + \")\") + \"\"))))))" + :group 'languagetool + :type '(choice + (const nil) + function)) + +(defcustom languagetool-hint-idle-delay 0.5 + "Number of seconds idle before showing hint." + :group 'languagetool + :type 'number) + +(defvar languagetool-core-hint-timer nil + "Idle timer that shows the hint in the minibuffer.") + +;; Function defintions: + +(defun languagetool-core-safe-language (lang) + "Return non-nil if LANG is safe to use." + (assoc lang languagetool-core-languages)) + +(defun languagetool-core-clear-buffer () + "Deletes all buffer overlays." + (save-restriction + (widen) + (save-excursion + (dolist (ov (overlays-in (point-min) (point-max))) + (when (overlay-get ov 'languagetool-message) + (delete-overlay ov)))))) + +(defun languagetool-core-hint-default-function () + "Default hint display function." + (dolist (ov (overlays-at (point))) + (when (overlay-get ov 'languagetool-message) + (unless (current-message) + (message + "%s%s" (overlay-get ov 'languagetool-short-message) + (if (/= 0 + (length (overlay-get ov 'languagetool-replacements))) + (concat + " -> (" + (mapconcat + #'identity (languagetool-core-get-replacements ov) ", ") + ")") + "")))))) + +(defun languagetool-core-get-replacements (overlay) + "Return the replacements of OVERLAY in a list." + (let ((replacements (overlay-get overlay 'languagetool-replacements)) + (replace nil)) + (dotimes (index (length replacements)) + (setq replace (append replace + (list (cdr (assoc 'value (aref replacements index))))))) + replace)) + +(provide 'languagetool-core) + +;;; languagetool-core.el ends here diff --git a/languagetool-correction.el b/languagetool-correction.el new file mode 100644 index 0000000..da04081 --- /dev/null +++ b/languagetool-correction.el @@ -0,0 +1,121 @@ +;;; languagetool-correction.el --- LanguageTool Correction module -*- lexical-binding: t; -*- + +;; Copyright (C) 2020-2022 Joar Buitrago + +;; Author: Joar Buitrago +;; Keywords: grammar text docs tools convenience checker +;; URL: https://github.com/PillFall/Emacs-LanguageTool.el +;; Version: 1.0.0 +;; Package-Requires: ((emacs "27.0") (request "0.3.2")) + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . + +;;; Commentary: + +;; LanguageTool Correction routines and variables. + +;;; Code: + +(require 'cl-lib) +(require 'languagetool-core) + +;; Variable definitions: + +(defvar languagetool-correction-keys + [?1 ?2 ?3 ?4 ?5 ?6 ?7 ?8 ?9 ?0 + ;; suggestions may over 10. + ;; define rest of alphabet just in case. + ?a ?b ?c ?d ?e ?f ?g ?h ?i ?j + ?k ?l ?m ?n ?o ?p ?q ?r ?s ?t + ?u ?v ?w ?x ?y ?z] + "LanguageTool suggestion keys.") + +;; Function definitions: + +(defun languagetool-correction-parse-message (overlay) + "Parse and style minibuffer correction. + +Get the information about corrections from OVERLAY." + (let ((msg nil) + (rule (cdr (assoc 'id (overlay-get overlay 'languagetool-rule)))) + (message (overlay-get overlay 'languagetool-message))) + ;; Add LanguageTool rule to the message + (setq msg (concat msg "[" rule "] ")) + + ;; Add LanguageTool correction suggestion + (setq msg (concat msg (propertize (format "%s" message) 'face 'font-lock-warning-face) "\n")) + + ;; Format all the possible replacements for the correction suggestion + (let ((replacements (languagetool-core-get-replacements overlay))) + (when (< 0 (length replacements)) + (let ((num-choices (length replacements))) + ;; If can't assoc each replacement with each hotkey + (when (> (length replacements) (length languagetool-correction-keys)) + (setq num-choices (length languagetool-correction-keys)) + (setq msg (concat msg "Not all choices shown.\n"))) + (setq msg (concat msg "\n")) + ;; Format all choices + (dotimes (index num-choices) + (setq msg (concat msg + "[" + (propertize + (format "%c" (aref languagetool-correction-keys index)) + 'face 'font-lock-keyword-face) + "]: ")) + (setq msg (concat msg (nth index replacements) " ")))))) + ;; Add default Ignore and Skip options + (setq msg (concat msg "\n[" + (propertize "C-i" 'face 'font-lock-keyword-face) + "]: Ignore ")) + (setq msg (concat msg "[" + (propertize "C-s" 'face 'font-lock-keyword-face) + "]: Skip\n")) + msg)) + +(defun languagetool-correction-apply (pressed-key overlay) + "Correct text marked by LanguageTool with user choice. + +PRESSED-KEY is the index of the suggestion in the array contained +on OVERLAY." + (cond + ((char-equal ?\C-i pressed-key) + (progn + (goto-char (overlay-end overlay)) + (delete-overlay overlay))) + ((char-equal ?\C-s pressed-key) + (goto-char (overlay-end overlay))) + ((not (cl-position pressed-key languagetool-correction-keys)) + (error "Key `%c' cannot be used" pressed-key)) + (t + (let ((size (length (languagetool-core-get-replacements overlay))) + (pos (cl-position pressed-key languagetool-correction-keys))) + (when (> (1+ pos) size) + (error "Correction key `%c' cannot be used" pressed-key)) + (delete-region (overlay-start overlay) (overlay-end overlay)) + (insert (nth pos (languagetool-core-get-replacements overlay))) + (delete-overlay overlay))))) + +(defun languagetool-correction-at-point () + "Show issue at point and try to apply suggestion." + (let (pressed-key) + (dolist (ov (overlays-at (point))) + (when (overlay-get ov 'languagetool-message) + (message nil) + (setq pressed-key + (read-char (languagetool-correction-parse-message ov))) + (languagetool-correction-apply pressed-key ov))))) + +(provide 'languagetool-correction) + +;;; languagetool-correction.el ends here diff --git a/languagetool-issue-faces.el b/languagetool-issue-faces.el deleted file mode 100644 index 6af23a6..0000000 --- a/languagetool-issue-faces.el +++ /dev/null @@ -1,86 +0,0 @@ -;;; languagetool-issue-faces.el --- LanguegeTool correction faces -*- lexical-binding: t; -*- - -;; Copyright (C) 2020 Joar Buitrago - -;; Author: Joar Buitrago -;; Keywords: grammar text docs tools convenience checker -;; URL: https://github.com/PillFall/Emacs-LanguageTool.el -;; Version: 0.4.3 -;; Package-Requires: ((emacs "25.1") (request "0.3.2")) - -;; This program is free software; you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation, either version 3 of the License, or -;; (at your option) any later version. - -;; This program is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with this program. If not, see . - -;;; Commentary: - -;; LanguageTool correction faces. Get from LanguageTool API. -;; -;; Addition, Characters, Duplication, Formatting, Grammar, -;; Inconsistency, InconsistentEntities, Internationalization, Legal, -;; Length, LocaleSpecificContent, LocaleViolation, Markup, -;; Misspelling, Mistranslation, NonConformance, Numbers, Omission, -;; Other, PatternProblem, Register, Style, Terminology, Typographical, -;; Uncategorized, Untranslated, Whitespace - -;;; Code: - -;; Faces: - -(defface languagetool-default-face - '((((class mono)) - :inverse-video t - :underline t) - (((class color)) - :background "yellow" - :foreground "black") - (t (:bold t))) - "Default error face." - :group 'languagetool) - -(defface languagetool-misspelling-face - '((((class mono)) - :inverse-video t - :underline t) - (((class color)) - :background "red" - :foreground "white") - (t :bold t)) - "LanguageTool misspelling error face." - :group 'languagetool) - -(defface languagetool-grammar-face - '((((class mono)) - :inverse-video t - :underline t) - (((class color)) - :background "green" - :foreground "black") - (t :bold t)) - "LanguageTool grammar error face." - :group 'languagetool) - -(defface languagetool-style-face - '((((class mono)) - :inverse-video t - :underline t) - (((class color)) - :background "blue" - :foreground "white") - (t :bold t)) - "LanguageTool style error face." - :group 'languagetool) - - -(provide 'languagetool-issue-faces) - -;;; languagetool-issue-faces.el ends here diff --git a/languagetool-issue.el b/languagetool-issue.el new file mode 100644 index 0000000..842a89c --- /dev/null +++ b/languagetool-issue.el @@ -0,0 +1,123 @@ +;;; languagetool-issue.el --- LanguegeTool issue faces -*- lexical-binding: t; -*- + +;; Copyright (C) 2020-2022 Joar Buitrago + +;; Author: Joar Buitrago +;; Keywords: grammar text docs tools convenience checker +;; URL: https://github.com/PillFall/Emacs-LanguageTool.el +;; Version: 1.0.0 +;; Package-Requires: ((emacs "27.0") (request "0.3.2")) + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . + +;;; Commentary: + +;; LanguageTool correction faces. Get from LanguageTool API. +;; +;; Addition, Characters, Duplication, Formatting, Grammar, +;; Inconsistency, InconsistentEntities, Internationalization, Legal, +;; Length, LocaleSpecificContent, LocaleViolation, Markup, +;; Misspelling, Mistranslation, NonConformance, Numbers, Omission, +;; Other, PatternProblem, Register, Style, Terminology, Typographical, +;; Uncategorized, Untranslated, Whitespace + +;;; Code: + +;; Group definition: + +(defgroup languagetool-issue nil + "LanguageTool faces for marking issues" + :tag "Issue Faces" + :prefix "languagetool-issue-" + :group 'languagetool) + +;; Face definitions: + +(defface languagetool-issue-default + '((((supports :underline (:style wave))) + :underline (:style wave :color "yellow")) + (t + :underline t :inherit error)) + "LanguageTool face for default issues." + :group 'languagetool-issue) + +(defface languagetool-issue-misspelling + '((((supports :underline (:style wave))) + :underline (:style wave :color "red")) + (t + :underline t :inherit error)) + "LanguageTool face for misspelling errors." + :group 'languagetool-issue) + +(defface languagetool-issue-grammar + '((((supports :underline (:style wave))) + :underline (:style wave :color "green")) + (t + :underline t :inherit error)) + "LanguageTool face for grammar errors." + :group 'languagetool-issue) + +(defface languagetool-issue-style + '((((supports :underline (:style wave))) + :underline (:style wave :color "blue")) + (t + :underline t :inherit error)) + "LanguageTool face for style errors." + :group 'languagetool-issue) + +(defcustom languagetool-issue-face-alist + '(("misspelling" . languagetool-issue-misspelling) + ("grammar" . languagetool-issue-grammar) + ("style" . languagetool-issue-style)) + "Alist with issue type associated with it's face. + +Each element is a cons cell with the form (ISSUE_TYPE . FACE_NAME)." + :group 'languagetool-issue + :type '(alist + :key-type (string :tag "Issue Type") + :value-type (face :tag "Face Name"))) + +;; Function definitions: + +(defun languagetool-issue-get-face (issue-type) + "Return the face for ISSUE-TYPE." + (or (cdr (assoc issue-type languagetool-issue-face-alist)) + 'languagetool-issue-default)) + +(defun languagetool-issue-create-overlay (begin end correction) + "Create an overlay for corrections. + +Create an overlay for correction in the region delimited by BEGIN +and END, parsing CORRECTION as overlay properties." + (save-excursion + (let* ((ov (make-overlay begin end)) + (short-message (cdr (assoc 'shortMessage correction))) + (message (cdr (assoc 'message correction))) + (replacements (cdr (assoc 'replacements correction))) + (rule (cdr (assoc 'rule correction))) + (issue-type (cdr (assoc 'issueType rule)))) + (when (string= short-message "") + (setq short-message message)) + (overlay-put ov 'languagetool-short-message short-message) + (overlay-put ov 'languagetool-message message) + (overlay-put ov 'languagetool-replacements replacements) + (overlay-put ov 'languagetool-rule rule) + (overlay-put ov 'help-echo short-message) + (overlay-put ov 'priority 1) + (overlay-put ov 'evaporate t) + (overlay-put ov 'face (languagetool-issue-get-face issue-type))))) + +(provide 'languagetool-issue) + +;;; languagetool-issue.el ends here diff --git a/languagetool-java.el b/languagetool-java.el new file mode 100644 index 0000000..b452660 --- /dev/null +++ b/languagetool-java.el @@ -0,0 +1,87 @@ +;;; languagetool-java.el --- LanguageTool Variables related to Java -*- lexical-binding: t; -*- + +;; Copyright (C) 2020-2022 Joar Buitrago + +;; Author: Joar Buitrago +;; Keywords: grammar text docs tools convenience checker +;; URL: https://github.com/PillFall/Emacs-LanguageTool.el +;; Version: 1.0.0 +;; Package-Requires: ((emacs "27.0") (request "0.3.2")) + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . + +;;; Commentary: + +;; All the variables related to Java management in just one place. + +;;; Code: + +;; Group definition: + +(defgroup languagetool-java nil + "LanguageTool Java related configuration" + :tag "Java" + :prefix "languagetool-java-" + :group 'languagetool) + +;; Variable definitions: + +(defcustom languagetool-java-bin (executable-find "java") + "Java executable path." + :group 'languagetool-java + :type 'file) + +(defcustom languagetool-java-arguments nil + "Java extra arguments. + +Described at http://wiki.languagetool.org/command-line-options, +recommends to use: + +\(setq `languagetool-java-arguments' '(\"-Dfile.encoding=UTF-8\")) + +When using LanguageTool via it classes this variable should be +set to: + +\(setq `languagetool-java-arguments' + '(\"-Dfile.encoding=UTF-8\" + \"-cp\" + \"/path/to/classes:/path/to/classes\")) + +For example to use in Arch Linux (with pacman dependency): + +\(setq `languagetool-java-arguments' + '(\"-Dfile.encoding=UTF-8\" + \"-cp\" + \"/usr/share/languagetool:/usr/share/java/languagetool/*\"))" + :group 'languagetool-java + :type '(choice + (const nil) + (repeat string))) + +;; Function definitions: + +(defun languagetool-java-parse-arguments () + "Return Java parsed arguments as a list." + (unless (listp languagetool-java-arguments) + (error "LanguageTool Java Arguments must be a list of strings")) + + (let ((arguments nil)) + + (setq arguments (append arguments languagetool-java-arguments)) + + arguments)) + +(provide 'languagetool-java) + +;;; languagetool-java.el ends here diff --git a/languagetool-server.el b/languagetool-server.el new file mode 100644 index 0000000..1668427 --- /dev/null +++ b/languagetool-server.el @@ -0,0 +1,346 @@ +;;; languagetool-server.el --- Description -*- lexical-binding: t; -*- + +;; Copyright (C) 2020-2022 Joar Buitrago + +;; Author: Joar Buitrago +;; Keywords: grammar text docs tools convenience checker +;; URL: https://github.com/PillFall/Emacs-LanguageTool.el +;; Version: 1.0.0 +;; Package-Requires: ((emacs "27.0") (request "0.3.2")) + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . + +;;; Commentary: + +;; LanguageTool commands and variables to use LanguageTool via +;; languagetool-server.jar or org.languagetool.server.HTTPServer. + +;;; Code: + +(require 'json) +(require 'cl-lib) +(require 'request) +(require 'languagetool-java) +(require 'languagetool-core) +(require 'languagetool-issue) + +;; Group definition: + +(defgroup languagetool-server nil + "Real time LanguageTool Server" + :tag "Server" + :prefix "languagetool-server-" + :group 'languagetool) + +;; Variable definitions: + +(defcustom languagetool-server-command nil + "LanguageTool Server path or class. + +When using LanguageTool you should set this variable to either +the path or the class to call LanguageTool." + :group 'languagetool-server + :type '(choice + file + string)) + +(defcustom languagetool-server-arguments nil + "LanguageTool Server extra arguments. + +More info at http://wiki.languagetool.org/command-line-options." + :group 'languagetool-server + :type '(choice + (const nil) + (repeat string))) + +(defcustom languagetool-server-url "http://localhost" + "LanguageTool Server host URL." + :group 'languagetool-server + :type 'string) + +(defcustom languagetool-server-port 8081 + "LanguageTool Server host port." + :group 'languagetool-server + :type 'integer) + +(defcustom languagetool-server-max-tries 20 + "LanguageTool Server max number of tries before disconnect from server." + :group 'languagetool-server + :type 'integer) + +(defcustom languagetool-server-check-delay 3.0 + "LanguageTool Server delay time before checking again for issues." + :group 'languagetool-server + :type 'number) + +(defvar languagetool-server-output-buffer-name "*LanguageTool Server Output*" + "LanguageTool Server output buffer for debugging.") + +(defvar languagetool-server-process nil + "LanguageTool Server inferior process reference if any.") + +(defvar-local languagetool-server-check-timer nil + "Hold idle time that send request to LanguageTool server.") + +(defvar languagetool-server-open-communication-p nil + "Set to t if server communication is open, nil otherwise.") + +(defvar languagetool-server-correction-p nil + "Set to t if correcting errors, nil otherwise.") + +;; Function definitions: + +;;;###autoload +(define-minor-mode languagetool-server-mode + "Toggle LanguageTool issue highlighting." + :group 'languagetool-server + :lighter " LT" + (if languagetool-server-mode + (languagetool-server-mode-on) + (languagetool-server-mode-off))) + + +(defun languagetool-server-mode-on () + "Turn on LanguageTool Server mode. + +Don't use this function, use `languagetool-server-mode' instead." + ;; Start checking for LanguageTool server is able to handle requests + (languagetool-server-check-for-communication) + + ;; Init checking timer + (setq languagetool-server-check-timer + (run-with-idle-timer languagetool-server-check-delay t + #'languagetool-server-check)) + + ;; Init hint timer in the current buffer if not already + (setq languagetool-core-hint-timer + (run-with-idle-timer languagetool-hint-idle-delay t + languagetool-hint-function))) + +(defun languagetool-server-mode-off () + "Turn off LanguageTool Server mode. + +Don't use this function, use `languagetool-server-mode' instead." + ;; Turn off buffer local flag for server communication. + (setq languagetool-server-open-communication-p nil) + + ;; Cancel check timer + (when (timerp languagetool-server-check-timer) + (cancel-timer languagetool-server-check-timer)) + + ;; Delete all LanguageTool overlays + (languagetool-core-clear-buffer)) + + +(defun languagetool-server-class-p () + "Return nil if `languagetool-server-command' is not a Java class." + (let ((regex (rx + line-start + (zero-or-more + (group + (in alpha ?_ ?$) + (zero-or-more + (in alnum ?_ ?$)) + ?.)) + (in alpha ?_ ?$) + (zero-or-more + (in alnum ?_ ?$)) + line-end))) + (string-match-p regex languagetool-server-command))) + +(defun languagetool-server-command-exists-p () + "Return t is `languagetool-console-command' can be used or exists. + +Also sets `languagetool-console-command' to a full path if needed +for this package to work." + (or (languagetool-server-class-p) + (when (file-readable-p (file-truename languagetool-server-command)) + (setq languagetool-server-command (file-truename languagetool-server-command)) + t))) + +;;;###autoload +(defun languagetool-server-start () + "Start the LanguageTool Server. + +It's not recommended to run this function more than once." + (interactive) + (unless (process-live-p languagetool-server-process) + (unless (executable-find languagetool-java-bin) + (error "Java could not be found")) + (unless languagetool-server-command + (error "LanguageTool Server Command is not set")) + (unless (languagetool-server-command-exists-p) + (error "LanguageTool Server Command could not be found")) + + (let ((buffer (get-buffer-create languagetool-server-output-buffer-name))) + ;; Clean the buffer before printing the LanguageTool Server Log + (with-current-buffer buffer + (erase-buffer)) + + ;; Start LanguageTool Server + (setq languagetool-server-process + (apply #'start-process + "*LanguageTool Server*" + buffer + languagetool-java-bin + (append + (languagetool-java-parse-arguments) + (languagetool-server-parse-arguments)))) + + ;; Does not block Emacs when close and do not shutdown the server + (set-process-query-on-exit-flag languagetool-server-process nil)) + + ;; Start running the hint idle timer + (setq languagetool-core-hint-timer + (run-with-idle-timer languagetool-hint-idle-delay t + languagetool-hint-function)))) + +(defun languagetool-server-parse-arguments () + "Parse the arguments needed to start HTTP server." + (unless (listp languagetool-server-arguments) + (error "LanguageTool Server Arguments must be a list of strings")) + + (let ((arguments nil)) + + ;; Appends the LanguageTool Server Command + (unless (languagetool-server-class-p) + (setq arguments (append arguments (list "-cp")))) + (setq arguments (append arguments (list languagetool-server-command))) + (unless (languagetool-server-class-p) + (setq arguments (append arguments (list "org.languagetool.server.HTTPServer")))) + + (setq arguments (append arguments languagetool-server-arguments)) + + ;; Appends the port information + (setq arguments (append arguments + (list "--port" + (format "%d" languagetool-server-port)))) + + arguments)) + +;;;###autoload +(defun languagetool-server-stop () + "Stops the LanguageTool Server." + (interactive) + (delete-process languagetool-server-process)) + +(defun languagetool-server-check-for-communication (&optional try) + "Check if the LanguageTool Server is able to handle requests. + +Optional parameter TRY is the try number before Emacs show an error." + (unless try (setq try 1)) + (when (>= try languagetool-server-max-tries) + (languagetool-server-mode -1) + (error "LanguageTool Server cannot communicate with server")) + (unless languagetool-server-open-communication-p + (request + (format "%s:%d/v2/languages" languagetool-server-url languagetool-server-port) + :type "GET" + :parser 'json-read + :success (cl-function + (lambda (&key _response &allow-other-keys) + (message "LanguageTool Server communication is up...") + (setq languagetool-server-open-communication-p t) + (languagetool-server-check))) + :error (cl-function + (lambda (&key _error-thrown &allow-other-keys) + (when (or + languagetool-server-mode + (not languagetool-server-open-communication-p)) + (languagetool-server-check-for-communication (+ try 1)))))))) + +(defun languagetool-server-parse-request () + "Return a json-like object with LanguageTool Server request arguments parsed. + +Return the arguments as an assoc list of string which will be +used in the POST request made to the LanguageTool server." + (let ((arguments (json-new-object))) + + ;; Appends the correction language information + (setq arguments (json-add-to-object arguments + "language" + languagetool-correction-language)) + + ;; Appends the mother tongue information + (when (stringp languagetool-mother-tongue) + (setq arguments (json-add-to-object arguments + "motherTongue" + languagetool-mother-tongue))) + + ;; Appends the disabled rules + (let ((rules "")) + ;; Global disabled rules + (dolist (rule languagetool-disabled-rules) + (if (string= rules "") + (setq rules (concat rules rule)) + (setq rules (concat rules "," rule)))) + ;; Local disabled rules + (dolist (rule languagetool-local-disabled-rules) + (if (string= rules "") + (setq rules (concat rules rule)) + (setq rules (concat rules "," rule)))) + (unless (string= rules "") + (setq arguments (json-add-to-object arguments "disabledRules" rules)))) + + ;; Add the buffer contents + (setq arguments (json-add-to-object arguments + "text" + (buffer-substring-no-properties + (point-min) + (point-max)))) + arguments)) + +(defun languagetool-server-check () + "Show LanguageTool Server suggestions in the buffer. + +This function checks for the actual showed region of the buffer +for suggestions." + (when (and languagetool-server-mode + (not languagetool-server-correction-p)) + (request + (format "%s:%d/v2/check" languagetool-server-url languagetool-server-port) + :type "POST" + :data (languagetool-server-parse-request) + :parser 'json-read + :success (cl-function + (lambda (&key response &allow-other-keys) + (languagetool-server-highlight-matches + (request-response-data response)))) + :error (cl-function + (lambda (&key error-thrown &allow-other-keys) + (languagetool-server-mode -1) + (error + "[Fatal Error] LanguageTool closed and got error: %S" + error-thrown)))))) + +(defun languagetool-server-highlight-matches (json-parsed) + "Highlight LanguageTool Server issues in the buffer. + +JSON-PARSED is a json object with the suggestions thrown by the +LanguageTool Server." + (languagetool-core-clear-buffer) + (when languagetool-server-mode + (let ((corrections (cdr (assoc 'matches json-parsed))) + (correction nil)) + (dotimes (index (length corrections)) + (setq correction (aref corrections index)) + (let ((offset (cdr (assoc 'offset correction))) + (size (cdr (assoc 'length correction)))) + (languagetool-issue-create-overlay + (+ (point-min) offset) (+ (point-min) offset size) + correction)))))) + +(provide 'languagetool-server) + +;;; languagetool-server.el ends here diff --git a/languagetool.el b/languagetool.el index 179d5fb..0a258a2 100644 --- a/languagetool.el +++ b/languagetool.el @@ -1,12 +1,12 @@ ;;; languagetool.el --- LanguageTool integration for grammar and spell check -*- lexical-binding: t; -*- -;; Copyright (C) 2021 Joar Buitrago +;; Copyright (C) 2020-2022 Joar Buitrago ;; Author: Joar Buitrago ;; Keywords: grammar text docs tools convenience checker ;; URL: https://github.com/PillFall/Emacs-LanguageTool.el -;; Version: 0.4.3 -;; Package-Requires: ((emacs "25.1") (request "0.3.2")) +;; Version: 1.0.0 +;; Package-Requires: ((emacs "27.0") (request "0.3.2")) ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by @@ -35,818 +35,96 @@ ;;; Code: -(eval-and-compile (require 'languagetool-issue-faces)) +(require 'languagetool-correction) +(require 'languagetool-console) +(require 'languagetool-server) -(require 'json) -(require 'request) -(eval-when-compile (require 'subr-x)) - - - -;; Group: +;; Group definition: (defgroup languagetool nil - "Spell check with LanguageTool." + "Grammar and spell checking with LanguageTool." :tag "LanguageTool" :prefix "languagetool-" :group 'applications) - - -;; Custom Variables: - -(defcustom languagetool-java-bin "java" - "Java executable path." - :group 'languagetool - :type 'file) - -(defcustom languagetool-java-arguments nil - "List of string passed to java command as arguments. - -Described at http://wiki.languagetool.org/command-line-options, -recommends to use: - -\(setq `langtool-java-arguments' '(\"-Dfile.encoding=UTF-8\"))" - :group 'languagetool - :type '(choice - (list string))) - - - -;; Variables related to LanguageTool Command Line: - -(defcustom languagetool-language-tool-class nil - "Tells if the package is using the LanguageTool classes directly. - -When nil this package will use the -jar flag in java, and will -omit it from arguments otherwise." - :group 'languagetool - :type 'boolean) - -(defcustom languagetool-language-tool-jar nil - "Path to LanguageTool Command Line jar file." - :group 'languagetool - :type 'file) - -(defcustom languagetool-language-tool-arguments nil - "List of string passed to LanguageTool jar as argument. - -More info at http://wiki.languagetool.org/command-line-options" - :group 'languagetool - :type '(choice - (list string))) - -(defvar languagetool-output-buffer-name "*LanguageTool Output*") - -(defcustom languagetool-error-exists-hook nil - "Hook run after LanguageTool process found any error(s)." - :group 'languagetool - :type 'hook) - -(defcustom languagetool-no-error-hook nil - "Hook run after LanguageTool report no error." - :group 'languagetool - :type 'hook) - -(defcustom languagetool-finish-hook nil - "Hook run after cleanup buffer." - :group 'languagetool - :type 'hook) - - - -;; Variables related to LanguageTool Server: - -(defcustom languagetool-server-language-tool-jar nil - "Path to LanguageTool server jar file." - :group 'languagetool - :type 'file) - -(defcustom languagetool-server-url "http://localhost" - "URL to be used to communicate to LanguageTool Server." - :group 'languagetool - :type 'string) - -(defcustom languagetool-server-port 8081 - "Port to be used to communicate to LanguageTool Server." - :group 'languagetool - :type 'integer) - -(defcustom languagetool-server-delayed-commands - '(after-save-hook) - "List of the commands that are searched for sending and parsing correction." - :group 'languagetool - :type '(repeat (symbol))) - -(defvar languagetool-server-output-buffer-name "*LanguageTool Server Output*") - -(defvar languagetool-server-process nil - "A reference to the LanguageTool Server executable.") - - - -;; Shared variables: - -(defcustom languagetool-default-language "auto" - "Language name which LanguageTool will set for correction. -This is string which indicate locale or \"auto\"." - :group 'languagetool - :type '(choice - string - (const auto))) - -(defcustom languagetool-mother-tongue nil - "Your mother tongue language name pass to LanguageTool." - :group 'languagetool - :type 'string) - -(defcustom languagetool-disabled-rules nil - "Disabled rules pass to LanguageTool. -List of strings." - :group 'languagetool - :type '(choice - (list string))) - -(defvar languagetool--correction-keys - [?1 ?2 ?3 ?4 ?5 ?6 ?7 ?8 ?9 ?0 - ;; suggestions may over 10. - ;; define rest of alphabet just in case. - ?a ?b ?c ?d ?e ?f ?g ?h ?i ?j - ?k ?l ?m ?n ?o ?p ?q ?r ?s ?t - ?u ?v ?w ?x ?y ?z]) - -(defcustom languagetool-hint-function - 'languagetool-hint-default-function - "Display error information in the minibuffer. - -The function must search for overlays at point. -You must pass the function symbol. - -A example hint function: -\(defun `languagetool-hint-default-function' () - \"Default hint display function.\" - (dolist (ov (overlays-at (point))) - (when (overlay-get ov 'languagetool-message) - (unless (current-message) - (message - \"%s%s\" (overlay-get ov 'languagetool-short-message) - (if (/= 0 - (length (overlay-get ov 'languagetool-replacements))) - (concat - \" -> (\" - (mapconcat - #'identity (languagetool--get-replacements ov) \", \") - \")\") - \"\"))))))" - :group 'languagetool - :type '(choice - (const nil) - function)) - -(defcustom languagetool-hint-idle-delay 0.5 - "Number of seconds while idle to wait before showing hint." - :group 'languagetool - :type 'number) - -(defcustom languagetool-server-request-delay 2.0 - "Number of seconds while idle to wait before sending request to server." - :group 'languagetool - :type 'number) - -(defvar languagetool-hint--timer nil - "Hold idle timer that shows the hint in the minibuffer.") - - - -;; Buffer Local Variables: - -(defvar languagetool-local-disabled-rules nil - "Disabled rules pass to LanguageTool. Buffer local. -List of strings.") -(make-variable-buffer-local 'languagetool-local-disabled-rules) - -(defvar languagetool-output-parsed nil) -(make-variable-buffer-local 'languagetool-output-parsed) - -(defvar languagetool-server--started-p nil - "Tell if the server can be used or not.") -(make-variable-buffer-local 'languagetool-server--started-p) - -(defvar languagetool-server--correcting-p nil - "Tell if we are actually correcting the buffer.") -(make-variable-buffer-local 'languagetool-server--correcting-p) - -(defvar languagetool-server--timer nil - "Hold idle time that send request to LanguageTool server.") -(make-variable-buffer-local 'languagetool-server--timer) - - -;; Functions: - -;; Start Server Functions: +;; Function definitions: ;;;###autoload -(define-minor-mode languagetool-server-mode - "Minor mode that highlights LanguageTool corrections." - :group 'languagetool - :lighter " LT" - (languagetool-server--toggle)) +(defun languagetool-set-language (lang) + "Set LanguageTool correction language to LANG." + (interactive + (list (read-string "LanguageTool new language: " + (alist-get languagetool-correction-language languagetool-core-languages) + languagetool-correction-language-history + (let (languages-choices) + (dolist (language + languagetool-core-languages + (reverse languages-choices)) + (push (cdr language) languages-choices)))))) + (setq languagetool-correction-language (or (car (rassoc lang languagetool-core-languages)) + lang))) ;;;###autoload -(defun languagetool-server-start () - "Start the LanguageTool Server. - -Its not recommended to run this function more than once." - (interactive) - (unless (process-live-p languagetool-server-process) - (unless (executable-find languagetool-java-bin) - (error "Java could not be found")) - (unless languagetool-server-language-tool-jar - (error "LanguageTool Server jar path is not set")) - (unless (or - languagetool-language-tool-class - (file-readable-p (file-truename languagetool-server-language-tool-jar))) - (error "LanguageTool Server jar is not readable or could not be found")) - - (let ((buffer (get-buffer-create languagetool-server-output-buffer-name))) - ;; Clean the buffer before printing the LanguageTool Server Log - (with-current-buffer buffer - (erase-buffer)) - - ;; Start LanguageTool Server - (setq languagetool-server-process - (apply #'start-process - "*LanguageTool Server*" - buffer - languagetool-java-bin - (languagetool-server--init-args))) - - ;; Does not block Emacs when close and do not shutdown the server - (set-process-query-on-exit-flag languagetool-server-process nil)) - - ;; Start running the hint idle timer - (setq languagetool-hint--timer - (run-with-idle-timer languagetool-hint-idle-delay t - languagetool-hint-function)))) - -(defun languagetool-server--init-args () - "Parse the arguments needed to start HTTP server." - (let ((arguments nil)) - - ;; Appends arguments given to java - (dolist (arg languagetool-java-arguments) - (setq arguments (append arguments (list arg)))) - - ;; Appends the LanguageTool Server jar path - (unless languagetool-language-tool-class - (setq arguments (append arguments (list "-cp")))) - (setq arguments (append arguments (list (file-truename languagetool-server-language-tool-jar)))) - (unless languagetool-language-tool-class - (setq arguments (append arguments (list "org.languagetool.server.HTTPServer")))) - - ;; Appends the port information - (setq arguments (append arguments - (list "--port" - (format "%d" languagetool-server-port)))) - - arguments)) +(defun languagetool-clear-suggestions () + "Clear all the buffer suggestions. -;;;###autoload -(defun languagetool-server-stop () - "Stops the LanguageTool Server." +If `languagetool-server-mode' is active, it would rise an error, +as you are not suppose to call this function." (interactive) - (delete-process languagetool-server-process)) - -(defun languagetool-server--toggle () - "Enables or disable LanguageTool Server Mode." - (if languagetool-server-mode - (progn - ;; Start checking for LanguageTool server is able to handle - ;; requests - (languagetool-server--server-check) - - ;; Create hint timer - (setq languagetool-hint--timer - (run-with-idle-timer languagetool-hint-idle-delay t - languagetool-hint-function)) - - ;; Start correction in changes - (add-hook - 'after-change-functions - #'languagetool-server--check-on-change - nil 'local) - ;; Start correction on hooks - (dolist (hook (reverse languagetool-server-delayed-commands)) - (add-hook hook #'languagetool-server-check nil 'local)) - - ;; Add timer to delay - (setq languagetool-server--timer - (run-with-idle-timer languagetool-server-request-delay t - #'languagetool-server-check))) - - (progn - ;; Delete the flag of server started. - (setq languagetool-server--started-p nil) - - ;; Delete all the checking hooks - (dolist (hook languagetool-server-delayed-commands) - (remove-hook hook #'languagetool-server-check 'local)) - ;; Delete correction in changes - (remove-hook 'after-change-functions #'languagetool-server--check-on-change 'local) - - ;; Cancel buffer local timer - (cancel-timer languagetool-server--timer) - - ;; Delete all LanguageTool overlays - (languagetool-server--clear-buffer)))) - - -;; Server Checking Functions: - -(defun languagetool-server--server-check () - "Check if the LanguageTool Server is able to handle requests." - (unless languagetool-server--started-p - (request - (format "%s:%d/v2/languages" languagetool-server-url languagetool-server-port) - :type "GET" - :parser 'json-read - :success (cl-function - (lambda (&key _response &allow-other-keys) - (message "LanguageTool Server communication up...") - (setq languagetool-server--started-p t) - (languagetool-server-check))) - :error (cl-function - (lambda (&rest args &key _error-thrown &allow-other-keys) - (when (or - languagetool-server-mode - (not languagetool-server--started-p)) - (languagetool-server--server-check))))))) - -(defun languagetool-server--check-on-change (_begin _end _len) - "Correct the buffer using LanguageTool and show its suggestion. - -This function checks for the actual showed region of the buffer -for suggestions. This function is an alias to -`languagetool-server-check'. - -_BEGIN, _END and _LEN are unused as the buffer needs to be check -completely." - (languagetool-server-check)) - -(defun languagetool-server-check () - "Correct the buffer using LanguageTool and show its suggestion. - -This function checks for the actual showed region of the buffer -for suggestions." (when languagetool-server-mode - (unless languagetool-server--correcting-p - (request - (format "%s:%d/v2/check" languagetool-server-url languagetool-server-port) - :type "POST" - :data (languagetool-server--parse-args) - :parser 'json-read - :success (cl-function - (lambda (&key response &allow-other-keys) - (languagetool-server--show-corrections - (request-response-data response)))) - :error (cl-function - (lambda (&rest args &key error-thrown &allow-other-keys) - (languagetool-server-mode -1) - (message - "[Fatal Error] LanguageTool closed and got error: %S" - error-thrown))))))) - -(defun languagetool-server--parse-args () - "Return the server argument list. - -Return the arguments as an assoc list of string which will be -used in the POST request made to the LanguageTool server." - (let ((arguments (json-new-object))) - - ;; Appends the correction language information - (setq arguments (json-add-to-object arguments - "language" - languagetool-default-language)) - - ;; Appends the mother tongue information - (when (stringp languagetool-mother-tongue) - (setq arguments (json-add-to-object arguments - "motherTongue" - languagetool-mother-tongue))) - - ;; Appends the disabled rules - (let ((rules "")) - ;; Global disabled rules - (dolist (rule languagetool-disabled-rules) - (if (string= rules "") - (setq rules (concat rules rule)) - (setq rules (concat rules "," rule)))) - ;; Local disabled rules - (dolist (rule languagetool-local-disabled-rules) - (if (string= rules "") - (setq rules (concat rules rule)) - (setq rules (concat rules "," rule)))) - (unless (string= rules "") - (setq arguments (json-add-to-object arguments "disabledRules" rules)))) - - ;; Add the buffer contents - (setq arguments (json-add-to-object arguments - "text" - (buffer-substring-no-properties - (point-min) - (point-max)))) - arguments)) - -(defun languagetool-server--show-corrections (json-parsed) - "Show the suggestions made by LanguageTool in the buffer. - -JSON-PARSED is a json object with the suggestions thrown by the -LanguageTool Server." - (languagetool-server--clear-buffer) - (when languagetool-server-mode - (let ((corrections (cdr (assoc 'matches json-parsed))) - (correction nil)) - (dotimes (index (length corrections)) - (setq correction (aref corrections index)) - (let ((offset (cdr (assoc 'offset correction))) - (size (cdr (assoc 'length correction)))) - (languagetool--create-overlay - (+ (point-min) offset) (+ (point-min) offset size) - correction)))))) - -(defun languagetool-server--clear-buffer () - "Deletes all the buffer overlays." - (save-restriction - (widen) - (save-excursion - (dolist (ov (overlays-in (point-min) (point-max))) - (when (overlay-get ov 'languagetool-message) - (delete-overlay ov)))))) - - - -;; Create Functions: - -(defun languagetool--parse-java-arguments () - "Return java arguments list. - -Return the arguments as a list of strings which will be used in -the call of LanguageTool when correcting the text." - (let ((arguments nil)) - - ;; Appends arguments given to java - (dolist (arg languagetool-java-arguments) - (setq arguments (append arguments (list arg)))) - - ;; Appends the LanguageTool jar path - (unless languagetool-language-tool-class - (setq arguments (append arguments (list "-jar")))) - (setq arguments (append arguments (list (file-truename languagetool-language-tool-jar)))) - - ;; Appends the LanguageTool arguments - (dolist (arg languagetool-language-tool-arguments) - (setq arguments (append arguments (list arg)))) - - ;; Appends the common arguments - (setq arguments (append arguments (list "-c" "utf8") - (list "--json"))) - - ;; Appends the correction language information - (if (string= languagetool-default-language "auto") - (setq arguments (append arguments (list "-adl"))) - (setq arguments (append arguments (list "-l" languagetool-default-language)))) - - ;; Appends the mother tongue information - (when (stringp languagetool-mother-tongue) - (setq arguments (append arguments (list "-m" languagetool-mother-tongue)))) - - ;; Appends the disabled rules - (let ((rules "")) - ;; Global disabled rules - (dolist (rule languagetool-disabled-rules) - (if (string= rules "") - (setq rules (concat rules rule)) - (setq rules (concat rules "," rule)))) - ;; Local disabled rules - (dolist (rule languagetool-local-disabled-rules) - (if (string= rules "") - (setq rules (concat rules rule)) - (setq rules (concat rules "," rule)))) - (unless (string= rules "") - (setq arguments (append arguments (list "-d" rules))))) - arguments)) - -(defun languagetool--get-face (issue-type) - "Return the face for the ISSUE-TYPE." - (cond - ((string= issue-type "misspelling") - 'languagetool-misspelling-face) - ((string= issue-type "grammar") - 'languagetool-grammar-face) - ((string= issue-type "style") - 'languagetool-style-face) - (t - 'languagetool-default-face))) - -(defun languagetool--create-overlay (begin end correction) - "Create an overlay for corrections. - -Create an overlay for correction in the region delimited by BEGIN -and END, parsing CORRECTION as overlay properties." - (save-excursion - (let* ((ov (make-overlay begin end)) - (short-message (cdr (assoc 'shortMessage correction))) - (message (cdr (assoc 'message correction))) - (replacements (cdr (assoc 'replacements correction))) - (rule (cdr (assoc 'rule correction))) - (issue-type (cdr (assoc 'issueType rule)))) - (when (string= short-message "") - (setq short-message message)) - (overlay-put ov 'languagetool-short-message short-message) - (overlay-put ov 'languagetool-message message) - (overlay-put ov 'languagetool-replacements replacements) - (overlay-put ov 'languagetool-rule rule) - (overlay-put ov 'help-echo short-message) - (overlay-put ov 'priority 1) - (overlay-put ov 'evaporate t) - (overlay-put ov 'face (languagetool--get-face issue-type))))) - - - -;; Output and debug functions: - -(defun languagetool--write-debug-info (text) - "Write debug info in `languagetool-output-buffer-name'. - -The argument TEXT is the region passed to LanguageTool for -checking." - (insert (propertize " ----- LanguageTool Command:" 'face 'font-lock-warning-face) - "\n\n") - (insert languagetool-java-bin " " - (mapconcat (lambda (x) (format "%s" x)) (languagetool--parse-java-arguments) " ") - "\n\n\n\n") - (insert (propertize " ----- LanguageTool Text:" 'face 'font-lock-warning-face) - "\n\n") - (insert text "\n\n\n\n") - (insert (propertize " ----- LanguageTool Output:" 'face 'font-lock-warning-face) - "\n\n")) - - - -;; Correction functions: - -(defun languagetool--invoke-command-region (begin end) - "Invoke LanguageTool passing the current region to STDIN. - -The region is delimited by BEGIN and END." - (languagetool--clear-buffer) - (unless (executable-find languagetool-java-bin) - (error "Java could not be found")) - (unless languagetool-language-tool-jar - (error "LanguageTool jar path is not set")) - (unless (or - languagetool-language-tool-class - (file-readable-p (file-truename languagetool-language-tool-jar))) - (error "LanguageTool jar is not readable or could not be found")) - - (save-excursion - (let ((status 0) - (buffer (get-buffer-create languagetool-output-buffer-name)) - (text (buffer-substring-no-properties begin end)) - (json-parsed nil)) - (with-current-buffer buffer - (erase-buffer) - (languagetool--write-debug-info text)) - (setq status - (apply #'call-process-region begin end - languagetool-java-bin - nil - languagetool-output-buffer-name - nil - (languagetool--parse-java-arguments))) - (when (/= status 0) - (error "LanguageTool returned with status %d" status)) - (with-current-buffer buffer - (widen) - (goto-char (point-max)) - (backward-sexp) - (setq json-parsed (json-read))) - (setq languagetool-output-parsed json-parsed))) - (pop-mark)) - -(defun languagetool--check-corrections-p () - "Return t if corrections can be made or nil otherwise." - (/= 0 (length (cdr (assoc 'matches languagetool-output-parsed))))) - -(defun languagetool--get-replacements (overlay) - "Return the replacements of OVERLAY in a list." - (let ((replacements (overlay-get overlay 'languagetool-replacements)) - (replace '())) - (dotimes (index (length replacements)) - (setq replace (append replace - (list (cdr (assoc 'value (aref replacements index))))))) - replace)) - -(defun languagetool--show-corrections (begin) - "Highlight corrections in the buffer. - -BEGIN defines the start of the current region." - (let ((corrections (cdr (assoc 'matches languagetool-output-parsed))) - (correction nil)) - (dotimes (index (length corrections)) - (setq correction (aref corrections index)) - (let ((offset (cdr (assoc 'offset correction))) - (size (cdr (assoc 'length correction)))) - (languagetool--create-overlay - (+ begin offset) (+ begin offset size) - correction)))) - (setq languagetool-hint--timer - (run-with-idle-timer languagetool-hint-idle-delay t - languagetool-hint-function))) - -(defun languagetool--clear-buffer () - "Deletes all buffer overlays." - (save-restriction - (widen) - (save-excursion - (dolist (ov (overlays-in (point-min) (point-max))) - (when (overlay-get ov 'languagetool-message) - (delete-overlay ov)))))) + (error "Do not use this function in server mode +If you want to clear the suggestions turn off the server mode)")) + (languagetool-core-clear-buffer)) ;;;###autoload (defun languagetool-check (begin end) "Correct the current buffer and highlight errors. -If region is selected before calling this function it would be -passed as an argument. -The region is delimited by BEGIN and END" +If region is selected before calling this function, that would be +the region passed as an argument. The region is delimited by +BEGIN and END. + +If `languagetool-server-mode' is active, send a request to the +server and ends. The parameters BEGIN and END did not make any +difference, as in this mode, the whole buffer needs to be +checked." (interactive (if (region-active-p) (list (region-beginning) (region-end)) (list (point-min) (point-max)))) - (unless languagetool-server-mode - (languagetool--invoke-command-region begin end) - (if (languagetool--check-corrections-p) - (progn - (message (substitute-command-keys "LangugeTool finished. -Use \\[languagetool-correct-buffer] to correct the buffer.")) - (languagetool--show-corrections begin) - (run-hooks 'languagetool-error-exists-hook)) - (progn - (message "LanguageTool finished. -Found no errors.") - (languagetool--clear-buffer) - (run-hooks 'languagetool-no-error-hook))))) - -;;;###autoload -(defun languagetool-clear-buffer () - "Deletes all buffer correction highlight. - -In server mode clears the buffer and recreate all the suggestions -in case of a bug." - (interactive) (if languagetool-server-mode - (progn - (message "Rechecking buffer...") - (languagetool-server-check)) - (progn - (languagetool--clear-buffer) - (run-hooks 'languagetool-finish-hook) - (message "Cleaned buffer from LanguageTool.")))) - -;;;###autoload -(defun languagetool-set-language (lang) - "Change LanguageTool correction language to LANG." - (interactive - (list (read-string "Language: " nil nil 'auto))) - (setq languagetool-default-language lang)) - - -;; Hint Message: - -(defun languagetool-hint-default-function () - "Default hint display function." - (dolist (ov (overlays-at (point))) - (when (overlay-get ov 'languagetool-message) - (unless (current-message) - (message - "%s%s" (overlay-get ov 'languagetool-short-message) - (if (/= 0 - (length (overlay-get ov 'languagetool-replacements))) - (concat - " -> (" - (mapconcat - #'identity (languagetool--get-replacements ov) ", ") - ")") - "")))))) - - -;; Correction functions: - -(defun languagetool--parse-correction-message (overlay) - "Parse and style minibuffer correction. - -Get the information about corrections from OVERLAY." - (let (msg) - ;; Add LanguageTool rule to the message - (setq msg (concat - "[" (cdr (assoc 'id (overlay-get overlay 'languagetool-rule))) "] ")) - - ;; Add LanguageTool correction suggestion - (setq msg (concat msg - (propertize - (format "%s" (overlay-get overlay 'languagetool-message)) - 'face 'font-lock-warning-face) - "\n")) - - ;; Format all the possible replacements for the correction suggestion - (let ((replacements (languagetool--get-replacements overlay))) - (when (< 0 (length replacements)) - (let ((num-choices (length replacements))) - ;; If can't assoc each replacement with each hotkey - (when (> (length replacements) (length languagetool--correction-keys)) - (setq num-choices (length languagetool--correction-keys)) - (setq msg (concat msg "Not all choices shown.\n"))) - (setq msg (concat msg "\n")) - ;; Format all choices - (dotimes (index num-choices) - (setq msg (concat msg - "[" - (propertize - (format "%c" (aref languagetool--correction-keys index)) - 'face 'font-lock-keyword-face) - "]: ")) - (setq msg (concat msg (nth index replacements) " ")))))) - ;; Add default Ignore and Skip options - (setq msg (concat msg "\n[" - (propertize "C-i" 'face 'font-lock-keyword-face) - "]: Ignore ")) - (setq msg (concat msg "[" - (propertize "C-s" 'face 'font-lock-keyword-face) - "]: Skip\n")) - msg)) - -(defun languagetool--do-correction (pressed-key overlay) - "Correct an delete the overlay with LanguageTool Suggestion. -The selected correction is given by PRESSED-KEY and the -position, and suggestions are given by OVERLAY." - (cond - ((char-equal ?\C-g pressed-key) - (progn - (goto-char (overlay-end overlay)) - (setq languagetool-server--correcting-p nil) - (error "Quit"))) - ((char-equal ?\C-i pressed-key) - (progn - (goto-char (overlay-end overlay)) - (delete-overlay overlay))) - ((char-equal ?\C-s pressed-key) - (goto-char (overlay-end overlay))) - ((not (cl-position pressed-key languagetool--correction-keys)) - (progn - (setq languagetool-server--correcting-p nil) - (error "Key `%c' cannot be used" pressed-key))) - (t - (let ((size (length (languagetool--get-replacements overlay))) - (pos (cl-position pressed-key languagetool--correction-keys))) - (when (> (1+ pos) size) - (setq languagetool-server--correcting-p nil) - (error "Correction key `%c' cannot be used" pressed-key)) - (delete-region (overlay-start overlay) (overlay-end overlay)) - (insert (nth pos (languagetool--get-replacements overlay))) - (delete-overlay overlay))))) - -(defun languagetool--correct-point () - "Show correction buffer at point and do correction." - (setq languagetool-server--correcting-p t) - (let (pressed-key - (inhibit-quit t)) - (dolist (ov (overlays-at (point))) - (when (overlay-get ov 'languagetool-message) - (message nil) - (setq pressed-key - (read-char (languagetool--parse-correction-message ov))) - (languagetool--do-correction pressed-key ov)))) - (setq languagetool-server--correcting-p nil)) + (languagetool-server-check) + (languagetool-console-check begin end))) ;;;###autoload (defun languagetool-correct-at-point () "Pops up transient buffer to do correction at point." (interactive) - (languagetool--correct-point)) + (when languagetool-server-mode + (setq languagetool-server-correction-p t)) + (languagetool-correction-at-point) + (when languagetool-server-mode + (setq languagetool-server-correction-p nil))) ;;;###autoload (defun languagetool-correct-buffer () - "Pops up transient buffer to do corrections at buffer." + "Pops up transient buffer to do correction in the whole buffer." (interactive) - (save-excursion - (dolist (ov (reverse (overlays-in (point-min) (point-max)))) - (when (and (overlay-get ov 'languagetool-message) - (overlay-start ov)) - (goto-char (overlay-start ov)) - (languagetool--correct-point))))) - - + (when languagetool-server-mode + (setq languagetool-server-correction-p t)) + (condition-case err + (save-excursion + (dolist (ov (reverse (overlays-in (point-min) (point-max)))) + (when (and (overlay-get ov 'languagetool-message) + (overlay-start ov)) + (goto-char (overlay-start ov)) + (languagetool-correction-at-point)))) + ((quit error) + (when languagetool-server-mode + (setq languagetool-server-correction-p nil)) + (error "%s" (error-message-string err)))) + (when languagetool-server-mode + (setq languagetool-server-correction-p nil))) (provide 'languagetool) diff --git a/makefile b/makefile index a0fe650..4de25a3 100644 --- a/makefile +++ b/makefile @@ -1,17 +1,66 @@ -EMACS = emacs +## Compiling executable variables #################################### -EMACS_FLAGS = -Q --batch --eval "(setq byte-compile-error-on-warn t)" -f package-initialize -L . -f batch-byte-compile +-include env.mk -SOURCES = $(filter-out %-autoloads.el %-pkg.el, $(wildcard *.el)) +LOAD_PATH ?= -L . +EMACSBIN ?= emacs +BATCH = $(EMACSBIN) -Q --batch $(LOAD_PATH) -TARGETS = $(SOURCES:.el=.elc) +PKG = languagetool +RQD_PKG = request -byte-compiled: $(TARGETS) +all: lisp -$(TARGETS): $(SOURCES) +## Source files ###################################################### + +ELS = $(PKG)-core.el +ELS += $(PKG)-issue.el +ELS += $(PKG)-java.el +ELS += $(PKG)-correction.el +ELS += $(PKG)-console.el +ELS += $(PKG)-server.el +ELS += $(PKG).el +ELCS = $(ELS:.el=.elc) + +## Build order ####################################################### + +$(PKG)-core.el: +$(PKG)-issue.el: +$(PKG)-java.el: +$(PKG)-correction.el: $(PKG)-core.el +$(PKG)-console.el: $(PKG)-core.el $(PKG)-issue.el $(PKG)-java.el +$(PKG)-server.el: $(PKG)-core.el $(PKG)-issue.el $(PKG)-java.el +$(PKG).el: $(PKG)-correction.el $(PKG)-console.el $(PKG)-server.el + +## Build ############################################################# + +lisp: $(ELCS) %.elc: %.el - $(EMACS) $(EMACS_FLAGS) $< + @printf "Compiling $<\n" + @$(BATCH) \ + --eval "(when (file-exists-p \"$@\") (delete-file \"$@\"))" \ + --eval "(setq byte-compile-error-on-warn t)" \ + -f batch-byte-compile $< clean: - rm *.elc + @printf "Cleaning byte compiled lisp...\n" + @rm -f *.elc + +## CI integration #################################################### + +ci: require lisp + +require: $(RQD_PKG) + +request: + @printf "Installing $@\n" + @$(BATCH) \ + --eval "(progn \ + (require 'package) \ + (add-to-list \ + 'package-archives \ + '(\"melpa\" . \"http://melpa.org/packages/\") t) \ + (package-initialize) \ + (package-refresh-contents) \ + (package-install 'request))"