@@ -113,44 +113,43 @@ function elementName(doc: Text, tree: SyntaxNode | null | undefined, max = doc.l
113113 return ""
114114}
115115
116- function isEndTag ( node : SyntaxNode | null ) {
117- return node && ( node . name == "JSXEndTag" || node . name == "JSXSelfCloseEndTag" )
118- }
119-
120116const android = typeof navigator == "object" && / A n d r o i d \b / . test ( navigator . userAgent )
121117
122118/// Extension that will automatically insert JSX close tags when a `>` or
123119/// `/` is typed.
124- export const autoCloseTags = EditorView . inputHandler . of ( ( view , from , to , text ) => {
120+ export const autoCloseTags = EditorView . inputHandler . of ( ( view , from , to , text , defaultInsert ) => {
125121 if ( ( android ? view . composing : view . compositionStarted ) || view . state . readOnly ||
126122 from != to || ( text != ">" && text != "/" ) ||
127123 ! javascriptLanguage . isActiveAt ( view . state , from , - 1 ) ) return false
128- let { state} = view
129- let changes = state . changeByRange ( range => {
130- let { head} = range , around = syntaxTree ( state ) . resolveInner ( head , - 1 ) , name
124+ let base = defaultInsert ( ) , { state} = base
125+ let closeTags = state . changeByRange ( range => {
126+ let { head} = range , around = syntaxTree ( state ) . resolveInner ( head - 1 , - 1 ) , name
131127 if ( around . name == "JSXStartTag" ) around = around . parent !
132- if ( around . name == "JSXAttributeValue" && around . to > head ) {
133- // Ignore input inside attribute
128+ if ( state . doc . sliceString ( head - 1 , head ) != text || around . name == "JSXAttributeValue" && around . to > head ) {
129+ // Ignore input inside attribute or cases where the text wasn't actually inserted
134130 } else if ( text == ">" && around . name == "JSXFragmentTag" ) {
135- return { range : EditorSelection . cursor ( head + 1 ) , changes : { from : head , insert : `> </>` } }
136- } else if ( text == "/" && around . name == "JSXFragmentTag " ) {
137- let empty = around . parent , base = empty ? .parent
138- if ( empty ! . from == head - 1 && base ! . lastChild ?. name != "JSXEndTag" &&
139- ( name = elementName ( state . doc , base ? .firstChild , head ) ) ) {
140- let insert = `/ ${ name } >`
141- return { range : EditorSelection . cursor ( head + insert . length ) , changes : { from : head , insert} }
131+ return { range, changes : { from : head , insert : `</>` } }
132+ } else if ( text == "/" && around . name == "JSXStartCloseTag " ) {
133+ let empty = around . parent ! , base = empty . parent
134+ if ( base && empty . from == head - 2 &&
135+ ( ( name = elementName ( state . doc , base . firstChild , head ) ) || base . firstChild ?. name == "JSXFragmentTag" ) ) {
136+ let insert = `${ name } >`
137+ return { range : EditorSelection . cursor ( head + insert . length , - 1 ) , changes : { from : head , insert} }
142138 }
143139 } else if ( text == ">" ) {
144140 let openTag = findOpenTag ( around )
145- if ( openTag && ! isEndTag ( openTag . lastChild ) &&
146- state . sliceDoc ( head , head + 2 ) != "</" &&
141+ if ( openTag &&
142+ ! / ^ \/ ? > | ^ < \/ / . test ( state . doc . sliceString ( head , head + 2 ) ) &&
147143 ( name = elementName ( state . doc , openTag , head ) ) )
148- return { range : EditorSelection . cursor ( head + 1 ) , changes : { from : head , insert : `> </${ name } >` } }
144+ return { range, changes : { from : head , insert : `</${ name } >` } }
149145 }
150146 return { range}
151147 } )
152- if ( changes . changes . empty ) return false
153- view . dispatch ( changes , { userEvent : "input.type" , scrollIntoView : true } )
148+ if ( closeTags . changes . empty ) return false
149+ view . dispatch ( [
150+ base ,
151+ state . update ( closeTags , { userEvent : "input.complete" , scrollIntoView : true } )
152+ ] )
154153 return true
155154} ) ;
156155
0 commit comments