1+ import { MarkdownEditorInput , MarkdownEditorInputSelection } from "./interface" ;
2+ import { MarkdownEditor } from "../index.mjs" ;
3+ import { EditorView } from "@codemirror/view" ;
4+ import { ChangeSpec , SelectionRange , TransactionSpec } from "@codemirror/state" ;
5+
6+
7+ export class CodemirrorInput implements MarkdownEditorInput {
8+
9+ protected editor : MarkdownEditor ;
10+ protected cm : EditorView ;
11+
12+ constructor ( cm : EditorView ) {
13+ this . cm = cm ;
14+ }
15+
16+ focus ( ) : void {
17+ if ( ! this . editor . cm . hasFocus ) {
18+ this . editor . cm . focus ( ) ;
19+ }
20+ }
21+
22+ getSelection ( ) : MarkdownEditorInputSelection {
23+ return this . editor . cm . state . selection . main ;
24+ }
25+
26+ getSelectionText ( selection : MarkdownEditorInputSelection | null = null ) : string {
27+ selection = selection || this . getSelection ( ) ;
28+ return this . editor . cm . state . sliceDoc ( selection . from , selection . to ) ;
29+ }
30+
31+ setSelection ( selection : MarkdownEditorInputSelection , scrollIntoView : boolean = false ) {
32+ this . editor . cm . dispatch ( {
33+ selection : { anchor : selection . from , head : selection . to } ,
34+ scrollIntoView,
35+ } ) ;
36+ }
37+
38+ getText ( ) : string {
39+ return this . editor . cm . state . doc . toString ( ) ;
40+ }
41+
42+ getTextAboveView ( ) : string {
43+ const blockInfo = this . editor . cm . lineBlockAtHeight ( scrollEl . scrollTop ) ;
44+ return this . editor . cm . state . sliceDoc ( 0 , blockInfo . from ) ;
45+ }
46+
47+ setText ( text : string , selection : MarkdownEditorInputSelection | null = null ) {
48+ selection = selection || this . getSelection ( ) ;
49+ const newDoc = this . editor . cm . state . toText ( text ) ;
50+ const newSelectFrom = Math . min ( selection . from , newDoc . length ) ;
51+ const scrollTop = this . editor . cm . scrollDOM . scrollTop ;
52+ this . dispatchChange ( 0 , this . editor . cm . state . doc . length , text , newSelectFrom ) ;
53+ this . focus ( ) ;
54+ window . requestAnimationFrame ( ( ) => {
55+ this . editor . cm . scrollDOM . scrollTop = scrollTop ;
56+ } ) ;
57+ }
58+
59+ spliceText ( from : number , to : number , newText : string , selection : MarkdownEditorInputSelection | null = null ) {
60+ const end = ( selection ?. from === selection ?. to ) ? null : selection ?. to ;
61+ this . dispatchChange ( from , to , newText , selection ?. from , end )
62+ }
63+
64+ appendText ( text : string ) {
65+ const end = this . editor . cm . state . doc . length ;
66+ this . dispatchChange ( end , end , `\n${ text } ` ) ;
67+ }
68+
69+ getLineText ( lineIndex : number = - 1 ) : string {
70+ const index = lineIndex > - 1 ? lineIndex : this . getSelection ( ) . from ;
71+ return this . editor . cm . state . doc . lineAt ( index ) . text ;
72+ }
73+
74+ wrapLine ( start : string , end : string ) {
75+ const selectionRange = this . getSelection ( ) ;
76+ const line = this . editor . cm . state . doc . lineAt ( selectionRange . from ) ;
77+ const lineContent = line . text ;
78+ let newLineContent ;
79+ let lineOffset = 0 ;
80+
81+ if ( lineContent . startsWith ( start ) && lineContent . endsWith ( end ) ) {
82+ newLineContent = lineContent . slice ( start . length , lineContent . length - end . length ) ;
83+ lineOffset = - ( start . length ) ;
84+ } else {
85+ newLineContent = `${ start } ${ lineContent } ${ end } ` ;
86+ lineOffset = start . length ;
87+ }
88+
89+ this . dispatchChange ( line . from , line . to , newLineContent , selectionRange . from + lineOffset ) ;
90+ }
91+
92+ coordsToSelection ( x : number , y : number ) : MarkdownEditorInputSelection {
93+ const cursorPos = this . editor . cm . posAtCoords ( { x, y} , false ) ;
94+ return { from : cursorPos , to : cursorPos } ;
95+ }
96+
97+ /**
98+ * Dispatch changes to the editor.
99+ */
100+ protected dispatchChange ( from : number , to : number | null = null , text : string | null = null , selectFrom : number | null = null , selectTo : number | null = null ) : void {
101+ const change : ChangeSpec = { from} ;
102+ if ( to ) {
103+ change . to = to ;
104+ }
105+ if ( text ) {
106+ change . insert = text ;
107+ }
108+ const tr : TransactionSpec = { changes : change } ;
109+
110+ if ( selectFrom ) {
111+ tr . selection = { anchor : selectFrom } ;
112+ if ( selectTo ) {
113+ tr . selection . head = selectTo ;
114+ }
115+ }
116+
117+ this . cm . dispatch ( tr ) ;
118+ }
119+
120+ }
0 commit comments