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