UNPKG

alm

Version:

The best IDE for TypeScript

181 lines (180 loc) 6.43 kB
"use strict"; /** * 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;