@sushichan044/kg-core 0.6.0 → 0.6.2
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/dist/index.d.mts +212 -212
- package/dist/index.mjs +114 -52
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1062,6 +1062,83 @@ const GridCell = {
|
|
|
1062
1062
|
annotations: []
|
|
1063
1063
|
})
|
|
1064
1064
|
};
|
|
1065
|
+
//#endregion
|
|
1066
|
+
//#region src/internal/japanese-brackets.ts
|
|
1067
|
+
/**
|
|
1068
|
+
* Brackets and quotes a Japanese paragraph may open with. Halfwidth forms are included because a
|
|
1069
|
+
* manuscript that uses them is still opening a paragraph, not writing prose.
|
|
1070
|
+
*/
|
|
1071
|
+
const OPENING_BRACKETS = "「『〖〈《((“\"‘'[[〔{{<<";
|
|
1072
|
+
/**
|
|
1073
|
+
* The closing counterparts, in the same order.
|
|
1074
|
+
*/
|
|
1075
|
+
const CLOSING_BRACKETS = "」』〗〉》))”\"’']]〕}}>>";
|
|
1076
|
+
/**
|
|
1077
|
+
* Escapes the characters a regular expression character class would otherwise read as syntax, so a
|
|
1078
|
+
* set declared above can be dropped into a pattern.
|
|
1079
|
+
*/
|
|
1080
|
+
function characterClass(characters) {
|
|
1081
|
+
return characters.replaceAll(/[\\\]^-]/gu, "\\$&");
|
|
1082
|
+
}
|
|
1083
|
+
//#endregion
|
|
1084
|
+
//#region src/internal/japanese-spaces.ts
|
|
1085
|
+
const LEADING_SPACES = /^[ \t]+/u;
|
|
1086
|
+
/**
|
|
1087
|
+
* The run of spaces the text begins with, or an empty string. Halfwidth spaces and tabs count: a
|
|
1088
|
+
* rule has to see them to say they are the wrong space.
|
|
1089
|
+
*/
|
|
1090
|
+
function leadingSpaces(text) {
|
|
1091
|
+
return LEADING_SPACES.exec(text)?.[0] ?? "";
|
|
1092
|
+
}
|
|
1093
|
+
//#endregion
|
|
1094
|
+
//#region src/internal/question-or-exclamation-spacing.ts
|
|
1095
|
+
/**
|
|
1096
|
+
* Runs, not single marks: `!?` is one mark and takes one gap, so the gap belongs after the run.
|
|
1097
|
+
*/
|
|
1098
|
+
const MARKS = /[?!]+/gu;
|
|
1099
|
+
function analyzeMatch(line, marks) {
|
|
1100
|
+
const start = marks.index;
|
|
1101
|
+
if (start === void 0) return void 0;
|
|
1102
|
+
const marksRange = {
|
|
1103
|
+
start,
|
|
1104
|
+
end: start + marks[0].length
|
|
1105
|
+
};
|
|
1106
|
+
const next = line[marksRange.end];
|
|
1107
|
+
if (next === void 0 || "」』〗〉》))”\"’']]〕}}>>".includes(next)) return void 0;
|
|
1108
|
+
const spaces = leadingSpaces(line.slice(marksRange.end));
|
|
1109
|
+
if (spaces === "") return {
|
|
1110
|
+
kind: "missing",
|
|
1111
|
+
marks: marksRange
|
|
1112
|
+
};
|
|
1113
|
+
const gap = {
|
|
1114
|
+
start: marksRange.end,
|
|
1115
|
+
end: marksRange.end + spaces.length
|
|
1116
|
+
};
|
|
1117
|
+
const following = line[gap.end];
|
|
1118
|
+
if (following !== void 0 && "」』〗〉》))”\"’']]〕}}>>".includes(following)) return {
|
|
1119
|
+
kind: "before-closing-bracket",
|
|
1120
|
+
marks: marksRange,
|
|
1121
|
+
gap
|
|
1122
|
+
};
|
|
1123
|
+
if (spaces !== " ") return {
|
|
1124
|
+
kind: "invalid-space",
|
|
1125
|
+
marks: marksRange,
|
|
1126
|
+
gap
|
|
1127
|
+
};
|
|
1128
|
+
return {
|
|
1129
|
+
kind: "valid",
|
|
1130
|
+
marks: marksRange,
|
|
1131
|
+
gap
|
|
1132
|
+
};
|
|
1133
|
+
}
|
|
1134
|
+
/**
|
|
1135
|
+
* Classifies the spacing after each run of fullwidth question or exclamation marks in one display
|
|
1136
|
+
* line. Runs at the end of the line or immediately before a closing bracket need no gap and are
|
|
1137
|
+
* omitted from the result.
|
|
1138
|
+
*/
|
|
1139
|
+
function questionOrExclamationSpacings(line) {
|
|
1140
|
+
return [...line.matchAll(MARKS)].map((marks) => analyzeMatch(line, marks)).filter((spacing) => spacing !== void 0);
|
|
1141
|
+
}
|
|
1065
1142
|
const GridLine = {
|
|
1066
1143
|
schema: readonlyObject({
|
|
1067
1144
|
range: v.nullable(ManuscriptRange.schema),
|
|
@@ -1191,12 +1268,32 @@ function toCells(sourceLine, annotations) {
|
|
|
1191
1268
|
annotations: ManuscriptAnnotation.overlapping(annotations, grapheme.range)
|
|
1192
1269
|
}));
|
|
1193
1270
|
}
|
|
1271
|
+
function hangingGapIndexes(sourceLine) {
|
|
1272
|
+
const line = sourceLine.map(({ value }) => value).join("");
|
|
1273
|
+
const gapStarts = new Set(questionOrExclamationSpacings(line).filter((spacing) => spacing.kind === "valid").map(({ gap }) => gap.start));
|
|
1274
|
+
const indexes = /* @__PURE__ */ new Set();
|
|
1275
|
+
let offset = 0;
|
|
1276
|
+
for (const [index, grapheme] of sourceLine.entries()) {
|
|
1277
|
+
if (gapStarts.has(offset)) indexes.add(index);
|
|
1278
|
+
offset += grapheme.value.length;
|
|
1279
|
+
}
|
|
1280
|
+
return indexes;
|
|
1281
|
+
}
|
|
1194
1282
|
/**
|
|
1195
|
-
* Wraps one source line across as many grid lines as its length requires.
|
|
1283
|
+
* Wraps one source line across as many grid lines as its length requires. A valid gap after ! or ?
|
|
1284
|
+
* that would start a wrapped line is consumed as hanging punctuation instead of occupying a cell.
|
|
1196
1285
|
*/
|
|
1197
|
-
function wrapLine(cells, charsPerLine) {
|
|
1286
|
+
function wrapLine(cells, charsPerLine, hangingGaps) {
|
|
1198
1287
|
if (cells.length === 0) return [GridLine.padded([], charsPerLine)];
|
|
1199
|
-
|
|
1288
|
+
const lines = [];
|
|
1289
|
+
let cursor = 0;
|
|
1290
|
+
while (cursor < cells.length) {
|
|
1291
|
+
if (lines.length > 0 && hangingGaps.has(cursor)) cursor += 1;
|
|
1292
|
+
if (cursor >= cells.length) break;
|
|
1293
|
+
lines.push(GridLine.padded(cells.slice(cursor, cursor + charsPerLine), charsPerLine));
|
|
1294
|
+
cursor += charsPerLine;
|
|
1295
|
+
}
|
|
1296
|
+
return lines;
|
|
1200
1297
|
}
|
|
1201
1298
|
function blankLines(count, charsPerLine) {
|
|
1202
1299
|
return Array.from({ length: count }, () => GridLine.padded([], charsPerLine));
|
|
@@ -1237,8 +1334,7 @@ function buildPages(contentLines, settings) {
|
|
|
1237
1334
|
function composeGrid(manuscript, settings) {
|
|
1238
1335
|
const { charsPerLine } = settings.grid;
|
|
1239
1336
|
const sourceLines = displayedLines(manuscript.graphemes);
|
|
1240
|
-
const
|
|
1241
|
-
const manuscriptLines = cellLines.flatMap((cells) => wrapLine(cells, charsPerLine));
|
|
1337
|
+
const manuscriptLines = sourceLines.flatMap((line) => wrapLine(toCells(line, manuscript.annotations), charsPerLine, hangingGapIndexes(line)));
|
|
1242
1338
|
const pages = buildPages([
|
|
1243
1339
|
...blankLines(settings.offsets.document.leading, charsPerLine),
|
|
1244
1340
|
...manuscriptLines,
|
|
@@ -1248,7 +1344,7 @@ function composeGrid(manuscript, settings) {
|
|
|
1248
1344
|
pages,
|
|
1249
1345
|
geometry: ManuscriptGeometry.of(settings.grid, settings.appearance),
|
|
1250
1346
|
stats: {
|
|
1251
|
-
chars:
|
|
1347
|
+
chars: sourceLines.reduce((total, line) => total + line.length, 0),
|
|
1252
1348
|
sourceLines: sourceLines.length,
|
|
1253
1349
|
pages: pages.length
|
|
1254
1350
|
}
|
|
@@ -1852,24 +1948,6 @@ const noConsecutivePunctuationRule = () => defineMatchRule({
|
|
|
1852
1948
|
message: "句読点が連続しています"
|
|
1853
1949
|
});
|
|
1854
1950
|
//#endregion
|
|
1855
|
-
//#region src/proofreading/rules/internal/brackets.ts
|
|
1856
|
-
/**
|
|
1857
|
-
* Brackets and quotes a Japanese paragraph may open with. Halfwidth forms are included because a
|
|
1858
|
-
* manuscript that uses them is still opening a paragraph, not writing prose.
|
|
1859
|
-
*/
|
|
1860
|
-
const OPENING_BRACKETS = "「『〖〈《((“\"‘'[[〔{{<<";
|
|
1861
|
-
/**
|
|
1862
|
-
* The closing counterparts, in the same order.
|
|
1863
|
-
*/
|
|
1864
|
-
const CLOSING_BRACKETS = "」』〗〉》))”\"’']]〕}}>>";
|
|
1865
|
-
/**
|
|
1866
|
-
* Escapes the characters a regular expression character class would otherwise read as syntax, so a
|
|
1867
|
-
* set declared above can be dropped into a pattern.
|
|
1868
|
-
*/
|
|
1869
|
-
function characterClass(characters) {
|
|
1870
|
-
return characters.replaceAll(/[\\\]^-]/gu, "\\$&");
|
|
1871
|
-
}
|
|
1872
|
-
//#endregion
|
|
1873
1951
|
//#region src/proofreading/rules/internal/decoration-line.ts
|
|
1874
1952
|
const DECORATION_LINE = /^[**◆◇■□●○▽▼△▲☆★・…―─━ー\-==~~\s]+$/u;
|
|
1875
1953
|
/**
|
|
@@ -1880,16 +1958,6 @@ function isDecorationLine(text) {
|
|
|
1880
1958
|
return DECORATION_LINE.test(text);
|
|
1881
1959
|
}
|
|
1882
1960
|
//#endregion
|
|
1883
|
-
//#region src/proofreading/rules/internal/spaces.ts
|
|
1884
|
-
const LEADING_SPACES = /^[ \t]+/u;
|
|
1885
|
-
/**
|
|
1886
|
-
* The run of spaces the text begins with, or an empty string. Halfwidth spaces and tabs count: a
|
|
1887
|
-
* rule has to see them to say they are the wrong space.
|
|
1888
|
-
*/
|
|
1889
|
-
function leadingSpaces(text) {
|
|
1890
|
-
return LEADING_SPACES.exec(text)?.[0] ?? "";
|
|
1891
|
-
}
|
|
1892
|
-
//#endregion
|
|
1893
1961
|
//#region src/proofreading/rules/paragraph-opening.ts
|
|
1894
1962
|
const RULE_ID$1 = "kg/paragraph-opening";
|
|
1895
1963
|
const MESSAGES$1 = {
|
|
@@ -1961,10 +2029,6 @@ const MESSAGES = {
|
|
|
1961
2029
|
"before-closing-bracket": "閉じ括弧の直前に空白を置くことはできません"
|
|
1962
2030
|
};
|
|
1963
2031
|
/**
|
|
1964
|
-
* Runs, not single marks: `!?` is one mark and takes one gap, so the gap belongs after the run.
|
|
1965
|
-
*/
|
|
1966
|
-
const MARKS = /[?!]+/gu;
|
|
1967
|
-
/**
|
|
1968
2032
|
* Checks the gap that follows every run of ! or ?. A run at the end of a line or right before a
|
|
1969
2033
|
* closing bracket needs no gap; anywhere else it takes exactly one ideographic space.
|
|
1970
2034
|
*/
|
|
@@ -1981,20 +2045,18 @@ const spaceAfterQuestionOrExclamationRule = () => ({
|
|
|
1981
2045
|
messageId
|
|
1982
2046
|
});
|
|
1983
2047
|
};
|
|
1984
|
-
for (const line of splitDisplayLines(manuscript.displayText)) for (const
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
report(line.start +
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
if (following !== void 0 && "」』〗〉》))”\"’']]〕}}>>".includes(following)) report(spacesStart, spacesEnd, "before-closing-bracket");
|
|
1997
|
-
else if (spaces !== " ") report(spacesStart, spacesEnd, "space-width");
|
|
2048
|
+
for (const line of splitDisplayLines(manuscript.displayText)) for (const spacing of questionOrExclamationSpacings(line.text)) switch (spacing.kind) {
|
|
2049
|
+
case "valid": break;
|
|
2050
|
+
case "missing":
|
|
2051
|
+
report(line.start + spacing.marks.start, line.start + spacing.marks.end, "default");
|
|
2052
|
+
break;
|
|
2053
|
+
case "invalid-space":
|
|
2054
|
+
report(line.start + spacing.gap.start, line.start + spacing.gap.end, "space-width");
|
|
2055
|
+
break;
|
|
2056
|
+
case "before-closing-bracket":
|
|
2057
|
+
report(line.start + spacing.gap.start, line.start + spacing.gap.end, "before-closing-bracket");
|
|
2058
|
+
break;
|
|
2059
|
+
default: assertNever(spacing);
|
|
1998
2060
|
}
|
|
1999
2061
|
}
|
|
2000
2062
|
});
|