Skip to content

Commit a88cf4b

Browse files
committed
Major overhaul including mobile support
1 parent 08dedc0 commit a88cf4b

File tree

4 files changed

+99
-128
lines changed

4 files changed

+99
-128
lines changed

README.md

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,15 +55,20 @@ It is possible to temporarily override a note's direction regardless of the fron
5555
## Known Issues
5656

5757
- This plugin only treats the Markdown editor, preview and export. There are some areas of the app, like the Outline view, that are not covered for now.
58-
- Obsidian Mobile not yet covered.
59-
- For the legacy editor (CM5):
60-
- Auto-pair brackets is disabled in RTL ([CodeMirror bug with a pending fix](https://github.com/esm7/obsidian-rtl/issues/7)).
61-
- When an RTL line ends with an LTR word, the End key misbehaves. [CodeMirror bug](https://github.com/codemirror/CodeMirror/issues/6531).
62-
- There are various glitches in handling some keyboard shortcuts in RTL, e.g. Ctrl+Home/Ctrl+End are not properly handled yet, Ctrl+Left lands on the wrong character and some others. These are very hard to overcome due to the patchy RTL support of the current CodeMirror versions, and occasionally I'm able to improve things a bit.
63-
- **None of these issues exist in the new (CM6) editor presented in Obsidian 0.13.x and above.**
6458

6559
## Changelog
6660

61+
### 0.3.0
62+
63+
**IMPORTANT: this version drops support for the legacy (CM5) Obsidian editor.**
64+
If you are sticking to the legacy editor until Obsidian removes it, you cannot upgrade to this version of the plugin.
65+
66+
**This release marks a major overhaul of the way this plugin works, fixing most known bugs in the process.**
67+
68+
- RTL/LTR is now (once again) set for a specific view instead of all the open panes.
69+
- All known bugs related to the plugin interfering with other parts of Obsidian (e.g. the Community Plugin view) are fixed.
70+
- **The plugin now works in Obsidian Mobile** (tested only on Android).
71+
6772
### 0.2.2
6873

6974
- Fixed an issue of not properly detecting the direction when the default is set as RTL (due to Obsidian's own styles).

main.ts

Lines changed: 84 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { App, Editor, MarkdownView, Plugin, PluginSettingTab, TFile, TAbstractFile, Setting } from 'obsidian';
1+
import { App, WorkspaceLeaf, MarkdownView, Plugin, PluginSettingTab, TFile, TAbstractFile, Setting } from 'obsidian';
22
import * as codemirror from 'codemirror';
33

44
class Settings {
@@ -25,7 +25,6 @@ export default class RtlPlugin extends Plugin {
2525
public settings = new Settings();
2626
private currentFile: TFile;
2727
public SETTINGS_PATH = '.obsidian/rtl.json'
28-
private editorMode: 'cm5' | 'cm6' = null;
2928
// This stores the value in CodeMirror's autoCloseBrackets option before overriding it, so it can be restored when
3029
// we're back to LTR
3130
private autoCloseBracketsValue: any = false;
@@ -42,15 +41,16 @@ export default class RtlPlugin extends Plugin {
4241

4342
this.loadSettings();
4443

45-
this.registerEvent(this.app.workspace.on('file-open', async (file: TFile) => {
46-
if (!this.initialized)
47-
await this.initialize();
48-
if (file && file.path) {
49-
this.syncDefaultDirection();
50-
this.currentFile = file;
51-
this.adjustDirectionToCurrentFile();
44+
this.app.workspace.on('active-leaf-change', async (leaf: WorkspaceLeaf) => {
45+
if (leaf.view instanceof MarkdownView) {
46+
const file = leaf.view.file;
47+
await this.onFileOpen(file);
5248
}
53-
}));
49+
});
50+
51+
this.app.workspace.on('file-open', async (file: TFile) => {
52+
await this.onFileOpen(file);
53+
});
5454

5555
this.registerEvent(this.app.vault.on('delete', (file: TAbstractFile) => {
5656
if (file && file.path && file.path in this.settings.fileDirections) {
@@ -70,45 +70,23 @@ export default class RtlPlugin extends Plugin {
7070
}
7171

7272
async initialize() {
73-
// Determine if we have the legacy Obsidian editor (CM5) or the new one (CM6).
74-
// This is only available after Obsidian is fully loaded, so we do it as part of the `file-open` event.
75-
if ('editor:toggle-source' in (this.app as any).commands.editorCommands) {
76-
this.editorMode = 'cm6';
77-
console.log('RTL plugin: using CodeMirror 6 mode');
78-
} else {
79-
this.editorMode = 'cm5';
80-
console.log('RTL plugin: using CodeMirror 5 mode');
81-
}
82-
83-
if (this.editorMode === 'cm5') {
84-
this.registerCodeMirror((cm: CodeMirror.Editor) => {
85-
let cmEditor = cm;
86-
let currentExtraKeys = cmEditor.getOption('extraKeys');
87-
let moreKeys = {
88-
'End': (cm: CodeMirror.Editor) => {
89-
if (cm.getOption('direction') == 'rtl')
90-
cm.execCommand('goLineLeftSmart');
91-
else
92-
cm.execCommand('goLineRight');
93-
},
94-
'Home': (cm: CodeMirror.Editor) => {
95-
if (cm.getOption('direction') == 'rtl')
96-
cm.execCommand('goLineRight');
97-
else
98-
cm.execCommand('goLineLeftSmart');
99-
}
100-
};
101-
cmEditor.setOption('extraKeys', Object.assign({}, currentExtraKeys, moreKeys));
102-
});
103-
}
104-
10573
this.initialized = true;
10674
}
10775

10876
onunload() {
10977
console.log('unloading RTL plugin');
11078
}
11179

80+
async onFileOpen(file: TFile) {
81+
if (!this.initialized)
82+
await this.initialize();
83+
if (file && file.path) {
84+
this.syncDefaultDirection();
85+
this.currentFile = file;
86+
this.adjustDirectionToCurrentFile();
87+
}
88+
}
89+
11290
adjustDirectionToCurrentFile() {
11391
if (this.currentFile && this.currentFile.path) {
11492
let requiredDirection = null;
@@ -150,61 +128,69 @@ export default class RtlPlugin extends Plugin {
150128

151129
setDocumentDirection(newDirection: string) {
152130
let view = this.app.workspace.getActiveViewOfType(MarkdownView);
153-
// Source / Live View editor direction
154-
if (this.editorMode === 'cm5') {
155-
var cmEditor = this.getCmEditor();
156-
if (cmEditor && cmEditor.getOption("direction") != newDirection) {
157-
this.patchAutoCloseBrackets(cmEditor, newDirection);
158-
cmEditor.setOption("direction", newDirection as any);
159-
cmEditor.setOption("rtlMoveVisually", true);
160-
}
161-
} else {
162-
if (!view.editor)
163-
return;
164-
this.replacePageStyleByString('New editor content div',
165-
`/* New editor content div */ .cm-editor { direction: ${newDirection}; }`, true);
166-
this.replacePageStyleByString('Markdown preview RTL',
167-
`/* Markdown preview RTL */ .markdown-preview-view { direction: ${newDirection}; }`, true);
168-
var containerEl = (view.editor.getDoc() as any)?.cm?.dom?.parentElement as HTMLDivElement;
169-
if (newDirection === 'rtl') {
170-
containerEl.classList.add('is-rtl');
171-
this.replacePageStyleByString('List indent fix',
172-
`/* List indent fix */ .cm-s-obsidian .HyperMD-list-line { text-indent: 0px !important; }`, true);
173-
// this.replaceStringInStyle('.markdown-source-view.mod-cm6 .cm-fold-indicator .collapse-indicator',
174-
// 'right: 0;', 'right: -15px;');
175-
} else {
176-
containerEl.classList.remove('is-rtl');
177-
this.replacePageStyleByString('List indent fix',
178-
`/* List indent fix */ /* Empty rule for LTR */`, true);
179-
// this.replaceStringInStyle('.markdown-source-view.mod-cm6 .cm-fold-indicator .collapse-indicator',
180-
// 'right: -15px;', 'right: 0;');
181-
}
182-
this.replacePageStyleByString('Embedded links always LTR',
183-
`/* Embedded links always LTR */ .embedded-backlinks { direction: ltr; }`, true);
184-
view.editor.refresh();
131+
if (!view || !view?.editor)
132+
return;
133+
134+
const editorDivs = view.contentEl.getElementsByClassName('cm-editor');
135+
for (const editorDiv of editorDivs) {
136+
if (editorDiv instanceof HTMLDivElement)
137+
this.setDocumentDirectionForEditorDiv(editorDiv, newDirection);
138+
}
139+
const markdownPreviews = view.contentEl.getElementsByClassName('markdown-preview-view');
140+
for (const preview of markdownPreviews) {
141+
if (preview instanceof HTMLDivElement)
142+
this.setDocumentDirectionForReadingDiv(preview, newDirection);
185143
}
186144

187-
if (view) {
188-
// Fix the list indentation style
189-
this.replacePageStyleByString('CodeMirror-rtl pre',
190-
`.CodeMirror-rtl pre { text-indent: 0px !important; }`,
191-
true);
145+
// --- General global fixes ---
146+
147+
// Fix list indentation problems in RTL
148+
this.replacePageStyleByString('List indent fix',
149+
`/* List indent fix */ .is-rtl .HyperMD-list-line { text-indent: 0px !important; }`, true);
150+
this.replacePageStyleByString('CodeMirror-rtl pre',
151+
`.CodeMirror-rtl pre { text-indent: 0px !important; }`,
152+
true);
153+
154+
// Embedded backlinks should always be shown as LTR
155+
this.replacePageStyleByString('Embedded links always LTR',
156+
`/* Embedded links always LTR */ .embedded-backlinks { direction: ltr; }`, true);
157+
158+
// Fold indicator fix (not perfect yet -- it can't be clicked)
159+
this.replacePageStyleByString('Fold symbol fix',
160+
`/* Fold symbol fix*/ .is-rtl .cm-fold-indicator { right: -15px !important; }`, true);
161+
162+
if (this.settings.setNoteTitleDirection) {
163+
const container = view.containerEl.parentElement;
164+
let header = container.getElementsByClassName('view-header-title-container');
165+
(header[0] as HTMLDivElement).style.direction = newDirection;
166+
}
192167

193-
if (this.settings.setYamlDirection) {
194-
const alignSide = newDirection == 'rtl' ? 'right' : 'left';
195-
this.replacePageStyleByString('Patch YAML',
196-
`/* Patch YAML RTL */ .language-yml code { text-align: ${alignSide}; }`, true);
197-
}
168+
view.editor.refresh();
198169

199-
if (this.settings.setNoteTitleDirection) {
200-
var leafContainer = (this.app.workspace.activeLeaf as any).containerEl as Document;
201-
let header = leafContainer.getElementsByClassName('view-header-title-container');
202-
(header[0] as any).style.direction = newDirection;
203-
}
170+
// Set the *currently active* export direction. This is global and changes every time the user
171+
// switches a pane
172+
this.setExportDirection(newDirection);
173+
}
204174

205-
this.setExportDirection(newDirection);
175+
setDocumentDirectionForEditorDiv(editorDiv: HTMLDivElement, newDirection: string) {
176+
editorDiv.style.direction = newDirection;
177+
if (newDirection === 'rtl') {
178+
editorDiv.parentElement.classList.add('is-rtl');
179+
} else {
180+
editorDiv.parentElement.classList.remove('is-rtl');
206181
}
182+
}
207183

184+
setDocumentDirectionForReadingDiv(readingDiv: HTMLDivElement, newDirection: string) {
185+
readingDiv.style.direction = newDirection;
186+
// Although Obsidian doesn't care about is-rtl in Markdown preview, we use it below for some more formatting
187+
if (newDirection === 'rtl')
188+
readingDiv.classList.add('is-rtl');
189+
else
190+
readingDiv.classList.remove('is-rtl');
191+
if (this.settings.setYamlDirection)
192+
this.replacePageStyleByString('Patch YAML',
193+
`/* Patch YAML RTL */ .is-rtl .language-yaml code { text-align: right; }`, true);
208194
}
209195

210196
setExportDirection(newDirection: string) {
@@ -230,17 +216,6 @@ export default class RtlPlugin extends Plugin {
230216
return style && !alreadyExists;
231217
}
232218

233-
// Returns true if a replacement was made
234-
replaceStringInStyle(searchString: string, whatToReplace: string, replacement: string) {
235-
let style = this.findPageStyle(searchString);
236-
if (style && style.getText().includes(whatToReplace)) {
237-
const newText = style.getText().replace(whatToReplace, replacement);
238-
style.textContent = newText;
239-
return true;
240-
}
241-
return false;
242-
}
243-
244219
findPageStyle(regex: string) {
245220
let styles = document.head.getElementsByTagName('style');
246221
for (let style of styles) {
@@ -250,18 +225,6 @@ export default class RtlPlugin extends Plugin {
250225
return null;
251226
}
252227

253-
patchAutoCloseBrackets(cmEditor: any, newDirection: string) {
254-
// Auto-close brackets doesn't work in RTL: https://github.com/esm7/obsidian-rtl/issues/7
255-
// Until the actual fix is released (as part of CodeMirror), we store the value of autoCloseBrackets when
256-
// switching to RTL, overriding it to 'false' and restoring it when back to LTR.
257-
if (newDirection == 'rtl') {
258-
this.autoCloseBracketsValue = cmEditor.getOption('autoCloseBrackets');
259-
cmEditor.setOption('autoCloseBrackets', false);
260-
} else {
261-
cmEditor.setOption('autoCloseBrackets', this.autoCloseBracketsValue);
262-
}
263-
}
264-
265228
toggleDocumentDirection() {
266229
let newDirection = this.getDocumentDirection() === 'ltr' ? 'rtl' : 'ltr';
267230
this.setDocumentDirection(newDirection);
@@ -272,12 +235,14 @@ export default class RtlPlugin extends Plugin {
272235
}
273236

274237
getDocumentDirection() {
275-
if (this.editorMode === 'cm5') {
276-
var cmEditor = this.getCmEditor();
277-
return cmEditor?.getOption('direction') === 'rtl' ? 'rtl' : 'ltr';
278-
} else {
279-
return this.findPageStyle('New editor content div.*direction: rtl') ? 'rtl' : 'ltr';
280-
}
238+
let view = this.app.workspace.getActiveViewOfType(MarkdownView);
239+
if (!view)
240+
return 'unknown';
241+
const rtlEditors = view.contentEl.getElementsByClassName('is-rtl');
242+
if (rtlEditors.length > 0)
243+
return 'rtl';
244+
else
245+
return 'ltr';
281246
}
282247

283248
getFrontMatterDirection(file: TFile) {

manifest.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
{
22
"id": "obsidian-rtl",
33
"name": "RTL Support",
4-
"version": "0.2.2",
4+
"version": "0.3.0",
5+
"minAppVersion": "0.15.3",
56
"description": "Right to Left (RTL) text direction support for languages like Arabic, Hebrew and Farsi.",
67
"author": "esm",
78
"authorUrl": "",
8-
"isDesktopOnly": true
9+
"isDesktopOnly": false
910
}

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "obsidian-rtl",
3-
"version": "0.2.2",
3+
"version": "0.3.0",
44
"description": "RTL (Right to Left) language support for Obsidian.",
55
"main": "main.js",
66
"scripts": {

0 commit comments

Comments
 (0)