Skip to content

Commit

Permalink
Fixed search through Drawer panel.
Browse files Browse the repository at this point in the history
Implemented DebuggerAgent.searchInContent.
Implemented PageAgent.searchInResource.
Added /lib/search.js to provide shared search functions.
Added resource/script source caching.
Fixed highlighting matches bug on minified-file lines.
  • Loading branch information
Marco Minetti committed May 19, 2015
1 parent 642e801 commit 74036dd
Show file tree
Hide file tree
Showing 6 changed files with 340 additions and 1 deletion.
1 change: 1 addition & 0 deletions front-end-node/inspector.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
{ "name": "node/settings", "type": "autostart" },
{ "name": "node/sources", "type": "autostart" },
{ "name": "node/console", "type": "autostart" },
{ "name": "node/search", "type": "autostart" },
{ "name": "node/main", "type": "autostart" },
{ "name": "platform", "type": "autostart" },
{ "name": "main", "type": "autostart" },
Expand Down
121 changes: 121 additions & 0 deletions front-end-node/search/SearchOverrides.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
/*jshint browser:true, nonew:false*/
/*global WebInspector:true, InspectorFrontendHost:true, InspectorFrontendHostAPI:true*/

(function() {
var createSearchRegex = function(query, caseSensitive, isRegex)
{
var regexFlags = caseSensitive ? "g" : "gi";
var regexObject;

if (isRegex) {
try {
regexObject = new RegExp('^.*?'+query+'.*?$|^.*?'+query+'.*?\n|\n.*?'+query+'.*?\n|\n.*?'+query+'.*?$', regexFlags);
} catch (e) {
// Silent catch.
}
}

if (!regexObject)
regexObject = createPlainTextSearchRegex(query, regexFlags);

return regexObject;
}

var createPlainTextSearchRegex = function(query, flags)
{
// This should be kept the same as the one in ContentSearchUtils.cpp.
var regexSpecialCharacters = "^[]{}()\\.^$*+?|-,";
var regex = "";
for (var i = 0; i < query.length; ++i) {
var c = query.charAt(i);
if (regexSpecialCharacters.indexOf(c) != -1)
regex += "\\";
regex += c;
}
return new RegExp('^.*?'+regex+'.*?$|^.*?'+regex+'.*?\n|\n.*?'+regex+'.*?\n|\n.*?'+regex+'.*?$', flags || "");
}



WebInspector.ContentProvider.performSearchInContent = function(content, query, caseSensitive, isRegex)
{
var regex = createSearchRegex(query, caseSensitive, isRegex);

var result = [];
var lastMatch;
var isMinified = false;

var firstNewLine = content.indexOf('\n');
if (content.length > 1024) {
if (firstNewLine > 1024 || firstNewLine === -1) {
isMinified = true;
}
}

while(lastMatch=regex.exec(content)) {
var lineContent = lastMatch[0];
var firstChar = lineContent.charCodeAt(0);
var lastChar = lineContent.charCodeAt(lineContent.length-1);
var lineMatchesBefore = content.substr(0,regex.lastIndex).match(/\n/g);
if (lineMatchesBefore){
var i = lineMatchesBefore.length;
if (lastChar !== 10){
++i;
} else {
lineContent = lineContent.substr(0,lineContent.length-1);
}
if (firstChar === 10){
lineContent = lineContent.substr(1);
}
if (isMinified === true && lineContent.length > 1024) {
lineContent = ' ... (line too long)';
}
result.push(new WebInspector.ContentProvider.SearchMatch(i, lineContent));
}
}
return result;
}
})()

WebInspector.FileBasedSearchResultsPane.FileTreeElement.prototype._appendSearchMatches = function(fromIndex, toIndex)
{
var searchResult = this._searchResult;
var uiSourceCode = searchResult.uiSourceCode;
var searchMatches = searchResult.searchMatches;

var queries = this._searchConfig.queries();
var regexes = [];
for (var i = 0; i < queries.length; ++i)
regexes.push(createSearchRegex(queries[i], !this._searchConfig.ignoreCase(), this._searchConfig.isRegex()));

for (var i = fromIndex; i < toIndex; ++i) {
var lineNumber = searchMatches[i].lineNumber;
var lineContent = searchMatches[i].lineContent;
var matchRanges = [];
for (var j = 0; j < regexes.length; ++j)
matchRanges = matchRanges.concat(this._regexMatchRanges(lineContent, regexes[j]));

var anchor;
if (!matchRanges[0]){
matchRanges[0] = new WebInspector.SourceRange(0,0);
anchor = this._createAnchor(uiSourceCode, lineNumber, matchRanges[0].offset);
} else {
anchor = this._createAnchor(uiSourceCode, lineNumber, matchRanges[0].offset);
}

var numberString = numberToStringWithSpacesPadding(lineNumber + 1, 4);
var lineNumberSpan = createElement("span");
lineNumberSpan.classList.add("search-match-line-number");
lineNumberSpan.textContent = numberString;
anchor.appendChild(lineNumberSpan);

var contentSpan = this._createContentSpan(lineContent, matchRanges);
anchor.appendChild(contentSpan);

var searchMatchElement = new TreeElement("");
searchMatchElement.selectable = false;
this.appendChild(searchMatchElement);
searchMatchElement.listItemElement.className = "search-match source-code";
searchMatchElement.listItemElement.appendChild(anchor);
}
}
9 changes: 9 additions & 0 deletions front-end-node/search/module.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"dependencies": [
"sources",
"console"
],
"scripts": [
"SearchOverrides.js"
]
}
25 changes: 25 additions & 0 deletions lib/DebuggerAgent.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ var convert = require('./convert.js'),
format = require('util').format,
path = require('path'),
async = require('async'),
search = require('./search.js'),
ScriptFileStorage = require('./ScriptFileStorage').ScriptFileStorage;

/**
Expand Down Expand Up @@ -209,6 +210,7 @@ DebuggerAgent.prototype = {
Number(params.scriptId),
function(err, source) {
if (err) return done(err);
self._searchScriptSourceCache[params.scriptId] = source;
return done(null, { scriptSource: source });
}
);
Expand Down Expand Up @@ -493,6 +495,29 @@ DebuggerAgent.prototype = {
done(new Error('Not implemented.'));
else
done();
},

_searchScriptSourceCache: {},

searchInContent: function(params, done) {
var self = this;

function initSearch(content) {
if (content) {
done(null, {result: search.performSearchInContent(content, params.query, params.caseSensitive, params.isRegex)});
} else {
done(null, {result: []});
}
}

if(self._searchScriptSourceCache.hasOwnProperty(params.scriptId)) {
initSearch(self._searchScriptSourceCache[params.scriptId]);
} else {
self.getScriptSource({scriptId: params.scriptId}, function (err, data) {
if (err) return done(null, {result: []});
initSearch(data.scriptSource);
});
}
}
};

Expand Down
26 changes: 25 additions & 1 deletion lib/PageAgent.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ var fs = require('fs'),
EventEmitter = require('events').EventEmitter,
async = require('async'),
convert = require('./convert.js'),
search = require('./search.js'),
ScriptFileStorage = require('./ScriptFileStorage.js').ScriptFileStorage;

// see Blink inspector > ContentSearchUtils.cpp > findMagicComment()
Expand Down Expand Up @@ -168,7 +169,7 @@ extend(PageAgent.prototype, {
'// This is expected when you are debugging node\'s interactive REPL console.';

return process.nextTick(
this._convertScriptSourceToGetResourceResponse.bind(this, content, done));
this._convertScriptSourceToGetResourceResponse.bind(this, null, content, done));
}

async.waterfall(
Expand Down Expand Up @@ -227,6 +228,29 @@ extend(PageAgent.prototype, {
reload: function(params, done) {
// This is called when user press Cmd+R (F5?), do we want to perform an action on this?
done();
},

_searchResourceContentCache: {},

searchInResource: function(params, done){
var self = this;

function initSearch(content){
if (content) {
done(null,{ result: search.performSearchInContent(content, params.query, params.caseSensitive, params.isRegex) });
} else {
done(null,{ result: [] });
}
}

if(self._searchResourceContentCache.hasOwnProperty(params.url)) {
initSearch(self._searchResourceContentCache[params.url]);
} else {
self.getResourceContent({ url: params.url }, function(err,data){
if (err) return done(null,{ result: [] });
initSearch(data.content);
});
}
}
});

Expand Down
159 changes: 159 additions & 0 deletions lib/search.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
// code extracted and adapted from ../front-end/platform/utilities.js
// _performSearchInContent is based on ../front-end/platform/utilities.js implementation
// performSearchInContent uses regexp to gain performance


var findAll = function(content, string)
{
var matches = [];
var i = content.indexOf(string);
while (i !== -1) {
matches.push(i);
i = content.indexOf(string, i + string.length);
}
return matches;
}

var getlineEndings = function(content)
{
var _lineEndings = findAll(content, "\n");
_lineEndings.push(content.length);
return _lineEndings;
}

var getlineAt = function(content, lineEndings, lineNumber) {
var lineStart = lineNumber > 0 ? lineEndings[lineNumber - 1] + 1 : 0;
var lineEnd = lineEndings[lineNumber];
var lineContent = content.substring(lineStart, lineEnd);
if (lineContent.length > 0 && lineContent.charAt(lineContent.length - 1) === "\r")
lineContent = lineContent.substring(0, lineContent.length - 1);
return lineContent;
}

var _createSearchRegex = function(query, caseSensitive, isRegex)
{
var regexFlags = caseSensitive ? "g" : "gi";
var regexObject;

if (isRegex) {
try {
regexObject = new RegExp(query, regexFlags);
} catch (e) {
// Silent catch.
}
}

if (!regexObject)
regexObject = _createPlainTextSearchRegex(query, regexFlags);

return regexObject;
}

var _createPlainTextSearchRegex = function(query, flags)
{
// This should be kept the same as the one in ContentSearchUtils.cpp.
var regexSpecialCharacters = "^[]{}()\\.^$*+?|-,";
var regex = "";
for (var i = 0; i < query.length; ++i) {
var c = query.charAt(i);
if (regexSpecialCharacters.indexOf(c) != -1)
regex += "\\";
regex += c;
}
return new RegExp(regex, flags || "");
}

exports._performSearchInContent = function(content, query, caseSensitive, isRegex)
{
var regex = _createSearchRegex(query, caseSensitive, isRegex);

var result = [];
var lineEndings = getlineEndings(content);
var lineCount = lineEndings.length;
for (var i = 0; i < lineCount; ++i) {
var lineContent = getlineAt(content, i, lineEndings);
regex.lastIndex = 0;
if (regex.exec(lineContent))
result.push(new SearchMatch(i, lineContent));
}
return result;
}

var SearchMatch = function(lineNumber, lineContent) {
this.lineNumber = lineNumber;
this.lineContent = lineContent;
}

var createSearchRegex = function(query, caseSensitive, isRegex)
{
var regexFlags = caseSensitive ? "g" : "gi";
var regexObject;

if (isRegex) {
try {
regexObject = new RegExp('^.*?'+query+'.*?$|^.*?'+query+'.*?\n|\n.*?'+query+'.*?\n|\n.*?'+query+'.*?$', regexFlags);
} catch (e) {
// Silent catch.
}
}

if (!regexObject)
regexObject = createPlainTextSearchRegex(query, regexFlags);

return regexObject;
}

var createPlainTextSearchRegex = function(query, flags)
{
// This should be kept the same as the one in ContentSearchUtils.cpp.
var regexSpecialCharacters = "^[]{}()\\.^$*+?|-,";
var regex = "";
for (var i = 0; i < query.length; ++i) {
var c = query.charAt(i);
if (regexSpecialCharacters.indexOf(c) != -1)
regex += "\\";
regex += c;
}
return new RegExp('^.*?'+regex+'.*?$|^.*?'+regex+'.*?\n|\n.*?'+regex+'.*?\n|\n.*?'+regex+'.*?$', flags || "");
}



exports.performSearchInContent = function(content, query, caseSensitive, isRegex)
{
var regex = createSearchRegex(query, caseSensitive, isRegex);

var result = [];
var lastMatch;
var isMinified = false;

var firstNewLine = content.indexOf('\n');
if (content.length > 1024) {
if (firstNewLine > 1024 || firstNewLine === -1) {
isMinified = true;
}
}

while(lastMatch=regex.exec(content)) {
var lineContent = lastMatch[0];
var firstChar = lineContent.charCodeAt(0);
var lastChar = lineContent.charCodeAt(lineContent.length-1);
var lineMatchesBefore = content.substr(0,regex.lastIndex).match(/\n/g);
if (lineMatchesBefore){
var i = lineMatchesBefore.length;
if (lastChar !== 10){
++i;
} else {
lineContent = lineContent.substr(0,lineContent.length-1);
}
if (firstChar === 10){
lineContent = lineContent.substr(1);
}
if (isMinified === true && lineContent.length > 1024) {
lineContent = ' ... (line too long)';
}
result.push(new SearchMatch(i, lineContent));
}
}
return result;
}

0 comments on commit 74036dd

Please sign in to comment.