├── .gitignore ├── CHANGELOG.md ├── tslint.json ├── README.md ├── src ├── test │ ├── extension.test.ts │ └── index.ts └── extension.ts ├── tsconfig.json └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | out 2 | node_modules 3 | .vscode-test/ 4 | *.vsix 5 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to the "languagetool" extension will be documented in this file. 3 | 4 | ## 1.0.1 5 | Make it pass tslint with a sledgehammer 6 | 7 | ## 1.0.0 8 | - Initial release -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "no-string-throw": true, 4 | "no-unused-expression": true, 5 | "no-duplicate-variable": true, 6 | "curly": true, 7 | "class-name": true, 8 | "semicolon": [ 9 | true, 10 | "always" 11 | ], 12 | "triple-equals": true 13 | }, 14 | "defaultSeverity": "warning" 15 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### LanguageTool 2 | 3 | This is a wrapper for the [LanguageTool](https://languagetool.org/) API. It provides basic grammar and spellchecking for your files. The main focus is on Markdown files and therefore I do some work stripping out front matter and then processing the resultant rendered Markdown versus the source (if that makes sense). Obviously this is rough - PR's are welcome! 4 | -------------------------------------------------------------------------------- /src/test/extension.test.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Note: This example test is leveraging the Mocha test framework. 3 | // Please refer to their documentation on https://mochajs.org/ for help. 4 | // 5 | 6 | // The module 'assert' provides assertion methods from node 7 | import * as assert from 'assert'; 8 | 9 | // You can import and use all API from the 'vscode' module 10 | // as well as import your extension to test it 11 | // import * as vscode from 'vscode'; 12 | // import * as myExtension from '../extension'; 13 | 14 | // Defines a Mocha test suite to group tests of similar kind together 15 | suite("Extension Tests", function () { 16 | 17 | // Defines a Mocha unit test 18 | test("Something 1", function() { 19 | assert.equal(-1, [1, 2, 3].indexOf(5)); 20 | assert.equal(-1, [1, 2, 3].indexOf(0)); 21 | }); 22 | }); -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es6", 5 | "outDir": "out", 6 | "lib": [ 7 | "es6" 8 | ], 9 | "sourceMap": true, 10 | "rootDir": "src", 11 | /* Strict Type-Checking Option */ 12 | "strict": true, /* enable all strict type-checking options */ 13 | /* Additional Checks */ 14 | "noUnusedLocals": true /* Report errors on unused locals. */ 15 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 16 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 17 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 18 | }, 19 | "exclude": [ 20 | "node_modules", 21 | ".vscode-test" 22 | ] 23 | } -------------------------------------------------------------------------------- /src/test/index.ts: -------------------------------------------------------------------------------- 1 | // 2 | // PLEASE DO NOT MODIFY / DELETE UNLESS YOU KNOW WHAT YOU ARE DOING 3 | // 4 | // This file is providing the test runner to use when running extension tests. 5 | // By default the test runner in use is Mocha based. 6 | // 7 | // You can provide your own test runner if you want to override it by exporting 8 | // a function run(testRoot: string, clb: (error:Error) => void) that the extension 9 | // host can call to run the tests. The test runner is expected to use console.log 10 | // to report the results back to the caller. When the tests are finished, return 11 | // a possible error to the callback or null if none. 12 | 13 | import * as testRunner from 'vscode/lib/testrunner'; 14 | 15 | // You can directly control Mocha options by uncommenting the following lines 16 | // See https://github.com/mochajs/mocha/wiki/Using-mocha-programmatically#set-options for more info 17 | testRunner.configure({ 18 | ui: 'tdd', // the TDD UI is being used in extension.test.ts (suite, test, etc.) 19 | useColors: true // colored output from test results 20 | }); 21 | 22 | module.exports = testRunner; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "languagetool", 3 | "repository": { 4 | "type":"git", 5 | "url":"https://github.com/cfjedimaster/vscode-languagetool" 6 | }, 7 | "displayName": "languagetool", 8 | "description": "A wrapper to the LanguageTool API, providing spelling and grammar checking.", 9 | "version": "1.0.1", 10 | "publisher": "raymondcamden", 11 | "engines": { 12 | "vscode": "^1.24.0" 13 | }, 14 | "categories": [ 15 | "Other" 16 | ], 17 | "activationEvents": [ 18 | "onCommand:extension.runLanguageTool" 19 | ], 20 | "main": "./out/extension", 21 | "contributes": { 22 | "commands": [ 23 | { 24 | "command": "extension.runLanguageTool", 25 | "title": "Run LanguageTool" 26 | } 27 | ] 28 | }, 29 | "scripts": { 30 | "vscode:prepublish": "npm run compile", 31 | "compile": "tsc -p ./", 32 | "watch": "tsc -watch -p ./", 33 | "postinstall": "node ./node_modules/vscode/bin/install", 34 | "test": "npm run compile && node ./node_modules/vscode/bin/test" 35 | }, 36 | "devDependencies": { 37 | "typescript": "^2.6.1", 38 | "vscode": "^1.1.6", 39 | "tslint": "^5.8.0", 40 | "@types/node": "^7.0.43", 41 | "@types/mocha": "^2.2.42" 42 | }, 43 | "dependencies": { 44 | "@types/request-promise": "^4.1.41", 45 | "@types/showdown": "^1.7.4", 46 | "request": "^2.87.0", 47 | "request-promise": "^4.2.2", 48 | "showdown": "^1.8.6" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/extension.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | // The module 'vscode' contains the VS Code extensibility API 3 | // Import the module and reference it with the alias vscode in your code below 4 | import * as vscode from 'vscode'; 5 | import * as rp from 'request-promise'; 6 | import * as showdown from 'showdown'; 7 | 8 | // this method is called when your extension is activated 9 | // your extension is activated the very first time the command is executed 10 | export function activate(context: vscode.ExtensionContext) { 11 | 12 | // The command has been defined in the package.json file 13 | // Now provide the implementation of the command with registerCommand 14 | // The commandId parameter must match the command field in package.json 15 | let disposable = vscode.commands.registerCommand('extension.runLanguageTool', async () => { 16 | // The code you place here will be executed every time your command is executed 17 | 18 | // get current document text 19 | 20 | let editor = vscode.window.activeTextEditor; 21 | if(!editor) { 22 | vscode.window.showErrorMessage('No active editor to check.'); 23 | return; 24 | } 25 | console.log('lang',editor.document.languageId); 26 | 27 | let text = editor.document.getText(); 28 | let type = editor.document.languageId; 29 | 30 | // now lets try to remove common MD front matter stuff (and maybe do more later) 31 | text = prepareText(text, type); 32 | 33 | if(text.length === 0) { 34 | vscode.window.showErrorMessage('No text to check.'); 35 | return; 36 | } 37 | 38 | let results = await checkText(text); 39 | 40 | console.log(results); 41 | 42 | // now id make some good html 43 | let html = generateHTML(results as Array); 44 | 45 | // now render it 46 | const panel = vscode.window.createWebviewPanel( 47 | 'languageToolResults', // Identifies the type of the webview. Used internally 48 | "LanguageTool Results", // Title of the panel displayed to the user 49 | vscode.ViewColumn.Two, // Editor column to show the new webview panel in. 50 | { } // Webview options. More on these later. 51 | ); 52 | panel.webview.html = html; 53 | 54 | }); 55 | 56 | context.subscriptions.push(disposable); 57 | } 58 | 59 | // this method is called when your extension is deactivated 60 | export function deactivate() { 61 | } 62 | 63 | function prepareText(s:string,type:string):string { 64 | /* 65 | lets first remove ---X--- 66 | */ 67 | s = s.replace(/---[\s\S]*---/m,'').trim(); 68 | // todo - more ;) 69 | 70 | //if type is markdown, lets render it to html and then remove it 71 | if(type === 'markdown') { 72 | let converter = new showdown.Converter(); 73 | s = converter.makeHtml(s); 74 | // remove code blocks 75 | s = s.replace(/
<\/pre>/mg, '');
 76 |         // now remove html
 77 |         s = s.replace(/<.*?>/g, '');
 78 |     }
 79 |     return s;
 80 | }
 81 | 
 82 | async function checkText(s:string) {
 83 |     return new Promise((resolve, reject) => {
 84 |         rp({
 85 |             uri:'https://languagetool.org/api/v2/check',
 86 |             method:'POST',
 87 |             form:{
 88 |                 text:s,
 89 |                 language:'auto',
 90 |                 disabledRules:'EN_QUOTES'
 91 |             }
 92 |         })
 93 |         .then(res => {
 94 |             resolve(JSON.parse(res).matches);
 95 |         })
 96 |         .catch(e => {
 97 |             console.log('error calling api', e);
 98 |             reject(e);
 99 |         });
100 | 
101 |     });
102 | 
103 | }
104 | 
105 | function generateHTML(data:Array):string {
106 | 
107 |     /*
108 |     So before release, I decided to simply render all the rules the same. I'm keeping some old bits in
109 |     for review later though...
110 | 
111 |     let replacementRules = ['MORFOLOGIK_RULE_EN_US','COMMA_COMPOUND_SENTENCE','COMMA_PARENTHESIS_WHITESPACE'];
112 |     */
113 |     let results = '';
114 |     let counter = 0;
115 | 
116 |     data.forEach(d => {
117 |         counter++;
118 |         let s = '

'+counter+') '+d.message+'
'; 119 | //if(replacementRules.indexOf(d.rule.id) >= 0) { 120 | 121 | // generate highlighted context 122 | let badword = d.context.text.substr(d.context.offset,d.context.length); 123 | let sentence = d.context.text.replace(badword, ''+badword+''); 124 | s += sentence+'
'; 125 | let replacements:string[] = []; 126 | d.replacements.forEach((r: any) => { 127 | replacements.push(r.value); 128 | }); 129 | s += 'Suggestions: '+replacements.join(','); 130 | 131 | //} 132 | s += '

'; 133 | results += s; 134 | }); 135 | 136 | let content = ` 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 |

LanguageTool Results

145 |

146 | The following results are provided by LanguageTool. 147 |

148 | 149 |

150 | There were ${data.length} result(s) found: 151 |

152 | 153 | ${results} 154 | 155 | 156 | 157 | `; 158 | 159 | return content; 160 | } --------------------------------------------------------------------------------