Skip to content
This repository has been archived by the owner on Sep 6, 2021. It is now read-only.

Commit

Permalink
Merge pull request #5191 from adobe/pflynn/find-tickmarks
Browse files Browse the repository at this point in the history
Show scroll track tickmarks for Find results
  • Loading branch information
gruehle committed Sep 16, 2013
2 parents 1c268c5 + 859119f commit 1deb307
Show file tree
Hide file tree
Showing 6 changed files with 301 additions and 14 deletions.
5 changes: 5 additions & 0 deletions src/editor/Editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -506,6 +506,11 @@ define(function (require, exports, module) {
});
};

/** @return {boolean} True if editor is not showing the entire text of the document (i.e. an inline editor) */
Editor.prototype.isTextSubset = function () {
return Boolean(this._visibleRange);
};

/**
* Ensures that the lines that are actually hidden in the inline editor correspond to
* the desired visible range.
Expand Down
40 changes: 30 additions & 10 deletions src/search/FindReplace.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
/*unittests: FindReplace*/


/*
/**
* Adds Find and Replace commands
*
* Originally based on the code in CodeMirror2/lib/util/search.js.
Expand All @@ -43,6 +43,7 @@ define(function (require, exports, module) {
Editor = require("editor/Editor"),
EditorManager = require("editor/EditorManager"),
ModalBar = require("widgets/ModalBar").ModalBar,
ScrollTrackMarkers = require("search/ScrollTrackMarkers"),
PanelManager = require("view/PanelManager"),
Resizer = require("utils/Resizer"),
StatusBar = require("widgets/StatusBar"),
Expand Down Expand Up @@ -151,6 +152,7 @@ define(function (require, exports, module) {
});
});
state.marked.length = 0;
ScrollTrackMarkers.clear();
}

function clearSearch(cm) {
Expand Down Expand Up @@ -191,7 +193,26 @@ define(function (require, exports, module) {
"<span id='find-counter'></span> " +
"<span style='color: #888'>(" + Strings.SEARCH_REGEXP_INFO + ")</span>" +
"</div>" +
"<div class='error'></div>";
"<div class='error'></div>";


function toggleHighlighting(editor, enabled) {
// Temporarily change selection color to improve highlighting - see LESS code for details
if (enabled) {
$(editor.getRootElement()).addClass("find-highlighting");
} else {
$(editor.getRootElement()).removeClass("find-highlighting");
}

ScrollTrackMarkers.setVisible(editor, enabled);
}

function addHighlight(editor, state, cursor) {
var cm = editor._codeMirror;
state.marked.push(cm.markText(cursor.from(), cursor.to(), { className: "CodeMirror-searching" }));

ScrollTrackMarkers.addTickmark(editor, cursor.from());
}

/**
* If no search pending, opens the search dialog. If search is already open, moves to
Expand Down Expand Up @@ -246,14 +267,13 @@ define(function (require, exports, module) {
// Highlight all matches
// (Except on huge documents, where this is too expensive)
if (cm.getValue().length < 500000) {
// Temporarily change selection color to improve highlighting - see LESS code for details
$(cm.getWrapperElement()).addClass("find-highlighting");
toggleHighlighting(editor, true);

// FUTURE: if last query was prefix of this one, could optimize by filtering existing result set
var resultCount = 0;
var cursor = getSearchCursor(cm, state.query);
while (cursor.findNext()) {
state.marked.push(cm.markText(cursor.from(), cursor.to(), { className: "CodeMirror-searching" }));
addHighlight(editor, state, cursor);
resultCount++;

//Remove this section when https://github.com/marijnh/CodeMirror/issues/1155 will be fixed
Expand All @@ -268,7 +288,7 @@ define(function (require, exports, module) {
if (resultCount === 0) {
$("#find-counter").text(Strings.FIND_NO_RESULTS);
} else if (resultCount === 1) {
$("#find-counter").text(Strings.FIND_RESULT_COUNT_SINGLE);
$("#find-counter").text(Strings.FIND_RESULT_COUNT_SINGLE);
} else {
$("#find-counter").text(StringUtils.format(Strings.FIND_RESULT_COUNT, resultCount));
enableNavigator = true;
Expand Down Expand Up @@ -313,15 +333,15 @@ define(function (require, exports, module) {
// Clear highlights but leave search state in place so Find Next/Previous work after closing
clearHighlights(cm, state);

// As soon as focus goes back to the editor, restore normal selection color
$(cm.getWrapperElement()).removeClass("find-highlighting");
// Dispose highlighting UI (important to restore normal selection color as soon as focus goes back to the editor)
toggleHighlighting(editor, false);
});

modalBar.getRoot().on("click", function (e) {
if (e.target.id === "find-next") {
_findNext();
doSearch(editor);
} else if (e.target.id === "find-prev") {
_findPrevious();
doSearch(editor, true);
}
});

Expand Down
158 changes: 158 additions & 0 deletions src/search/ScrollTrackMarkers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
/*
* Copyright (c) 2013 Adobe Systems Incorporated. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
*/

/*jslint vars: true, plusplus: true, devel: true, nomen: true, regexp: true, indent: 4, maxerr: 50 */
/*global define, $, brackets, window */


/**
* Manages tickmarks shown along the scrollbar track.
* NOT yet intended for use by anyone other than the FindReplace module.
* It is assumed that markers are always clear()ed when switching editors.
*/
define(function (require, exports, module) {
"use strict";

var Editor = require("editor/Editor"),
EditorManager = require("editor/EditorManager"),
Async = require("utils/Async");


/** @const @type {number} Height (and width) or scrollbar up/down arrow button on Win */
var WIN_ARROW_HT = 17;

/** @type {?Editor} Editor the markers are currently shown for, or null if not shown */
var editor;

/** @type {number} Top of scrollbar track area, relative to top of scrollbar */
var trackOffset;

/** @type {number} Height of scrollbar track area */
var trackHt;

/** @type {!Array.<{line: number, ch: number}>} Text positions of markers */
var marks = [];


/** Measure scrollbar track */
function _calcScaling() {
var rootElem = editor.getRootElement();
var $sb = $(".CodeMirror-vscrollbar", rootElem);

trackHt = $sb[0].offsetHeight;

if (trackHt > 0) {
// Scrollbar visible: determine offset of track from top of scrollbar
if (brackets.platform === "win") {
trackOffset = WIN_ARROW_HT; // Up arrow pushes down track
} else {
trackOffset = 0; // No arrows
}

} else {
// No scrollbar: use the height of the entire code content
trackHt = $(".CodeMirror-sizer", rootElem)[0].offsetHeight;
trackOffset = 0;
}

trackHt -= trackOffset * 2;
}

/** Add one tickmark to the DOM */
function _renderMark(pos) {
var top = Math.round(pos.line / editor.lineCount() * trackHt) + trackOffset;
top--; // subtract ~1/2 the ht of a tickmark to center it on ideal pos

var $mark = $("<div class='tickmark' style='top:" + top + "px'></div>");

$(".tickmark-track", editor.getRootElement()).append($mark);
}


/**
* Clear any markers in the editor's tickmark track, but leave it visible. Safe to call when
* tickmark track is not visible also.
*/
function clear() {
if (editor) {
$(".tickmark-track", editor.getRootElement()).empty();
marks = [];
}
}

/** Add or remove the tickmark track from the editor's UI */
function setVisible(curEditor, visible) {
// short-circuit no-ops
if ((visible && curEditor === editor) || (!visible && !editor)) {
return;
}

if (visible) {
console.assert(!editor);
editor = curEditor;

// Don't support inline editors yet - search inside them is pretty screwy anyway (#2110)
if (editor.isTextSubset()) {
return;
}

var rootElem = editor.getRootElement();
var $sb = $(".CodeMirror-vscrollbar", rootElem);
var $overlay = $("<div class='tickmark-track'></div>");
$sb.parent().append($overlay);

_calcScaling();

// Update tickmarks during window resize (whenever resizing has paused/stopped for > 1/3 sec)
$(window).on("resize.ScrollTrackMarkers", Async.whenIdle(300, function () {
if (marks.length) {
_calcScaling();
$(".tickmark-track", editor.getRootElement()).empty();
marks.forEach(function (pos) {
_renderMark(pos);
});
}
}));

} else {
console.assert(editor === curEditor);
$(".tickmark-track", curEditor.getRootElement()).remove();
editor = null;
marks = [];
$(window).off("resize.ScrollTrackMarkers");
}
}

/** Add a tickmark to the editor's tickmark track, if it's visible */
function addTickmark(curEditor, pos) {
console.assert(editor === curEditor);

marks.push(pos);
_renderMark(pos);
}


exports.clear = clear;
exports.setVisible = setVisible;
exports.addTickmark = addTickmark;
});
22 changes: 22 additions & 0 deletions src/styles/brackets.less
Original file line number Diff line number Diff line change
Expand Up @@ -926,6 +926,28 @@ a, img {
padding: 0 5px;
}

.tickmark-track {
position: absolute;
bottom: 0;
top: 0;
right: 0;
width: 16px;
z-index: @z-index-cm-max;
pointer-events: none;

.tickmark {
position: absolute;
width: 16px;
body.platform-mac & { width: 15px; }

height: 1px;
background-color: #eddd23;
border-top: 1px solid #e0d123;
border-bottom: 1px solid #d4c620;
opacity: 0.85; // allow thumb to show through
}
}


/* Quick Open search bar & dropdown */

Expand Down
30 changes: 28 additions & 2 deletions src/utils/Async.js
Original file line number Diff line number Diff line change
Expand Up @@ -419,14 +419,40 @@ define(function (require, exports, module) {
});
}
};


/**
* Implements "debouncing." Returns a function that can be called frequently, triggering 'callback' only when calls
* to this function have paused for >= 'idleDelay' ms. The callback may be called multiple times, if there are
* multiple idleDelay-sized gaps in the event sequence. Invoking the callback can be delayed *indefinitely* if the
* event sequence continues forever with no idleDelay-sized gaps at all.
*
* @param {number} idleDelay Minimum delay (ms) before invoking callback.
* @param {!function()} callback
* @return {!function()}
*/
function whenIdle(idleDelay, callback) {
var timer;
return function () {
if (timer) {
window.clearTimeout(timer);
}
timer = window.setTimeout(function () {
timer = null;
callback();
}, idleDelay);
};
}


// Define public API
exports.doInParallel = doInParallel;
exports.doSequentially = doSequentially;
exports.doSequentiallyInBackground = doSequentiallyInBackground;
exports.doSequentiallyInBackground = doSequentiallyInBackground;
exports.doInParallel_aggregateErrors = doInParallel_aggregateErrors;
exports.withTimeout = withTimeout;
exports.ERROR_TIMEOUT = ERROR_TIMEOUT;
exports.chain = chain;
exports.PromiseQueue = PromiseQueue;
exports.ERROR_TIMEOUT = ERROR_TIMEOUT;
exports.whenIdle = whenIdle;
});
Loading

0 comments on commit 1deb307

Please sign in to comment.