hanlint 0.0.18 → 0.0.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -16
- package/data/calqueFrame.json +20 -20
- package/data/cliches.json +33 -33
- package/data/confusable.json +20 -20
- package/data/easyWords.json +12 -12
- package/data/exemplars.json +30 -30
- package/data/hiddenDeps.txt +11 -11
- package/data/japaneseLoan.json +27 -27
- package/data/koreanProse.txt +31 -31
- package/data/patterns.json +26 -26
- package/data/phraseRepeat.json +2 -2
- package/data/platformApis.txt +11 -11
- package/data/redundantPair.json +16 -16
- package/data/ruleDocs.json +56 -56
- package/data/screenNarration.json +10 -10
- package/data/screenSentence.json +12 -12
- package/data/screenTone.json +6 -6
- package/data/screenWord.json +70 -70
- package/data/spacing.json +11 -11
- package/data/spelling.json +29 -29
- package/data/translationese.json +23 -24
- package/data/usageCounts.report.json +2 -2
- package/data/version.json +1 -1
- package/package.json +1 -1
- package/src/analysis/grammar/ending.js +1 -1
- package/src/analysis/grammar/hangul.js +4 -4
- package/src/analysis/grammar/josa.js +1 -1
- package/src/analysis/grammar/register.js +1 -1
- package/src/analysis/grammar/voice.js +1 -1
- package/src/analysis/index.js +2 -2
- package/src/analysis/josa.js +1 -1
- package/src/analysis/splitSentences.js +1 -1
- package/src/analysis/tokenize.js +13 -13
- package/src/baseline/store.js +8 -8
- package/src/cli/help.js +11 -11
- package/src/cli/main.js +135 -124
- package/src/cli/walk.js +1 -1
- package/src/cli/welcome.js +8 -8
- package/src/config/loadConfig.js +3 -3
- package/src/config/patch.js +6 -6
- package/src/config/readerContract.js +26 -26
- package/src/config/settings.js +30 -28
- package/src/config/toml.js +9 -9
- package/src/data/exemplars.js +21 -21
- package/src/data/load.js +6 -6
- package/src/data/operations.js +14 -14
- package/src/data/patches.js +10 -10
- package/src/data/patterns.js +5 -5
- package/src/data/profiles.js +5 -5
- package/src/data/prose.js +5 -5
- package/src/document/model.js +4 -4
- package/src/document/parseMarkdown.js +7 -7
- package/src/document/plainText.js +3 -3
- package/src/document/sourceText.js +24 -24
- package/src/edit/applyFixes.js +0 -0
- package/src/fingerprint/build.js +6 -6
- package/src/fingerprint/codeBlocks.js +3 -3
- package/src/fingerprint/codeMarkers.js +4 -4
- package/src/fingerprint/dictionaries.js +7 -7
- package/src/fingerprint/markers.js +7 -7
- package/src/fingerprint/readerState.js +9 -9
- package/src/fingerprint/sourceSentence.js +1 -1
- package/src/fingerprint/topics.js +1 -1
- package/src/guard/contract.js +6 -6
- package/src/guard/editLocality.js +1 -1
- package/src/guard/outline.js +1 -1
- package/src/guard/receipt.js +13 -13
- package/src/guard/surface.js +4 -4
- package/src/index.js +6 -6
- package/src/learn/edits.js +2 -2
- package/src/learn/operations.js +1 -1
- package/src/regex.js +2 -2
- package/src/report/compactReport.js +3 -3
- package/src/report/fingerprintJson.js +2 -2
- package/src/report/githubReport.js +1 -1
- package/src/report/jsonReport.js +2 -2
- package/src/report/registerMatch.js +2 -2
- package/src/report/sheet.js +16 -16
- package/src/report/textReport.js +12 -12
- package/src/report/writingSpec.js +15 -15
- package/src/rules/code/inputFileSource.js +1 -1
- package/src/rules/code/installImport.js +2 -2
- package/src/rules/code/platformApi.js +1 -1
- package/src/rules/document/countMismatch.js +2 -1
- package/src/rules/document/enoughOnce.js +2 -1
- package/src/rules/document/fieldEcho.js +10 -6
- package/src/rules/document/keywordHeading.js +1 -1
- package/src/rules/document/keywordMissing.js +1 -1
- package/src/rules/document/noQuestion.js +2 -2
- package/src/rules/document/promiseRecall.js +1 -1
- package/src/rules/document/tableOddCell.js +1 -1
- package/src/rules/finding.js +4 -4
- package/src/rules/paragraph/factListParagraph.js +1 -1
- package/src/rules/paragraph/paraFragment.js +1 -1
- package/src/rules/readerDebt.js +1 -1
- package/src/rules/registry.js +26 -26
- package/src/rules/sentence/connectorRepeat.js +2 -1
- package/src/rules/sentence/danglingDeixis.js +1 -1
- package/src/rules/sentence/dash.js +1 -1
- package/src/rules/sentence/deixis.js +1 -1
- package/src/rules/sentence/doubleNegative.js +2 -1
- package/src/rules/sentence/doublePassive.js +2 -1
- package/src/rules/sentence/draftHistory.js +2 -1
- package/src/rules/sentence/endingRepeat.js +3 -2
- package/src/rules/sentence/euiChain.js +1 -1
- package/src/rules/sentence/fillerOpener.js +1 -1
- package/src/rules/sentence/imperativePeriod.js +1 -1
- package/src/rules/sentence/longSentence.js +1 -1
- package/src/rules/sentence/negationRedefine.js +1 -1
- package/src/rules/sentence/numberOrphan.js +1 -1
- package/src/rules/sentence/outsideProfile.js +8 -3
- package/src/rules/sentence/phraseRepeat.js +2 -1
- package/src/rules/shared/candidates.js +2 -2
- package/src/rules/shared/dictionaryRule.js +9 -2
- package/src/rules/shared/repeat.js +2 -2
- package/src/rules/structure/bridgeRepeat.js +3 -3
- package/src/rules/structure/emojiBullet.js +1 -1
- package/src/rules/structure/headingSkip.js +1 -1
- package/src/rules/structure/headingUniform.js +2 -1
- package/src/rules/structure/loneSubheading.js +1 -1
- package/src/rules/structure/moreLater.js +1 -1
- package/src/text.js +2 -2
- package/src/usage/counts.js +3 -3
- package/src/usage/index.js +3 -3
- package/src/usage/sentences.js +17 -17
package/data/translationese.json
CHANGED
|
@@ -3,112 +3,111 @@
|
|
|
3
3
|
{
|
|
4
4
|
"pattern": "에 있어서",
|
|
5
5
|
"fix": "에서",
|
|
6
|
-
"why": "번역투다. `분야에
|
|
6
|
+
"why": "번역투다. `분야에 있어서`는 `분야에서`로 쓴다",
|
|
7
7
|
"source": "urimal.org 김민지 2020"
|
|
8
8
|
},
|
|
9
9
|
{
|
|
10
10
|
"pattern": "에 위치해 있",
|
|
11
11
|
"fix": "에 있",
|
|
12
|
-
"why": "번역투다. `서울에 위치해
|
|
12
|
+
"why": "번역투다. `서울에 위치해 있다`는 `서울에 있다`로 쓴다",
|
|
13
13
|
"source": "egowriting 번역투 고치기"
|
|
14
14
|
},
|
|
15
15
|
{
|
|
16
16
|
"pattern": "에 위치하",
|
|
17
17
|
"fix": "에 있",
|
|
18
|
-
"why": "번역투다. `평창에 위치하고
|
|
18
|
+
"why": "번역투다. `평창에 위치하고 있습니다`는 `평창에 있습니다`로 쓴다",
|
|
19
19
|
"source": "egowriting 번역투 고치기"
|
|
20
20
|
},
|
|
21
21
|
{
|
|
22
|
-
"pattern": "([가-힣])로부터",
|
|
23
|
-
"
|
|
24
|
-
"why": "번역투다. `상사로부터 인정받다` 는 `상사에게 인정받다` 다. 출발점이 장소면 `에서` 다",
|
|
22
|
+
"pattern": "(?<=[가-힣])로부터",
|
|
23
|
+
"why": "번역투다. 받는 사람이면 `에게`, 출발점이면 `에서`로 쓴다. `상사로부터 인정받다`는 `상사에게 인정받다`로, `부산으로부터 왔다`는 `부산에서 왔다`로 쓴다",
|
|
25
24
|
"source": "egowriting 번역투 고치기"
|
|
26
25
|
},
|
|
27
26
|
{
|
|
28
27
|
"pattern": "음에도 불구하고",
|
|
29
28
|
"fix": "지만",
|
|
30
|
-
"why": "번역투다. `늦었음에도
|
|
29
|
+
"why": "번역투다. `늦었음에도 불구하고`는 `늦었지만`으로 쓴다",
|
|
31
30
|
"source": "egowriting 번역투 고치기"
|
|
32
31
|
},
|
|
33
32
|
{
|
|
34
33
|
"pattern": "에도 불구하고",
|
|
35
|
-
"why": "번역투다.
|
|
34
|
+
"why": "번역투다. `-지만`이나 `-는데도`로 풀어 쓴다",
|
|
36
35
|
"source": "egowriting 번역투 고치기"
|
|
37
36
|
},
|
|
38
37
|
{
|
|
39
38
|
"pattern": "([가-힣])(?:을|를) 통해(?:서)?",
|
|
40
|
-
"why": "through
|
|
39
|
+
"why": "영어 through를 그대로 옮긴 말이다. `밤샘 공부를 통해` 대신 `밤샘 공부로`라고 쓴다. 수단이면 `로`, 경로면 `거쳐`를 쓴다",
|
|
41
40
|
"source": "한빛미디어 번역투 TOP 12, im-not-ai A-2"
|
|
42
41
|
},
|
|
43
42
|
{
|
|
44
43
|
"pattern": "에 의(?:해|하여|해서)",
|
|
45
|
-
"why": "by
|
|
44
|
+
"why": "영어 수동태의 by를 그대로 옮긴 말이다. 행동한 쪽을 주어로 세워 능동으로 쓴다. `보육원에 의해 보호되었다`는 `보육원이 보호했다`로 쓴다",
|
|
46
45
|
"source": "urimal.org 김민지 2020"
|
|
47
46
|
},
|
|
48
47
|
{
|
|
49
48
|
"pattern": "(?:으)?로 인(?:해|하여)",
|
|
50
|
-
"why": "번역투다. `국민투표로 인하여
|
|
49
|
+
"why": "번역투다. `국민투표로 인하여 결정`은 `국민투표로 결정`으로 쓴다. 원인이면 `때문에`를 쓴다",
|
|
51
50
|
"source": "urimal.org 김민지 2020"
|
|
52
51
|
},
|
|
53
52
|
{
|
|
54
53
|
"pattern": "에 대(?:해|하여|해서)",
|
|
55
|
-
"why": "번역투다. `물음에 대해
|
|
54
|
+
"why": "번역투다. `물음에 대해 답하다`는 `물음에 답하다`로 쓴다. 목적어로 되돌린다",
|
|
56
55
|
"source": "urimal.org 김민지 2020"
|
|
57
56
|
},
|
|
58
57
|
{
|
|
59
58
|
"pattern": "에 관(?:해|하여|해서)",
|
|
60
|
-
"why": "번역투다. 목적어나
|
|
59
|
+
"why": "번역투다. 목적어나 `~은`으로 되돌린다",
|
|
61
60
|
"source": "urimal.org 김민지 2020"
|
|
62
61
|
},
|
|
63
62
|
{
|
|
64
63
|
"pattern": "에 대한",
|
|
65
|
-
"why": "번역투다. `문제점에 대한
|
|
64
|
+
"why": "번역투다. `문제점에 대한 이해`는 `문제점을 아는 것`으로 쓴다. 목적어로 되돌린다",
|
|
66
65
|
"source": "urimal.org 김민지 2020"
|
|
67
66
|
},
|
|
68
67
|
{
|
|
69
68
|
"pattern": "에 관한",
|
|
70
|
-
"why": "번역투다. `설정에 관한
|
|
69
|
+
"why": "번역투다. `설정에 관한 설명`은 `설정 설명`으로 쓴다. 목적어나 `~은`으로 되돌린다",
|
|
71
70
|
"source": "urimal.org 김민지 2020"
|
|
72
71
|
},
|
|
73
72
|
{
|
|
74
73
|
"pattern": "(?:을|를) 위(?:해|하여|해서)",
|
|
75
|
-
"why": "번역투다. `여행을 가기
|
|
74
|
+
"why": "번역투다. `여행을 가기 위해`는 `여행 가려고`로 쓴다",
|
|
76
75
|
"source": "urimal.org 김민지 2020"
|
|
77
76
|
},
|
|
78
77
|
{
|
|
79
78
|
"pattern": "(?:을|를) 필요로 하",
|
|
80
|
-
"why": "번역투다. `사회가 필요로 하는
|
|
79
|
+
"why": "번역투다. `사회가 필요로 하는 것`은 `사회가 필요한 것`으로 쓴다",
|
|
81
80
|
"source": "서울신문 바른 말글 2018"
|
|
82
81
|
},
|
|
83
82
|
{
|
|
84
83
|
"pattern": "하는 중(?:이다|입니다|이었다)",
|
|
85
|
-
"why": "
|
|
84
|
+
"why": "영어 진행형을 그대로 옮긴 말이다. `도착하는 중입니다`는 `도착합니다`로 쓴다",
|
|
86
85
|
"source": "egowriting 번역투 고치기"
|
|
87
86
|
},
|
|
88
87
|
{
|
|
89
88
|
"pattern": "[가-힣](?:으로의|에의) ",
|
|
90
|
-
"why": "전치사구를
|
|
89
|
+
"why": "전치사구를 명사로 만든 번역투다. `성공으로의 길`은 `성공에 이르는 길`로 쓴다",
|
|
91
90
|
"source": "국립국어원 새국어생활 1990"
|
|
92
91
|
},
|
|
93
92
|
{
|
|
94
93
|
"pattern": "에 다름 아니",
|
|
95
|
-
"why": "
|
|
94
|
+
"why": "일본어를 그대로 옮긴 말이다. `바로 ~이다`로 쓴다",
|
|
96
95
|
"source": "부산일보 바른말 광 2018"
|
|
97
96
|
},
|
|
98
97
|
{
|
|
99
98
|
"pattern": "(?:이|가) 가능(?:하다|합니다|해진다)",
|
|
100
|
-
"why": "possible
|
|
99
|
+
"why": "영어 possible을 그대로 옮긴 말이다. `스킵이 가능하다`는 `스킵할 수 있다`로 쓴다",
|
|
101
100
|
"source": "나무위키 번역체 문장"
|
|
102
101
|
},
|
|
103
102
|
{
|
|
104
103
|
"pattern": "하게 만들(?:었|었습|었다|다)",
|
|
105
|
-
"why": "make
|
|
104
|
+
"why": "영어 make를 쓴 사동문을 그대로 옮긴 말이다. `나를 행복하게 만들었다`는 `난 그 일로 행복했다`로 쓴다",
|
|
106
105
|
"source": "hanmal 번역투 정리 2011"
|
|
107
106
|
},
|
|
108
107
|
{
|
|
109
108
|
"pattern": "(?:을|를) 가지고 있",
|
|
110
|
-
"why": "have
|
|
111
|
-
"source": "김순영 2012
|
|
109
|
+
"why": "영어 have를 그대로 옮긴 말이다. `목소리를 가지고 있다`는 `목소리가 좋다`로, `책을 가지고 있다`는 `책이 있다`로 쓴다",
|
|
110
|
+
"source": "김순영 2012, 이근희 2005 재인용"
|
|
112
111
|
}
|
|
113
112
|
]
|
|
114
113
|
}
|
|
@@ -1644,7 +1644,7 @@
|
|
|
1644
1644
|
"(?<![가-힣])잔고": 165
|
|
1645
1645
|
},
|
|
1646
1646
|
"redundantPair": {
|
|
1647
|
-
"(?:또)?다시 재(?!(?:력|주|산|물|화|벌|원|무|정|능|치|간|료|목|질|난|해|앙|고|판){조사})([가-힣]+)": 12,
|
|
1647
|
+
"(?:또)?다시 재(?!(?:력|주|산|물|화|벌|원|무|정|능|치|간|료|목|질|난|해|앙|고|판){조사})(?!(?:거나|면|서|며|니|는|어|어서|었다|야|요|다|도|지)(?![가-힣]))([가-힣]+)": 12,
|
|
1648
1648
|
"결실을 맺": 25,
|
|
1649
1649
|
"과반수 이상": 94,
|
|
1650
1650
|
"남은 여생": 1,
|
|
@@ -1656,8 +1656,8 @@
|
|
|
1656
1656
|
"(?:을|를) 위(?:해|하여|해서)": 3184,
|
|
1657
1657
|
"(?:을|를) 필요로 하": 1196,
|
|
1658
1658
|
"(?:이|가) 가능(?:하다|합니다|해진다)": 2099,
|
|
1659
|
+
"(?<=[가-힣])로부터": 3184,
|
|
1659
1660
|
"([가-힣])(?:을|를) 통해(?:서)?": 3171,
|
|
1660
|
-
"([가-힣])로부터": 3184,
|
|
1661
1661
|
"[가-힣](?:으로의|에의) ": 1906,
|
|
1662
1662
|
"에 관(?:해|하여|해서)": 1676,
|
|
1663
1663
|
"에 관한": 3189,
|
package/data/version.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version": "0.0.
|
|
1
|
+
{"version": "0.0.19"}
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @ts-check
|
|
2
|
-
/** 파이썬 `analysis/grammar/ending.py`와 `predicate.py
|
|
2
|
+
/** 파이썬 `analysis/grammar/ending.py`와 `predicate.py`를 옮긴 것. 종결 서술어를 풀고 세 문체로 짠다. */
|
|
3
3
|
import { loadLines } from "../../data/load.js";
|
|
4
4
|
import * as hangul from "./hangul.js";
|
|
5
5
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
/**
|
|
3
|
-
* 한글 음절의 자모 산술. 파이썬 `analysis/grammar/hangul.py
|
|
3
|
+
* 한글 음절의 자모 산술. 파이썬 `analysis/grammar/hangul.py`를 옮긴 것이다. 표 대신 산술로 하는 까닭은 그 파일에 적었다.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
export const BASE = 0xac00;
|
|
@@ -31,7 +31,7 @@ export const EU = 18;
|
|
|
31
31
|
export const UI = 19;
|
|
32
32
|
export const IH = 20;
|
|
33
33
|
|
|
34
|
-
// 종성 자리 번호 가운데 형태
|
|
34
|
+
// 종성 자리 번호 가운데 형태 부분이 이름으로 부르는 것.
|
|
35
35
|
export const NONE = 0;
|
|
36
36
|
export const GIYEOK = 1;
|
|
37
37
|
export const NIEUN = 4;
|
|
@@ -44,7 +44,7 @@ export const SSANGSIOT = 20;
|
|
|
44
44
|
export const IEUNG = 21;
|
|
45
45
|
export const HIEUT = 27;
|
|
46
46
|
|
|
47
|
-
/** 양성 모음. 이 모음 뒤에는
|
|
47
|
+
/** 양성 모음. 이 모음 뒤에는 `아`가 붙고 나머지에는 `어`가 붙는다. */
|
|
48
48
|
export const BRIGHT = new Set([A, OH, YA, YO, WA]);
|
|
49
49
|
|
|
50
50
|
/** @param {string} ch */
|
|
@@ -88,7 +88,7 @@ export function withVowel(ch, vowel) {
|
|
|
88
88
|
return join(initial, vowel, final);
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
-
/**
|
|
91
|
+
/** 단어의 마지막 한글 음절. 없으면 null. @param {string} word */
|
|
92
92
|
export function lastSyllable(word) {
|
|
93
93
|
return word && isSyllable(word[word.length - 1]) ? word[word.length - 1] : null;
|
|
94
94
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @ts-check
|
|
2
|
-
/** 파이썬 `analysis/grammar/register.py
|
|
2
|
+
/** 파이썬 `analysis/grammar/register.py`를 옮긴 것. 글의 문체를 판별하고 문장 끝 서술어를 맞춘다. */
|
|
3
3
|
import * as hangul from "./hangul.js";
|
|
4
4
|
import { HAEYO, HANDA, HAPNIDA, REGISTERS, parsePredicate, render } from "./ending.js";
|
|
5
5
|
|
package/src/analysis/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
/**
|
|
3
|
-
* 분석
|
|
4
|
-
*
|
|
3
|
+
* 분석 부분. 문장 분리와 어절 판정을 글자 모양(어절과 꼬리 사전)만으로 한다. 형태소 분석기는 없다. 파이썬 analysis/__init__.py와 같다.
|
|
4
|
+
* 글 분석에 필요한 것은 넷이다. splitSentences, euiCount와 euiAdjacent, longestNounRun, doublePassives.
|
|
5
5
|
*/
|
|
6
6
|
export { splitSentences } from "./splitSentences.js";
|
|
7
7
|
export { doublePassives, doublePassiveSpans, euiAdjacent, euiCount, longestNounRun, nounRuns } from "./tokenize.js";
|
package/src/analysis/josa.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
// 이전 deep path 호환.
|
|
1
|
+
// 이전 deep path 호환. 실제 구현은 grammar/josa.js에 있다.
|
|
2
2
|
export * from "./grammar/josa.js";
|
package/src/analysis/tokenize.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
/**
|
|
3
|
-
* 어절 단위
|
|
3
|
+
* 어절 단위 어림. 조사와 어미 꼬리 사전으로 명사 어절을 가려낸다. 파이썬 analysis/tokenize.py와 같은 판정이다.
|
|
4
4
|
* 띄어 쓴 조사와 계사는 앞 어절에 붙은 것으로, 영문 어절 연속은 한 덩어리로, 수사와 바로 뒤 단위는 수량으로 본다.
|
|
5
5
|
*/
|
|
6
6
|
import { loadLines } from "../data/load.js";
|
|
@@ -9,7 +9,7 @@ import { splitWords, stripChars } from "../text.js";
|
|
|
9
9
|
const HANGUL = /[가-힣]/;
|
|
10
10
|
const HANGUL_WORD = /^[가-힣]+$/;
|
|
11
11
|
const WORD_CHARS = /^[가-힣A-Za-z0-9]+$/;
|
|
12
|
-
/** 관형격 조사
|
|
12
|
+
/** 관형격 조사 `의`. 영문과 숫자 뒤(`API의`, `600MiB의`)와 닫는 괄호 뒤(`L7(HTTP)의`)도 센다. 파이썬 tokenize.py와 같다. */
|
|
13
13
|
const GENITIVE = /([가-힣A-Za-z0-9]+|\))의(?=[\s,.)\]]|$)/g;
|
|
14
14
|
const SPACED_GENITIVE = /(?<=\s)의(?=\s)/g;
|
|
15
15
|
export const COPULA = /^(?:이(?:고|며|다|라|란|면|라서|므로|지만|어서|었다|었고)|인|인데|입니다|였다)$/;
|
|
@@ -77,8 +77,8 @@ export function inNouns() {
|
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
/**
|
|
80
|
-
* `도구인`,
|
|
81
|
-
* 외국인 같은 세 음절 명사는 data/inNouns.txt
|
|
80
|
+
* `도구인`, `규모인`처럼 명사에 계사 관형형 `인`이 붙은 어절. 서술어라 명사 나열을 끊는다. 어간이 두 음절 이상일 때만이고
|
|
81
|
+
* 외국인 같은 세 음절 명사는 data/inNouns.txt가 뺀다. 파이썬 tokenize.py와 같다.
|
|
82
82
|
* @param {string} core
|
|
83
83
|
*/
|
|
84
84
|
export function isCopulaAdnominal(core) {
|
|
@@ -126,12 +126,12 @@ export function isBareNoun(core) {
|
|
|
126
126
|
}
|
|
127
127
|
|
|
128
128
|
/**
|
|
129
|
-
* 명사 어절 연속의 최대 길이. 의존명사와 관형사와 부사
|
|
130
|
-
*
|
|
129
|
+
* 명사 어절 연속의 최대 길이. 의존명사와 관형사와 부사(`수`, `몇`, `직접`)는 조사도 어미도 안 붙어
|
|
130
|
+
* 글자 모양으로는 명사로 보이지만 명사 쌓기의 재료가 아니다. data/nonNouns.txt가 그 목록이고 연속을 끊는다.
|
|
131
131
|
* @param {string} text
|
|
132
132
|
*/
|
|
133
133
|
/**
|
|
134
|
-
* 명사 어절 연속마다 [어절들, 센 길이]. 뜻은 파이썬 analysis/tokenize.py
|
|
134
|
+
* 명사 어절 연속마다 [어절들, 센 길이]. 뜻은 파이썬 analysis/tokenize.py의 nounRuns가 정한다.
|
|
135
135
|
* 수사와 단위는 세지도 끊지도 않고 어절에도 안 넣는다. 잇달은 영문 어절은 하나로 센다.
|
|
136
136
|
* @param {string} text @returns {[string[], number][]}
|
|
137
137
|
*/
|
|
@@ -167,14 +167,14 @@ export function nounRuns(text) {
|
|
|
167
167
|
return runs;
|
|
168
168
|
}
|
|
169
169
|
|
|
170
|
-
/** 명사 어절 연속의 최대 길이. nounRuns
|
|
170
|
+
/** 명사 어절 연속의 최대 길이. nounRuns의 센 길이 가운데 가장 큰 것. @param {string} text */
|
|
171
171
|
export function longestNounRun(text) {
|
|
172
172
|
let longest = 0;
|
|
173
173
|
for (const [, length] of nounRuns(text)) longest = Math.max(longest, length);
|
|
174
174
|
return longest;
|
|
175
175
|
}
|
|
176
176
|
|
|
177
|
-
/** 관형격 조사
|
|
177
|
+
/** 관형격 조사 `의`가 붙은 곳 [시작, 끝]. `정의`, `회의`처럼 `의`로 끝나는 단어는 빼고 띄어 쓴 `의`는 넣는다. @param {string} text @returns {[number, number][]} */
|
|
178
178
|
export function genitiveSpans(text) {
|
|
179
179
|
/** @type {[number, number][]} */
|
|
180
180
|
const spans = [];
|
|
@@ -185,13 +185,13 @@ export function genitiveSpans(text) {
|
|
|
185
185
|
return spans.sort((a, b) => a[0] - b[0] || a[1] - b[1]);
|
|
186
186
|
}
|
|
187
187
|
|
|
188
|
-
/**
|
|
188
|
+
/** `의`로 끝나는 어절 둘이 붙어 있는가(`회사의 팀의`). 사이가 공백뿐이어야 한다. @param {string} text */
|
|
189
189
|
export function euiAdjacent(text) {
|
|
190
190
|
const spans = genitiveSpans(text);
|
|
191
191
|
return spans.some((span, index) => index > 0 && /^\s+$/.test(text.slice(spans[index - 1][1], span[0])));
|
|
192
192
|
}
|
|
193
193
|
|
|
194
|
-
/** 관형격 조사
|
|
194
|
+
/** 관형격 조사 `의`의 수. @param {string} text */
|
|
195
195
|
export function euiCount(text) {
|
|
196
196
|
return genitiveSpans(text).length;
|
|
197
197
|
}
|
|
@@ -220,7 +220,7 @@ export function doublePassivePattern() {
|
|
|
220
220
|
return passivePattern;
|
|
221
221
|
}
|
|
222
222
|
|
|
223
|
-
/** 이중 피동의 [시작, 끝, `되어지`
|
|
223
|
+
/** 이중 피동의 [시작, 끝, `되어지` 꼴로 쓴 글자]. @param {string} text @returns {[number, number, string][]} */
|
|
224
224
|
export function doublePassiveSpans(text) {
|
|
225
225
|
return [...text.matchAll(doublePassivePattern())].map((match) => {
|
|
226
226
|
const start = /** @type {number} */ (match.index);
|
|
@@ -228,7 +228,7 @@ export function doublePassiveSpans(text) {
|
|
|
228
228
|
});
|
|
229
229
|
}
|
|
230
230
|
|
|
231
|
-
/** 이중 피동의
|
|
231
|
+
/** 이중 피동의 글자를 `되어지` 꼴로. @param {string} text */
|
|
232
232
|
export function doublePassives(text) {
|
|
233
233
|
return doublePassiveSpans(text).map(([, , surface]) => surface);
|
|
234
234
|
}
|
package/src/baseline/store.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
/**
|
|
3
|
-
* 이미 있는 지적을
|
|
4
|
-
* 왜 줄 번호가 아니라 글자인지, 왜 사람이 읽는
|
|
3
|
+
* 이미 있는 지적을 baseline 파일에 적어 두고 새로 생긴 지적만 막는다. 파이썬 `hanlint/baseline/store.py`를 옮긴 것이다.
|
|
4
|
+
* 왜 줄 번호가 아니라 글자인지, 왜 사람이 읽는 형식인지는 그 파일의 docstring에 있다.
|
|
5
5
|
*/
|
|
6
6
|
import { existsSync, readFileSync } from "node:fs";
|
|
7
7
|
import { dirname, posix, relative, resolve, sep } from "node:path";
|
|
@@ -20,7 +20,7 @@ export function keyOf(finding) {
|
|
|
20
20
|
return `${finding.rule}\u0000${normalizeQuote(finding.quote)}`;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
/**
|
|
23
|
+
/** baseline 파일이 있는 폴더에서 본 상대 경로. @param {string} path @param {string | null} target */
|
|
24
24
|
export function pathKey(path, target) {
|
|
25
25
|
if (!target || !path || path.startsWith("<")) return path;
|
|
26
26
|
return relative(dirname(resolve(target)), resolve(path)).split(sep).join(posix.sep);
|
|
@@ -44,7 +44,7 @@ export class Baseline {
|
|
|
44
44
|
return (this.locked.get(pathKey(path, this.source)) ?? new Set()).has(keyOf(finding));
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
/**
|
|
47
|
+
/** baseline 파일에 없는 지적만 남긴다. @param {string} path @param {import("../rules/finding.js").Finding[]} findings */
|
|
48
48
|
keep(path, findings) {
|
|
49
49
|
return findings.filter((finding) => !this.isLocked(path, finding));
|
|
50
50
|
}
|
|
@@ -60,7 +60,7 @@ export function build(results, target = null) {
|
|
|
60
60
|
return new Baseline(locked, target ? resolve(target) : null);
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
/** 사람이 읽고 리뷰하는
|
|
63
|
+
/** 사람이 읽고 리뷰하는 형식. 정렬해 담아 같은 입력에 같은 파일이 나온다. @param {Baseline} baseline */
|
|
64
64
|
export function render(baseline) {
|
|
65
65
|
/** @type {Record<string, {rule: string, quote: string}[]>} */
|
|
66
66
|
const files = {};
|
|
@@ -80,7 +80,7 @@ export function render(baseline) {
|
|
|
80
80
|
/** @param {string} text @param {string | null} [source] */
|
|
81
81
|
export function parse(text, source = null) {
|
|
82
82
|
const data = JSON.parse(text);
|
|
83
|
-
if (data.version !== VERSION) throw new Error(
|
|
83
|
+
if (data.version !== VERSION) throw new Error(`지원하지 않는 baseline 버전: ${data.version}. hanlint baseline으로 다시 만든다`);
|
|
84
84
|
/** @type {Map<string, Set<string>>} */
|
|
85
85
|
const locked = new Map();
|
|
86
86
|
for (const [path, entries] of Object.entries(data.files ?? {})) {
|
|
@@ -92,7 +92,7 @@ export function parse(text, source = null) {
|
|
|
92
92
|
/** @param {string} path */
|
|
93
93
|
export function load(path) {
|
|
94
94
|
if (!existsSync(path)) {
|
|
95
|
-
const error = /** @type {NodeJS.ErrnoException} */ (new Error(
|
|
95
|
+
const error = /** @type {NodeJS.ErrnoException} */ (new Error(`파일을 찾지 못했다: ${path}`));
|
|
96
96
|
error.code = "ENOENT";
|
|
97
97
|
error.path = path;
|
|
98
98
|
throw error;
|
|
@@ -100,7 +100,7 @@ export function load(path) {
|
|
|
100
100
|
return parse(readFileSync(path, "utf-8"), resolve(path));
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
-
/** 지금 글에
|
|
103
|
+
/** 지금 글에 더는 없는 지적을 baseline 파일에서 지운다. @param {Baseline} baseline @param {Map<string, import("../rules/finding.js").Finding[]>} results */
|
|
104
104
|
export function prune(baseline, results) {
|
|
105
105
|
const kept = new Map(baseline.locked);
|
|
106
106
|
for (const [path, findings] of results) {
|
package/src/cli/help.js
CHANGED
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
/** 첫 화면 다음에 보이는 작업 흐름 중심 도움말. */
|
|
3
3
|
|
|
4
4
|
export function rootHelp() {
|
|
5
|
-
return `hanlint: 한국어
|
|
5
|
+
return `hanlint: 한국어 글에서 규칙으로 잡을 수 있는 잘못을 찾는다
|
|
6
6
|
|
|
7
7
|
일상 검사
|
|
8
8
|
hanlint 글.md 글을 검사한다
|
|
9
9
|
hanlint fix 글.md 안전한 고침만 적용한다
|
|
10
|
-
hanlint hook AI
|
|
10
|
+
hanlint hook AI가 쓰기 전에 글쓰기 지침을 넣어 주고, 저장한 마크다운을 같은 턴에 검사한다
|
|
11
11
|
|
|
12
|
-
요구사항
|
|
12
|
+
요구사항 고정
|
|
13
13
|
hanlint contract init 글.md --reader "독자" --goal "목표" --outline h2
|
|
14
14
|
hanlint check contract.json 글.md --format text
|
|
15
15
|
hanlint verify-patch contract.json 글.md patch.json
|
|
@@ -17,20 +17,20 @@ export function rootHelp() {
|
|
|
17
17
|
설정과 이해
|
|
18
18
|
hanlint init 설정 파일을 만든다
|
|
19
19
|
hanlint doctor 적용 설정과 꺼진 규칙을 본다
|
|
20
|
-
hanlint rules 규칙을
|
|
21
|
-
hanlint explain <규칙> 규칙의 이유와
|
|
20
|
+
hanlint rules 규칙을 카테고리별로 본다
|
|
21
|
+
hanlint explain <규칙> 규칙의 이유와 예시를 본다
|
|
22
22
|
hanlint patterns --rule <규칙> 다시 쓸 문장 틀을 본다
|
|
23
23
|
hanlint spec --preset <종류> --chars 800
|
|
24
|
-
쓰기
|
|
25
|
-
hanlint primer --preset <종류> 쓰기 전에 규칙의 고치는 법과
|
|
26
|
-
hanlint sheet src/ --preset screen 소스의 화면 글을 표 하나로 내보낸다. 고친 표는 sheet apply
|
|
27
|
-
hanlint usage "
|
|
24
|
+
쓰기 전에 볼 스펙을 만든다
|
|
25
|
+
hanlint primer --preset <종류> 쓰기 전에 규칙의 고치는 법과 예시를 한 장으로 본다
|
|
26
|
+
hanlint sheet src/ --preset screen 소스의 화면 글을 표 하나로 내보낸다. 고친 표는 sheet apply로 원본에 반영한다
|
|
27
|
+
hanlint usage "단어 단어" 그 종류의 실제 글에서 단어가 쓰인 문장을 본다. 색인은 usage build가 만든다
|
|
28
28
|
|
|
29
29
|
추가 명령
|
|
30
30
|
baseline, print
|
|
31
31
|
Python 전용: audit, map, watch, profile, coverage, diff, learn, packet,
|
|
32
32
|
guard, arena, blueprint, evidence, entailment, terms
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
종료 코드: 0
|
|
34
|
+
명령마다의 옵션은 README의 명령 표에 있다. 폴더를 주면 그 아래 마크다운을 검사한다.
|
|
35
|
+
종료 코드: 0은 오류 없음, 1은 오류 있음, 2는 입력이나 설정 문제. 좋은 글인지는 판정하지 않는다.`;
|
|
36
36
|
}
|