Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions docs/API-Reference/language/CodeInspection.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ Values for problem's 'type' property
* [.ERROR](#Type.ERROR)
* [.WARNING](#Type.WARNING)
* [.META](#Type.META)
* [.SPELL](#Type.SPELL)

<a name="Type.ERROR"></a>

Expand All @@ -52,6 +53,12 @@ Maintainability issue, probable error / bad smell, etc.
### Type.META
Inspector unable to continue, code too complex for static analysis, etc. Not counted in err/warn tally.

**Kind**: static property of [<code>Type</code>](#Type)
<a name="Type.SPELL"></a>

### Type.SPELL
For spelling or grammatical error

**Kind**: static property of [<code>Type</code>](#Type)
<a name="getProvidersForPath"></a>

Expand Down
1 change: 1 addition & 0 deletions src/extensions/default/DefaultExtensions.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"Phoenix-prettier",
"PrefsCodeHints",
"QuickView",
"SpellGrammarCheck",
"SVGCodeHints",
"UrlCodeHints",
"HealthData"
Expand Down
105 changes: 105 additions & 0 deletions src/extensions/default/SpellGrammarCheck/grammar-checker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
define(function (require, exports, module) {
const CodeInspection = brackets.getModule("language/CodeInspection");

const GRAMMAR_CHECKER_NAME = "GrammarChecker";
const supportedExtensions = [
"txt",
"md",
"markdown",
"html",
"htm",
"js",
"ts",
"jsx",
"tsx",
"css",
"less",
"scss"
];

const GRAMMAR_ERRORS = [
{ pattern: /\bthere\s+house\b/gi, suggestion: "their house", message: "Did you mean 'their house'?" },
{ pattern: /\bits\s+self\b/gi, suggestion: "itself", message: "Did you mean 'itself'?" },
{ pattern: /\byour\s+welcome\b/gi, suggestion: "you're welcome", message: "Did you mean 'you're welcome'?" },
{ pattern: /\bto\s+many\b/gi, suggestion: "too many", message: "Did you mean 'too many'?" },
{ pattern: /\bwould\s+of\b/gi, suggestion: "would have", message: "Did you mean 'would have'?" },
{ pattern: /\bcould\s+of\b/gi, suggestion: "could have", message: "Did you mean 'could have'?" },
{ pattern: /\bshould\s+of\b/gi, suggestion: "should have", message: "Did you mean 'should have'?" },
{ pattern: /\balot\b/gi, suggestion: "a lot", message: "Did you mean 'a lot'?" }
];

/**
* Check for grammar errors in the text
*
* @param {string} text - the text to check
* @param {number} lineNumber - the line number (0-based)
* @returns {Array} Array of spell error objects
*/
function checkGrammar(text, lineNumber) {
const errors = [];

GRAMMAR_ERRORS.forEach(function (rule) {
let match;
while ((match = rule.pattern.exec(text)) !== null) {
errors.push({
pos: { line: lineNumber, ch: match.index },
endPos: { line: lineNumber, ch: match.index + match[0].length },
message: rule.message,
type: CodeInspection.Type.META // META type for blue underline
});
}
});

return errors;
}

/**
* This function is responsible to run grammar check on the given text
* TODO: right now we just check the whole file. later need to make it efficient
*
* @param {string} text - The text content to check
* @param {string} fullPath - The full path to the file
* @returns {Object} Results object with errors array
*/
function lintOneFile(text, fullPath) {
const fileExtension = fullPath.split(".").pop().toLowerCase();

if (!supportedExtensions.includes(fileExtension)) {
return null;
}

const lines = text.split("\n");
const allErrors = [];

lines.forEach(function (line, lineIndex) {
const grammarErrors = checkGrammar(line, lineIndex);
allErrors.push(...grammarErrors);
});

if (allErrors.length > 0) {
return { errors: allErrors };
}

return null;
}

/**
* Initialize the grammar checker
* this function is called inside main.js
*/
function init() {
CodeInspection.register("text", {
name: GRAMMAR_CHECKER_NAME,
scanFile: lintOneFile
});

supportedExtensions.forEach(function (languageId) {
CodeInspection.register(languageId, {
name: GRAMMAR_CHECKER_NAME,
scanFile: lintOneFile
});
});
}

exports.init = init;
});
30 changes: 30 additions & 0 deletions src/extensions/default/SpellGrammarCheck/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* GNU AGPL-3.0 License
*
* Copyright (c) 2021 - present core.ai . All rights reserved.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero 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 Affero General Public License
* for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see https://opensource.org/licenses/AGPL-3.0.
*
*/

/**
* Spell and Grammar Check Extension
*/
define(function (require, exports, module) {
const SpellChecker = require("./spell-checker");
const GrammarChecker = require("./grammar-checker");

SpellChecker.init();
GrammarChecker.init();
});
5 changes: 5 additions & 0 deletions src/extensions/default/SpellGrammarCheck/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "SpellGrammarCheck",
"version": "1.0.0",
"dependencies": {}
}
123 changes: 123 additions & 0 deletions src/extensions/default/SpellGrammarCheck/spell-checker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
define(function (require, exports, module) {
const CodeInspection = brackets.getModule("language/CodeInspection");

const SPELL_CHECKER_NAME = "SpellChecker";
const supportedExtensions = [
"txt",
"md",
"markdown",
"html",
"htm",
"js",
"ts",
"jsx",
"tsx",
"css",
"less",
"scss"
];

const MISSPELLED_WORDS = ["teh", "adn", "wich", "thier"];

/**
* This function is responsible for giving spelling suggestions for a misspelled word
*
* @param {string} word - The misspelled word
* @returns {Array} Array of suggestions
*/
function getSuggestions(word) {
const suggestions = {
teh: ["the"],
adn: ["and"],
wich: ["which"],
thier: ["their"]
};

return suggestions[word] || [word];
}

/**
* Check for spelling errors in the text
*
* @param {string} text - the text to check
* @param {number} lineNumber - the line number (0-based)
* @returns {Array} Array of spell error objects
*/
function checkSpelling(text, lineNumber) {
const errors = [];
const words = text.split(/\s+/);
let currentPos = 0;

words.forEach(function (word) {
// remove punctuation for checking
const cleanWord = word.replace(/[^\w]/g, "").toLowerCase();

if (cleanWord && MISSPELLED_WORDS.includes(cleanWord)) {
const wordStart = text.indexOf(word, currentPos);
const wordEnd = wordStart + word.length;

errors.push({
pos: { line: lineNumber, ch: wordStart },
endPos: { line: lineNumber, ch: wordEnd },
message: `
"${word}" may be misspelled. Did you mean one of: ${getSuggestions(cleanWord).join(", ")}?
`,
type: CodeInspection.Type.SPELL
});
}
currentPos = text.indexOf(word, currentPos) + word.length;
});

return errors;
}

/**
* This function is responsible to run spell check on the given text
* TODO: right now we just check the whole file. later need to make it efficient
*
* @param {string} text - The text content to check
* @param {string} fullPath - The full path to the file
* @returns {Object} Results object with errors array
*/
function lintOneFile(text, fullPath) {
const fileExtension = fullPath.split(".").pop().toLowerCase();

if (!supportedExtensions.includes(fileExtension)) {
return null;
}

const lines = text.split("\n");
const allErrors = [];

lines.forEach(function (line, lineIndex) {
const spellErrors = checkSpelling(line, lineIndex);
allErrors.push(...spellErrors);
});

if (allErrors.length > 0) {
return { errors: allErrors };
}

return null;
}

/**
* Initialize the spell checker
* this function is called inside main.js
*/
function init() {
CodeInspection.register("text", {
name: SPELL_CHECKER_NAME,
scanFile: lintOneFile
});

supportedExtensions.forEach(function (languageId) {
CodeInspection.register(languageId, {
name: SPELL_CHECKER_NAME,
scanFile: lintOneFile
});
});
}

exports.init = init;
});
13 changes: 10 additions & 3 deletions src/language/CodeInspection.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,9 @@ define(function (require, exports, module) {
/** Maintainability issue, probable error / bad smell, etc. */
WARNING: "warning",
/** Inspector unable to continue, code too complex for static analysis, etc. Not counted in err/warn tally. */
META: "meta"
META: "meta",
/** For spelling or grammatical error */
SPELL: "spell"
};

function _getIconClassForType(type, isFixable) {
Expand All @@ -98,6 +100,9 @@ define(function (require, exports, module) {
case Type.META: return isFixable ?
"line-icon-problem_type_info fa-solid fa-wrench":
"line-icon-problem_type_info fa-solid fa-info-circle";
case Type.SPELL: return isFixable ?
"line-icon-problem_type_spell fa-solid fa-wrench":
"line-icon-problem_type_spell fa-solid fa-book";
default: return isFixable ?
"line-icon-problem_type_info fa-solid fa-wrench":
"line-icon-problem_type_info fa-solid fa-info-circle";
Expand Down Expand Up @@ -430,13 +435,15 @@ define(function (require, exports, module) {
case Type.ERROR: return Editor.getMarkOptionUnderlineError();
case Type.WARNING: return Editor.getMarkOptionUnderlineWarn();
case Type.META: return Editor.getMarkOptionUnderlineInfo();
case Type.SPELL: return Editor.getMarkOptionUnderlineSpellcheck();
}
}

function _getMarkTypePriority(type){
switch (type) {
case Type.ERROR: return 3;
case Type.WARNING: return 2;
case Type.ERROR: return 4;
case Type.WARNING: return 3;
case Type.SPELL: return 2;
case Type.META: return 1;
}
}
Expand Down
Loading