diff --git a/docs/API-Reference/language/CodeInspection.md b/docs/API-Reference/language/CodeInspection.md
index 3993007c05..ba029906f5 100644
--- a/docs/API-Reference/language/CodeInspection.md
+++ b/docs/API-Reference/language/CodeInspection.md
@@ -34,6 +34,7 @@ Values for problem's 'type' property
* [.ERROR](#Type.ERROR)
* [.WARNING](#Type.WARNING)
* [.META](#Type.META)
+ * [.SPELL](#Type.SPELL)
@@ -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 [Type](#Type)
+
+
+### Type.SPELL
+For spelling or grammatical error
+
**Kind**: static property of [Type](#Type)
diff --git a/src/extensions/default/DefaultExtensions.json b/src/extensions/default/DefaultExtensions.json
index fd1fc5483f..bd5390cd99 100644
--- a/src/extensions/default/DefaultExtensions.json
+++ b/src/extensions/default/DefaultExtensions.json
@@ -21,6 +21,7 @@
"Phoenix-prettier",
"PrefsCodeHints",
"QuickView",
+ "SpellGrammarCheck",
"SVGCodeHints",
"UrlCodeHints",
"HealthData"
diff --git a/src/extensions/default/SpellGrammarCheck/grammar-checker.js b/src/extensions/default/SpellGrammarCheck/grammar-checker.js
new file mode 100644
index 0000000000..bf131b04bb
--- /dev/null
+++ b/src/extensions/default/SpellGrammarCheck/grammar-checker.js
@@ -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;
+});
diff --git a/src/extensions/default/SpellGrammarCheck/main.js b/src/extensions/default/SpellGrammarCheck/main.js
new file mode 100644
index 0000000000..0af78b8eb3
--- /dev/null
+++ b/src/extensions/default/SpellGrammarCheck/main.js
@@ -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();
+});
diff --git a/src/extensions/default/SpellGrammarCheck/package.json b/src/extensions/default/SpellGrammarCheck/package.json
new file mode 100644
index 0000000000..5b136b2fca
--- /dev/null
+++ b/src/extensions/default/SpellGrammarCheck/package.json
@@ -0,0 +1,5 @@
+{
+ "name": "SpellGrammarCheck",
+ "version": "1.0.0",
+ "dependencies": {}
+}
diff --git a/src/extensions/default/SpellGrammarCheck/spell-checker.js b/src/extensions/default/SpellGrammarCheck/spell-checker.js
new file mode 100644
index 0000000000..e8e986cbf1
--- /dev/null
+++ b/src/extensions/default/SpellGrammarCheck/spell-checker.js
@@ -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;
+});
diff --git a/src/language/CodeInspection.js b/src/language/CodeInspection.js
index 8cbb15d89f..2c3a9c7b87 100644
--- a/src/language/CodeInspection.js
+++ b/src/language/CodeInspection.js
@@ -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) {
@@ -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";
@@ -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;
}
}