1- import { App , Editor , MarkdownView , Plugin , PluginSettingTab , TFile , TAbstractFile , Setting } from 'obsidian' ;
1+ import { App , WorkspaceLeaf , MarkdownView , Plugin , PluginSettingTab , TFile , TAbstractFile , Setting } from 'obsidian' ;
22import * as codemirror from 'codemirror' ;
33
44class 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 ) {
0 commit comments