function _updateResults(doc, changeList) {
        var i, diff, matches, lines, start, howMany,
            resultsChanged = false,
            fullPath       = doc.file.fullPath,
            resultInfo     = searchModel.results[fullPath];

        // Remove the results before we make any changes, so the SearchModel can accurately update its count.
        searchModel.removeResults(fullPath);

        changeList.forEach(function (change) {
            lines = [];
            start = 0;
            howMany = 0;

            // There is no from or to positions, so the entire file changed, we must search all over again
            if (!change.from || !change.to) {
                // TODO: add unit test exercising timestamp logic in this case
                // We don't just call _updateSearchMatches() here because we want to continue iterating through changes in
                // the list and update at the end.
                resultInfo = {matches: _getSearchMatches(doc.getText(), searchModel.queryExpr), timestamp: doc.diskTimestamp};
                resultsChanged = true;

            } else {
                // Get only the lines that changed
                for (i = 0; i < change.text.length; i++) {
                    lines.push(doc.getLine(change.from.line + i));
                }

                // We need to know how many newlines were inserted/deleted in order to update the rest of the line indices;
                // this is the total number of newlines inserted (which is the length of the lines array minus
                // 1, since the last line in the array is inserted without a newline after it) minus the
                // number of original newlines being removed.
                diff = lines.length - 1 - (change.to.line - change.from.line);

                if (resultInfo) {
                    // Search the last match before a replacement, the amount of matches deleted and update
                    // the lines values for all the matches after the change
                    resultInfo.matches.forEach(function (item) {
                        if (item.end.line < change.from.line) {
                            start++;
                        } else if (item.end.line <= change.to.line) {
                            howMany++;
                        } else {
                            item.start.line += diff;
                            item.end.line   += diff;
                        }
                    });

                    // Delete the lines that where deleted or replaced
                    if (howMany > 0) {
                        resultInfo.matches.splice(start, howMany);
                    }
                    resultsChanged = true;
                }

                // Searches only over the lines that changed
                matches = _getSearchMatches(lines.join("\r\n"), searchModel.queryExpr);
                if (matches.length) {
                    // Updates the line numbers, since we only searched part of the file
                    matches.forEach(function (value, key) {
                        matches[key].start.line += change.from.line;
                        matches[key].end.line   += change.from.line;
                    });

                    // If the file index exists, add the new matches to the file at the start index found before
                    if (resultInfo) {
                        Array.prototype.splice.apply(resultInfo.matches, [start, 0].concat(matches));
                    // If not, add the matches to a new file index
                    } else {
                        // TODO: add unit test exercising timestamp logic in self case
                        resultInfo = {
                            matches:   matches,
                            collapsed: false,
                            timestamp: doc.diskTimestamp
                        };
                    }
                    resultsChanged = true;
                }
            }
        });

        // Always re-add the results, even if nothing changed.
        if (resultInfo && resultInfo.matches.length) {
            searchModel.setResults(fullPath, resultInfo);
        }

        if (resultsChanged) {
            // Pass `true` for quickChange here. This will make listeners debounce the change event,
            // avoiding lots of updates if the user types quickly.
            searchModel.fireChanged(true);
        }
    }
@private
Update the search results using the given list of changes for the given document
@param {Document} doc  The Document that changed, should be the current one
@param {Array.<{from: {line:number,ch:number}, to: {line:number,ch:number}, text: !Array.<string>}>} changeList
     An array of changes as described in the Document constructor