161847f8eSopenharmony_ci/* 261847f8eSopenharmony_ci * Copyright (c) 2021-2022 Huawei Device Co., Ltd. 361847f8eSopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License"); 461847f8eSopenharmony_ci * you may not use this file except in compliance with the License. 561847f8eSopenharmony_ci * You may obtain a copy of the License at 661847f8eSopenharmony_ci * 761847f8eSopenharmony_ci * http://www.apache.org/licenses/LICENSE-2.0 861847f8eSopenharmony_ci * 961847f8eSopenharmony_ci * Unless required by applicable law or agreed to in writing, software 1061847f8eSopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS, 1161847f8eSopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1261847f8eSopenharmony_ci * See the License for the specific language governing permissions and 1361847f8eSopenharmony_ci * limitations under the License. 1461847f8eSopenharmony_ci */ 1561847f8eSopenharmony_ci 1661847f8eSopenharmony_ciconst fs = require('fs'); 1761847f8eSopenharmony_ciconst path = require('path'); 1861847f8eSopenharmony_ciconst { hasAPINote, getAPINote, overwriteIndexOf, ErrorType, ErrorLevel, FileType, requireTypescriptModule } = require('./utils'); 1961847f8eSopenharmony_ciconst { addAPICheckErrorLogs } = require('./compile_info'); 2061847f8eSopenharmony_ciconst rules = require('../code_style_rule.json'); 2161847f8eSopenharmony_ciconst dictionariesContent = fs.readFileSync(path.resolve(__dirname, '../plugin/dictionaries.txt'), 'utf-8'); 2261847f8eSopenharmony_ciconst dictionariesArr = dictionariesContent.split(/[(\r\n)\r\n]+/g); 2361847f8eSopenharmony_ciconst dictionariesSupplementaryContent = fs.readFileSync(path.resolve(__dirname, 2461847f8eSopenharmony_ci '../plugin/dictionaries_supplementary.txt'), 'utf-8'); 2561847f8eSopenharmony_ciconst dictionariesSupplementaryArr = dictionariesSupplementaryContent.split(/[(\r\n)\r\n]+/g); 2661847f8eSopenharmony_ciconst dictionariesSet = new Set([...dictionariesArr, ...dictionariesSupplementaryArr, ...rules.decorators.customDoc, 2761847f8eSopenharmony_ci ...rules.decorators.jsDoc]); 2861847f8eSopenharmony_ciconst ts = requireTypescriptModule(); 2961847f8eSopenharmony_ci 3061847f8eSopenharmony_cifunction checkSpelling(node, sourcefile, fileName) { 3161847f8eSopenharmony_ci if (ts.isIdentifier(node) && node.escapedText) { 3261847f8eSopenharmony_ci checkWordSpelling(node.escapedText.toString(), node, sourcefile, fileName, FileType.API); 3361847f8eSopenharmony_ci } else if (hasAPINote(node)) { 3461847f8eSopenharmony_ci const apiNote = getAPINote(node); 3561847f8eSopenharmony_ci const words = splitParagraph(apiNote); 3661847f8eSopenharmony_ci words.forEach(word => { 3761847f8eSopenharmony_ci checkWordSpelling(word, node, sourcefile, fileName, FileType.JSDOC); 3861847f8eSopenharmony_ci }); 3961847f8eSopenharmony_ci } 4061847f8eSopenharmony_ci} 4161847f8eSopenharmony_ciexports.checkSpelling = checkSpelling; 4261847f8eSopenharmony_ci 4361847f8eSopenharmony_cifunction checkWordSpelling(nodeText, node, sourcefile, fileName, type) { 4461847f8eSopenharmony_ci const basicWords = splitComplexWords(nodeText); 4561847f8eSopenharmony_ci const errorWords = []; 4661847f8eSopenharmony_ci const suggest = []; 4761847f8eSopenharmony_ci basicWords.forEach(word => { 4861847f8eSopenharmony_ci if (!checkBaseWord(word.toLowerCase())) { 4961847f8eSopenharmony_ci errorWords.push(word); 5061847f8eSopenharmony_ci } 5161847f8eSopenharmony_ci }); 5261847f8eSopenharmony_ci if (errorWords.length !== 0) { 5361847f8eSopenharmony_ci errorWords.forEach(errorWord => { 5461847f8eSopenharmony_ci const levArr = []; 5561847f8eSopenharmony_ci for (let i = 0; i < dictionariesArr.length; i++) { 5661847f8eSopenharmony_ci const dictionary = dictionariesArr[i]; 5761847f8eSopenharmony_ci levArr.push(getLevenshteinValue(errorWord, dictionary)); 5861847f8eSopenharmony_ci } 5961847f8eSopenharmony_ci const minLev = Math.min(...levArr); 6061847f8eSopenharmony_ci const indexArr = overwriteIndexOf(minLev, levArr); 6161847f8eSopenharmony_ci const MAX_LENGTH = 5; 6261847f8eSopenharmony_ci for (let i = 0; i < indexArr.length; i++) { 6361847f8eSopenharmony_ci if (i === MAX_LENGTH) { 6461847f8eSopenharmony_ci break; 6561847f8eSopenharmony_ci } 6661847f8eSopenharmony_ci suggest.push(dictionariesArr[indexArr[i]]); 6761847f8eSopenharmony_ci } 6861847f8eSopenharmony_ci }); 6961847f8eSopenharmony_ci const errorInfo = `Error words in [${nodeText}]: {${errorWords}}.Do you want to spell it as [${suggest}]?`; 7061847f8eSopenharmony_ci addAPICheckErrorLogs(node, sourcefile, fileName, ErrorType.MISSPELL_WORDS, errorInfo, type, ErrorLevel.MIDDLE); 7161847f8eSopenharmony_ci } 7261847f8eSopenharmony_ci} 7361847f8eSopenharmony_ci 7461847f8eSopenharmony_ci/** 7561847f8eSopenharmony_ci * check base word 7661847f8eSopenharmony_ci * @param {string} word 7761847f8eSopenharmony_ci * @return true or false 7861847f8eSopenharmony_ci**/ 7961847f8eSopenharmony_cifunction checkBaseWord(word) { 8061847f8eSopenharmony_ci if (/^[a-z][0-9]+$/g.test(word)) { 8161847f8eSopenharmony_ci return false; 8261847f8eSopenharmony_ci } else if (/^[A-Za-z]+/g.test(word) && !dictionariesSet.has(word.toLowerCase())) { 8361847f8eSopenharmony_ci return false; 8461847f8eSopenharmony_ci } 8561847f8eSopenharmony_ci return true; 8661847f8eSopenharmony_ci} 8761847f8eSopenharmony_ci 8861847f8eSopenharmony_cifunction splitComplexWords(complexWord) { 8961847f8eSopenharmony_ci let basicWords = []; 9061847f8eSopenharmony_ci // splite underlineWord 9161847f8eSopenharmony_ci if (hasUnderline(complexWord)) { 9261847f8eSopenharmony_ci basicWords = complexWord.split(/(?<!^)\_/g); 9361847f8eSopenharmony_ci } else { 9461847f8eSopenharmony_ci // splite complexWord 9561847f8eSopenharmony_ci if (!/(?<!^)(?=[A-Z])/g.test(complexWord)) { 9661847f8eSopenharmony_ci basicWords.push(complexWord); 9761847f8eSopenharmony_ci } else { 9861847f8eSopenharmony_ci basicWords = complexWord.split(/(?<!^)(?=[A-Z])/g); 9961847f8eSopenharmony_ci } 10061847f8eSopenharmony_ci } 10161847f8eSopenharmony_ci const newBaseWords = []; 10261847f8eSopenharmony_ci basicWords.forEach(word => { 10361847f8eSopenharmony_ci if (/[0-9]/g.test(word)) { 10461847f8eSopenharmony_ci newBaseWords.concat(word.split(/0-9/g)); 10561847f8eSopenharmony_ci } else { 10661847f8eSopenharmony_ci newBaseWords.push(word); 10761847f8eSopenharmony_ci }; 10861847f8eSopenharmony_ci }); 10961847f8eSopenharmony_ci return newBaseWords; 11061847f8eSopenharmony_ci} 11161847f8eSopenharmony_ciexports.splitComplexWords = splitComplexWords; 11261847f8eSopenharmony_ci 11361847f8eSopenharmony_cifunction hasUnderline(word) { 11461847f8eSopenharmony_ci return /(?<!^)\_/g.test(word); 11561847f8eSopenharmony_ci} 11661847f8eSopenharmony_ciexports.hasUnderline = hasUnderline; 11761847f8eSopenharmony_ci 11861847f8eSopenharmony_cifunction splitParagraph(paragraph) { 11961847f8eSopenharmony_ci const splitParagraphRegex = /[^\w]/g; 12061847f8eSopenharmony_ci const words = paragraph.split(splitParagraphRegex); 12161847f8eSopenharmony_ci return words; 12261847f8eSopenharmony_ci} 12361847f8eSopenharmony_ciexports.splitParagraph = splitParagraph; 12461847f8eSopenharmony_ci 12561847f8eSopenharmony_ci// Levenshtein method 12661847f8eSopenharmony_cifunction getLevenshteinValue(word1, word2) { 12761847f8eSopenharmony_ci const word1Len = word1.length; 12861847f8eSopenharmony_ci const word2Len = word2.length; 12961847f8eSopenharmony_ci 13061847f8eSopenharmony_ci if (word1Len * word2Len === 0) { 13161847f8eSopenharmony_ci return Math.max(word1Len, word2Len); 13261847f8eSopenharmony_ci } 13361847f8eSopenharmony_ci // create Levenshtein two-dimensional array 13461847f8eSopenharmony_ci const levArr = Array.from(new Array(word1Len + 1), () => new Array(word2Len + 1)); 13561847f8eSopenharmony_ci // set value 0 to Levenshtein two-dimensional array 13661847f8eSopenharmony_ci for (let i = 0; i < word1Len + 1; i++) { 13761847f8eSopenharmony_ci for (let j = 0; j < word2Len + 1; j++) { 13861847f8eSopenharmony_ci levArr[i][j] = 0; 13961847f8eSopenharmony_ci } 14061847f8eSopenharmony_ci } 14161847f8eSopenharmony_ci 14261847f8eSopenharmony_ci // init Levenshtein two-dimensional array 14361847f8eSopenharmony_ci for (let i = 0; i < word1Len + 1; i++) { 14461847f8eSopenharmony_ci levArr[i][0] = i; 14561847f8eSopenharmony_ci } 14661847f8eSopenharmony_ci for (let j = 0; j < word2Len + 1; j++) { 14761847f8eSopenharmony_ci levArr[0][j] = j; 14861847f8eSopenharmony_ci } 14961847f8eSopenharmony_ci 15061847f8eSopenharmony_ci // calculate levinstein distance 15161847f8eSopenharmony_ci for (let i = 1; i < word1Len + 1; i++) { 15261847f8eSopenharmony_ci for (let j = 1; j < word2Len + 1; j++) { 15361847f8eSopenharmony_ci const countByInsert = levArr[i][j - 1] + 1; 15461847f8eSopenharmony_ci const countByDel = levArr[i - 1][j] + 1; 15561847f8eSopenharmony_ci const countByReplace = word1.charAt(i - 1) === word2.charAt(j - 1) ? 15661847f8eSopenharmony_ci levArr[i - 1][j - 1] : levArr[i - 1][j - 1] + 1; 15761847f8eSopenharmony_ci levArr[i][j] = Math.min(countByInsert, countByDel, countByReplace); 15861847f8eSopenharmony_ci } 15961847f8eSopenharmony_ci } 16061847f8eSopenharmony_ci 16161847f8eSopenharmony_ci return levArr[word1Len][word2Len]; 16261847f8eSopenharmony_ci} 16361847f8eSopenharmony_ciexports.getLevenshteinValue = getLevenshteinValue; 164