add find and replace feature

This commit is contained in:
tinkoff-dwh 2017-07-18 14:20:05 +03:00
parent 87705a9324
commit f269b8aa79
5 changed files with 332 additions and 0 deletions

View file

@ -164,6 +164,45 @@ limitations under the License.
</div>
</span>
<span class="labelBtn btn-group" id="searchGroup" style="vertical-align:middle; display:inline-block;">
<button type="button" class="btn btn-default btn-xs dropdown-toggle"
data-toggle="dropdown" tooltip-placement="bottom" uib-tooltip="Search code">
<i class="fa fa-search"></i>
</button>
<ul class="dropdown-menu search-dropdown" ng-click="$event.stopPropagation()" style="width: 350px">
<li>
<div class="input-group input-group-sm search-group">
<span class="input-group-addon">Find</span>
<input type="text" class="form-control"
ng-class="{'no-match': !search.occurrencesExists && search.searchText !== ''}"
ng-change="markAllOccurrencesAndHighlightFirst()"
ng-click="markAllOccurrences()"
ng-model="search.searchText" ng-trim="false"/>
<div class="input-group-btn">
<button class="btn btn-default" ng-click="prevOccurrence()">
<i class="fa fa-angle-left" aria-hidden="true"></i>
</button>
</div>
<div class="input-group-btn">
<button class="btn btn-default" ng-click="nextOccurrence()">
<i class="fa fa-angle-right" aria-hidden="true"></i>
</button>
</div>
</div>
<div class="input-group input-group-sm search-group">
<span class="input-group-addon">Replace</span>
<input type="text" class="form-control" ng-model="search.replaceText" ng-trim="false"/>
<div class="input-group-btn">
<button class="btn btn-default" ng-click="replace()">Replace</button>
</div>
<div class="input-group-btn">
<button class="btn btn-default" ng-click="replaceAll()">All</button>
</div>
</div>
</li>
</ul>
</span>
<!-- put the delete action by itself for your protection -->
<span class="labelBtn" style="vertical-align:middle; display:inline-block;">
<!-- if the note is in the trash, remove note permanently -->

View file

@ -62,6 +62,16 @@ function NotebookCtrl ($scope, $route, $routeParams, $location, $rootScope,
$scope.currentRevision = 'Head'
$scope.revisionView = isRevisionPath($location.path())
$scope.search = {
searchText: '',
occurrencesExists: false,
needHighlightFirst: false,
occurrencesHidden: false,
replaceText: '',
needToSendNextOccurrenceAfterReplace: false
}
let currentSearchParagraph = 0
$scope.$on('setConnectedStatus', function (event, param) {
if (connectedOnce && param) {
initNotebook()
@ -724,6 +734,136 @@ function NotebookCtrl ($scope, $route, $routeParams, $location, $rootScope,
angular.element('.permissionsForm select').find('option:not([is-select2="false"])').remove()
}
const markAllOccurrences = function() {
$scope.search.occurrencesExists = false
$scope.search.occurrencesHidden = false
currentSearchParagraph = 0
$scope.$broadcast('markAllOccurrences', $scope.search.searchText)
}
$scope.markAllOccurrences = function() {
$scope.search.needHighlightFirst = false
markAllOccurrences()
}
$scope.markAllOccurrencesAndHighlightFirst = function() {
$scope.search.needHighlightFirst = true
markAllOccurrences()
}
const sendNextOccurrenceMessage = function() {
if (!$scope.search.occurrencesExists) {
markAllOccurrences()
if (!$scope.search.occurrencesExists) {
return
}
}
if ($scope.search.occurrencesHidden) {
markAllOccurrences()
}
$scope.$broadcast('nextOccurrence', $scope.note.paragraphs[currentSearchParagraph].id)
}
const sendPrevOccurrenceMessage = function() {
if (!$scope.search.occurrencesExists) {
markAllOccurrences()
if (!$scope.search.occurrencesExists) {
return
}
}
if ($scope.search.occurrencesHidden) {
markAllOccurrences()
currentSearchParagraph = $scope.note.paragraphs.length - 1
}
$scope.$broadcast('prevOccurrence', $scope.note.paragraphs[currentSearchParagraph].id)
}
const increaseCurrentSearchParagraph = function() {
++currentSearchParagraph
if (currentSearchParagraph >= $scope.note.paragraphs.length) {
currentSearchParagraph = 0
}
}
const decreaseCurrentSearchParagraph = function () {
--currentSearchParagraph
if (currentSearchParagraph === -1) {
currentSearchParagraph = $scope.note.paragraphs.length - 1
}
}
$scope.$on('occurrencesExists', function(event) {
if ($scope.search.occurrencesExists) {
return
}
$scope.search.occurrencesExists = true
if ($scope.search.needHighlightFirst) {
sendNextOccurrenceMessage()
}
})
$scope.nextOccurrence = function() {
sendNextOccurrenceMessage()
}
$scope.$on('noNextOccurrence', function(event) {
increaseCurrentSearchParagraph()
sendNextOccurrenceMessage()
})
$scope.prevOccurrence = function() {
sendPrevOccurrenceMessage()
}
$scope.$on('noPrevOccurrence', function(event) {
decreaseCurrentSearchParagraph()
sendPrevOccurrenceMessage()
})
$scope.$on('editorClicked', function() {
$scope.search.occurrencesHidden = true
$scope.$broadcast('unmarkAll')
})
$scope.replace = function() {
if (!$scope.search.occurrencesExists) {
$scope.markAllOccurrencesAndHighlightFirst()
if (!$scope.search.occurrencesExists) {
return
}
}
if ($scope.search.occurrencesHidden) {
$scope.markAllOccurrencesAndHighlightFirst()
return
}
$scope.$broadcast('replaceCurrent', $scope.search.searchText, $scope.search.replaceText)
if ($scope.search.needToSendNextOccurrenceAfterReplace) {
sendNextOccurrenceMessage()
}
}
$scope.replaceAll = function() {
if (!$scope.search.occurrencesExists) {
return
}
if ($scope.search.occurrencesHidden) {
$scope.markAllOccurrences()
}
$scope.$broadcast('replaceAll', $scope.search.searchText, $scope.search.replaceText)
$scope.$broadcast('markAllOccurrences', $scope.search.searchText)
}
$scope.$on('noNextOccurrenceAfterReplace', function() {
$scope.search.occurrencesExists = false
$scope.search.needHighlightFirst = false
$scope.search.needToSendNextOccurrenceAfterReplace = false
$scope.$broadcast('checkOccurrences')
increaseCurrentSearchParagraph()
if ($scope.search.occurrencesExists) {
$scope.search.needToSendNextOccurrenceAfterReplace = true
}
})
$scope.restartInterpreter = function(interpreter) {
const thisConfirm = BootstrapDialog.confirm({
closable: false,

View file

@ -333,3 +333,27 @@
}
.noteAction.headroom--unpinned { top: -100px; }
.noteAction.headroom--pinned { top: 50px; /** `noteAction` top */ }
.search-group input.no-match {
border: 1px solid red;
box-shadow: none;
}
.search-group .input-group-addon {
min-width: 64px;
}
.search-group input,
.search-group .input-group-addon,
.search-group .input-group-btn .btn {
border-radius: 0;
}
.search-dropdown {
border-radius: 0;
padding: 0;
}
.search-dropdown li {
margin: 0;
}

View file

@ -52,6 +52,12 @@ function ParagraphCtrl ($scope, $rootScope, $route, $window, $routeParams, $loca
paragraphText: '',
}
let searchRanges = []
const getCurrentRangeDefault = function() {
return {id: -1, markerId: -1}
}
let currentRange = getCurrentRangeDefault()
let editorSetting = {}
// flag that is used to set editor setting on paste percent sign
let pastePercentSign = false
@ -801,6 +807,8 @@ function ParagraphCtrl ($scope, $rootScope, $route, $window, $routeParams, $loca
// remove binding
$scope.editor.commands.removeCommand('showSettingsMenu')
$scope.editor.commands.removeCommand('find')
$scope.editor.commands.removeCommand('replace')
let isOption = $rootScope.isMac ? 'option' : 'alt'
@ -1444,6 +1452,14 @@ function ParagraphCtrl ($scope, $rootScope, $route, $window, $routeParams, $loca
$scope.clearParagraphOutput($scope.paragraph)
} else if (keyEvent.ctrlKey && keyEvent.altKey && keyCode === 87) { // Ctrl + Alt + w
$scope.goToSingleParagraph()
} else if (keyEvent.ctrlKey && keyCode === 70) { // Ctrl + f
let elem = angular.element('#searchGroup')
let openClass = 'open'
if (elem.hasClass(openClass)) {
elem.removeClass(openClass)
} else {
elem.addClass(openClass)
}
} else {
noShortcutDefined = true
}
@ -1545,4 +1561,116 @@ function ParagraphCtrl ($scope, $rootScope, $route, $window, $routeParams, $loca
})
}
})
const clearSearchSelection = function() {
for (let i = 0; i < searchRanges.length; ++i) {
$scope.editor.session.removeMarker(searchRanges[i].markerId)
}
searchRanges = []
if (currentRange.id !== -1) {
$scope.editor.session.removeMarker(currentRange.markerId)
}
currentRange = getCurrentRangeDefault()
}
$scope.onEditorClick = function() {
$scope.$emit('editorClicked')
}
$scope.$on('unmarkAll', function() {
clearSearchSelection()
})
const markAllOccurrences = function(text) {
clearSearchSelection()
if (text === '') {
return
}
if ($scope.editor.findAll(text) === 0) {
return
}
let ranges = $scope.editor.selection.getAllRanges()
$scope.editor.selection.toSingleRange()
$scope.editor.selection.clearSelection()
for (let i = 0; i < ranges.length; ++i) {
let id = $scope.editor.session.addMarker(ranges[i], 'ace_selected-word', 'text')
searchRanges.push({markerId: id, range: ranges[i]})
}
}
$scope.$on('markAllOccurrences', function(event, text) {
markAllOccurrences(text)
if (searchRanges.length > 0) {
$scope.$emit('occurrencesExists')
}
})
$scope.$on('nextOccurrence', function(event, paragraphId) {
if ($scope.paragraph.id !== paragraphId) {
return
}
let highlightedRangeExists = currentRange.id !== -1
if (highlightedRangeExists) {
$scope.editor.session.removeMarker(currentRange.markerId)
currentRange.markerId = -1
}
++currentRange.id
if (currentRange.id >= searchRanges.length) {
currentRange.id = -1
$scope.$emit('noNextOccurrence')
return
}
currentRange.markerId = $scope.editor.session.addMarker(
searchRanges[currentRange.id].range, 'ace_selection', 'text')
})
$scope.$on('prevOccurrence', function(event, paragraphId) {
if ($scope.paragraph.id !== paragraphId) {
return
}
let highlightedRangeExists = currentRange.id !== -1
if (highlightedRangeExists) {
$scope.editor.session.removeMarker(currentRange.markerId)
currentRange.markerId = -1
}
if (currentRange.id === -1) {
currentRange.id = searchRanges.length
}
--currentRange.id
if (currentRange.id === -1) {
$scope.$emit('noPrevOccurrence')
return
}
currentRange.markerId = $scope.editor.session.addMarker(
searchRanges[currentRange.id].range, 'ace_selection', 'text')
})
$scope.$on('replaceCurrent', function(event, from, to) {
if (currentRange.id === -1) {
return
}
let numberFromEnd = searchRanges.length - currentRange.id - 1
$scope.editor.session.removeMarker(currentRange.markerId)
$scope.editor.session.replace(searchRanges[currentRange.id].range, to)
markAllOccurrences(from)
currentRange.id = searchRanges.length - numberFromEnd
if (currentRange.id === searchRanges.length) {
currentRange.id = -1
$scope.$emit('noNextOccurrenceAfterReplace')
} else {
currentRange.markerId = $scope.editor.session.addMarker(
searchRanges[currentRange.id].range, 'ace_selection', 'text')
}
})
$scope.$on('replaceAll', function(event, from, to) {
clearSearchSelection()
$scope.editor.replaceAll(to, {needle: from})
})
$scope.$on('checkOccurrences', function() {
if (searchRanges.length > 0) {
$scope.$emit('occurrencesExists')
}
})
}

View file

@ -44,6 +44,7 @@ limitations under the License.
original-text="originalText"
on-load="aceLoaded"
revision-view="revisionView"
ng-click="onEditorClick()"
></code-editor>
</div>