mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 18:46:40 -05:00
Merge from vscode c58aaab8a1cc22a7139b761166a0d4f37d41e998 (#7880)
* Merge from vscode c58aaab8a1cc22a7139b761166a0d4f37d41e998 * fix pipelines * fix strict-null-checks * add missing files
This commit is contained in:
@@ -29,7 +29,6 @@ export interface IAction extends IDisposable {
|
||||
class: string | undefined;
|
||||
enabled: boolean;
|
||||
checked: boolean;
|
||||
radio: boolean;
|
||||
run(event?: any): Promise<any>;
|
||||
}
|
||||
|
||||
@@ -54,7 +53,6 @@ export interface IActionChangeEvent {
|
||||
readonly class?: string;
|
||||
readonly enabled?: boolean;
|
||||
readonly checked?: boolean;
|
||||
readonly radio?: boolean;
|
||||
}
|
||||
|
||||
export class Action extends Disposable implements IAction {
|
||||
@@ -68,7 +66,6 @@ export class Action extends Disposable implements IAction {
|
||||
protected _cssClass: string | undefined;
|
||||
protected _enabled: boolean = true;
|
||||
protected _checked: boolean = false;
|
||||
protected _radio: boolean = false;
|
||||
protected readonly _actionCallback?: (event?: any) => Promise<any>;
|
||||
|
||||
constructor(id: string, label: string = '', cssClass: string = '', enabled: boolean = true, actionCallback?: (event?: any) => Promise<any>) {
|
||||
@@ -152,14 +149,6 @@ export class Action extends Disposable implements IAction {
|
||||
this._setChecked(value);
|
||||
}
|
||||
|
||||
get radio(): boolean {
|
||||
return this._radio;
|
||||
}
|
||||
|
||||
set radio(value: boolean) {
|
||||
this._setRadio(value);
|
||||
}
|
||||
|
||||
protected _setChecked(value: boolean): void {
|
||||
if (this._checked !== value) {
|
||||
this._checked = value;
|
||||
@@ -167,13 +156,6 @@ export class Action extends Disposable implements IAction {
|
||||
}
|
||||
}
|
||||
|
||||
protected _setRadio(value: boolean): void {
|
||||
if (this._radio !== value) {
|
||||
this._radio = value;
|
||||
this._onDidChange.fire({ radio: value });
|
||||
}
|
||||
}
|
||||
|
||||
run(event?: any, _data?: ITelemetryData): Promise<any> {
|
||||
if (this._actionCallback) {
|
||||
return this._actionCallback(event);
|
||||
|
||||
@@ -116,7 +116,10 @@ export class CancellationTokenSource {
|
||||
}
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
dispose(cancel: boolean = false): void {
|
||||
if (cancel) {
|
||||
this.cancel();
|
||||
}
|
||||
if (this._parentListener) {
|
||||
this._parentListener.dispose();
|
||||
}
|
||||
|
||||
135
src/vs/base/common/codicon.ts
Normal file
135
src/vs/base/common/codicon.ts
Normal file
@@ -0,0 +1,135 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { matchesFuzzy, IMatch } from 'vs/base/common/filters';
|
||||
import { ltrim } from 'vs/base/common/strings';
|
||||
|
||||
const codiconStartMarker = '$(';
|
||||
|
||||
export interface IParsedCodicons {
|
||||
readonly text: string;
|
||||
readonly codiconOffsets?: readonly number[];
|
||||
}
|
||||
|
||||
export function parseCodicons(text: string): IParsedCodicons {
|
||||
const firstCodiconIndex = text.indexOf(codiconStartMarker);
|
||||
if (firstCodiconIndex === -1) {
|
||||
return { text }; // return early if the word does not include an codicon
|
||||
}
|
||||
|
||||
return doParseCodicons(text, firstCodiconIndex);
|
||||
}
|
||||
|
||||
function doParseCodicons(text: string, firstCodiconIndex: number): IParsedCodicons {
|
||||
const codiconOffsets: number[] = [];
|
||||
let textWithoutCodicons: string = '';
|
||||
|
||||
function appendChars(chars: string) {
|
||||
if (chars) {
|
||||
textWithoutCodicons += chars;
|
||||
|
||||
for (const _ of chars) {
|
||||
codiconOffsets.push(codiconsOffset); // make sure to fill in codicon offsets
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let currentCodiconStart = -1;
|
||||
let currentCodiconValue: string = '';
|
||||
let codiconsOffset = 0;
|
||||
|
||||
let char: string;
|
||||
let nextChar: string;
|
||||
|
||||
let offset = firstCodiconIndex;
|
||||
const length = text.length;
|
||||
|
||||
// Append all characters until the first codicon
|
||||
appendChars(text.substr(0, firstCodiconIndex));
|
||||
|
||||
// example: $(file-symlink-file) my cool $(other-codicon) entry
|
||||
while (offset < length) {
|
||||
char = text[offset];
|
||||
nextChar = text[offset + 1];
|
||||
|
||||
// beginning of codicon: some value $( <--
|
||||
if (char === codiconStartMarker[0] && nextChar === codiconStartMarker[1]) {
|
||||
currentCodiconStart = offset;
|
||||
|
||||
// if we had a previous potential codicon value without
|
||||
// the closing ')', it was actually not an codicon and
|
||||
// so we have to add it to the actual value
|
||||
appendChars(currentCodiconValue);
|
||||
|
||||
currentCodiconValue = codiconStartMarker;
|
||||
|
||||
offset++; // jump over '('
|
||||
}
|
||||
|
||||
// end of codicon: some value $(some-codicon) <--
|
||||
else if (char === ')' && currentCodiconStart !== -1) {
|
||||
const currentCodiconLength = offset - currentCodiconStart + 1; // +1 to include the closing ')'
|
||||
codiconsOffset += currentCodiconLength;
|
||||
currentCodiconStart = -1;
|
||||
currentCodiconValue = '';
|
||||
}
|
||||
|
||||
// within codicon
|
||||
else if (currentCodiconStart !== -1) {
|
||||
// Make sure this is a real codicon name
|
||||
if (/^[a-z0-9\-]$/i.test(char)) {
|
||||
currentCodiconValue += char;
|
||||
} else {
|
||||
// This is not a real codicon, treat it as text
|
||||
appendChars(currentCodiconValue);
|
||||
|
||||
currentCodiconStart = -1;
|
||||
currentCodiconValue = '';
|
||||
}
|
||||
}
|
||||
|
||||
// any value outside of codicons
|
||||
else {
|
||||
appendChars(char);
|
||||
}
|
||||
|
||||
offset++;
|
||||
}
|
||||
|
||||
// if we had a previous potential codicon value without
|
||||
// the closing ')', it was actually not an codicon and
|
||||
// so we have to add it to the actual value
|
||||
appendChars(currentCodiconValue);
|
||||
|
||||
return { text: textWithoutCodicons, codiconOffsets };
|
||||
}
|
||||
|
||||
export function matchesFuzzyCodiconAware(query: string, target: IParsedCodicons, enableSeparateSubstringMatching = false): IMatch[] | null {
|
||||
const { text, codiconOffsets } = target;
|
||||
|
||||
// Return early if there are no codicon markers in the word to match against
|
||||
if (!codiconOffsets || codiconOffsets.length === 0) {
|
||||
return matchesFuzzy(query, text, enableSeparateSubstringMatching);
|
||||
}
|
||||
|
||||
// Trim the word to match against because it could have leading
|
||||
// whitespace now if the word started with an codicon
|
||||
const wordToMatchAgainstWithoutCodiconsTrimmed = ltrim(text, ' ');
|
||||
const leadingWhitespaceOffset = text.length - wordToMatchAgainstWithoutCodiconsTrimmed.length;
|
||||
|
||||
// match on value without codicons
|
||||
const matches = matchesFuzzy(query, wordToMatchAgainstWithoutCodiconsTrimmed, enableSeparateSubstringMatching);
|
||||
|
||||
// Map matches back to offsets with codicons and trimming
|
||||
if (matches) {
|
||||
for (const match of matches) {
|
||||
const codiconOffset = codiconOffsets[match.start + leadingWhitespaceOffset] /* codicon offsets at index */ + leadingWhitespaceOffset /* overall leading whitespace offset */;
|
||||
match.start += codiconOffset;
|
||||
match.end += codiconOffset;
|
||||
}
|
||||
}
|
||||
|
||||
return matches;
|
||||
}
|
||||
@@ -507,10 +507,6 @@ export namespace Color {
|
||||
* The default format will use HEX if opaque and RGBA otherwise.
|
||||
*/
|
||||
export function format(color: Color): string | null {
|
||||
if (!color) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (color.isOpaque()) {
|
||||
return Color.Format.CSS.formatHex(color);
|
||||
}
|
||||
@@ -524,11 +520,6 @@ export namespace Color {
|
||||
* @param hex string (#RGB, #RGBA, #RRGGBB or #RRGGBBAA).
|
||||
*/
|
||||
export function parseHex(hex: string): Color | null {
|
||||
if (!hex) {
|
||||
// Invalid color
|
||||
return null;
|
||||
}
|
||||
|
||||
const length = hex.length;
|
||||
|
||||
if (length === 0) {
|
||||
|
||||
@@ -4,22 +4,29 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { DiffChange } from 'vs/base/common/diff/diffChange';
|
||||
import { stringHash } from 'vs/base/common/hash';
|
||||
import { Constants } from 'vs/base/common/uint';
|
||||
|
||||
function createStringSequence(a: string): ISequence {
|
||||
return {
|
||||
getLength() { return a.length; },
|
||||
getElementAtIndex(pos: number) { return a.charCodeAt(pos); }
|
||||
};
|
||||
export class StringDiffSequence implements ISequence {
|
||||
|
||||
constructor(private source: string) { }
|
||||
|
||||
getElements(): Int32Array | number[] | string[] {
|
||||
const source = this.source;
|
||||
const characters = new Int32Array(source.length);
|
||||
for (let i = 0, len = source.length; i < len; i++) {
|
||||
characters[i] = source.charCodeAt(i);
|
||||
}
|
||||
return characters;
|
||||
}
|
||||
}
|
||||
|
||||
export function stringDiff(original: string, modified: string, pretty: boolean): IDiffChange[] {
|
||||
return new LcsDiff(createStringSequence(original), createStringSequence(modified)).ComputeDiff(pretty);
|
||||
return new LcsDiff(new StringDiffSequence(original), new StringDiffSequence(modified)).ComputeDiff(pretty).changes;
|
||||
}
|
||||
|
||||
|
||||
export interface ISequence {
|
||||
getLength(): number;
|
||||
getElementAtIndex(index: number): number | string;
|
||||
getElements(): Int32Array | number[] | string[];
|
||||
}
|
||||
|
||||
export interface IDiffChange {
|
||||
@@ -49,7 +56,12 @@ export interface IDiffChange {
|
||||
}
|
||||
|
||||
export interface IContinueProcessingPredicate {
|
||||
(furthestOriginalIndex: number, originalSequence: ISequence, matchLengthOfLongest: number): boolean;
|
||||
(furthestOriginalIndex: number, matchLengthOfLongest: number): boolean;
|
||||
}
|
||||
|
||||
export interface IDiffResult {
|
||||
quitEarly: boolean;
|
||||
changes: IDiffChange[];
|
||||
}
|
||||
|
||||
//
|
||||
@@ -86,6 +98,11 @@ export class MyArray {
|
||||
destinationArray[destinationIndex + i] = sourceArray[sourceIndex + i];
|
||||
}
|
||||
}
|
||||
public static Copy2(sourceArray: Int32Array, sourceIndex: number, destinationArray: Int32Array, destinationIndex: number, length: number) {
|
||||
for (let i = 0; i < length; i++) {
|
||||
destinationArray[destinationIndex + i] = sourceArray[sourceIndex + i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//*****************************************************************************
|
||||
@@ -100,11 +117,9 @@ export class MyArray {
|
||||
// Our total memory usage for storing history is (worst-case):
|
||||
// 2 * [(MaxDifferencesHistory + 1) * (MaxDifferencesHistory + 1) - 1] * sizeof(int)
|
||||
// 2 * [1448*1448 - 1] * 4 = 16773624 = 16MB
|
||||
let MaxDifferencesHistory = 1447;
|
||||
//let MaxDifferencesHistory = 100;
|
||||
|
||||
|
||||
|
||||
const enum LocalConstants {
|
||||
MaxDifferencesHistory = 1447
|
||||
}
|
||||
|
||||
/**
|
||||
* A utility class which helps to create the set of DiffChanges from
|
||||
@@ -127,8 +142,8 @@ class DiffChangeHelper {
|
||||
*/
|
||||
constructor() {
|
||||
this.m_changes = [];
|
||||
this.m_originalStart = Number.MAX_VALUE;
|
||||
this.m_modifiedStart = Number.MAX_VALUE;
|
||||
this.m_originalStart = Constants.MAX_SAFE_SMALL_INTEGER;
|
||||
this.m_modifiedStart = Constants.MAX_SAFE_SMALL_INTEGER;
|
||||
this.m_originalCount = 0;
|
||||
this.m_modifiedCount = 0;
|
||||
}
|
||||
@@ -147,8 +162,8 @@ class DiffChangeHelper {
|
||||
// Reset for the next change
|
||||
this.m_originalCount = 0;
|
||||
this.m_modifiedCount = 0;
|
||||
this.m_originalStart = Number.MAX_VALUE;
|
||||
this.m_modifiedStart = Number.MAX_VALUE;
|
||||
this.m_originalStart = Constants.MAX_SAFE_SMALL_INTEGER;
|
||||
this.m_modifiedStart = Constants.MAX_SAFE_SMALL_INTEGER;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -214,39 +229,81 @@ class DiffChangeHelper {
|
||||
*/
|
||||
export class LcsDiff {
|
||||
|
||||
private OriginalSequence: ISequence;
|
||||
private ModifiedSequence: ISequence;
|
||||
private ContinueProcessingPredicate: IContinueProcessingPredicate | null;
|
||||
private readonly ContinueProcessingPredicate: IContinueProcessingPredicate | null;
|
||||
|
||||
private m_forwardHistory: number[][];
|
||||
private m_reverseHistory: number[][];
|
||||
private readonly _hasStrings: boolean;
|
||||
private readonly _originalStringElements: string[];
|
||||
private readonly _originalElementsOrHash: Int32Array;
|
||||
private readonly _modifiedStringElements: string[];
|
||||
private readonly _modifiedElementsOrHash: Int32Array;
|
||||
|
||||
private m_forwardHistory: Int32Array[];
|
||||
private m_reverseHistory: Int32Array[];
|
||||
|
||||
/**
|
||||
* Constructs the DiffFinder
|
||||
*/
|
||||
constructor(originalSequence: ISequence, newSequence: ISequence, continueProcessingPredicate: IContinueProcessingPredicate | null = null) {
|
||||
this.OriginalSequence = originalSequence;
|
||||
this.ModifiedSequence = newSequence;
|
||||
constructor(originalSequence: ISequence, modifiedSequence: ISequence, continueProcessingPredicate: IContinueProcessingPredicate | null = null) {
|
||||
this.ContinueProcessingPredicate = continueProcessingPredicate;
|
||||
|
||||
const [originalStringElements, originalElementsOrHash, originalHasStrings] = LcsDiff._getElements(originalSequence);
|
||||
const [modifiedStringElements, modifiedElementsOrHash, modifiedHasStrings] = LcsDiff._getElements(modifiedSequence);
|
||||
|
||||
this._hasStrings = (originalHasStrings && modifiedHasStrings);
|
||||
this._originalStringElements = originalStringElements;
|
||||
this._originalElementsOrHash = originalElementsOrHash;
|
||||
this._modifiedStringElements = modifiedStringElements;
|
||||
this._modifiedElementsOrHash = modifiedElementsOrHash;
|
||||
|
||||
this.m_forwardHistory = [];
|
||||
this.m_reverseHistory = [];
|
||||
}
|
||||
|
||||
private static _isStringArray(arr: Int32Array | number[] | string[]): arr is string[] {
|
||||
return (arr.length > 0 && typeof arr[0] === 'string');
|
||||
}
|
||||
|
||||
private static _getElements(sequence: ISequence): [string[], Int32Array, boolean] {
|
||||
const elements = sequence.getElements();
|
||||
|
||||
if (LcsDiff._isStringArray(elements)) {
|
||||
const hashes = new Int32Array(elements.length);
|
||||
for (let i = 0, len = elements.length; i < len; i++) {
|
||||
hashes[i] = stringHash(elements[i], 0);
|
||||
}
|
||||
return [elements, hashes, true];
|
||||
}
|
||||
|
||||
if (elements instanceof Int32Array) {
|
||||
return [[], elements, false];
|
||||
}
|
||||
|
||||
return [[], new Int32Array(elements), false];
|
||||
}
|
||||
|
||||
private ElementsAreEqual(originalIndex: number, newIndex: number): boolean {
|
||||
return (this.OriginalSequence.getElementAtIndex(originalIndex) === this.ModifiedSequence.getElementAtIndex(newIndex));
|
||||
if (this._originalElementsOrHash[originalIndex] !== this._modifiedElementsOrHash[newIndex]) {
|
||||
return false;
|
||||
}
|
||||
return (this._hasStrings ? this._originalStringElements[originalIndex] === this._modifiedStringElements[newIndex] : true);
|
||||
}
|
||||
|
||||
private OriginalElementsAreEqual(index1: number, index2: number): boolean {
|
||||
return (this.OriginalSequence.getElementAtIndex(index1) === this.OriginalSequence.getElementAtIndex(index2));
|
||||
if (this._originalElementsOrHash[index1] !== this._originalElementsOrHash[index2]) {
|
||||
return false;
|
||||
}
|
||||
return (this._hasStrings ? this._originalStringElements[index1] === this._originalStringElements[index2] : true);
|
||||
}
|
||||
|
||||
private ModifiedElementsAreEqual(index1: number, index2: number): boolean {
|
||||
return (this.ModifiedSequence.getElementAtIndex(index1) === this.ModifiedSequence.getElementAtIndex(index2));
|
||||
if (this._modifiedElementsOrHash[index1] !== this._modifiedElementsOrHash[index2]) {
|
||||
return false;
|
||||
}
|
||||
return (this._hasStrings ? this._modifiedStringElements[index1] === this._modifiedStringElements[index2] : true);
|
||||
}
|
||||
|
||||
public ComputeDiff(pretty: boolean): IDiffChange[] {
|
||||
return this._ComputeDiff(0, this.OriginalSequence.getLength() - 1, 0, this.ModifiedSequence.getLength() - 1, pretty);
|
||||
public ComputeDiff(pretty: boolean): IDiffResult {
|
||||
return this._ComputeDiff(0, this._originalElementsOrHash.length - 1, 0, this._modifiedElementsOrHash.length - 1, pretty);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -254,18 +311,21 @@ export class LcsDiff {
|
||||
* sequences on the bounded range.
|
||||
* @returns An array of the differences between the two input sequences.
|
||||
*/
|
||||
private _ComputeDiff(originalStart: number, originalEnd: number, modifiedStart: number, modifiedEnd: number, pretty: boolean): DiffChange[] {
|
||||
let quitEarlyArr = [false];
|
||||
private _ComputeDiff(originalStart: number, originalEnd: number, modifiedStart: number, modifiedEnd: number, pretty: boolean): IDiffResult {
|
||||
const quitEarlyArr = [false];
|
||||
let changes = this.ComputeDiffRecursive(originalStart, originalEnd, modifiedStart, modifiedEnd, quitEarlyArr);
|
||||
|
||||
if (pretty) {
|
||||
// We have to clean up the computed diff to be more intuitive
|
||||
// but it turns out this cannot be done correctly until the entire set
|
||||
// of diffs have been computed
|
||||
return this.PrettifyChanges(changes);
|
||||
changes = this.PrettifyChanges(changes);
|
||||
}
|
||||
|
||||
return changes;
|
||||
return {
|
||||
quitEarly: quitEarlyArr[0],
|
||||
changes: changes
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -318,11 +378,12 @@ export class LcsDiff {
|
||||
}
|
||||
|
||||
// This problem can be solved using the Divide-And-Conquer technique.
|
||||
let midOriginalArr = [0], midModifiedArr = [0];
|
||||
let result = this.ComputeRecursionPoint(originalStart, originalEnd, modifiedStart, modifiedEnd, midOriginalArr, midModifiedArr, quitEarlyArr);
|
||||
const midOriginalArr = [0];
|
||||
const midModifiedArr = [0];
|
||||
const result = this.ComputeRecursionPoint(originalStart, originalEnd, modifiedStart, modifiedEnd, midOriginalArr, midModifiedArr, quitEarlyArr);
|
||||
|
||||
let midOriginal = midOriginalArr[0];
|
||||
let midModified = midModifiedArr[0];
|
||||
const midOriginal = midOriginalArr[0];
|
||||
const midModified = midModifiedArr[0];
|
||||
|
||||
if (result !== null) {
|
||||
// Result is not-null when there was enough memory to compute the changes while
|
||||
@@ -334,7 +395,7 @@ export class LcsDiff {
|
||||
// Second Half: (midOriginal + 1, minModified + 1) to (originalEnd, modifiedEnd)
|
||||
// NOTE: ComputeDiff() is inclusive, therefore the second range starts on the next point
|
||||
|
||||
let leftChanges = this.ComputeDiffRecursive(originalStart, midOriginal, modifiedStart, midModified, quitEarlyArr);
|
||||
const leftChanges = this.ComputeDiffRecursive(originalStart, midOriginal, modifiedStart, midModified, quitEarlyArr);
|
||||
let rightChanges: DiffChange[] = [];
|
||||
|
||||
if (!quitEarlyArr[0]) {
|
||||
@@ -358,24 +419,25 @@ export class LcsDiff {
|
||||
|
||||
private WALKTRACE(diagonalForwardBase: number, diagonalForwardStart: number, diagonalForwardEnd: number, diagonalForwardOffset: number,
|
||||
diagonalReverseBase: number, diagonalReverseStart: number, diagonalReverseEnd: number, diagonalReverseOffset: number,
|
||||
forwardPoints: number[], reversePoints: number[],
|
||||
forwardPoints: Int32Array, reversePoints: Int32Array,
|
||||
originalIndex: number, originalEnd: number, midOriginalArr: number[],
|
||||
modifiedIndex: number, modifiedEnd: number, midModifiedArr: number[],
|
||||
deltaIsEven: boolean, quitEarlyArr: boolean[]): DiffChange[] {
|
||||
let forwardChanges: DiffChange[] | null = null, reverseChanges: DiffChange[] | null = null;
|
||||
deltaIsEven: boolean, quitEarlyArr: boolean[]
|
||||
): DiffChange[] {
|
||||
let forwardChanges: DiffChange[] | null = null;
|
||||
let reverseChanges: DiffChange[] | null = null;
|
||||
|
||||
// First, walk backward through the forward diagonals history
|
||||
let changeHelper = new DiffChangeHelper();
|
||||
let diagonalMin = diagonalForwardStart;
|
||||
let diagonalMax = diagonalForwardEnd;
|
||||
let diagonalRelative = (midOriginalArr[0] - midModifiedArr[0]) - diagonalForwardOffset;
|
||||
let lastOriginalIndex = Number.MIN_VALUE;
|
||||
let lastOriginalIndex = Constants.MIN_SAFE_SMALL_INTEGER;
|
||||
let historyIndex = this.m_forwardHistory.length - 1;
|
||||
let diagonal: number;
|
||||
|
||||
do {
|
||||
// Get the diagonal index from the relative diagonal number
|
||||
diagonal = diagonalRelative + diagonalForwardBase;
|
||||
const diagonal = diagonalRelative + diagonalForwardBase;
|
||||
|
||||
// Figure out where we came from
|
||||
if (diagonal === diagonalMin || (diagonal < diagonalMax && forwardPoints[diagonal - 1] < forwardPoints[diagonal + 1])) {
|
||||
@@ -420,7 +482,7 @@ export class LcsDiff {
|
||||
let modifiedStartPoint = midModifiedArr[0] + 1;
|
||||
|
||||
if (forwardChanges !== null && forwardChanges.length > 0) {
|
||||
let lastForwardChange = forwardChanges[forwardChanges.length - 1];
|
||||
const lastForwardChange = forwardChanges[forwardChanges.length - 1];
|
||||
originalStartPoint = Math.max(originalStartPoint, lastForwardChange.getOriginalEnd());
|
||||
modifiedStartPoint = Math.max(modifiedStartPoint, lastForwardChange.getModifiedEnd());
|
||||
}
|
||||
@@ -435,12 +497,12 @@ export class LcsDiff {
|
||||
diagonalMin = diagonalReverseStart;
|
||||
diagonalMax = diagonalReverseEnd;
|
||||
diagonalRelative = (midOriginalArr[0] - midModifiedArr[0]) - diagonalReverseOffset;
|
||||
lastOriginalIndex = Number.MAX_VALUE;
|
||||
lastOriginalIndex = Constants.MAX_SAFE_SMALL_INTEGER;
|
||||
historyIndex = (deltaIsEven) ? this.m_reverseHistory.length - 1 : this.m_reverseHistory.length - 2;
|
||||
|
||||
do {
|
||||
// Get the diagonal index from the relative diagonal number
|
||||
diagonal = diagonalRelative + diagonalReverseBase;
|
||||
const diagonal = diagonalRelative + diagonalReverseBase;
|
||||
|
||||
// Figure out where we came from
|
||||
if (diagonal === diagonalMin || (diagonal < diagonalMax && reversePoints[diagonal - 1] >= reversePoints[diagonal + 1])) {
|
||||
@@ -501,7 +563,6 @@ export class LcsDiff {
|
||||
let originalIndex = 0, modifiedIndex = 0;
|
||||
let diagonalForwardStart = 0, diagonalForwardEnd = 0;
|
||||
let diagonalReverseStart = 0, diagonalReverseEnd = 0;
|
||||
let numDifferences: number;
|
||||
|
||||
// To traverse the edit graph and produce the proper LCS, our actual
|
||||
// start position is just outside the given boundary
|
||||
@@ -521,26 +582,26 @@ export class LcsDiff {
|
||||
// The integer value in the cell represents the originalIndex of the furthest
|
||||
// reaching point found so far that ends in that diagonal.
|
||||
// The modifiedIndex can be computed mathematically from the originalIndex and the diagonal number.
|
||||
let maxDifferences = (originalEnd - originalStart) + (modifiedEnd - modifiedStart);
|
||||
let numDiagonals = maxDifferences + 1;
|
||||
let forwardPoints: number[] = new Array<number>(numDiagonals);
|
||||
let reversePoints: number[] = new Array<number>(numDiagonals);
|
||||
const maxDifferences = (originalEnd - originalStart) + (modifiedEnd - modifiedStart);
|
||||
const numDiagonals = maxDifferences + 1;
|
||||
const forwardPoints = new Int32Array(numDiagonals);
|
||||
const reversePoints = new Int32Array(numDiagonals);
|
||||
// diagonalForwardBase: Index into forwardPoints of the diagonal which passes through (originalStart, modifiedStart)
|
||||
// diagonalReverseBase: Index into reversePoints of the diagonal which passes through (originalEnd, modifiedEnd)
|
||||
let diagonalForwardBase = (modifiedEnd - modifiedStart);
|
||||
let diagonalReverseBase = (originalEnd - originalStart);
|
||||
const diagonalForwardBase = (modifiedEnd - modifiedStart);
|
||||
const diagonalReverseBase = (originalEnd - originalStart);
|
||||
// diagonalForwardOffset: Geometric offset which allows modifiedIndex to be computed from originalIndex and the
|
||||
// diagonal number (relative to diagonalForwardBase)
|
||||
// diagonalReverseOffset: Geometric offset which allows modifiedIndex to be computed from originalIndex and the
|
||||
// diagonal number (relative to diagonalReverseBase)
|
||||
let diagonalForwardOffset = (originalStart - modifiedStart);
|
||||
let diagonalReverseOffset = (originalEnd - modifiedEnd);
|
||||
const diagonalForwardOffset = (originalStart - modifiedStart);
|
||||
const diagonalReverseOffset = (originalEnd - modifiedEnd);
|
||||
|
||||
// delta: The difference between the end diagonal and the start diagonal. This is used to relate diagonal numbers
|
||||
// relative to the start diagonal with diagonal numbers relative to the end diagonal.
|
||||
// The Even/Oddn-ness of this delta is important for determining when we should check for overlap
|
||||
let delta = diagonalReverseBase - diagonalForwardBase;
|
||||
let deltaIsEven = (delta % 2 === 0);
|
||||
const delta = diagonalReverseBase - diagonalForwardBase;
|
||||
const deltaIsEven = (delta % 2 === 0);
|
||||
|
||||
// Here we set up the start and end points as the furthest points found so far
|
||||
// in both the forward and reverse directions, respectively
|
||||
@@ -559,15 +620,14 @@ export class LcsDiff {
|
||||
// away from the reference diagonal (which is diagonalForwardBase for forward, diagonalReverseBase for reverse).
|
||||
// --We extend on even diagonals (relative to the reference diagonal) only when numDifferences
|
||||
// is even and odd diagonals only when numDifferences is odd.
|
||||
let diagonal: number, tempOriginalIndex: number;
|
||||
for (numDifferences = 1; numDifferences <= (maxDifferences / 2) + 1; numDifferences++) {
|
||||
for (let numDifferences = 1; numDifferences <= (maxDifferences / 2) + 1; numDifferences++) {
|
||||
let furthestOriginalIndex = 0;
|
||||
let furthestModifiedIndex = 0;
|
||||
|
||||
// Run the algorithm in the forward direction
|
||||
diagonalForwardStart = this.ClipDiagonalBound(diagonalForwardBase - numDifferences, numDifferences, diagonalForwardBase, numDiagonals);
|
||||
diagonalForwardEnd = this.ClipDiagonalBound(diagonalForwardBase + numDifferences, numDifferences, diagonalForwardBase, numDiagonals);
|
||||
for (diagonal = diagonalForwardStart; diagonal <= diagonalForwardEnd; diagonal += 2) {
|
||||
for (let diagonal = diagonalForwardStart; diagonal <= diagonalForwardEnd; diagonal += 2) {
|
||||
// STEP 1: We extend the furthest reaching point in the present diagonal
|
||||
// by looking at the diagonals above and below and picking the one whose point
|
||||
// is further away from the start point (originalStart, modifiedStart)
|
||||
@@ -579,7 +639,7 @@ export class LcsDiff {
|
||||
modifiedIndex = originalIndex - (diagonal - diagonalForwardBase) - diagonalForwardOffset;
|
||||
|
||||
// Save the current originalIndex so we can test for false overlap in step 3
|
||||
tempOriginalIndex = originalIndex;
|
||||
const tempOriginalIndex = originalIndex;
|
||||
|
||||
// STEP 2: We can continue to extend the furthest reaching point in the present diagonal
|
||||
// so long as the elements are equal.
|
||||
@@ -603,7 +663,7 @@ export class LcsDiff {
|
||||
midOriginalArr[0] = originalIndex;
|
||||
midModifiedArr[0] = modifiedIndex;
|
||||
|
||||
if (tempOriginalIndex <= reversePoints[diagonal] && MaxDifferencesHistory > 0 && numDifferences <= (MaxDifferencesHistory + 1)) {
|
||||
if (tempOriginalIndex <= reversePoints[diagonal] && LocalConstants.MaxDifferencesHistory > 0 && numDifferences <= (LocalConstants.MaxDifferencesHistory + 1)) {
|
||||
// BINGO! We overlapped, and we have the full trace in memory!
|
||||
return this.WALKTRACE(diagonalForwardBase, diagonalForwardStart, diagonalForwardEnd, diagonalForwardOffset,
|
||||
diagonalReverseBase, diagonalReverseStart, diagonalReverseEnd, diagonalReverseOffset,
|
||||
@@ -622,9 +682,9 @@ export class LcsDiff {
|
||||
}
|
||||
|
||||
// Check to see if we should be quitting early, before moving on to the next iteration.
|
||||
let matchLengthOfLongest = ((furthestOriginalIndex - originalStart) + (furthestModifiedIndex - modifiedStart) - numDifferences) / 2;
|
||||
const matchLengthOfLongest = ((furthestOriginalIndex - originalStart) + (furthestModifiedIndex - modifiedStart) - numDifferences) / 2;
|
||||
|
||||
if (this.ContinueProcessingPredicate !== null && !this.ContinueProcessingPredicate(furthestOriginalIndex, this.OriginalSequence, matchLengthOfLongest)) {
|
||||
if (this.ContinueProcessingPredicate !== null && !this.ContinueProcessingPredicate(furthestOriginalIndex, matchLengthOfLongest)) {
|
||||
// We can't finish, so skip ahead to generating a result from what we have.
|
||||
quitEarlyArr[0] = true;
|
||||
|
||||
@@ -632,7 +692,7 @@ export class LcsDiff {
|
||||
midOriginalArr[0] = furthestOriginalIndex;
|
||||
midModifiedArr[0] = furthestModifiedIndex;
|
||||
|
||||
if (matchLengthOfLongest > 0 && MaxDifferencesHistory > 0 && numDifferences <= (MaxDifferencesHistory + 1)) {
|
||||
if (matchLengthOfLongest > 0 && LocalConstants.MaxDifferencesHistory > 0 && numDifferences <= (LocalConstants.MaxDifferencesHistory + 1)) {
|
||||
// Enough of the history is in memory to walk it backwards
|
||||
return this.WALKTRACE(diagonalForwardBase, diagonalForwardStart, diagonalForwardEnd, diagonalForwardOffset,
|
||||
diagonalReverseBase, diagonalReverseStart, diagonalReverseEnd, diagonalReverseOffset,
|
||||
@@ -659,7 +719,7 @@ export class LcsDiff {
|
||||
// Run the algorithm in the reverse direction
|
||||
diagonalReverseStart = this.ClipDiagonalBound(diagonalReverseBase - numDifferences, numDifferences, diagonalReverseBase, numDiagonals);
|
||||
diagonalReverseEnd = this.ClipDiagonalBound(diagonalReverseBase + numDifferences, numDifferences, diagonalReverseBase, numDiagonals);
|
||||
for (diagonal = diagonalReverseStart; diagonal <= diagonalReverseEnd; diagonal += 2) {
|
||||
for (let diagonal = diagonalReverseStart; diagonal <= diagonalReverseEnd; diagonal += 2) {
|
||||
// STEP 1: We extend the furthest reaching point in the present diagonal
|
||||
// by looking at the diagonals above and below and picking the one whose point
|
||||
// is further away from the start point (originalEnd, modifiedEnd)
|
||||
@@ -671,7 +731,7 @@ export class LcsDiff {
|
||||
modifiedIndex = originalIndex - (diagonal - diagonalReverseBase) - diagonalReverseOffset;
|
||||
|
||||
// Save the current originalIndex so we can test for false overlap
|
||||
tempOriginalIndex = originalIndex;
|
||||
const tempOriginalIndex = originalIndex;
|
||||
|
||||
// STEP 2: We can continue to extend the furthest reaching point in the present diagonal
|
||||
// as long as the elements are equal.
|
||||
@@ -689,7 +749,7 @@ export class LcsDiff {
|
||||
midOriginalArr[0] = originalIndex;
|
||||
midModifiedArr[0] = modifiedIndex;
|
||||
|
||||
if (tempOriginalIndex >= forwardPoints[diagonal] && MaxDifferencesHistory > 0 && numDifferences <= (MaxDifferencesHistory + 1)) {
|
||||
if (tempOriginalIndex >= forwardPoints[diagonal] && LocalConstants.MaxDifferencesHistory > 0 && numDifferences <= (LocalConstants.MaxDifferencesHistory + 1)) {
|
||||
// BINGO! We overlapped, and we have the full trace in memory!
|
||||
return this.WALKTRACE(diagonalForwardBase, diagonalForwardStart, diagonalForwardEnd, diagonalForwardOffset,
|
||||
diagonalReverseBase, diagonalReverseStart, diagonalReverseEnd, diagonalReverseOffset,
|
||||
@@ -708,24 +768,22 @@ export class LcsDiff {
|
||||
}
|
||||
|
||||
// Save current vectors to history before the next iteration
|
||||
if (numDifferences <= MaxDifferencesHistory) {
|
||||
if (numDifferences <= LocalConstants.MaxDifferencesHistory) {
|
||||
// We are allocating space for one extra int, which we fill with
|
||||
// the index of the diagonal base index
|
||||
let temp: number[] = new Array<number>(diagonalForwardEnd - diagonalForwardStart + 2);
|
||||
let temp = new Int32Array(diagonalForwardEnd - diagonalForwardStart + 2);
|
||||
temp[0] = diagonalForwardBase - diagonalForwardStart + 1;
|
||||
MyArray.Copy(forwardPoints, diagonalForwardStart, temp, 1, diagonalForwardEnd - diagonalForwardStart + 1);
|
||||
MyArray.Copy2(forwardPoints, diagonalForwardStart, temp, 1, diagonalForwardEnd - diagonalForwardStart + 1);
|
||||
this.m_forwardHistory.push(temp);
|
||||
|
||||
temp = new Array<number>(diagonalReverseEnd - diagonalReverseStart + 2);
|
||||
temp = new Int32Array(diagonalReverseEnd - diagonalReverseStart + 2);
|
||||
temp[0] = diagonalReverseBase - diagonalReverseStart + 1;
|
||||
MyArray.Copy(reversePoints, diagonalReverseStart, temp, 1, diagonalReverseEnd - diagonalReverseStart + 1);
|
||||
MyArray.Copy2(reversePoints, diagonalReverseStart, temp, 1, diagonalReverseEnd - diagonalReverseStart + 1);
|
||||
this.m_reverseHistory.push(temp);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
// If we got here, then we have the full trace in history. We just have to convert it to a change list
|
||||
// NOTE: This part is a bit messy
|
||||
return this.WALKTRACE(diagonalForwardBase, diagonalForwardStart, diagonalForwardEnd, diagonalForwardOffset,
|
||||
@@ -750,8 +808,8 @@ export class LcsDiff {
|
||||
// Shift all the changes down first
|
||||
for (let i = 0; i < changes.length; i++) {
|
||||
const change = changes[i];
|
||||
const originalStop = (i < changes.length - 1) ? changes[i + 1].originalStart : this.OriginalSequence.getLength();
|
||||
const modifiedStop = (i < changes.length - 1) ? changes[i + 1].modifiedStart : this.ModifiedSequence.getLength();
|
||||
const originalStop = (i < changes.length - 1) ? changes[i + 1].originalStart : this._originalElementsOrHash.length;
|
||||
const modifiedStop = (i < changes.length - 1) ? changes[i + 1].modifiedStart : this._modifiedElementsOrHash.length;
|
||||
const checkOriginal = change.originalLength > 0;
|
||||
const checkModified = change.modifiedLength > 0;
|
||||
|
||||
@@ -795,8 +853,8 @@ export class LcsDiff {
|
||||
let bestScore = this._boundaryScore(change.originalStart, change.originalLength, change.modifiedStart, change.modifiedLength);
|
||||
|
||||
for (let delta = 1; ; delta++) {
|
||||
let originalStart = change.originalStart - delta;
|
||||
let modifiedStart = change.modifiedStart - delta;
|
||||
const originalStart = change.originalStart - delta;
|
||||
const modifiedStart = change.modifiedStart - delta;
|
||||
|
||||
if (originalStart < originalStop || modifiedStart < modifiedStop) {
|
||||
break;
|
||||
@@ -810,7 +868,7 @@ export class LcsDiff {
|
||||
break;
|
||||
}
|
||||
|
||||
let score = this._boundaryScore(originalStart, change.originalLength, modifiedStart, change.modifiedLength);
|
||||
const score = this._boundaryScore(originalStart, change.originalLength, modifiedStart, change.modifiedLength);
|
||||
|
||||
if (score > bestScore) {
|
||||
bestScore = score;
|
||||
@@ -826,11 +884,10 @@ export class LcsDiff {
|
||||
}
|
||||
|
||||
private _OriginalIsBoundary(index: number): boolean {
|
||||
if (index <= 0 || index >= this.OriginalSequence.getLength() - 1) {
|
||||
if (index <= 0 || index >= this._originalElementsOrHash.length - 1) {
|
||||
return true;
|
||||
}
|
||||
const element = this.OriginalSequence.getElementAtIndex(index);
|
||||
return (typeof element === 'string' && /^\s*$/.test(element));
|
||||
return (this._hasStrings && /^\s*$/.test(this._originalStringElements[index]));
|
||||
}
|
||||
|
||||
private _OriginalRegionIsBoundary(originalStart: number, originalLength: number): boolean {
|
||||
@@ -838,7 +895,7 @@ export class LcsDiff {
|
||||
return true;
|
||||
}
|
||||
if (originalLength > 0) {
|
||||
let originalEnd = originalStart + originalLength;
|
||||
const originalEnd = originalStart + originalLength;
|
||||
if (this._OriginalIsBoundary(originalEnd - 1) || this._OriginalIsBoundary(originalEnd)) {
|
||||
return true;
|
||||
}
|
||||
@@ -847,11 +904,10 @@ export class LcsDiff {
|
||||
}
|
||||
|
||||
private _ModifiedIsBoundary(index: number): boolean {
|
||||
if (index <= 0 || index >= this.ModifiedSequence.getLength() - 1) {
|
||||
if (index <= 0 || index >= this._modifiedElementsOrHash.length - 1) {
|
||||
return true;
|
||||
}
|
||||
const element = this.ModifiedSequence.getElementAtIndex(index);
|
||||
return (typeof element === 'string' && /^\s*$/.test(element));
|
||||
return (this._hasStrings && /^\s*$/.test(this._modifiedStringElements[index]));
|
||||
}
|
||||
|
||||
private _ModifiedRegionIsBoundary(modifiedStart: number, modifiedLength: number): boolean {
|
||||
@@ -859,7 +915,7 @@ export class LcsDiff {
|
||||
return true;
|
||||
}
|
||||
if (modifiedLength > 0) {
|
||||
let modifiedEnd = modifiedStart + modifiedLength;
|
||||
const modifiedEnd = modifiedStart + modifiedLength;
|
||||
if (this._ModifiedIsBoundary(modifiedEnd - 1) || this._ModifiedIsBoundary(modifiedEnd)) {
|
||||
return true;
|
||||
}
|
||||
@@ -868,8 +924,8 @@ export class LcsDiff {
|
||||
}
|
||||
|
||||
private _boundaryScore(originalStart: number, originalLength: number, modifiedStart: number, modifiedLength: number): number {
|
||||
let originalScore = (this._OriginalRegionIsBoundary(originalStart, originalLength) ? 1 : 0);
|
||||
let modifiedScore = (this._ModifiedRegionIsBoundary(modifiedStart, modifiedLength) ? 1 : 0);
|
||||
const originalScore = (this._OriginalRegionIsBoundary(originalStart, originalLength) ? 1 : 0);
|
||||
const modifiedScore = (this._ModifiedRegionIsBoundary(modifiedStart, modifiedLength) ? 1 : 0);
|
||||
return (originalScore + modifiedScore);
|
||||
}
|
||||
|
||||
@@ -890,14 +946,14 @@ export class LcsDiff {
|
||||
// might recurse in the middle of a change thereby splitting it into
|
||||
// two changes. Here in the combining stage, we detect and fuse those
|
||||
// changes back together
|
||||
let result = new Array<DiffChange>(left.length + right.length - 1);
|
||||
const result = new Array<DiffChange>(left.length + right.length - 1);
|
||||
MyArray.Copy(left, 0, result, 0, left.length - 1);
|
||||
result[left.length - 1] = mergedChangeArr[0];
|
||||
MyArray.Copy(right, 1, result, left.length, right.length - 1);
|
||||
|
||||
return result;
|
||||
} else {
|
||||
let result = new Array<DiffChange>(left.length + right.length);
|
||||
const result = new Array<DiffChange>(left.length + right.length);
|
||||
MyArray.Copy(left, 0, result, 0, left.length);
|
||||
MyArray.Copy(right, 0, result, left.length, right.length);
|
||||
|
||||
@@ -918,9 +974,9 @@ export class LcsDiff {
|
||||
Debug.Assert(left.modifiedStart <= right.modifiedStart, 'Left change is not less than or equal to right change');
|
||||
|
||||
if (left.originalStart + left.originalLength >= right.originalStart || left.modifiedStart + left.modifiedLength >= right.modifiedStart) {
|
||||
let originalStart = left.originalStart;
|
||||
const originalStart = left.originalStart;
|
||||
let originalLength = left.originalLength;
|
||||
let modifiedStart = left.modifiedStart;
|
||||
const modifiedStart = left.modifiedStart;
|
||||
let modifiedLength = left.modifiedLength;
|
||||
|
||||
if (left.originalStart + left.originalLength >= right.originalStart) {
|
||||
@@ -958,15 +1014,15 @@ export class LcsDiff {
|
||||
|
||||
// diagonalsBelow: The number of diagonals below the reference diagonal
|
||||
// diagonalsAbove: The number of diagonals above the reference diagonal
|
||||
let diagonalsBelow = diagonalBaseIndex;
|
||||
let diagonalsAbove = numDiagonals - diagonalBaseIndex - 1;
|
||||
let diffEven = (numDifferences % 2 === 0);
|
||||
const diagonalsBelow = diagonalBaseIndex;
|
||||
const diagonalsAbove = numDiagonals - diagonalBaseIndex - 1;
|
||||
const diffEven = (numDifferences % 2 === 0);
|
||||
|
||||
if (diagonal < 0) {
|
||||
let lowerBoundEven = (diagonalsBelow % 2 === 0);
|
||||
const lowerBoundEven = (diagonalsBelow % 2 === 0);
|
||||
return (diffEven === lowerBoundEven) ? 0 : 1;
|
||||
} else {
|
||||
let upperBoundEven = (diagonalsAbove % 2 === 0);
|
||||
const upperBoundEven = (diagonalsAbove % 2 === 0);
|
||||
return (diffEven === upperBoundEven) ? numDiagonals - 1 : numDiagonals - 2;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ function booleanHash(b: boolean, initialHashVal: number): number {
|
||||
return numberHash(b ? 433 : 863, initialHashVal);
|
||||
}
|
||||
|
||||
function stringHash(s: string, hashVal: number) {
|
||||
export function stringHash(s: string, hashVal: number) {
|
||||
hashVal = numberHash(149417, hashVal);
|
||||
for (let i = 0, length = s.length; i < length; i++) {
|
||||
hashVal = numberHash(s.charCodeAt(i), hashVal);
|
||||
|
||||
@@ -7,23 +7,27 @@ import { equals } from 'vs/base/common/arrays';
|
||||
import { UriComponents } from 'vs/base/common/uri';
|
||||
|
||||
export interface IMarkdownString {
|
||||
value: string;
|
||||
isTrusted?: boolean;
|
||||
readonly value: string;
|
||||
readonly isTrusted?: boolean;
|
||||
uris?: { [href: string]: UriComponents };
|
||||
}
|
||||
|
||||
export class MarkdownString implements IMarkdownString {
|
||||
|
||||
value: string;
|
||||
isTrusted?: boolean;
|
||||
private _value: string;
|
||||
private _isTrusted: boolean;
|
||||
|
||||
constructor(value: string = '') {
|
||||
this.value = value;
|
||||
constructor(value: string = '', isTrusted = false) {
|
||||
this._value = value;
|
||||
this._isTrusted = isTrusted;
|
||||
}
|
||||
|
||||
get value() { return this._value; }
|
||||
get isTrusted() { return this._isTrusted; }
|
||||
|
||||
appendText(value: string): MarkdownString {
|
||||
// escape markdown syntax tokens: http://daringfireball.net/projects/markdown/syntax#backslash
|
||||
this.value += value
|
||||
this._value += value
|
||||
.replace(/[\\`*_{}[\]()#+\-.!]/g, '\\$&')
|
||||
.replace('\n', '\n\n');
|
||||
|
||||
@@ -31,16 +35,16 @@ export class MarkdownString implements IMarkdownString {
|
||||
}
|
||||
|
||||
appendMarkdown(value: string): MarkdownString {
|
||||
this.value += value;
|
||||
this._value += value;
|
||||
return this;
|
||||
}
|
||||
|
||||
appendCodeblock(langId: string, code: string): MarkdownString {
|
||||
this.value += '\n```';
|
||||
this.value += langId;
|
||||
this.value += '\n';
|
||||
this.value += code;
|
||||
this.value += '\n```\n';
|
||||
this._value += '\n```';
|
||||
this._value += langId;
|
||||
this._value += '\n';
|
||||
this._value += code;
|
||||
this._value += '\n```\n';
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ export module Iterator {
|
||||
};
|
||||
}
|
||||
|
||||
export function fromArray<T>(array: T[], index = 0, length = array.length): Iterator<T> {
|
||||
export function fromArray<T>(array: ReadonlyArray<T>, index = 0, length = array.length): Iterator<T> {
|
||||
return {
|
||||
next(): IteratorResult<T> {
|
||||
if (index >= length) {
|
||||
|
||||
@@ -4,12 +4,11 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { sep, posix, normalize } from 'vs/base/common/path';
|
||||
import { sep, posix, normalize, win32 } from 'vs/base/common/path';
|
||||
import { endsWith, startsWithIgnoreCase, rtrim, startsWith } from 'vs/base/common/strings';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { isLinux, isWindows, isMacintosh } from 'vs/base/common/platform';
|
||||
import { isEqual, basename, relativePath } from 'vs/base/common/resources';
|
||||
import { CharCode } from 'vs/base/common/charCode';
|
||||
|
||||
export interface IWorkspaceFolderProvider {
|
||||
getWorkspaceFolder(resource: URI): { uri: URI, name?: string } | null;
|
||||
@@ -44,7 +43,7 @@ export function getPathLabel(resource: URI | string, userHomeProvider?: IUserHom
|
||||
}
|
||||
|
||||
if (hasMultipleRoots) {
|
||||
const rootName = (baseResource && baseResource.name) ? baseResource.name : basename(baseResource.uri);
|
||||
const rootName = baseResource.name ? baseResource.name : basename(baseResource.uri);
|
||||
pathLabel = pathLabel ? (rootName + ' • ' + pathLabel) : rootName; // always show root basename if there are multiple
|
||||
}
|
||||
|
||||
@@ -388,11 +387,12 @@ export function unmnemonicLabel(label: string): string {
|
||||
* Splits a path in name and parent path, supporting both '/' and '\'
|
||||
*/
|
||||
export function splitName(fullPath: string): { name: string, parentPath: string } {
|
||||
for (let i = fullPath.length - 1; i >= 1; i--) {
|
||||
const code = fullPath.charCodeAt(i);
|
||||
if (code === CharCode.Slash || code === CharCode.Backslash) {
|
||||
return { parentPath: fullPath.substr(0, i), name: fullPath.substr(i + 1) };
|
||||
}
|
||||
const p = fullPath.indexOf('/') !== -1 ? posix : win32;
|
||||
const name = p.basename(fullPath);
|
||||
const parentPath = p.dirname(fullPath);
|
||||
if (name.length) {
|
||||
return { name, parentPath };
|
||||
}
|
||||
return { parentPath: '', name: fullPath };
|
||||
// only the root segment
|
||||
return { name: parentPath, parentPath: '' };
|
||||
}
|
||||
|
||||
65
src/vs/base/common/lazy.ts
Normal file
65
src/vs/base/common/lazy.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* A value that is resolved synchronously when it is first needed.
|
||||
*/
|
||||
export interface Lazy<T> {
|
||||
|
||||
hasValue(): boolean;
|
||||
|
||||
|
||||
getValue(): T;
|
||||
|
||||
|
||||
map<R>(f: (x: T) => R): Lazy<R>;
|
||||
}
|
||||
|
||||
export class Lazy<T> {
|
||||
|
||||
private _didRun: boolean = false;
|
||||
private _value?: T;
|
||||
private _error: any;
|
||||
|
||||
constructor(
|
||||
private readonly executor: () => T,
|
||||
) { }
|
||||
|
||||
/**
|
||||
* True if the lazy value has been resolved.
|
||||
*/
|
||||
hasValue() { return this._didRun; }
|
||||
|
||||
/**
|
||||
* Get the wrapped value.
|
||||
*
|
||||
* This will force evaluation of the lazy value if it has not been resolved yet. Lazy values are only
|
||||
* resolved once. `getValue` will re-throw exceptions that are hit while resolving the value
|
||||
*/
|
||||
getValue(): T {
|
||||
if (!this._didRun) {
|
||||
try {
|
||||
this._value = this.executor();
|
||||
} catch (err) {
|
||||
this._error = err;
|
||||
} finally {
|
||||
this._didRun = true;
|
||||
}
|
||||
}
|
||||
if (this._error) {
|
||||
throw this._error;
|
||||
}
|
||||
return this._value!;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new lazy value that is the result of applying `f` to the wrapped value.
|
||||
*
|
||||
* This does not force the evaluation of the current lazy value.
|
||||
*/
|
||||
map<R>(f: (x: T) => R): Lazy<R> {
|
||||
return new Lazy<R>(() => f(this.getValue()));
|
||||
}
|
||||
}
|
||||
@@ -138,7 +138,7 @@ export class DisposableStore implements IDisposable {
|
||||
|
||||
export abstract class Disposable implements IDisposable {
|
||||
|
||||
static None = Object.freeze<IDisposable>({ dispose() { } });
|
||||
static readonly None = Object.freeze<IDisposable>({ dispose() { } });
|
||||
|
||||
private readonly _store = new DisposableStore();
|
||||
|
||||
|
||||
@@ -117,6 +117,8 @@ export class PathIterator implements IKeyIterator {
|
||||
private _from!: number;
|
||||
private _to!: number;
|
||||
|
||||
constructor(private _splitOnBackslash: boolean = true) { }
|
||||
|
||||
reset(key: string): this {
|
||||
this._value = key.replace(/\\$|\/$/, '');
|
||||
this._from = 0;
|
||||
@@ -134,7 +136,7 @@ export class PathIterator implements IKeyIterator {
|
||||
let justSeps = true;
|
||||
for (; this._to < this._value.length; this._to++) {
|
||||
const ch = this._value.charCodeAt(this._to);
|
||||
if (ch === CharCode.Slash || ch === CharCode.Backslash) {
|
||||
if (ch === CharCode.Slash || this._splitOnBackslash && ch === CharCode.Backslash) {
|
||||
if (justSeps) {
|
||||
this._from++;
|
||||
} else {
|
||||
|
||||
@@ -56,53 +56,49 @@ export namespace Schemas {
|
||||
}
|
||||
|
||||
class RemoteAuthoritiesImpl {
|
||||
private readonly _hosts: { [authority: string]: string; };
|
||||
private readonly _ports: { [authority: string]: number; };
|
||||
private readonly _connectionTokens: { [authority: string]: string; };
|
||||
private _preferredWebSchema: 'http' | 'https';
|
||||
private _delegate: ((uri: URI) => URI) | null;
|
||||
private readonly _hosts: { [authority: string]: string | undefined; } = Object.create(null);
|
||||
private readonly _ports: { [authority: string]: number | undefined; } = Object.create(null);
|
||||
private readonly _connectionTokens: { [authority: string]: string | undefined; } = Object.create(null);
|
||||
private _preferredWebSchema: 'http' | 'https' = 'http';
|
||||
private _delegate: ((uri: URI) => URI) | null = null;
|
||||
|
||||
constructor() {
|
||||
this._hosts = Object.create(null);
|
||||
this._ports = Object.create(null);
|
||||
this._connectionTokens = Object.create(null);
|
||||
this._preferredWebSchema = 'http';
|
||||
this._delegate = null;
|
||||
}
|
||||
|
||||
public setPreferredWebSchema(schema: 'http' | 'https') {
|
||||
setPreferredWebSchema(schema: 'http' | 'https') {
|
||||
this._preferredWebSchema = schema;
|
||||
}
|
||||
|
||||
public setDelegate(delegate: (uri: URI) => URI): void {
|
||||
setDelegate(delegate: (uri: URI) => URI): void {
|
||||
this._delegate = delegate;
|
||||
}
|
||||
|
||||
public set(authority: string, host: string, port: number): void {
|
||||
set(authority: string, host: string, port: number): void {
|
||||
this._hosts[authority] = host;
|
||||
this._ports[authority] = port;
|
||||
}
|
||||
|
||||
public setConnectionToken(authority: string, connectionToken: string): void {
|
||||
setConnectionToken(authority: string, connectionToken: string): void {
|
||||
this._connectionTokens[authority] = connectionToken;
|
||||
}
|
||||
|
||||
public rewrite(uri: URI): URI {
|
||||
rewrite(uri: URI): URI {
|
||||
if (this._delegate) {
|
||||
return this._delegate(uri);
|
||||
}
|
||||
const authority = uri.authority;
|
||||
let host = this._hosts[authority];
|
||||
if (host.indexOf(':') !== -1) {
|
||||
if (host && host.indexOf(':') !== -1) {
|
||||
host = `[${host}]`;
|
||||
}
|
||||
const port = this._ports[authority];
|
||||
const connectionToken = this._connectionTokens[authority];
|
||||
let query = `path=${encodeURIComponent(uri.path)}`;
|
||||
if (typeof connectionToken === 'string') {
|
||||
query += `&tkn=${encodeURIComponent(connectionToken)}`;
|
||||
}
|
||||
return URI.from({
|
||||
scheme: platform.isWeb ? this._preferredWebSchema : Schemas.vscodeRemoteResource,
|
||||
authority: `${host}:${port}`,
|
||||
path: `/vscode-remote-resource`,
|
||||
query: `path=${encodeURIComponent(uri.path)}&tkn=${encodeURIComponent(connectionToken)}`
|
||||
query
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,126 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { matchesFuzzy, IMatch } from 'vs/base/common/filters';
|
||||
import { ltrim } from 'vs/base/common/strings';
|
||||
|
||||
const octiconStartMarker = '$(';
|
||||
|
||||
export interface IParsedOcticons {
|
||||
text: string;
|
||||
octiconOffsets?: number[];
|
||||
}
|
||||
|
||||
export function parseOcticons(text: string): IParsedOcticons {
|
||||
const firstOcticonIndex = text.indexOf(octiconStartMarker);
|
||||
if (firstOcticonIndex === -1) {
|
||||
return { text }; // return early if the word does not include an octicon
|
||||
}
|
||||
|
||||
return doParseOcticons(text, firstOcticonIndex);
|
||||
}
|
||||
|
||||
function doParseOcticons(text: string, firstOcticonIndex: number): IParsedOcticons {
|
||||
const octiconOffsets: number[] = [];
|
||||
let textWithoutOcticons: string = '';
|
||||
|
||||
function appendChars(chars: string) {
|
||||
if (chars) {
|
||||
textWithoutOcticons += chars;
|
||||
|
||||
for (const _ of chars) {
|
||||
octiconOffsets.push(octiconsOffset); // make sure to fill in octicon offsets
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let currentOcticonStart = -1;
|
||||
let currentOcticonValue: string = '';
|
||||
let octiconsOffset = 0;
|
||||
|
||||
let char: string;
|
||||
let nextChar: string;
|
||||
|
||||
let offset = firstOcticonIndex;
|
||||
const length = text.length;
|
||||
|
||||
// Append all characters until the first octicon
|
||||
appendChars(text.substr(0, firstOcticonIndex));
|
||||
|
||||
// example: $(file-symlink-file) my cool $(other-octicon) entry
|
||||
while (offset < length) {
|
||||
char = text[offset];
|
||||
nextChar = text[offset + 1];
|
||||
|
||||
// beginning of octicon: some value $( <--
|
||||
if (char === octiconStartMarker[0] && nextChar === octiconStartMarker[1]) {
|
||||
currentOcticonStart = offset;
|
||||
|
||||
// if we had a previous potential octicon value without
|
||||
// the closing ')', it was actually not an octicon and
|
||||
// so we have to add it to the actual value
|
||||
appendChars(currentOcticonValue);
|
||||
|
||||
currentOcticonValue = octiconStartMarker;
|
||||
|
||||
offset++; // jump over '('
|
||||
}
|
||||
|
||||
// end of octicon: some value $(some-octicon) <--
|
||||
else if (char === ')' && currentOcticonStart !== -1) {
|
||||
const currentOcticonLength = offset - currentOcticonStart + 1; // +1 to include the closing ')'
|
||||
octiconsOffset += currentOcticonLength;
|
||||
currentOcticonStart = -1;
|
||||
currentOcticonValue = '';
|
||||
}
|
||||
|
||||
// within octicon
|
||||
else if (currentOcticonStart !== -1) {
|
||||
currentOcticonValue += char;
|
||||
}
|
||||
|
||||
// any value outside of octicons
|
||||
else {
|
||||
appendChars(char);
|
||||
}
|
||||
|
||||
offset++;
|
||||
}
|
||||
|
||||
// if we had a previous potential octicon value without
|
||||
// the closing ')', it was actually not an octicon and
|
||||
// so we have to add it to the actual value
|
||||
appendChars(currentOcticonValue);
|
||||
|
||||
return { text: textWithoutOcticons, octiconOffsets };
|
||||
}
|
||||
|
||||
export function matchesFuzzyOcticonAware(query: string, target: IParsedOcticons, enableSeparateSubstringMatching = false): IMatch[] | null {
|
||||
const { text, octiconOffsets } = target;
|
||||
|
||||
// Return early if there are no octicon markers in the word to match against
|
||||
if (!octiconOffsets || octiconOffsets.length === 0) {
|
||||
return matchesFuzzy(query, text, enableSeparateSubstringMatching);
|
||||
}
|
||||
|
||||
// Trim the word to match against because it could have leading
|
||||
// whitespace now if the word started with an octicon
|
||||
const wordToMatchAgainstWithoutOcticonsTrimmed = ltrim(text, ' ');
|
||||
const leadingWhitespaceOffset = text.length - wordToMatchAgainstWithoutOcticonsTrimmed.length;
|
||||
|
||||
// match on value without octicons
|
||||
const matches = matchesFuzzy(query, wordToMatchAgainstWithoutOcticonsTrimmed, enableSeparateSubstringMatching);
|
||||
|
||||
// Map matches back to offsets with octicons and trimming
|
||||
if (matches) {
|
||||
for (const match of matches) {
|
||||
const octiconOffset = octiconOffsets[match.start + leadingWhitespaceOffset] /* octicon offsets at index */ + leadingWhitespaceOffset /* overall leading whitespace offset */;
|
||||
match.start += octiconOffset;
|
||||
match.end += octiconOffset;
|
||||
}
|
||||
}
|
||||
|
||||
return matches;
|
||||
}
|
||||
@@ -169,7 +169,7 @@ function _format(sep: string, pathObject: ParsedPath) {
|
||||
return dir + sep + base;
|
||||
}
|
||||
|
||||
interface ParsedPath {
|
||||
export interface ParsedPath {
|
||||
root: string;
|
||||
dir: string;
|
||||
base: string;
|
||||
@@ -177,7 +177,7 @@ interface ParsedPath {
|
||||
name: string;
|
||||
}
|
||||
|
||||
interface IPath {
|
||||
export interface IPath {
|
||||
normalize(path: string): string;
|
||||
isAbsolute(path: string): boolean;
|
||||
join(...paths: string[]): string;
|
||||
|
||||
@@ -164,6 +164,34 @@ export const setImmediate: ISetImmediate = (function defineSetImmediate() {
|
||||
if (globals.setImmediate) {
|
||||
return globals.setImmediate.bind(globals);
|
||||
}
|
||||
if (typeof globals.postMessage === 'function' && !globals.importScripts) {
|
||||
interface IQueueElement {
|
||||
id: number;
|
||||
callback: () => void;
|
||||
}
|
||||
let pending: IQueueElement[] = [];
|
||||
globals.addEventListener('message', (e: MessageEvent) => {
|
||||
if (e.data && e.data.vscodeSetImmediateId) {
|
||||
for (let i = 0, len = pending.length; i < len; i++) {
|
||||
const candidate = pending[i];
|
||||
if (candidate.id === e.data.vscodeSetImmediateId) {
|
||||
pending.splice(i, 1);
|
||||
candidate.callback();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
let lastId = 0;
|
||||
return (callback: () => void) => {
|
||||
const myId = ++lastId;
|
||||
pending.push({
|
||||
id: myId,
|
||||
callback: callback
|
||||
});
|
||||
globals.postMessage({ vscodeSetImmediateId: myId });
|
||||
};
|
||||
}
|
||||
if (typeof process !== 'undefined' && typeof process.nextTick === 'function') {
|
||||
return process.nextTick.bind(process);
|
||||
}
|
||||
|
||||
@@ -9,93 +9,77 @@ import { Iterator } from 'vs/base/common/iterator';
|
||||
import { relativePath, joinPath } from 'vs/base/common/resources';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { mapValues } from 'vs/base/common/collections';
|
||||
import { PathIterator } from 'vs/base/common/map';
|
||||
|
||||
export interface ILeafNode<T, C = void> {
|
||||
export interface IResourceNode<T, C = void> {
|
||||
readonly uri: URI;
|
||||
readonly relativePath: string;
|
||||
readonly name: string;
|
||||
readonly element: T;
|
||||
readonly element: T | undefined;
|
||||
readonly children: Iterator<IResourceNode<T, C>>;
|
||||
readonly childrenCount: number;
|
||||
readonly parent: IResourceNode<T, C> | undefined;
|
||||
readonly context: C;
|
||||
get(childName: string): IResourceNode<T, C> | undefined;
|
||||
}
|
||||
|
||||
export interface IBranchNode<T, C = void> {
|
||||
readonly uri: URI;
|
||||
readonly relativePath: string;
|
||||
readonly name: string;
|
||||
readonly size: number;
|
||||
readonly children: Iterator<INode<T, C>>;
|
||||
readonly parent: IBranchNode<T, C> | undefined;
|
||||
readonly context: C;
|
||||
get(childName: string): INode<T, C> | undefined;
|
||||
}
|
||||
class Node<T, C> implements IResourceNode<T, C> {
|
||||
|
||||
export type INode<T, C> = IBranchNode<T, C> | ILeafNode<T, C>;
|
||||
private _children = new Map<string, Node<T, C>>();
|
||||
|
||||
// Internals
|
||||
|
||||
class Node<C> {
|
||||
|
||||
@memoize
|
||||
get name(): string { return paths.posix.basename(this.relativePath); }
|
||||
|
||||
constructor(readonly uri: URI, readonly relativePath: string, readonly context: C) { }
|
||||
}
|
||||
|
||||
class BranchNode<T, C> extends Node<C> implements IBranchNode<T, C> {
|
||||
|
||||
private _children = new Map<string, BranchNode<T, C> | LeafNode<T, C>>();
|
||||
|
||||
get size(): number {
|
||||
get childrenCount(): number {
|
||||
return this._children.size;
|
||||
}
|
||||
|
||||
get children(): Iterator<BranchNode<T, C> | LeafNode<T, C>> {
|
||||
get children(): Iterator<Node<T, C>> {
|
||||
return Iterator.fromArray(mapValues(this._children));
|
||||
}
|
||||
|
||||
constructor(uri: URI, relativePath: string, context: C, readonly parent: IBranchNode<T, C> | undefined = undefined) {
|
||||
super(uri, relativePath, context);
|
||||
@memoize
|
||||
get name(): string {
|
||||
return paths.posix.basename(this.relativePath);
|
||||
}
|
||||
|
||||
get(path: string): BranchNode<T, C> | LeafNode<T, C> | undefined {
|
||||
constructor(
|
||||
readonly uri: URI,
|
||||
readonly relativePath: string,
|
||||
readonly context: C,
|
||||
public element: T | undefined = undefined,
|
||||
readonly parent: IResourceNode<T, C> | undefined = undefined
|
||||
) { }
|
||||
|
||||
get(path: string): Node<T, C> | undefined {
|
||||
return this._children.get(path);
|
||||
}
|
||||
|
||||
set(path: string, child: BranchNode<T, C> | LeafNode<T, C>): void {
|
||||
set(path: string, child: Node<T, C>): void {
|
||||
this._children.set(path, child);
|
||||
}
|
||||
|
||||
delete(path: string): void {
|
||||
this._children.delete(path);
|
||||
}
|
||||
}
|
||||
|
||||
class LeafNode<T, C> extends Node<C> implements ILeafNode<T, C> {
|
||||
|
||||
constructor(uri: URI, path: string, context: C, readonly element: T) {
|
||||
super(uri, path, context);
|
||||
clear(): void {
|
||||
this._children.clear();
|
||||
}
|
||||
}
|
||||
|
||||
function collect<T, C>(node: INode<T, C>, result: T[]): T[] {
|
||||
if (ResourceTree.isBranchNode(node)) {
|
||||
Iterator.forEach(node.children, child => collect(child, result));
|
||||
} else {
|
||||
function collect<T, C>(node: IResourceNode<T, C>, result: T[]): T[] {
|
||||
if (typeof node.element !== 'undefined') {
|
||||
result.push(node.element);
|
||||
}
|
||||
|
||||
Iterator.forEach(node.children, child => collect(child, result));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export class ResourceTree<T extends NonNullable<any>, C> {
|
||||
|
||||
readonly root: BranchNode<T, C>;
|
||||
readonly root: Node<T, C>;
|
||||
|
||||
static isBranchNode<T, C>(obj: any): obj is IBranchNode<T, C> {
|
||||
return obj instanceof BranchNode;
|
||||
}
|
||||
|
||||
static getRoot<T, C>(node: IBranchNode<T, C>): IBranchNode<T, C> {
|
||||
static getRoot<T, C>(node: IResourceNode<T, C>): IResourceNode<T, C> {
|
||||
while (node.parent) {
|
||||
node = node.parent;
|
||||
}
|
||||
@@ -103,89 +87,101 @@ export class ResourceTree<T extends NonNullable<any>, C> {
|
||||
return node;
|
||||
}
|
||||
|
||||
static collect<T, C>(node: INode<T, C>): T[] {
|
||||
static collect<T, C>(node: IResourceNode<T, C>): T[] {
|
||||
return collect(node, []);
|
||||
}
|
||||
|
||||
static isResourceNode<T, C>(obj: any): obj is IResourceNode<T, C> {
|
||||
return obj instanceof Node;
|
||||
}
|
||||
|
||||
constructor(context: C, rootURI: URI = URI.file('/')) {
|
||||
this.root = new BranchNode(rootURI, '', context);
|
||||
this.root = new Node(rootURI, '', context);
|
||||
}
|
||||
|
||||
add(uri: URI, element: T): void {
|
||||
const key = relativePath(this.root.uri, uri) || uri.fsPath;
|
||||
const parts = key.split(/[\\\/]/).filter(p => !!p);
|
||||
const iterator = new PathIterator(false).reset(key);
|
||||
let node = this.root;
|
||||
let path = '';
|
||||
|
||||
for (let i = 0; i < parts.length; i++) {
|
||||
const name = parts[i];
|
||||
while (true) {
|
||||
const name = iterator.value();
|
||||
path = path + '/' + name;
|
||||
|
||||
let child = node.get(name);
|
||||
|
||||
if (!child) {
|
||||
if (i < parts.length - 1) {
|
||||
child = new BranchNode(joinPath(this.root.uri, path), path, this.root.context, node);
|
||||
node.set(name, child);
|
||||
} else {
|
||||
child = new LeafNode(uri, path, this.root.context, element);
|
||||
node.set(name, child);
|
||||
return;
|
||||
}
|
||||
}
|
||||
child = new Node(
|
||||
joinPath(this.root.uri, path),
|
||||
path,
|
||||
this.root.context,
|
||||
iterator.hasNext() ? undefined : element,
|
||||
node
|
||||
);
|
||||
|
||||
if (!(child instanceof BranchNode)) {
|
||||
if (i < parts.length - 1) {
|
||||
throw new Error('Inconsistent tree: can\'t override leaf with branch.');
|
||||
}
|
||||
|
||||
// replace
|
||||
node.set(name, new LeafNode(uri, path, this.root.context, element));
|
||||
return;
|
||||
} else if (i === parts.length - 1) {
|
||||
throw new Error('Inconsistent tree: can\'t override branch with leaf.');
|
||||
node.set(name, child);
|
||||
} else if (!iterator.hasNext()) {
|
||||
child.element = element;
|
||||
}
|
||||
|
||||
node = child;
|
||||
|
||||
if (!iterator.hasNext()) {
|
||||
return;
|
||||
}
|
||||
|
||||
iterator.next();
|
||||
}
|
||||
}
|
||||
|
||||
delete(uri: URI): T | undefined {
|
||||
const key = relativePath(this.root.uri, uri) || uri.fsPath;
|
||||
const parts = key.split(/[\\\/]/).filter(p => !!p);
|
||||
return this._delete(this.root, parts, 0);
|
||||
const iterator = new PathIterator(false).reset(key);
|
||||
return this._delete(this.root, iterator);
|
||||
}
|
||||
|
||||
private _delete(node: BranchNode<T, C>, parts: string[], index: number): T | undefined {
|
||||
const name = parts[index];
|
||||
private _delete(node: Node<T, C>, iterator: PathIterator): T | undefined {
|
||||
const name = iterator.value();
|
||||
const child = node.get(name);
|
||||
|
||||
if (!child) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// not at end
|
||||
if (index < parts.length - 1) {
|
||||
if (child instanceof BranchNode) {
|
||||
const result = this._delete(child, parts, index + 1);
|
||||
if (iterator.hasNext()) {
|
||||
const result = this._delete(child, iterator.next());
|
||||
|
||||
if (typeof result !== 'undefined' && child.size === 0) {
|
||||
node.delete(name);
|
||||
}
|
||||
|
||||
return result;
|
||||
} else {
|
||||
throw new Error('Inconsistent tree: Expected a branch, found a leaf instead.');
|
||||
if (typeof result !== 'undefined' && child.childrenCount === 0) {
|
||||
node.delete(name);
|
||||
}
|
||||
}
|
||||
|
||||
//at end
|
||||
if (child instanceof BranchNode) {
|
||||
// TODO: maybe we can allow this
|
||||
throw new Error('Inconsistent tree: Expected a leaf, found a branch instead.');
|
||||
return result;
|
||||
}
|
||||
|
||||
node.delete(name);
|
||||
return child.element;
|
||||
}
|
||||
|
||||
clear(): void {
|
||||
this.root.clear();
|
||||
}
|
||||
|
||||
getNode(uri: URI): IResourceNode<T, C> | undefined {
|
||||
const key = relativePath(this.root.uri, uri) || uri.fsPath;
|
||||
const iterator = new PathIterator(false).reset(key);
|
||||
let node = this.root;
|
||||
|
||||
while (true) {
|
||||
const name = iterator.value();
|
||||
const child = node.get(name);
|
||||
|
||||
if (!child || !iterator.hasNext()) {
|
||||
return child;
|
||||
}
|
||||
|
||||
node = child;
|
||||
iterator.next();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,20 +7,19 @@ import * as strings from './strings';
|
||||
|
||||
export function buildReplaceStringWithCasePreserved(matches: string[] | null, pattern: string): string {
|
||||
if (matches && (matches[0] !== '')) {
|
||||
const containsHyphens = validateSpecificSpecialCharacter(matches, pattern, '-');
|
||||
const containsUnderscores = validateSpecificSpecialCharacter(matches, pattern, '_');
|
||||
if (containsHyphens && !containsUnderscores) {
|
||||
return buildReplaceStringForSpecificSpecialCharacter(matches, pattern, '-');
|
||||
} else if (!containsHyphens && containsUnderscores) {
|
||||
return buildReplaceStringForSpecificSpecialCharacter(matches, pattern, '_');
|
||||
}
|
||||
if (matches[0].toUpperCase() === matches[0]) {
|
||||
return pattern.toUpperCase();
|
||||
} else if (matches[0].toLowerCase() === matches[0]) {
|
||||
return pattern.toLowerCase();
|
||||
} else if (strings.containsUppercaseCharacter(matches[0][0])) {
|
||||
const containsHyphens = validateSpecificSpecialCharacter(matches, pattern, '-');
|
||||
const containsUnderscores = validateSpecificSpecialCharacter(matches, pattern, '_');
|
||||
if (containsHyphens && !containsUnderscores) {
|
||||
return buildReplaceStringForSpecificSpecialCharacter(matches, pattern, '-');
|
||||
} else if (!containsHyphens && containsUnderscores) {
|
||||
return buildReplaceStringForSpecificSpecialCharacter(matches, pattern, '_');
|
||||
} else {
|
||||
return pattern[0].toUpperCase() + pattern.substr(1);
|
||||
}
|
||||
return pattern[0].toUpperCase() + pattern.substr(1);
|
||||
} else {
|
||||
// we don't understand its pattern yet.
|
||||
return pattern;
|
||||
|
||||
@@ -350,21 +350,10 @@ function isAsciiLetter(code: number): boolean {
|
||||
}
|
||||
|
||||
export function equalsIgnoreCase(a: string, b: string): boolean {
|
||||
const len1 = a ? a.length : 0;
|
||||
const len2 = b ? b.length : 0;
|
||||
|
||||
if (len1 !== len2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return doEqualsIgnoreCase(a, b);
|
||||
return a.length === b.length && doEqualsIgnoreCase(a, b);
|
||||
}
|
||||
|
||||
function doEqualsIgnoreCase(a: string, b: string, stopAt = a.length): boolean {
|
||||
if (typeof a !== 'string' || typeof b !== 'string') {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let i = 0; i < stopAt; i++) {
|
||||
const codeA = a.charCodeAt(i);
|
||||
const codeB = b.charCodeAt(i);
|
||||
|
||||
@@ -93,6 +93,39 @@ export function isUndefinedOrNull(obj: any): obj is undefined | null {
|
||||
return isUndefined(obj) || obj === null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the argument passed in is neither undefined nor null.
|
||||
*/
|
||||
export function assertIsDefined<T>(arg: T | null | undefined): T {
|
||||
if (isUndefinedOrNull(arg)) {
|
||||
throw new Error('Assertion Failed: argument is undefined or null');
|
||||
}
|
||||
|
||||
return arg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that each argument passed in is neither undefined nor null.
|
||||
*/
|
||||
export function assertAllDefined<T1, T2>(t1: T1 | null | undefined, t2: T2 | null | undefined): [T1, T2];
|
||||
export function assertAllDefined<T1, T2, T3>(t1: T1 | null | undefined, t2: T2 | null | undefined, t3: T3 | null | undefined): [T1, T2, T3];
|
||||
export function assertAllDefined<T1, T2, T3, T4>(t1: T1 | null | undefined, t2: T2 | null | undefined, t3: T3 | null | undefined, t4: T4 | null | undefined): [T1, T2, T3, T4];
|
||||
export function assertAllDefined(...args: (unknown | null | undefined)[]): unknown[] {
|
||||
const result = [];
|
||||
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
const arg = args[i];
|
||||
|
||||
if (isUndefinedOrNull(arg)) {
|
||||
throw new Error(`Assertion Failed: argument at index ${i} is undefined or null`);
|
||||
}
|
||||
|
||||
result.push(arg);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
const hasOwnProperty = Object.prototype.hasOwnProperty;
|
||||
|
||||
/**
|
||||
|
||||
68
src/vs/base/common/uint.ts
Normal file
68
src/vs/base/common/uint.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
export const enum Constants {
|
||||
/**
|
||||
* MAX SMI (SMall Integer) as defined in v8.
|
||||
* one bit is lost for boxing/unboxing flag.
|
||||
* one bit is lost for sign flag.
|
||||
* See https://thibaultlaurens.github.io/javascript/2013/04/29/how-the-v8-engine-works/#tagged-values
|
||||
*/
|
||||
MAX_SAFE_SMALL_INTEGER = 1 << 30,
|
||||
|
||||
/**
|
||||
* MIN SMI (SMall Integer) as defined in v8.
|
||||
* one bit is lost for boxing/unboxing flag.
|
||||
* one bit is lost for sign flag.
|
||||
* See https://thibaultlaurens.github.io/javascript/2013/04/29/how-the-v8-engine-works/#tagged-values
|
||||
*/
|
||||
MIN_SAFE_SMALL_INTEGER = -(1 << 30),
|
||||
|
||||
/**
|
||||
* Max unsigned integer that fits on 8 bits.
|
||||
*/
|
||||
MAX_UINT_8 = 255, // 2^8 - 1
|
||||
|
||||
/**
|
||||
* Max unsigned integer that fits on 16 bits.
|
||||
*/
|
||||
MAX_UINT_16 = 65535, // 2^16 - 1
|
||||
|
||||
/**
|
||||
* Max unsigned integer that fits on 32 bits.
|
||||
*/
|
||||
MAX_UINT_32 = 4294967295, // 2^32 - 1
|
||||
|
||||
|
||||
}
|
||||
|
||||
export function toUint8(v: number): number {
|
||||
if (v < 0) {
|
||||
return 0;
|
||||
}
|
||||
if (v > Constants.MAX_UINT_8) {
|
||||
return Constants.MAX_UINT_8;
|
||||
}
|
||||
return v | 0;
|
||||
}
|
||||
|
||||
export function toUint32(v: number): number {
|
||||
if (v < 0) {
|
||||
return 0;
|
||||
}
|
||||
if (v > Constants.MAX_UINT_32) {
|
||||
return Constants.MAX_UINT_32;
|
||||
}
|
||||
return v | 0;
|
||||
}
|
||||
|
||||
export function toUint32Array(arr: number[]): Uint32Array {
|
||||
const len = arr.length;
|
||||
const r = new Uint32Array(len);
|
||||
for (let i = 0; i < len; i++) {
|
||||
r[i] = toUint32(arr[i]);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
@@ -10,26 +10,11 @@ const _schemePattern = /^\w[\w\d+.-]*$/;
|
||||
const _singleSlashStart = /^\//;
|
||||
const _doubleSlashStart = /^\/\//;
|
||||
|
||||
let _throwOnMissingSchema: boolean = true;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export function setUriThrowOnMissingScheme(value: boolean): boolean {
|
||||
const old = _throwOnMissingSchema;
|
||||
_throwOnMissingSchema = value;
|
||||
return old;
|
||||
}
|
||||
|
||||
function _validateUri(ret: URI, _strict?: boolean): void {
|
||||
function _validateUri(ret: URI): void {
|
||||
|
||||
// scheme, must be set
|
||||
if (!ret.scheme) {
|
||||
if (_strict || _throwOnMissingSchema) {
|
||||
throw new Error(`[UriError]: Scheme is missing: {scheme: "", authority: "${ret.authority}", path: "${ret.path}", query: "${ret.query}", fragment: "${ret.fragment}"}`);
|
||||
} else {
|
||||
console.warn(`[UriError]: Scheme is missing: {scheme: "", authority: "${ret.authority}", path: "${ret.path}", query: "${ret.query}", fragment: "${ret.fragment}"}`);
|
||||
}
|
||||
throw new Error(`[UriError]: Scheme is missing: {scheme: "", authority: "${ret.authority}", path: "${ret.path}", query: "${ret.query}", fragment: "${ret.fragment}"}`);
|
||||
}
|
||||
|
||||
// scheme, https://tools.ietf.org/html/rfc3986#section-3.1
|
||||
@@ -56,14 +41,8 @@ function _validateUri(ret: URI, _strict?: boolean): void {
|
||||
}
|
||||
}
|
||||
|
||||
// for a while we allowed uris *without* schemes and this is the migration
|
||||
// for them, e.g. an uri without scheme and without strict-mode warns and falls
|
||||
// back to the file-scheme. that should cause the least carnage and still be a
|
||||
// clear warning
|
||||
function _schemeFix(scheme: string, _strict: boolean): string {
|
||||
if (_strict || _throwOnMissingSchema) {
|
||||
return scheme || _empty;
|
||||
}
|
||||
// graceful behaviour when scheme is missing: fallback to using 'file'-scheme
|
||||
function _schemeFix(scheme: string): string {
|
||||
if (!scheme) {
|
||||
console.trace('BAD uri lacks scheme, falling back to file-scheme.');
|
||||
scheme = 'file';
|
||||
@@ -159,7 +138,7 @@ export class URI implements UriComponents {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
protected constructor(scheme: string, authority?: string, path?: string, query?: string, fragment?: string, _strict?: boolean);
|
||||
protected constructor(scheme: string, authority?: string, path?: string, query?: string, fragment?: string);
|
||||
|
||||
/**
|
||||
* @internal
|
||||
@@ -169,7 +148,7 @@ export class URI implements UriComponents {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
protected constructor(schemeOrData: string | UriComponents, authority?: string, path?: string, query?: string, fragment?: string, _strict: boolean = false) {
|
||||
protected constructor(schemeOrData: string | UriComponents, authority?: string, path?: string, query?: string, fragment?: string) {
|
||||
|
||||
if (typeof schemeOrData === 'object') {
|
||||
this.scheme = schemeOrData.scheme || _empty;
|
||||
@@ -181,13 +160,13 @@ export class URI implements UriComponents {
|
||||
// that creates uri components.
|
||||
// _validateUri(this);
|
||||
} else {
|
||||
this.scheme = _schemeFix(schemeOrData, _strict);
|
||||
this.scheme = _schemeFix(schemeOrData);
|
||||
this.authority = authority || _empty;
|
||||
this.path = _referenceResolution(this.scheme, path || _empty);
|
||||
this.query = query || _empty;
|
||||
this.fragment = fragment || _empty;
|
||||
|
||||
_validateUri(this, _strict);
|
||||
_validateUri(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -226,7 +205,7 @@ export class URI implements UriComponents {
|
||||
|
||||
// ---- modify to new -------------------------
|
||||
|
||||
with(change: { scheme?: string; authority?: string | null; path?: string | null; query?: string | null; fragment?: string | null }): URI {
|
||||
with(change: { scheme?: string; authority?: string | null; path?: string | null; query?: string | null; fragment?: string | null; }): URI {
|
||||
|
||||
if (!change) {
|
||||
return this;
|
||||
@@ -279,7 +258,7 @@ export class URI implements UriComponents {
|
||||
*
|
||||
* @param value A string which represents an URI (see `URI#toString`).
|
||||
*/
|
||||
static parse(value: string, _strict: boolean = false): URI {
|
||||
static parse(value: string): URI {
|
||||
const match = _regexp.exec(value);
|
||||
if (!match) {
|
||||
return new _URI(_empty, _empty, _empty, _empty, _empty);
|
||||
@@ -289,8 +268,7 @@ export class URI implements UriComponents {
|
||||
decodeURIComponent(match[4] || _empty),
|
||||
decodeURIComponent(match[5] || _empty),
|
||||
decodeURIComponent(match[7] || _empty),
|
||||
decodeURIComponent(match[9] || _empty),
|
||||
_strict
|
||||
decodeURIComponent(match[9] || _empty)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -342,7 +320,7 @@ export class URI implements UriComponents {
|
||||
return new _URI('file', authority, path, _empty, _empty);
|
||||
}
|
||||
|
||||
static from(components: { scheme: string; authority?: string; path?: string; query?: string; fragment?: string }): URI {
|
||||
static from(components: { scheme: string; authority?: string; path?: string; query?: string; fragment?: string; }): URI {
|
||||
return new _URI(
|
||||
components.scheme,
|
||||
components.authority,
|
||||
@@ -467,7 +445,7 @@ class _URI extends URI {
|
||||
}
|
||||
|
||||
// reserved characters: https://tools.ietf.org/html/rfc3986#section-2.2
|
||||
const encodeTable: { [ch: number]: string } = {
|
||||
const encodeTable: { [ch: number]: string; } = {
|
||||
[CharCode.Colon]: '%3A', // gen-delims
|
||||
[CharCode.Slash]: '%2F',
|
||||
[CharCode.QuestionMark]: '%3F',
|
||||
|
||||
Reference in New Issue
Block a user