text: import { TokenType, createScanner } from '../parser/htmlScanner';
  import { TextDocument, Range, DocumentLink } from 'vscode-languageserver-types';
  import { URI } from 'vscode-uri';
  import { DocumentContext } from '../../../types';
  
  function stripQuotes(url: string): string {
    return url.replace(/^'([^']*)'$/, (substr, match1) => match1).replace(/^"([^"]*)"$/, (substr, match1) => match1);
  }
  
  function getWorkspaceUrl(
    modelAbsoluteUri: URI,
    tokenContent: string,
    documentContext: DocumentContext,
    base: string
  ): string | null {
    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)) {
      // Absolute link that needs no treatment
      return tokenContent;
    }
  
    if (/^\/\//i.test(tokenContent)) {
      // Absolute link (that does not name the protocol)
      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: TextDocument,
    documentContext: DocumentContext,
    attributeValue: string,
    startOffset: number,
    endOffset: number,
    base: string
  ): DocumentLink | null {
    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: string) {
    try {
      URI.parse(uri);
      return true;
    } catch (e) {
      return false;
    }
  }
  
  export function findDocumentLinks(document: TextDocument, documentContext: DocumentContext): DocumentLink[] {
    const newLinks: DocumentLink[] = [];
  
    const scanner = createScanner(document.getText(), 0);
    let token = scanner.scan();
    let afterHrefOrSrc = false;
    let afterBase = false;
    let base: string | undefined = undefined;
    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;
  }
  