summaryrefslogtreecommitdiff
path: root/scripts/languagetool-flycheck
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2025-11-04 23:26:42 -0600
committerCraig Jennings <c@cjennings.net>2025-11-04 23:26:42 -0600
commiteda461086e94265b67ab5b21032e7ee23112ad87 (patch)
tree90983461736e8b7f0aa435a7b1fddb4b2825cefa /scripts/languagetool-flycheck
parentb520add37ae23f0411e2c6512fe6b8d7418bd525 (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-xscripts/languagetool-flycheck82
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()