alm
Version:
The best IDE for TypeScript
616 lines (615 loc) • 18.3 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
function log() {
console.log('log');
}
exports.log = log;
/**
* Create a quick lookup map from list
*/
function createMap(arr) {
return arr.reduce(function (result, key) {
result[key] = true;
return result;
}, {});
}
exports.createMap = createMap;
/**
* Create a quick lookup map from list
*/
function createMapByKey(arr, getKey) {
var result = {};
arr.forEach(function (item) {
var key = getKey(item);
result[key] = result[key] ? result[key].concat(item) : [item];
});
return result;
}
exports.createMapByKey = createMapByKey;
/**
* Turns keys into values and values into keys
*/
function reverseKeysAndValues(obj) {
var toret = {};
Object.keys(obj).forEach(function (key) {
toret[obj[key]] = key;
});
return toret;
}
exports.reverseKeysAndValues = reverseKeysAndValues;
/** Sloppy but effective code to find distinct */
function distinct(arr) {
var map = createMap(arr);
return Object.keys(map);
}
exports.distinct = distinct;
exports.uniq = distinct;
/**
* Values for dictionary
*/
function values(dict) {
return Object.keys(dict).map(function (key) { return dict[key]; });
}
exports.values = values;
/**
* Debounce
*/
var now = function () { return new Date().getTime(); };
function debounce(func, milliseconds, immediate) {
if (immediate === void 0) { immediate = false; }
var timeout, args, context, timestamp, result;
var wait = milliseconds;
var later = function () {
var last = now() - timestamp;
if (last < wait && last > 0) {
timeout = setTimeout(later, wait - last);
}
else {
timeout = null;
if (!immediate) {
result = func.apply(context, args);
if (!timeout)
context = args = null;
}
}
};
return function () {
context = this;
args = arguments;
timestamp = now();
var callNow = immediate && !timeout;
if (!timeout)
timeout = setTimeout(later, wait);
if (callNow) {
result = func.apply(context, args);
context = args = null;
}
return result;
};
}
exports.debounce = debounce;
;
/**
* Like debounce but will also call if a state change is significant enough to not ignore silently
* Note:
* - Significant changes : the function is called *immediately* without debouncing (but still marked for future debouncing).
*/
function triggeredDebounce(config) {
var lastArg, lastCallTimeStamp;
var hasALastArg = false; // true if we are `holding back` any previous arg
var pendingTimeout = null;
var later = function () {
var timeSinceLast = now() - lastCallTimeStamp;
if (timeSinceLast < config.milliseconds) {
if (pendingTimeout) {
clearTimeout(pendingTimeout);
pendingTimeout = null;
}
pendingTimeout = setTimeout(later, config.milliseconds - timeSinceLast);
}
else {
config.func(lastArg);
hasALastArg = false;
}
};
return function (arg) {
var stateChangeSignificant = hasALastArg && config.mustcall(arg, lastArg);
if (stateChangeSignificant) {
config.func(lastArg);
}
lastArg = arg;
hasALastArg = true;
lastCallTimeStamp = now();
later();
};
}
exports.triggeredDebounce = triggeredDebounce;
;
function throttle(func, milliseconds, options) {
var context, args, result;
var timeout = null;
var previous = 0;
if (!options)
options = {};
var gnow = now;
var later = function () {
previous = options.leading === false ? 0 : gnow();
timeout = null;
result = func.apply(context, args);
if (!timeout)
context = args = null;
};
return function () {
var now = gnow();
if (!previous && options.leading === false)
previous = now;
var remaining = milliseconds - (now - previous);
context = this;
args = arguments;
if (remaining <= 0 || remaining > milliseconds) {
if (timeout) {
clearTimeout(timeout);
timeout = null;
}
previous = now;
result = func.apply(context, args);
if (!timeout)
context = args = null;
}
else if (!timeout && options.trailing !== false) {
timeout = setTimeout(later, remaining);
}
return result;
};
}
exports.throttle = throttle;
;
function once(func) {
var ran = false;
var memo = undefined;
return function () {
if (ran)
return memo;
ran = true;
memo = func.apply(this, arguments);
func = null;
return memo;
};
}
exports.once = once;
function rangeLimited(args) {
var num = args.num, min = args.min, max = args.max, loopAround = args.loopAround;
var limited = Math.max(Math.min(num, max), min);
if (loopAround && limited > num) {
return max;
}
if (loopAround && limited < num) {
return min;
}
return limited;
}
exports.rangeLimited = rangeLimited;
/**
* Is TypeSript or is JavaScript file checks
*/
exports.isTs = function (filePath, ext) {
if (ext === void 0) { ext = getExt(filePath); }
return ext == 'ts' || ext == 'tsx';
};
exports.isTsx = function (filePath, ext) {
if (ext === void 0) { ext = getExt(filePath); }
return ext == 'tsx';
};
exports.isJs = function (filePath, ext) {
if (ext === void 0) { ext = getExt(filePath); }
return ext == 'js' || ext == 'jsx';
};
exports.isJsOrTs = function (filePath) {
var ext = getExt(filePath);
return exports.isJs(filePath, ext) || exports.isTs(filePath, ext);
};
/** `/asdf/bar/j.ts` => `ts` */
function getExt(filePath) {
var parts = filePath.split('.');
return parts[parts.length - 1].toLowerCase();
}
exports.getExt = getExt;
/**
* `/asdf/bar/j.ts` => `/asdf/bar/j`
* `/asdf/bar/j.d.ts` => `/asdf/bar/j.d`
*/
function removeExt(filePath) {
var dot = filePath.lastIndexOf('.');
if (dot === -1) {
return filePath;
}
return filePath.substring(0, dot);
}
exports.removeExt = removeExt;
/**
* asdf/asdf:123 => asdf/asdf + 122
* Note: returned line is 0 based
*/
function getFilePathLine(query) {
var _a = query.split(':'), filePath = _a[0], lineNum = _a[1];
var line = lineNum ? parseInt(lineNum) - 1 : 0;
line = line > 0 ? line : 0;
return { filePath: filePath, line: line };
}
exports.getFilePathLine = getFilePathLine;
/** `/asdf/bar/j.ts` => `j.ts` */
function getFileName(fullFilePath) {
var parts = fullFilePath.split('/');
return parts[parts.length - 1];
}
exports.getFileName = getFileName;
/** `/asdf/bar/j.ts` => `/asdf/bar` */
function getDirectory(filePath) {
var directory = filePath.substring(0, filePath.lastIndexOf("/"));
return directory;
}
exports.getDirectory = getDirectory;
/** Folder + filename only e.g. `/asdf/something/tsconfig.json` => `something/tsconfig.json` */
function getDirectoryAndFileName(filePath) {
var directoryPath = getDirectory(filePath);
var directoryName = getFileName(directoryPath);
var fileName = getFileName(filePath);
return directoryName + "/" + fileName;
}
exports.getDirectoryAndFileName = getDirectoryAndFileName;
/**
* shallow equality of sorted arrays
*/
function arraysEqual(a, b) {
if (a === b)
return true;
if (a == null || b == null)
return false;
if (a.length !== b.length)
return false;
for (var i = 0; i < a.length; ++i) {
if (a[i] !== b[i])
return false;
}
return true;
}
exports.arraysEqual = arraysEqual;
/** Creates a Guid (UUID v4) */
function createId() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
exports.createId = createId;
/** Creates a Guid (UUID v4) */
exports.createGuid = createId;
// Not optimized
function selectMany(arr) {
var result = [];
for (var i = 0; i < arr.length; i++) {
for (var j = 0; j < arr[i].length; j++) {
result.push(arr[i][j]);
}
}
return result;
}
exports.selectMany = selectMany;
/** From `file://filePath` to `filePath` */
function getFilePathFromUrl(url) {
var filePath = getFilePathAndProtocolFromUrl(url).filePath;
return filePath;
}
exports.getFilePathFromUrl = getFilePathFromUrl;
/** We consistently have tabs with protocol + filePath */
function getFilePathAndProtocolFromUrl(url) {
// TODO: error handling
var protocol = url.substr(0, url.indexOf('://'));
var filePath = url.substr((protocol + '://').length);
return { protocol: protocol, filePath: filePath };
}
exports.getFilePathAndProtocolFromUrl = getFilePathAndProtocolFromUrl;
function getUrlFromFilePathAndProtocol(config) {
return config.protocol + '://' + config.filePath;
}
exports.getUrlFromFilePathAndProtocol = getUrlFromFilePathAndProtocol;
/**
* Promise.resolve is something I call the time (allows you to take x|promise and return promise ... aka make sync prog async if needed)
*/
exports.resolve = Promise.resolve.bind(Promise);
/** Useful for various editor related stuff e.g. completions */
var punctuations = createMap([';', '{', '}', '(', ')', '.', ':', '<', '>', "'", '"']);
/** Does the prefix end in punctuation */
exports.prefixEndsInPunctuation = function (prefix) { return prefix.length && prefix.trim().length && punctuations[prefix.trim()[prefix.trim().length - 1]]; };
/** String based enum pattern */
function stringEnum(x) {
// make values same as keys
Object.keys(x).map(function (key) { return x[key] = key; });
}
exports.stringEnum = stringEnum;
/**
* Just adds your intercept function to be called whenever the original function is called
* Calls your function *before* the original is called
*/
function intercepted(config) {
return function () {
config.intercept.apply(null, arguments);
return config.orig.apply(config.context, arguments);
};
}
exports.intercepted = intercepted;
/**
* path.relative for browser
* from : https://github.com/substack/path-browserify/blob/master/index.js
* but modified to not depened on `path.resolve` as from and to are already resolved in our case
*/
exports.relative = function (from, to) {
function trim(arr) {
var start = 0;
for (; start < arr.length; start++) {
if (arr[start] !== '')
break;
}
var end = arr.length - 1;
for (; end >= 0; end--) {
if (arr[end] !== '')
break;
}
if (start > end)
return [];
return arr.slice(start, end - start + 1);
}
var fromParts = trim(from.split('/'));
var toParts = trim(to.split('/'));
var length = Math.min(fromParts.length, toParts.length);
var samePartsLength = length;
for (var i = 0; i < length; i++) {
if (fromParts[i] !== toParts[i]) {
samePartsLength = i;
break;
}
}
var outputParts = [];
for (var i = samePartsLength; i < fromParts.length; i++) {
outputParts.push('..');
}
outputParts = outputParts.concat(toParts.slice(samePartsLength));
return outputParts.join('/');
};
exports.imageUrl = '/images';
var supportedImages = {
'svg': 'image/svg+xml',
'gif': 'image/gif',
'png': 'image/png',
'jpg': 'image/jpeg',
'jpeg': 'image/jpeg',
'bmp': 'image/bmp'
};
var imageExtensions = Object.keys(supportedImages);
/**
* Provides info on the image files we support
*/
function isImage(url) {
return imageExtensions.some(function (ext) { return url.endsWith("." + ext); });
}
exports.isImage = isImage;
function getImageMimeType(filePath) {
var ext = getExt(filePath);
return supportedImages[ext];
}
exports.getImageMimeType = getImageMimeType;
/**
* Great for find and replace
*/
function findOptionsToQueryRegex(options) {
// Note that Code mirror only takes `query` string *tries* to detect case senstivity, regex on its own
// So simpler if we just convert options into regex, and then code mirror will happy use the regex as is
var str = options.query;
var query;
/** This came from search.js in code mirror */
var defaultQuery = /x^/;
if (!options.isRegex) {
// from CMs search.js
str = str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
}
if (options.isFullWord) {
str = "\\b" + str + "\\b";
}
try {
query = new RegExp(str, options.isCaseSensitive ? "g" : "gi");
}
catch (e) {
query = defaultQuery;
}
if (query.test("")) {
query = defaultQuery;
}
return query;
}
exports.findOptionsToQueryRegex = findOptionsToQueryRegex;
/**
* Quick and dirty pad left
*/
function padLeft(str, paddingValue) {
var pad = ' ';
return pad.substring(0, paddingValue - str.length) + str;
}
exports.padLeft = padLeft;
function extend() {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
var newObj = {};
for (var _a = 0, args_1 = args; _a < args_1.length; _a++) {
var obj = args_1[_a];
for (var key in obj) {
//copy all the fields
newObj[key] = obj[key];
}
}
return newObj;
}
exports.extend = extend;
;
/**
* Simple timer
*/
function timer() {
var timeStart = new Date().getTime();
return {
/** <integer>s e.g 2s etc. */
get seconds() {
var seconds = Math.ceil((new Date().getTime() - timeStart) / 1000) + 's';
return seconds;
},
/** Milliseconds e.g. 2000ms etc. */
get ms() {
var ms = (new Date().getTime() - timeStart) + 'ms';
return ms;
}
};
}
exports.timer = timer;
/**
* Returns a nice conversion of milliseconds into seconds / mintues as needed
*/
function formatMilliseconds(ms) {
if (ms < 1000)
return ms + "ms";
var s = ms / 1000;
if (s < 60) {
return s.toFixed(2) + "s";
}
var m = s / 60;
return m.toFixed(2) + "min";
}
exports.formatMilliseconds = formatMilliseconds;
/**
* If you add a new schema make sure you download its schema as well
*/
exports.supportedAutocompleteConfigFileNames = {
'tsconfig.json': true,
'package.json': true,
'tslint.json': true,
'alm.json': true,
};
/**
* Files for which we have autocomplete intelligence
*/
function isSupportedConfigFileForAutocomplete(filePath) {
var fileName = getFileName(filePath);
return !!exports.supportedAutocompleteConfigFileNames[fileName];
}
exports.isSupportedConfigFileForAutocomplete = isSupportedConfigFileForAutocomplete;
exports.supportedHoverConfigFileNames = {
'package.json': true,
};
/**
* Files for which we have hover intelligence
*/
function isSupportedConfigFileForHover(filePath) {
var fileName = getFileName(filePath);
return !!exports.supportedHoverConfigFileNames[fileName];
}
exports.isSupportedConfigFileForHover = isSupportedConfigFileForHover;
exports.cancellationToken = function () {
var cancelled = false;
return {
get isCancelled() {
return cancelled;
},
cancel: function () { return cancelled = true; }
};
};
exports.cancelled = "cancelled";
/**
* Cancellable For Each
*/
function cancellableForEach(config) {
return new Promise(function (resolve, reject) {
var index = 0;
var lookAtNext = function () {
// Completed?
if (index === config.items.length) {
resolve({});
}
else if (config.cancellationToken.isCancelled) {
reject(exports.cancelled);
}
else {
var nextItem = config.items[index++];
config.cb(nextItem);
// Yield the thread for a bit
setTimeout(lookAtNext);
}
};
lookAtNext();
});
}
exports.cancellableForEach = cancellableForEach;
/**
* https://github.com/facebook/react/issues/5465#issuecomment-157888325
*/
var makeCancelable = function (promise) {
var hasCanceled_ = false;
var wrappedPromise = new Promise(function (resolve, reject) {
promise.then(function (val) {
return hasCanceled_ ? reject({ isCanceled: true }) : resolve(val);
});
promise.catch(function (error) {
return hasCanceled_ ? reject({ isCanceled: true }) : reject(error);
});
});
return {
promise: wrappedPromise,
cancel: function () {
hasCanceled_ = true;
},
};
};
/**
* For promise chains that might become invalid fast
*/
function onlyLastCall(call) {
/**
* Kept the delay to 0 as I am using this in select list view
* and that really benifits from immediate feedback.
*/
var delay = 0;
var timeout;
var _resolve;
var _reject;
var _cancel = function () { return null; };
var later = function () {
timeout = null;
var _a = makeCancelable(call()), promise = _a.promise, cancel = _a.cancel;
_cancel = cancel;
promise
.then(function (res) {
_resolve(res);
})
.catch(function (rej) {
_reject(rej);
});
};
var trueCall = function () { return new Promise(function (resolve, reject) {
if (timeout) {
clearTimeout(timeout);
}
// Cancel any pending
_cancel();
_resolve = resolve;
_reject = reject;
timeout = setTimeout(later, delay);
}); };
return trueCall;
}
exports.onlyLastCall = onlyLastCall;
/**
* Delay given ms
*/
function delay(ms) {
return new Promise(function (res) { return setTimeout(res, ms); });
}
exports.delay = delay;