import { TextDocument , workspace , Uri , Position , Range } from "vscode" ; import * as path from "path" ; import { isAttribute , getXPath , isTagName , isClosingTagName } from "./xmlUtils" ; import { Location } from "./dart-ext-types" ; export const isWin = /^win/ . test ( process . platform ) ; export function forceWindowsDriveLetterToUppercase ( p ) { if ( p && isWin && path . isAbsolute ( p ) && p . charAt ( 0 ) === p . charAt ( 0 ) . toLowerCase ( ) ) p = p . substr ( 0 , 1 ) . toUpperCase ( ) + p . substr ( 1 ) ; return p ; } export function fsPath ( uri ) { return forceWindowsDriveLetterToUppercase ( uri instanceof Uri ? uri . fsPath : uri ) ; } export function sortBy < T > ( items , f ) { return items . sort ( ( item1 , item2 ) => { const r1 = f ( item1 ) ; const r2 = f ( item2 ) ; if ( r1 < r2 ) return - 1 ; if ( r1 > r2 ) return 1 ; return 0 ; } ) ; } export function uniq < T > ( array ) { return array . filter ( ( value , index ) => array . indexOf ( value ) === index ) ; } export function flatMap < T1 , T2 > ( input , f ) { return input . reduce ( ( acc , x ) => acc . concat ( f ( x ) ) , [ ] ) ; } export function toPosition ( location ) { return new Position ( location . startLine - 1 , location . startColumn - 1 ) ; } export function toRangeOnLine ( location ) { const startPos = toPosition ( location ) ; return new Range ( startPos , startPos . translate ( 0 , location . length ) ) ; } export function toRange ( document , offset , length ) { return new Range ( document . positionAt ( offset ) , document . positionAt ( offset + length ) ) ; } export function countWords ( text , word , endAt ) { let count = 0 ; let pos = 0 ; while ( pos > - 1 && ( pos = text . indexOf ( word , pos ) ) > - 1 && pos < endAt ) { pos += word . length ; count ++ ; } return count ; } export function wordIndexAfter ( text , word , until , offset = 0 ) { let count = until ; let pos = 0 ; while ( count > 0 && ( pos = text . indexOf ( word , offset ) ) > - 1 ) { offset = pos + word . length ; count -- ; } return pos + word . length ; } export function getDartFileUri ( xmlDocument ) { const xmlFilePath = fsPath ( xmlDocument . uri ) ; const xmlFile = path . parse ( xmlFilePath ) ; const dartFilePath = path . join ( xmlFile . dir , xmlFile . base + '.dart' ) ; return Uri . file ( dartFilePath ) ; } export async function getDartDocument ( xmlDocument ) { const uri = getDartFileUri ( xmlDocument ) ; const dartDocument = await workspace . openTextDocument ( uri ) ; return dartDocument ; } function removeXmlComments ( xml ) { return xml . replace ( /<!--[\s\S\n]*?-->/ig , '' ) ; } export function getDartCodeIndex ( xmlDocument , xmlPosition , dartDocument , wordRange , includeCloseTag = false , useTagName ? , translateLine ? ) { let xml = xmlDocument . getText ( ) ; const xPath = getXPath ( xmlDocument , xmlPosition ) ; const dart = dartDocument . getText ( ) ; const offset = xmlDocument . offsetAt ( xmlPosition ) ; let dartOffset = - 1 ; xml = removeXmlComments ( xml ) ; if ( isAttribute ( xmlDocument , xmlPosition ) ) { const tag = xPath [ xPath . length - 1 ] ; let count = countWords ( xml , '<' + tag + ' ' , offset + tag . length ) ; count += countWords ( xml , '<' + tag + '>' , offset + tag . length ) ; let classIndex = wordIndexAfter ( dart , ' ' + tag + '(' , count ) + 2 ; if ( classIndex === tag . length + 3 ) { const namedCtorIndex = wordIndexAfter ( xml , ':use="' , count ) + 6 ; if ( namedCtorIndex > 6 ) { const namedCtorPos = xmlDocument . positionAt ( namedCtorIndex ) ; const namedCtor = xmlDocument . getText ( new Range ( namedCtorPos , new Position ( namedCtorPos . line , namedCtorPos . character + 50 ) ) ) . split ( '"\'' ) [ 0 ] ; classIndex = wordIndexAfter ( dart , ' ' + tag + '.' + namedCtor + '(' , count ) + 3 + namedCtor . length ; } } dartOffset = classIndex ; if ( wordRange ) { const attrName = xmlDocument . getText ( wordRange ) ; dartOffset = dart . indexOf ( attrName , classIndex ) ; } } else if ( isTagName ( xmlDocument , xmlPosition ) || ( includeCloseTag && isClosingTagName ( xmlDocument , xmlPosition ) ) ) { if ( wordRange ) { const tag = xmlDocument . getText ( wordRange ) ; const isAttributeOrCustomTag = tag . length && /[a-z].*/g . test ( tag [ 0 ] ) ; if ( isAttributeOrCustomTag ) { const parentTag = xPath [ xPath . length - 1 ] ; let count = countWords ( xml , '<' + parentTag + ' ' , offset + parentTag . length ) ; count += countWords ( xml , '<' + parentTag + '>' , offset + parentTag . length ) ; const classIndex = wordIndexAfter ( dart , ' ' + parentTag + '(' , count ) + 2 ; const attrIndex = wordIndexAfter ( dart , ' ' + tag + ':' , 1 , classIndex ) + 2 ; if ( attrIndex === tag . length + 3 ) { dartOffset = classIndex ; } else { dartOffset = attrIndex - 3 ; } } else { let count = countWords ( xml , '<' + tag + ' ' , offset + tag . length ) ; count += countWords ( xml , '<' + tag + '>' , offset + tag . length ) ; let classIndex = wordIndexAfter ( dart , ' ' + tag + '(' , count ) + 2 ; if ( classIndex == tag . length + 3 ) { classIndex = wordIndexAfter ( dart , ' ' + tag + '.' , count ) + 2 ; } dartOffset = classIndex - 3 ; } } else if ( useTagName ) { const parentTag = xPath [ xPath . length - 1 ] ; let count = countWords ( xml , '<' + parentTag + ' ' , offset + parentTag . length ) ; count += countWords ( xml , '<' + parentTag + '>' , offset + parentTag . length ) ; const classIndex = wordIndexAfter ( dart , ' ' + parentTag + '(' , count ) + 2 ; if ( translateLine ) { dartOffset = dartDocument . offsetAt ( dartDocument . positionAt ( classIndex ) . translate ( { lineDelta : 1 , characterDelta : 1000 } ) ) ; } else { dartOffset = classIndex ; } } } return dartOffset ; } export function getXmlCodeWordLocation ( xmlDocument , dart , location ) { let xmlOffset = - 1 ; const xml = xmlDocument . getText ( ) ; const word = dart . substr ( location . offset , location . length ) ; if ( word ) { xmlOffset = xml . indexOf ( '<' + word + '>' ) ; if ( xmlOffset === - 1 ) { xmlOffset = xml . indexOf ( '<' + word + ' ' ) ; } if ( xmlOffset === - 1 ) { xmlOffset = xml . indexOf ( ' ' + word + '=' ) ; } if ( xmlOffset === - 1 ) { xmlOffset = xml . indexOf ( '"' + word + '"' ) ; } if ( xmlOffset === - 1 ) { try { const match = new RegExp ( `\\b${ word }\\b` , 'ig' ) . exec ( xml ) ; if ( match ) { xmlOffset = match . index - 1 ; } } catch { return null ; } } } const startPos = xmlDocument . positionAt ( xmlOffset + 1 ) ; return new Range ( startPos , startPos . translate ( 0 , word . length ) ) ; }