'use strict' ; import { Range , TextDocument } from 'vscode-languageserver-types' ; import Uri from 'vscode-uri' ; import { DocumentContext , DocumentLink } from '../htmlLanguageService' ; import { createScanner , TokenType } from '../parser/htmlScanner' ; function stripQuotes ( url ) { return url . replace ( /^'([^']*)'$/ , ( substr , match1 ) => match1 ) . replace ( /^"([^"]*)"$/ , ( substr , match1 ) => match1 ) ; } function getWorkspaceUrl ( modelAbsoluteUri , tokenContent , documentContext , base ) { if ( /^\s*javascript\:/i . test ( tokenContent ) || /^\s*\#/i . test ( tokenContent ) || /[\n\r]/ . test ( tokenContent ) ) { return null ; } tokenContent = tokenContent . replace ( /^\s*/g , '' ) ; if ( /^https?:\/\//i . test ( tokenContent ) || /^file:\/\//i . test ( tokenContent ) ) { return tokenContent ; } if ( /^\/\//i . test ( tokenContent ) ) { let pickedScheme = 'http' ; if ( modelAbsoluteUri . scheme === 'https' ) { pickedScheme = 'https' ; } return pickedScheme + ':' + tokenContent . replace ( /^\s*/g , '' ) ; } if ( documentContext ) { return documentContext . resolveReference ( tokenContent , base ) ; } return tokenContent ; } function createLink ( document , documentContext , attributeValue , startOffset , endOffset , base ) { const documentUri = Uri . parse ( document . uri ) ; const tokenContent = stripQuotes ( attributeValue ) ; if ( tokenContent . length === 0 ) { return null ; } if ( tokenContent . length < attributeValue . length ) { startOffset ++ ; endOffset -- ; } const workspaceUrl = getWorkspaceUrl ( documentUri , tokenContent , documentContext , base ) ; if ( ! workspaceUrl || ! isValidURI ( workspaceUrl ) ) { return null ; } return { range : Range . create ( document . positionAt ( startOffset ) , document . positionAt ( endOffset ) ) , target : workspaceUrl } ; } function isValidURI ( uri ) { try { Uri . parse ( uri ) ; return true ; } catch ( e ) { return false ; } } export function findDocumentLinks ( document , documentContext ) { const newLinks = [ ] ; const scanner = createScanner ( document . getText ( ) , 0 ) ; let token = scanner . scan ( ) ; let afterHrefOrSrc = false ; let afterBase = false ; let base = void 0 ; while ( token !== TokenType . EOS ) { switch ( token ) { case TokenType . StartTag : if ( ! base ) { const tagName = scanner . getTokenText ( ) . toLowerCase ( ) ; afterBase = tagName === 'base' ; } break ; case TokenType . AttributeName : const attributeName = scanner . getTokenText ( ) . toLowerCase ( ) ; afterHrefOrSrc = attributeName === 'src' || attributeName === 'href' ; break ; case TokenType . AttributeValue : if ( afterHrefOrSrc ) { const attributeValue = scanner . getTokenText ( ) ; const link = createLink ( document , documentContext , attributeValue , scanner . getTokenOffset ( ) , scanner . getTokenEnd ( ) , base ) ; if ( link ) { newLinks . push ( link ) ; } if ( afterBase && typeof base === 'undefined' ) { base = stripQuotes ( attributeValue ) ; } afterBase = false ; afterHrefOrSrc = false ; } break ; } token = scanner . scan ( ) ; } return newLinks ; }