import { Position , Range , TextDocument } from 'vscode' ; const startTagPattern = '<\s*[\\.\\-:_a-zA-Z0-9]+' ; const endTagPattern = '<\\/\s*[\\.\\-:_a-zA-Z0-9]+' ; const autoClosePattern = '\\/>' ; const startCommentPattern = '\s*<!--' ; const endCommentPattern = '\s*-->' ; const fullPattern = new RegExp ( "(" + startTagPattern + "|" + endTagPattern + "|" + autoClosePattern + "|" + startCommentPattern + "|" + endCommentPattern + ")" , "g" ) ; export function getXPath ( document , position ) { const xpath = [ ] ; const skipList = [ ] ; let waitingStartTag = false ; let waitingStartComment = false ; const wordRange = document . getWordRangeAtPosition ( position ) ; const wordStart = wordRange ? wordRange . start : position ; let line = document . getText ( new Range ( position . line , 0 , position . line , wordStart . character ) ) ; let row = position . line ; while ( row >= 0 ) { row -- ; let matches = line . match ( fullPattern ) ; if ( matches ) { matches . reverse ( ) ; for ( let i = 0 ; i < matches . length ; i ++ ) { let match = matches [ i ] ; let tagName ; if ( match === "<!--" ) { waitingStartComment = false ; } else if ( match === "-->" ) { waitingStartComment = true ; } else if ( waitingStartComment ) { continue ; } else if ( match === "/>" ) { waitingStartTag = true ; } else if ( match [ 0 ] === "<" && match [ 1 ] === "/" ) { skipList . push ( match . slice ( 2 ) ) ; } else if ( match [ 0 ] === "<" && waitingStartTag ) { waitingStartTag = false ; } else if ( match [ 0 ] == "<" ) { tagName = match . slice ( 1 ) ; if ( tagName === "?xml" ) { continue ; } let idx = skipList . lastIndexOf ( tagName ) ; if ( idx != - 1 ) { skipList . splice ( idx , 1 ) ; } else { xpath . push ( tagName ) ; } } } ; } if ( row >= 0 ) { line = document . lineAt ( row ) . text ; } } return xpath . reverse ( ) ; } export function textBeforeWordEquals ( document , position , textToMatch ) { const wordRange = document . getWordRangeAtPosition ( position ) ; const wordStart = wordRange ? wordRange . start : position ; if ( wordStart . character < textToMatch . length ) { return false ; } const charBeforeWord = document . getText ( new Range ( new Position ( wordStart . line , wordStart . character - textToMatch . length ) , wordStart ) ) ; return charBeforeWord === textToMatch ; } export function isTagName ( document , position ) { return textBeforeWordEquals ( document , position , '<' ) ; } export function isClosingTagName ( document , position ) { return textBeforeWordEquals ( document , position , '</' ) ; } export function isAttributeValue ( document , position ) { const wordRange = document . getWordRangeAtPosition ( position ) ; const wordStart = wordRange ? wordRange . start : position ; const wordEnd = wordRange ? wordRange . end : position ; if ( wordStart . character === 0 || wordEnd . character > document . lineAt ( wordEnd . line ) . text . length - 1 ) { return false ; } const rangeBefore = new Range ( wordStart . line , wordStart . character - 1 , wordStart . line , wordStart . character ) ; if ( document . getText ( rangeBefore ) . match ( /'|"/ ) ) { return true ; } const word = document . getText ( wordRange ) ; if ( word && /\b\w+\b/ . exec ( word ) ) { return true ; } return false ; } export function isAttribute ( document , position ) { const lineContent = document . lineAt ( position ) . text ; const wordRange = document . getWordRangeAtPosition ( position ) ; const wordStart = wordRange ? wordRange . start : position ; const text = document . getText ( ) ; const offset = document . offsetAt ( wordStart ) ; const quotsBeforeCount = lineContent . substring ( position . character , 0 ) . split ( '"' ) . length - 1 ; return quotsBeforeCount % 2 === 0 && ( text . lastIndexOf ( '<' , offset ) > text . lastIndexOf ( '>' , offset ) && text . lastIndexOf ( ' ' , offset ) > text . lastIndexOf ( '<' , offset ) || text [ offset ] === '>' && text . lastIndexOf ( '<' , offset ) > text . lastIndexOf ( '>' , offset - 1 ) ) ; }