1/* 2 * Copyright (c) 2021-2022 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16const fs = require("fs"); 17const path = require("path"); 18const ts = require(path.resolve(__dirname, "../node_modules/typescript")); 19const { hasAPINote, getAPINote, overwriteIndexOf, error_type } = require("./utils"); 20const { addAPICheckErrorLogs } = require("./compile_info"); 21const rules = require("../code_style_rule.json"); 22const content = fs.readFileSync(path.resolve(__dirname, "../plugin/dictionaries.txt"), 'utf-8'); 23const dictionariesArr = content.split("\n"); 24const dictionariesSet = new Set([...dictionariesArr, ...rules.decorators.customDoc, ...rules.decorators.jsDoc]); 25 26function checkSpelling(node, sourcefile, fileName) { 27 if (ts.isIdentifier(node) && node.escapedText) { 28 checkWordSpelling(node.escapedText.toString(), node, sourcefile, fileName); 29 } else if (hasAPINote(node)) { 30 const apiNote = getAPINote(node); 31 const words = splitParagraph(apiNote); 32 words.forEach(word => { 33 checkWordSpelling(word, node, sourcefile, fileName); 34 }); 35 } 36} 37exports.checkSpelling = checkSpelling; 38 39function checkWordSpelling(nodeText, node, sourcefile, fileName) { 40 const basicWords = splitComplexWords(nodeText); 41 const errorWords = []; 42 const suggest = []; 43 basicWords.forEach(word => { 44 if (/^[A-Za-z]+/g.test(word) && !dictionariesSet.has(word.toLowerCase())) { 45 errorWords.push(word); 46 } 47 }); 48 if (errorWords.length !== 0) { 49 errorWords.forEach(errorWord => { 50 const levArr = []; 51 for (let i = 0; i < dictionariesArr.length; i++) { 52 const dictionary = dictionariesArr[i]; 53 levArr.push(getLevenshteinValue(errorWord, dictionary)); 54 } 55 const minLev = Math.min(...levArr); 56 const indexArr = overwriteIndexOf(minLev, levArr); 57 for (let i = 0; i < indexArr.length; i++) { 58 if (i === 5) { 59 break; 60 } 61 suggest.push(dictionariesArr[indexArr[i]]); 62 } 63 }); 64 const errorInfo = `Error words in [${nodeText}]: {${errorWords}}.Do you want to spell it as [${suggest}]?`; 65 addAPICheckErrorLogs(node, sourcefile, fileName, error_type.MISSPELL_WORDS, errorInfo); 66 } 67} 68 69function splitComplexWords(complexWord) { 70 let basicWords = []; 71 // splite underlineWord 72 if (hasUnderline(complexWord)) { 73 basicWords = complexWord.split(/(?<!^)\_/g); 74 } else { 75 // splite complexWord 76 if (!/(?<!^)(?=[A-Z])/g.test(complexWord)) { 77 basicWords.push(complexWord) 78 } else { 79 basicWords = complexWord.split(/(?<!^)(?=[A-Z])/g); 80 } 81 } 82 return basicWords; 83} 84exports.splitComplexWords = splitComplexWords; 85 86function hasUnderline(word) { 87 return /(?<!^)\_/g.test(word); 88} 89exports.hasUnderline = hasUnderline; 90 91function splitParagraph(paragraph) { 92 const splitParagraphRegex = /[^\w]/g; 93 const words = paragraph.split(splitParagraphRegex); 94 return words; 95} 96exports.splitParagraph = splitParagraph; 97 98// Levenshtein method 99function getLevenshteinValue(word1, word2) { 100 const word1Len = word1.length; 101 const word2Len = word2.length; 102 103 if (word1Len * word2Len === 0) { 104 return Math.max(word1Len, word2Len); 105 } 106 // create Levenshtein two-dimensional array 107 const levArr = Array.from(new Array(word1Len + 1), () => new Array(word2Len + 1)); 108 // set value 0 to Levenshtein two-dimensional array 109 for (let i = 0; i < word1Len + 1; i++) { 110 for (let j = 0; j < word2Len + 1; j++) { 111 levArr[i][j] = 0; 112 } 113 } 114 115 // init Levenshtein two-dimensional array 116 for (let i = 0; i < word1Len + 1; i++) { 117 levArr[i][0] = i; 118 } 119 for (let j = 0; j < word2Len + 1; j++) { 120 levArr[0][j] = j; 121 } 122 123 // calculate levinstein distance 124 for (let i = 1; i < word1Len + 1; i++) { 125 for (let j = 1; j < word2Len + 1; j++) { 126 const countByInsert = levArr[i][j - 1] + 1; 127 const countByDel = levArr[i - 1][j] + 1; 128 const countByReplace = word1.charAt(i - 1) === word2.charAt(j - 1) ? 129 levArr[i - 1][j - 1] : levArr[i - 1][j - 1] + 1; 130 levArr[i][j] = Math.min(countByInsert, countByDel, countByReplace); 131 } 132 } 133 134 return levArr[word1Len][word2Len]; 135} 136exports.getLevenshteinValue = getLevenshteinValue; 137