diff options
| author | Craig Jennings <c@cjennings.net> | 2025-11-04 23:26:42 -0600 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2025-11-04 23:26:42 -0600 |
| commit | eda461086e94265b67ab5b21032e7ee23112ad87 (patch) | |
| tree | 90983461736e8b7f0aa435a7b1fddb4b2825cefa /scripts/languagetool-flycheck | |
| parent | b520add37ae23f0411e2c6512fe6b8d7418bd525 (diff) | |
feat: Add LanguageTool integration for comprehensive grammar checking
Integrated LanguageTool as an on-demand grammar checker, replacing the
previously disabled proselint checker.
Changes:
- Created scripts/languagetool-flycheck wrapper for flycheck integration
- Converts LanguageTool JSON output to flycheck format
- Includes suggestions in error messages
- 30-second timeout for large files
- Updated modules/flycheck-config.el:
- Defined languagetool checker for text/markdown/org/gfm modes
- Updated cj/flycheck-prose-on-demand to use LanguageTool
- Added installation instructions (sudo pacman -S languagetool)
- Improved documentation clarity
- Usage: Press C-; ? in org/text/markdown files
- Enables flycheck with LanguageTool
- Shows errors in *Flycheck errors* buffer
- On-demand only (no performance impact)
- Updated docs/NOTES.org:
- Added best practice: Test Emacs launch after non-trivial changes
- Example: emacs --eval "(kill-emacs)"
- Catches startup errors before committing
- Disabled weather debug mode (wttrin-debug nil)
- Marked todo.org grammar checker task as DONE
LanguageTool catches real grammar issues (subject-verb agreement, tense,
punctuation, common mistakes) that proselint missed.
Installation: LanguageTool 6.6 (222MB) from Arch repos
Dependencies: Python 3 (for wrapper script)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Diffstat (limited to 'scripts/languagetool-flycheck')
| -rwxr-xr-x | scripts/languagetool-flycheck | 82 |
1 files changed, 82 insertions, 0 deletions
diff --git a/scripts/languagetool-flycheck b/scripts/languagetool-flycheck new file mode 100755 index 00000000..ecbc900f --- /dev/null +++ b/scripts/languagetool-flycheck @@ -0,0 +1,82 @@ +#!/usr/bin/env python3 +""" +Wrapper for LanguageTool to produce flycheck-compatible output. +Output format: filename:line:column: message +""" + +import json +import sys +import subprocess + +def main(): + if len(sys.argv) < 2: + print("Usage: languagetool-flycheck FILE", file=sys.stderr) + sys.exit(1) + + filename = sys.argv[1] + + # Run languagetool with JSON output + try: + result = subprocess.run( + ['languagetool', '-l', 'en-US', '--json', filename], + capture_output=True, + text=True, + timeout=30 + ) + except subprocess.TimeoutExpired: + print(f"{filename}:1:1: LanguageTool timeout", file=sys.stderr) + sys.exit(1) + except Exception as e: + print(f"{filename}:1:1: LanguageTool error: {e}", file=sys.stderr) + sys.exit(1) + + # Parse JSON output + try: + # Find the JSON in the output (skip warning lines) + json_output = None + for line in result.stdout.split('\n'): + if line.startswith('{'): + json_output = line + break + + if not json_output: + sys.exit(0) # No errors found + + data = json.loads(json_output) + + # Read file to calculate line numbers from character offsets + with open(filename, 'r', encoding='utf-8') as f: + content = f.read() + + # Convert matches to flycheck format + for match in data.get('matches', []): + offset = match['offset'] + length = match['length'] + message = match['message'] + rule_id = match['rule']['id'] + + # Calculate line and column from offset + line = content[:offset].count('\n') + 1 + line_start = content.rfind('\n', 0, offset) + 1 + column = offset - line_start + 1 + + # Get first suggestion if available + suggestions = match.get('replacements', []) + if suggestions: + suggestion = suggestions[0]['value'] + message = f"{rule_id}: {message} Suggestion: {suggestion}" + else: + message = f"{rule_id}: {message}" + + # Output in flycheck format + print(f"{filename}:{line}:{column}: {message}") + + except json.JSONDecodeError as e: + print(f"{filename}:1:1: Failed to parse LanguageTool JSON: {e}", file=sys.stderr) + sys.exit(1) + except Exception as e: + print(f"{filename}:1:1: Error processing LanguageTool output: {e}", file=sys.stderr) + sys.exit(1) + +if __name__ == '__main__': + main() |
