-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.ts
165 lines (137 loc) · 5.61 KB
/
main.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
import { App, Editor, MarkdownView, Notice, Plugin } from "obsidian";
export default class TypographPlugin extends Plugin {
async onload() {
// Adding a command to the command palette
this.addCommand({
id: "apply-typography",
name: "Apply Typography to Current File",
editorCallback: (editor: Editor) => this.applyTypography(editor),
});
// Adding a button to the toolbar
this.addRibbonIcon("pencil", "Apply Typography", () => {
const activeView = this.app.workspace.getActiveViewOfType(MarkdownView);
if (activeView) {
this.applyTypography(activeView.editor);
}
});
console.log("Typograph plugin loaded");
}
applyTypography(editor: Editor) {
const text = editor.getValue();
const lines = text.split("\n");
let changesCount = 0;
let formattedText = lines
.map((line) => {
const lang = this.detectLanguage(line);
if (lang === "en") {
// Now checks for English language first
const result = this.typographyEnglish(line);
changesCount += result.changesCount;
return result.text;
} else if (lang === "ru") {
// Then checks for Russian language
const result = this.typographyRussian(line);
changesCount += result.changesCount;
return result.text;
}
return line;
})
.join("\n");
if (changesCount > 0) {
editor.setValue(formattedText);
new Notice(`${changesCount} changes applied.`);
} else {
new Notice("No changes were necessary.");
}
}
// Detect the language of the text based on the presence of Cyrillic characters
detectLanguage(text: string): string {
const cyrillic = /[а-яА-ЯЁё]/;
return cyrillic.test(text) ? "ru" : "en";
}
// Function for typography rules specific to English
typographyEnglish(text: string): { text: string; changesCount: number } {
let changesCount = 0;
// Replace spaces after short words (prepositions, articles, conjunctions) with non-breaking spaces
const prepositionsEnglish = /\b(and|the|a|an|to|at|in|on|by|of|for|from|as|with|but)\s+/gi;
text = text.replace(prepositionsEnglish, (match, p1) => {
changesCount++;
return `${p1}\u00A0`;
});
// Replace spaces between numbers and metric units with non-breaking spaces
text = text.replace(/(\d+)\s+(cm|mm|m|km|kg|g|mg|lb|oz)/g, (match, p1, p2) => {
changesCount++;
return `${p1}\u00A0${p2}`;
});
// Replace ": " or ", " before direct speech with the appropriate quotation marks
text = text.replace(/([:,])\s*"([^"]*)"/g, (match, p1, p2) => {
changesCount++;
return `${p1} ‘${p2}’`;
});
// Replace all double quotation marks with “...”
text = text.replace(/"([^"]*)"/g, (match, p1) => {
changesCount++;
return `“${p1}”`;
});
// Replace nested quotation marks inside “...” with ‘...’
text = text.replace(/“([^“”]*)”/g, (match, p1) => {
const newText = p1.replace(/'([^']*)'/g, (subMatch, p2) => {
changesCount++;
return `‘${p2}’`;
});
return `“${newText}”`;
});
// Replace >> with » and << with «
text = text.replace(/>>/g, '»').replace(/<</g, '«');
// Replace -- and - with —
text = text.replace(/--/g, '—').replace(/-/g, '—');
return { text, changesCount };
}
// Function for typography rules specific to Russian
typographyRussian(text: string): { text: string; changesCount: number } {
let changesCount = 0;
// Replace ": " or ", " before direct speech with the appropriate quotation marks
text = text.replace(/([:,])\s*"([^"]*)"/g, (match, p1, p2) => {
changesCount++;
return `${p1} «${p2}»`;
});
// Replace nested quotation marks inside «...» with „...“
text = text.replace(/«([^«»][^„“]*)»/g, (match, p1) => {
changesCount++;
return `„${p1}“`;
});
// Replace all double quotation marks with «...»
text = text.replace(/"([^"]*)"/g, (match, p1) => {
changesCount++;
return `«${p1}»`;
});
// Replace >> with » and << with «
text = text.replace(/>>/g, '»');
text = text.replace(/<</g, '«');
changesCount++;
// Replace spaces after short words (prepositions) with non-breaking spaces
const prepositionsRussian = /(в|и|к|с|у|о|на|по|за|от|для|до|со)(\s+)/gi;
text = text.replace(prepositionsRussian, (match, p1, p2) => {
changesCount++;
return `${p1}\u00A0`; // Replace only the space after the word
});
// Replace spaces between numbers and metric units with non-breaking spaces
text = text.replace(/(\d+)\s+(см|мм|м|км|кг|г|мг|фунт|унц)/g, (match, p1, p2) => {
changesCount++;
return `${p1}\u00A0${p2}`;
});
// Replace -- with — (em dash)
text = text.replace(/--/g, '—');
changesCount += (text.match(/--/g) || []).length;
// Replace standalone - with — (em dash), but preserve:
// 1. Dashes at the beginning of a line (for lists)
// 2. Dashes in number ranges (e.g., 1-5)
// 3. Dashes in compound words (e.g., что-то)
text = text.replace(/(?<!^|\d|[а-яА-ЯёЁ])-(?!\d|[а-яА-ЯёЁ])/gm, '—');
changesCount += (text.match(/(?<!^|\d|[а-яА-ЯёЁ])-(?!\d|[а-яА-ЯёЁ])/gm) || []).length;
return { text, changesCount };
}
onunload() {
console.log("Typograph plugin unloaded");
}
}