alm
Version:
The best IDE for TypeScript
181 lines (180 loc) • 6.43 kB
JavaScript
;
/**
* Find and replace multi. The single file version is client only.
*/
Object.defineProperty(exports, "__esModule", { value: true });
var wd = require("../../disk/workingDir");
var cp = require("child_process");
var utils = require("../../../common/utils");
var events_1 = require("../../../common/events");
var types = require("../../../common/types");
/**
* Maintains current farm state
*/
var FarmState = /** @class */ (function () {
function FarmState(config) {
var _this = this;
this.config = config;
/**
* Key search state variables
*/
this.results = [];
this.completed = false;
/** Exposed event */
this.resultsUpdated = new events_1.TypedEvent();
this.notifyUpdate = utils.throttle(function () {
var _a = _this, results = _a.results, completed = _a.completed, config = _a.config;
_this.resultsUpdated.emit({ results: results, completed: completed, config: config });
}, 500);
/** Allows us to dispose any running search */
this.disposed = false;
this.dispose = function () {
_this.disposed = true;
_this.completed = true;
_this.notifyUpdate();
};
var searchTerm = config.query;
/**
* https://git-scm.com/docs/git-grep
*
* We don't do `--full-name` as that is relative to `.git` parent.
* Without that it is relative to cwd which is better for us.
*
* // General main ones
* n: line number
* I: don't match binary files
*
* // Useful toggles
* w: Match the pattern only at word boundary (also takes into account new lines 💟)
* i: ignore case
*
* E: extended regexp
* F: Don't interpret pattern as regexp
*/
var grep = cp.spawn("git", [
"--no-pager",
"grep",
"-In"
+ (config.isRegex ? 'E' : 'F')
+ (config.isFullWord ? 'w' : '')
+ (config.isCaseSensitive ? '' : 'i'),
searchTerm,
"--" // signals pathspec
].concat(config.globs));
grep.stdout.on('data', function (data) {
// console.log(`Grep stdout: ${data}`);
/** If no one cares anymore */
if (farmState.disposed) {
grep.kill();
return;
}
/**
* Don't want to run out of memory
*/
if (_this.results.length > types.maxCountFindAndReplaceMultiResults) {
grep.kill();
return;
}
// Sample :
// src/typings/express/express.d.ts:907: app.enable('foo')
// src/typings/express/express.d.ts:908: app.disabled('foo')
// String
data = data.toString();
// Split by \n and trim each to get lines
var lines = data.split('\n')
.map(function (x) { return x.trim(); })
.filter(function (x) { return x; });
var newResults = lines.map(function (line) {
var originalLine = line;
// Split line by `:\d:` to get relativeName as first
var relativeFilePath = line.split(/:\d+:/)[0];
line = line.substr(relativeFilePath.length);
// :123: some preview
// =>
// 123: some preview
line = line.substr(1);
// line number!
var lineNumber = line.split(':')[0];
line = line.substr(lineNumber.length);
// : some preview
// =>
// some preview
var preview = line.split(':').slice(1).join(':')
.trim();
var result = {
filePath: wd.makeAbsolute(relativeFilePath),
line: +lineNumber,
preview: preview
};
/* Debug
console.log(originalLine);
console.log('\n')
console.log(result);
console.log('------------------------------------------\n')
/* */
if (!result.preview) {
// TODO: this is probably because the line boundries are wrong as data comes in.
// We should store the line remainder and prepend to new data.
return null;
}
return result;
}).filter(function (x) { return !!x; });
// Add to results
_this.results = _this.results.concat(newResults);
_this.notifyUpdate();
});
grep.stderr.on('data', function (data) {
if (farmState.disposed)
return;
console.log("Grep stderr: " + data);
});
grep.on('close', function (code) {
if (farmState.disposed)
return;
_this.completed = true;
_this.notifyUpdate();
if (!code) {
// TODO: Search complete!
}
if (code) {
// Also happens if search returned no results
// console.error(`Grep process exited with code ${code}`);
}
});
}
return FarmState;
}());
/**
* The current farm state
*/
var farmState = null;
/**
*
*
* The exposed service API
*
*
*/
/**
* Subscribe to this if you want notifications about any current farming
*/
exports.farmResultsUpdated = new events_1.TypedEvent();
// initiate as completed with no results
exports.farmResultsUpdated.emit({ completed: true, results: [], config: null });
/** Also safely stops any previous running farming */
function startFarming(cfg) {
stopFarmingIfRunning({});
farmState = new FarmState(cfg);
exports.farmResultsUpdated.emit({ completed: false, results: [], config: cfg });
farmState.resultsUpdated.pipe(exports.farmResultsUpdated);
return Promise.resolve({});
}
exports.startFarming = startFarming;
function stopFarmingIfRunning(args) {
if (farmState) {
farmState.dispose();
farmState = null;
}
return Promise.resolve({});
}
exports.stopFarmingIfRunning = stopFarmingIfRunning;