• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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