Modules (180)

FindUtils

Description

Dependencies

Functions

Private

_doReplaceInDocument

Does a set of replacements in a single document in memory.

doc non-nullable Document
The document to do the replacements in.
matchInfo Object
The match info for this file, as returned by `_addSearchMatches()`. Might be mutated.
replaceText string
The text to replace each result with.
isRegexp optional boolean
Whether the original query was a regexp.
Returns: $.Promise
A promise that's resolved when the replacement is finished or rejected with an error if there were one or more errors.
    function _doReplaceInDocument(doc, matchInfo, replaceText, isRegexp) {
        // Double-check that the open document's timestamp matches the one we recorded. This
        // should normally never go out of sync, because if it did we wouldn't start the
        // replace in the first place (due to the fact that we immediately close the search
        // results panel whenever we detect a filesystem change that affects the results),
        // but we want to double-check in case we don't happen to get the change in time.
        // This will *not* handle cases where the document has been edited in memory since
        // the matchInfo was generated.
        if (doc.diskTimestamp.getTime() !== matchInfo.timestamp.getTime()) {
            return new $.Deferred().reject(exports.ERROR_FILE_CHANGED).promise();
        }

        // Do the replacements in reverse document order so the offsets continue to be correct.
        doc.batchOperation(function () {
            matchInfo.matches.reverse().forEach(function (match) {
                if (match.isChecked) {
                    doc.replaceRange(isRegexp ? parseDollars(replaceText, match.result) : replaceText, match.start, match.end);
                }
            });
        });

        return new $.Deferred().resolve().promise();
    }
Private

_doReplaceInOneFile

Does a set of replacements in a single file. If the file is already open in a Document in memory, will do the replacement there, otherwise does it directly on disk.

fullPath string
The full path to the file.
matchInfo Object
The match info for this file, as returned by `_addSearchMatches()`.
replaceText string
The text to replace each result with.
options optional Object
An options object: forceFilesOpen: boolean - Whether to open the file in an editor and do replacements there rather than doing the replacements on disk. Note that even if this is false, files that are already open in editors will have replacements done in memory. isRegexp: boolean - Whether the original query was a regexp. If true, $-substitution is performed on the replaceText.
Returns: $.Promise
A promise that's resolved when the replacement is finished or rejected with an error if there were one or more errors.
    function _doReplaceInOneFile(fullPath, matchInfo, replaceText, options) {
        var doc = DocumentManager.getOpenDocumentForPath(fullPath);
        options = options || {};
        // If we're forcing files open, or if the document is in the working set but not actually open
        // yet, we want to open the file and do the replacement in memory.
        if (!doc && (options.forceFilesOpen || MainViewManager.findInWorkingSet(MainViewManager.ALL_PANES, fullPath) !== -1)) {
            return DocumentManager.getDocumentForPath(fullPath).then(function (newDoc) {
                return _doReplaceInDocument(newDoc, matchInfo, replaceText, options.isRegexp);
            });
        } else if (doc) {
            return _doReplaceInDocument(doc, matchInfo, replaceText, options.isRegexp);
        } else {
            return _doReplaceOnDisk(fullPath, matchInfo, replaceText, options.isRegexp);
        }
    }
Private

_doReplaceOnDisk

Does a set of replacements in a single file on disk.

fullPath string
The full path to the file.
matchInfo Object
The match info for this file, as returned by `_addSearchMatches()`.
replaceText string
The text to replace each result with.
isRegexp optional boolean
Whether the original query was a regexp.
Returns: $.Promise
A promise that's resolved when the replacement is finished or rejected with an error if there were one or more errors.
    function _doReplaceOnDisk(fullPath, matchInfo, replaceText, isRegexp) {
        var file = FileSystem.getFileForPath(fullPath);
        return DocumentManager.getDocumentText(file, true).then(function (contents, timestamp, lineEndings) {
            if (timestamp.getTime() !== matchInfo.timestamp.getTime()) {
                // Return a promise that we'll reject immediately. (We can't just return the
                // error since this is the success handler.)
                return new $.Deferred().reject(exports.ERROR_FILE_CHANGED).promise();
            }

            // Note that this assumes that the matches are sorted.
            // TODO: is there a more efficient way to do this in a large string?
            var result = [],
                lastIndex = 0;
            matchInfo.matches.forEach(function (match) {
                if (match.isChecked) {
                    result.push(contents.slice(lastIndex, match.startOffset));
                    result.push(isRegexp ? parseDollars(replaceText, match.result) : replaceText);
                    lastIndex = match.endOffset;
                }
            });
            result.push(contents.slice(lastIndex));

            var newContents = result.join("");
            // TODO: duplicated logic from Document - should refactor this?
            if (lineEndings === FileUtils.LINE_ENDINGS_CRLF) {
                newContents = newContents.replace(/\n/g, "\r\n");
            }

            return Async.promisify(file, "write", newContents);
        });
    }
Private

_prefInstantSearchDisabled

returns true if the used instant search in his preferences

Returns: boolean
    function _prefInstantSearchDisabled() {
        return !PreferencesManager.get("findInFiles.instantSearch");
    }
Private

_prefNodeSearchDisabled

returns true if the used disabled node based search in his preferences

Returns: boolean
    function _prefNodeSearchDisabled() {
        return !PreferencesManager.get("findInFiles.nodeSearch");
    }
Public API

getHealthReport

Returns the health data pertaining to Find in files

    function getHealthReport() {
        return {
            prefNodeSearchDisabled : _prefNodeSearchDisabled(),
            prefInstantSearchDisabled : _prefInstantSearchDisabled()
        };
    }

    exports.parseDollars                    = parseDollars;
    exports.hasCheckedMatches               = hasCheckedMatches;
    exports.performReplacements             = performReplacements;
    exports.labelForScope                   = labelForScope;
    exports.parseQueryInfo                  = parseQueryInfo;
    exports.prioritizeOpenFile              = prioritizeOpenFile;
    exports.getOpenFilePath                 = getOpenFilePath;
    exports.setNodeSearchDisabled           = setNodeSearchDisabled;
    exports.isNodeSearchDisabled            = isNodeSearchDisabled;
    exports.setInstantSearchDisabled        = setInstantSearchDisabled;
    exports.isInstantSearchDisabled         = isInstantSearchDisabled;
    exports.isNodeSearchInProgress          = isNodeSearchInProgress;
    exports.isIndexingInProgress            = isIndexingInProgress;
    exports.setCollapseResults              = setCollapseResults;
    exports.isCollapsedResults              = isCollapsedResults;
    exports.getHealthReport                 = getHealthReport;
    exports.ERROR_FILE_CHANGED              = "fileChanged";

    // event notification functions
    exports.notifyFileFiltersChanged        = notifyFileFiltersChanged;
    exports.notifySearchScopeChanged        = notifySearchScopeChanged;
    exports.notifyNodeSearchStarted         = notifyNodeSearchStarted;
    exports.notifyNodeSearchFinished        = notifyNodeSearchFinished;
    exports.notifyIndexingStarted           = notifyIndexingStarted;
    exports.notifyIndexingFinished          = notifyIndexingFinished;

    // events raised by FindUtils
    exports.SEARCH_FILE_FILTERS_CHANGED              = "fileFiltersChanged";
    exports.SEARCH_SCOPE_CHANGED                     = "searchScopeChanged";
    exports.SEARCH_INDEXING_STARTED                  = "searchIndexingStarted";
    exports.SEARCH_INDEXING_FINISHED                 = "searchIndexingFinished";
    exports.SEARCH_COLLAPSE_RESULTS                  = "searchCollapseResults";
});
Public API

getOpenFilePath

Returns the path of the currently open file or null if there isn't one open

Returns: ?string
    function getOpenFilePath() {
        var currentDoc = DocumentManager.getCurrentDocument();
        return currentDoc ? currentDoc.file.fullPath : null;
    }
Private Public API

hasCheckedMatches

    function hasCheckedMatches(result) {
        return result.matches.some(function (match) { return match.isChecked; });
    }
Public API

isCollapsedResults

check if results should be collapsed

Returns: boolean
true if results should be collapsed
    function isCollapsedResults() {
        return collapseResults;
    }
Public API

isIndexingInProgress

Return true if indexing is in pregress in node

Returns: boolean
true if files are being indexed in node
    function isIndexingInProgress() {
        return indexingInProgress;
    }
Public API

isInstantSearchDisabled

if instant search is disabled, this will return true we can only do instant search through node

Returns: boolean
    function isInstantSearchDisabled() {
        return _prefNodeSearchDisabled() || _prefInstantSearchDisabled() || nodeSearchDisabled || instantSearchDisabled;
    }
Public API

isNodeSearchDisabled

if node search is disabled, this will return true

Returns: boolean
    function isNodeSearchDisabled() {
        return _prefNodeSearchDisabled() || nodeSearchDisabled;
    }
Public API

isNodeSearchInProgress

check if a search is progressing in node

Returns: Boolean
true if search is processing in node
    function isNodeSearchInProgress() {
        if (nodeSearchCount === 0) {
            return false;
        } else {
            return true;
        }
    }
Public API

labelForScope

Returns label text to indicate the search scope. Already HTML-escaped.

scope nullable Entry
Returns: string
    function labelForScope(scope) {
        if (scope) {
            return StringUtils.format(
                Strings.FIND_IN_FILES_SCOPED,
                StringUtils.breakableUrl(
                    ProjectManager.makeProjectRelativeIfPossible(scope.fullPath)
                )
            );
        } else {
            return Strings.FIND_IN_FILES_NO_SCOPE;
        }
    }
Public API

notifyFileFiltersChanged

Raises an event when the file filters applied to a search changes

    function notifyFileFiltersChanged() {
        exports.trigger(exports.SEARCH_FILE_FILTERS_CHANGED);
    }
Public API

notifyIndexingFinished

Notifies that a node has finished indexing the files

    function notifyIndexingFinished() {
        indexingInProgress = false;
        exports.trigger(exports.SEARCH_INDEXING_FINISHED);
    }
Public API

notifyIndexingStarted

Notifies that a node has started indexing the files

    function notifyIndexingStarted() {
        indexingInProgress = true;
        exports.trigger(exports.SEARCH_INDEXING_STARTED);
    }
Public API

notifyNodeSearchFinished

Notifies that a node search has finished so that we FindUtils can figure out if any outstanding node search requests are pendind

    function notifyNodeSearchFinished() {
        nodeSearchCount--;
    }
Public API

notifyNodeSearchStarted

Notifies that a node search has started so that we FindUtils can figure out if any outstanding node search requests are pendind

    function notifyNodeSearchStarted() {
        nodeSearchCount++;
    }
Public API

notifySearchScopeChanged

Raises an event when the search scope changes[say search in a sub drictory in the project]

    function notifySearchScopeChanged() {
        exports.trigger(exports.SEARCH_SCOPE_CHANGED);
    }
Public API

parseDollars

Given a replace string that contains $-expressions, replace them with data from the given regexp match info. NOTE: we can't just use the ordinary replace() function here because the string has been extracted from the original text and so might be missing some context that the regexp matched.

replaceWith string
The string containing the $-expressions.
match Object
The match data from the regexp.
Returns: string
The replace text with the $-expressions substituted.
    function parseDollars(replaceWith, match) {
        replaceWith = replaceWith.replace(/(\$+)(\d{1,2}|&)/g, function (whole, dollars, index) {
            if (dollars.length % 2 === 1) { // make sure dollar signs don't escape themselves (like $$1, $$$$&)
                if (index === "&") { // handle $&
                    // slice the first dollar (but leave any others to get unescaped below) and return the
                    // whole match
                    return dollars.substr(1) + (match[0] || "");
                } else {
                    // now we're sure index is an integer, so we can parse it
                    var parsedIndex = parseInt(index, 10);
                    if (parsedIndex !== 0) { // handle $n or $nn, but don't handle $0 or $00
                        // slice the first dollar (but leave any others to get unescaped below) and return the
                        // the corresponding match
                        return dollars.substr(1) + (match[parsedIndex] || "");
                    }
                }
            }
            // this code gets called if the dollar signs escape themselves or if $0/$00 (not handled) was present
            return whole; // return everything to get handled below
        });
        // replace escaped dollar signs (i.e. $$, $$$$, ...) with single ones (unescaping)
        replaceWith = replaceWith.replace(/\$\$/g, "$");
        return replaceWith;
    }
Public API

parseQueryInfo

Parses the given query into a regexp, and returns whether it was valid or not.

queryInfo {query: string,caseSensitive: boolean,isRegexp: boolean}
Returns: {queryExpr: RegExp,valid: boolean,empty: boolean,error: string}
queryExpr - the regexp representing the query valid - set to true if query is a nonempty string or a valid regexp. empty - set to true if query was empty. error - set to an error string if valid is false and query is nonempty.
    function parseQueryInfo(queryInfo) {
        var queryExpr;

        if (!queryInfo || !queryInfo.query) {
            return {empty: true};
        }

        // For now, treat all matches as multiline (i.e. ^/$ match on every line, not the whole
        // document). This is consistent with how single-file find works. Eventually we should add
        // an option for this.
        var flags = "gm";
        if (!queryInfo.isCaseSensitive) {
            flags += "i";
        }

        // Is it a (non-blank) regex?
        if (queryInfo.isRegexp) {
            try {
                queryExpr = new RegExp(queryInfo.query, flags);
            } catch (e) {
                return {valid: false, error: e.message};
            }
        } else {
            // Query is a plain string. Turn it into a regexp
            queryExpr = new RegExp(StringUtils.regexEscape(queryInfo.query), flags);
        }
        return {valid: true, queryExpr: queryExpr};
    }
Public API

performReplacements

Given a set of search results, replaces them with the given replaceText, either on disk or in memory. Checks timestamps to ensure replacements are not performed in files that have changed on disk since the original search results were generated. However, does not check whether edits have been performed in in-memory documents since the search; it's up to the caller to guarantee this hasn't happened. (When called from the standard Find in Files UI, SearchResultsView guarantees this. If called headlessly, the caller needs to track changes.)

Replacements in documents that are already open in memory at the start of the replacement are guaranteed to happen synchronously; replacements in files on disk will return an error if the on-disk file changes between the time performReplacements() is called and the time the replacement actually happens.

results Object.<fullPath: string,{matches: Array.<{start: {line:number,ch:number},end: {line:number,ch:number},startOffset: number,endOffset: number,line: string}>,collapsed: boolean}>
The list of results to replace, as returned from _doSearch..
replaceText string
The text to replace each result with.
options nullable Object
An options object: forceFilesOpen: boolean - Whether to open all files in editors and do replacements there rather than doing the replacements on disk. Note that even if this is false, files that are already open in editors will have replacements done in memory. isRegexp: boolean - Whether the original query was a regexp. If true, $-substitution is performed on the replaceText.
Returns: $.Promise
A promise that's resolved when the replacement is finished or rejected with an array of errors if there were one or more errors. Each individual item in the array will be a {item: string, error: string} object, where item is the full path to the file that could not be updated, and error is either a FileSystem error or one of the `FindUtils.ERROR_*` constants.
    function performReplacements(results, replaceText, options) {
        return Async.doInParallel_aggregateErrors(Object.keys(results), function (fullPath) {
            return _doReplaceInOneFile(fullPath, results[fullPath], replaceText, options);
        }).done(function () {
            if (options && options.forceFilesOpen) {
                // If the currently selected document wasn't modified by the search, or there is no open document,
                // then open the first modified document.
                var doc = DocumentManager.getCurrentDocument();
                if (!doc ||
                        !results[doc.file.fullPath] ||
                        !hasCheckedMatches(results[doc.file.fullPath])) {
                    // Figure out the first modified document. This logic is slightly different from
                    // SearchResultsView._getSortedFiles() because it doesn't sort the currently open file to
                    // the top. But if the currently open file were in the search results, we wouldn't be
                    // doing this anyway.
                    var sortedPaths = Object.keys(results).sort(FileUtils.comparePaths),
                        firstPath = _.find(sortedPaths, function (path) {
                            return hasCheckedMatches(results[path]);
                        });

                    if (firstPath) {
                        var newDoc = DocumentManager.getOpenDocumentForPath(firstPath);
                        // newDoc might be null if the replacement failed.
                        if (newDoc) {
                            // @todo change the `_edit` call to this:
                            //
                            ///    CommandManager.execute(Commands.FILE_OPEN, {fullPath: firstPath});
                            //
                            // The problem with doing that is that the promise returned by this
                            // function has already been resolved by `Async.doInParallel()` and
                            // `CommandManager.execute` is an asynchronous operation.
                            // An asynchronous open can't be waited on (since the promise has been
                            //  resolved already) so use the synchronous version so that the next `done`
                            //  handler is blocked until the open completes
                            MainViewManager._edit(MainViewManager.ACTIVE_PANE, newDoc);
                        }
                    }
                }
            }
        });
    }
Public API

prioritizeOpenFile

Prioritizes the open file and then the working set files to the starting of the list of files

files Array.<*>
An array of file paths or file objects to sort
firstFile nullable string
If specified, the path to the file that should be sorted to the top.
Returns: Array.<*>
    function prioritizeOpenFile(files, firstFile) {
        var workingSetFiles = MainViewManager.getWorkingSet(MainViewManager.ALL_PANES),
            workingSetFileFound = {},
            fileSetWithoutWorkingSet = [],
            startingWorkingFileSet = [],
            propertyName = "",
            i = 0;
        firstFile = firstFile || "";

        // Create a working set path map which indicates if a file in working set is found in file list
        for (i = 0; i < workingSetFiles.length; i++) {
            workingSetFileFound[workingSetFiles[i].fullPath] = false;
        }

        // Remove all the working set files from the filtration list
        fileSetWithoutWorkingSet = files.filter(function (key) {
            if (workingSetFileFound[key] !== undefined) {
                workingSetFileFound[key] = true;
                return false;
            }
            return true;
        });

        //push in the first file
        if (workingSetFileFound[firstFile] === true) {
            startingWorkingFileSet.push(firstFile);
            workingSetFileFound[firstFile] = false;
        }
        //push in the rest of working set files already present in file list
        for (propertyName in workingSetFileFound) {
            if (workingSetFileFound.hasOwnProperty(propertyName) && workingSetFileFound[propertyName]) {
                startingWorkingFileSet.push(propertyName);
            }
        }
        return startingWorkingFileSet.concat(fileSetWithoutWorkingSet);
    }
Public API

setCollapseResults

Set if we need to collapse all results in the results pane

collapse boolean
true to collapse
    function setCollapseResults(collapse) {
        collapseResults = collapse;
        exports.trigger(exports.SEARCH_COLLAPSE_RESULTS);
    }
Public API

setInstantSearchDisabled

enable/disable instant search

disable boolean
true to disable node based search
    function setInstantSearchDisabled(disable) {
        instantSearchDisabled = disable;
    }
Public API

setNodeSearchDisabled

enable/disable node based search

disable boolean
true to disable node based search
    function setNodeSearchDisabled(disable) {
        if (disable) {
            // only set disable. Enabling node earch doesnt mean we have to enable instant search.
            setInstantSearchDisabled(disable);
        }
        nodeSearchDisabled = disable;
    }