ChFlick/firecode

View on GitHub
src/utils/textmate/text-util.ts

Summary

Maintainability
A
3 hrs
Test Coverage
import * as vscode from 'vscode';

// 'sticky' flag is not yet supported :(
const lineEndingRE = /([^\r\n]*)(\r\n|\r|\n)?/;


export interface RangeDelta {
  start: vscode.Position;
  end: vscode.Position;
  linesDelta: number;
  endCharactersDelta: number; // delta for positions on the same line as the end position
}

/**
 * @returns the Position (line, column) for the location (character position)
 */
function positionAt(text: string, offset: number): vscode.Position {
  if (offset > text.length) { offset = text.length; }
  let line = 0;
  let lastIndex = 0;
  // eslint-disable-next-line no-constant-condition
  while (true) {
    const match = lineEndingRE.exec(text.substring(lastIndex));
    if (!match) {
      throw new Error();
    }
    if (lastIndex + match[1].length >= offset) {
      return new vscode.Position(line, offset - lastIndex);
    }
    lastIndex += match[0].length;
    ++line;
  }
}

/**
 * @returns the lines and characters represented by the text
 */
export function toRangeDelta(oldRange: vscode.Range, text: string): RangeDelta {
  const newEnd = positionAt(text, text.length);
  let charsDelta;
  if (oldRange.start.line === oldRange.end.line) {
    charsDelta = newEnd.character - (oldRange.end.character - oldRange.start.character);
  } else {
    charsDelta = newEnd.character - oldRange.end.character;
  }
  return {
    start: oldRange.start,
    end: oldRange.end,
    linesDelta: newEnd.line - (oldRange.end.line - oldRange.start.line),
    endCharactersDelta: charsDelta
  };
}

export function rangeDeltaNewRange(delta: RangeDelta): vscode.Range {
  let x: number;
  if (delta.linesDelta > 0) {
    x = delta.endCharactersDelta;
  } else if (delta.linesDelta < 0 && delta.start.line === delta.end.line + delta.linesDelta) {
    x = delta.end.character + delta.endCharactersDelta + delta.start.character;
  } else {
    x = delta.end.character + delta.endCharactersDelta;
  } return new vscode.Range(delta.start, new vscode.Position(delta.end.line + delta.linesDelta, x));
}

function positionRangeDeltaTranslateStart(pos: vscode.Position, delta: RangeDelta): vscode.Position {
  if (pos.isBefore(delta.end)) {
    return pos;
  }

  return positionRangeDeltaTranslate(pos, delta);
}

function positionRangeDeltaTranslateEnd(pos: vscode.Position, delta: RangeDelta): vscode.Position {
  if (pos.isBeforeOrEqual(delta.end)) {
    return pos;
  }

  return positionRangeDeltaTranslate(pos, delta);
}

function positionRangeDeltaTranslate(pos: vscode.Position, delta: RangeDelta): vscode.Position {
  if (delta.end.line === pos.line) {
    let x = pos.character + delta.endCharactersDelta;
    if (delta.linesDelta > 0) {
      x = x - delta.end.character;
    } else if (delta.start.line === delta.end.line + delta.linesDelta && delta.linesDelta < 0) {
      x = x + delta.start.character;
    }

    return new vscode.Position(pos.line + delta.linesDelta, x);
  }
  else // if(pos.line > delta.end.line)
  {
    return new vscode.Position(pos.line + delta.linesDelta, pos.character);
  }
}

export function rangeTranslate(range: vscode.Range, delta: RangeDelta): vscode.Range {
  return new vscode.Range(
    positionRangeDeltaTranslateStart(range.start, delta),
    positionRangeDeltaTranslateEnd(range.end, delta)
  );
}

export function rangeContains(range: vscode.Range, pos: vscode.Position, exclStart = false, inclEnd = false): boolean {
  return range.start.isBeforeOrEqual(pos)
    && (!exclStart || !range.start.isEqual(pos))
    && ((inclEnd && range.end.isEqual(pos)) || range.end.isAfter(pos));
}

export function maxPosition(x: vscode.Position, y: vscode.Position): vscode.Position {
  if (x.line < y.line) {
    return x;
  } if (x.line < x.line) {
    return y;
  } if (x.character < y.character) {
    return x;
  } else {
    return y;
  }
}