mirror of
https://github.com/atom/atom.git
synced 2026-01-23 13:58:08 -05:00
Pull out command-panel package into a separate repo
This commit is contained in:
@@ -71,6 +71,7 @@
|
||||
"bookmarks": "0.1.0",
|
||||
"bracket-matcher": "0.1.0",
|
||||
"command-logger": "0.1.0",
|
||||
"command-panel": "0.1.0",
|
||||
"command-palette": "0.1.0",
|
||||
"fuzzy-finder": "0.1.0",
|
||||
"editor-stats": "0.1.0",
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
'body':
|
||||
'meta-:': 'command-panel:toggle-preview'
|
||||
'meta-;': 'command-panel:toggle'
|
||||
'meta-F': 'command-panel:find-in-project'
|
||||
|
||||
'.command-panel .preview-list, .command-panel .editor input':
|
||||
'enter': 'core:confirm'
|
||||
|
||||
'.editor':
|
||||
'meta-g': 'command-panel:repeat-relative-address'
|
||||
'meta-G': 'command-panel:repeat-relative-address-in-reverse'
|
||||
'meta-e': 'command-panel:set-selection-as-regex-address'
|
||||
'meta-f': 'command-panel:find-in-file'
|
||||
'meta-F': 'command-panel:find-in-project'
|
||||
|
||||
'.command-panel':
|
||||
'ctrl-{': 'command-panel:collapse-all'
|
||||
'ctrl-}': 'command-panel:expand-all'
|
||||
|
||||
'.command-panel .preview-list':
|
||||
'left': 'command-panel:collapse-result'
|
||||
'ctrl-[': 'command-panel:collapse-result'
|
||||
'right': 'command-panel:expand-result'
|
||||
'ctrl-]': 'command-panel:expand-result'
|
||||
@@ -1,23 +0,0 @@
|
||||
fsUtils = require 'fs-utils'
|
||||
PEG = require 'pegjs'
|
||||
shell = require 'shell'
|
||||
|
||||
module.exports =
|
||||
class CommandInterpreter
|
||||
constructor: (@project) ->
|
||||
|
||||
eval: (string, activeEditSession) ->
|
||||
@parser ?= PEG.buildParser(fsUtils.read(require.resolve 'command-panel/lib/commands.pegjs'))
|
||||
compositeCommand = @parser.parse(string)
|
||||
@lastRelativeAddress = compositeCommand if compositeCommand.isRelativeAddress()
|
||||
compositeCommand.execute(@project, activeEditSession)
|
||||
|
||||
repeatRelativeAddress: (activeEditSession, {reverse}={}) ->
|
||||
return unless @lastRelativeAddress
|
||||
reverse ?= false
|
||||
previousSelectionRange = activeEditSession.getSelection().getBufferRange()
|
||||
address = if reverse then @lastRelativeAddress.reverse() else @lastRelativeAddress
|
||||
|
||||
address.execute(@project, activeEditSession).done ->
|
||||
currentSelectionRange = activeEditSession.getSelection().getBufferRange()
|
||||
shell.beep() if previousSelectionRange.isEqual(currentSelectionRange)
|
||||
@@ -1,186 +0,0 @@
|
||||
{View, $$, $$$} = require 'space-pen'
|
||||
CommandInterpreter = require './command-interpreter'
|
||||
RegexAddress = require './commands/regex-address'
|
||||
CompositeCommand = require './commands/composite-command'
|
||||
PreviewList = require './preview-list'
|
||||
Editor = require 'editor'
|
||||
EditSession = require 'edit-session'
|
||||
{SyntaxError} = require('pegjs').parser
|
||||
_ = require 'underscore'
|
||||
|
||||
module.exports =
|
||||
class CommandPanelView extends View
|
||||
@content: ->
|
||||
@div class: 'command-panel tool-panel', =>
|
||||
@div class: 'loading is-loading', outlet: 'loadingMessage', =>
|
||||
@span 'Searching...'
|
||||
@div class: 'header', outlet: 'previewHeader', =>
|
||||
@button outlet: 'collapseAll', class: 'btn btn-mini pull-right', 'Collapse All'
|
||||
@button outlet: 'expandAll', class: 'btn btn-mini pull-right', 'Expand All'
|
||||
@span outlet: 'previewCount', class: 'preview-count'
|
||||
|
||||
@subview 'previewList', new PreviewList(rootView)
|
||||
@ul class: 'error-messages', outlet: 'errorMessages'
|
||||
@div class: 'prompt-and-editor', =>
|
||||
@div class: 'prompt', outlet: 'prompt'
|
||||
@subview 'miniEditor', new Editor(mini: true)
|
||||
|
||||
commandInterpreter: null
|
||||
history: null
|
||||
historyIndex: 0
|
||||
maxSerializedHistorySize: 100
|
||||
|
||||
initialize: (state) ->
|
||||
@commandInterpreter = new CommandInterpreter(project)
|
||||
|
||||
@command 'tool-panel:unfocus', => rootView.focus()
|
||||
@command 'core:close', => @detach(); false
|
||||
@command 'core:cancel', => @detach(); false
|
||||
@command 'core:confirm', => @execute()
|
||||
@command 'core:move-up', => @navigateBackwardInHistory()
|
||||
@command 'core:move-down', => @navigateForwardInHistory()
|
||||
|
||||
@subscribeToCommand rootView, 'command-panel:toggle', => @toggle()
|
||||
@subscribeToCommand rootView, 'command-panel:toggle-preview', => @togglePreview()
|
||||
@subscribeToCommand rootView, 'command-panel:find-in-file', => @findInFile()
|
||||
@subscribeToCommand rootView, 'command-panel:find-in-project', => @findInProject()
|
||||
@subscribeToCommand rootView, 'command-panel:repeat-relative-address', => @repeatRelativeAddress()
|
||||
@subscribeToCommand rootView, 'command-panel:repeat-relative-address-in-reverse', => @repeatRelativeAddress(reverse: true)
|
||||
@subscribeToCommand rootView, 'command-panel:set-selection-as-regex-address', => @setSelectionAsLastRelativeAddress()
|
||||
|
||||
@expandAll.on 'click', @onExpandAll
|
||||
@collapseAll.on 'click', @onCollapseAll
|
||||
|
||||
@previewList.hide()
|
||||
@previewHeader.hide()
|
||||
@errorMessages.hide()
|
||||
@loadingMessage.hide()
|
||||
@prompt.iconSize(@miniEditor.getFontSize())
|
||||
|
||||
@history = state.history ? []
|
||||
@historyIndex = @history.length
|
||||
|
||||
serialize: ->
|
||||
text: @miniEditor.getText()
|
||||
history: @history[-@maxSerializedHistorySize..]
|
||||
|
||||
destroy: ->
|
||||
@previewList.destroy()
|
||||
@remove()
|
||||
|
||||
toggle: ->
|
||||
if @miniEditor.isFocused
|
||||
@detach()
|
||||
rootView.focus()
|
||||
else
|
||||
@attach() unless @hasParent()
|
||||
@miniEditor.focus()
|
||||
|
||||
togglePreview: ->
|
||||
if @previewList.is(':focus')
|
||||
@previewList.hide()
|
||||
@previewHeader.hide()
|
||||
@detach()
|
||||
rootView.focus()
|
||||
else
|
||||
@attach() unless @hasParent()
|
||||
if @previewList.hasOperations()
|
||||
@previewList.show().focus()
|
||||
@previewHeader.show()
|
||||
else
|
||||
@miniEditor.focus()
|
||||
|
||||
onExpandAll: (event) =>
|
||||
@previewList.expandAllPaths()
|
||||
@previewList.focus()
|
||||
|
||||
onCollapseAll: (event) =>
|
||||
@previewList.collapseAllPaths()
|
||||
@previewList.focus()
|
||||
|
||||
attach: (text, options={}) ->
|
||||
@errorMessages.hide()
|
||||
|
||||
focus = options.focus ? true
|
||||
rootView.vertical.append(this)
|
||||
@miniEditor.focus() if focus
|
||||
if text?
|
||||
@miniEditor.setText(text)
|
||||
@miniEditor.setCursorBufferPosition([0, Infinity])
|
||||
else
|
||||
@miniEditor.selectAll()
|
||||
|
||||
detach: ->
|
||||
rootView.focus()
|
||||
@previewList.hide()
|
||||
@previewHeader.hide()
|
||||
super
|
||||
|
||||
findInFile: ->
|
||||
if @miniEditor.getText()[0] is '/'
|
||||
@attach()
|
||||
@miniEditor.setSelectedBufferRange([[0, 1], [0, Infinity]])
|
||||
else
|
||||
@attach('/')
|
||||
|
||||
findInProject: ->
|
||||
if @miniEditor.getText().indexOf('Xx/') is 0
|
||||
@attach()
|
||||
@miniEditor.setSelectedBufferRange([[0, 3], [0, Infinity]])
|
||||
else
|
||||
@attach('Xx/')
|
||||
|
||||
escapedCommand: ->
|
||||
@miniEditor.getText()
|
||||
|
||||
execute: (command=@escapedCommand()) ->
|
||||
@loadingMessage.show()
|
||||
@previewList.hide()
|
||||
@previewHeader.hide()
|
||||
@errorMessages.empty()
|
||||
|
||||
try
|
||||
activePaneItem = rootView.getActivePaneItem()
|
||||
editSession = activePaneItem if activePaneItem instanceof EditSession
|
||||
@commandInterpreter.eval(command, editSession).done ({operationsToPreview, errorMessages}) =>
|
||||
@loadingMessage.hide()
|
||||
@history.push(command)
|
||||
@historyIndex = @history.length
|
||||
|
||||
if errorMessages.length > 0
|
||||
@flashError()
|
||||
@errorMessages.show()
|
||||
@errorMessages.append $$ ->
|
||||
@li errorMessage for errorMessage in errorMessages
|
||||
else if operationsToPreview?.length
|
||||
@previewHeader.show()
|
||||
@previewList.populate(operationsToPreview)
|
||||
@previewList.focus()
|
||||
@previewCount.text("#{_.pluralize(operationsToPreview.length, 'match', 'matches')} in #{_.pluralize(@previewList.getPathCount(), 'file')}").show()
|
||||
else
|
||||
@detach()
|
||||
catch error
|
||||
@loadingMessage.hide()
|
||||
if error.name is "SyntaxError"
|
||||
@flashError()
|
||||
return
|
||||
else
|
||||
throw error
|
||||
|
||||
navigateBackwardInHistory: ->
|
||||
return if @historyIndex == 0
|
||||
@historyIndex--
|
||||
@miniEditor.setText(@history[@historyIndex])
|
||||
|
||||
navigateForwardInHistory: ->
|
||||
return if @historyIndex == @history.length
|
||||
@historyIndex++
|
||||
@miniEditor.setText(@history[@historyIndex] or '')
|
||||
|
||||
repeatRelativeAddress: (options) ->
|
||||
@commandInterpreter.repeatRelativeAddress(rootView.getActivePaneItem(), options)
|
||||
|
||||
setSelectionAsLastRelativeAddress: ->
|
||||
selection = rootView.getActiveView().getSelectedText()
|
||||
regex = _.escapeRegExp(selection)
|
||||
@commandInterpreter.lastRelativeAddress = new CompositeCommand([new RegexAddress(regex)])
|
||||
@@ -1,14 +0,0 @@
|
||||
CommandPanelView = require './command-panel-view'
|
||||
|
||||
module.exports =
|
||||
commandPanelView: null
|
||||
|
||||
activate: (state) ->
|
||||
@commandPanelView = new CommandPanelView(state)
|
||||
|
||||
deactivate: ->
|
||||
@commandPanelView?.destroy()
|
||||
@commandPanelView = null
|
||||
|
||||
serialize: ->
|
||||
@commandPanelView.serialize()
|
||||
@@ -1,67 +0,0 @@
|
||||
{
|
||||
var CompositeCommand = require('command-panel/lib/commands/composite-command')
|
||||
var Substitution = require('command-panel/lib/commands/substitution');
|
||||
var ZeroAddress = require('command-panel/lib/commands/zero-address');
|
||||
var EofAddress = require('command-panel/lib/commands/eof-address');
|
||||
var LineAddress = require('command-panel/lib/commands/line-address');
|
||||
var AddressRange = require('command-panel/lib/commands/address-range');
|
||||
var DefaultAddressRange = require('command-panel/lib/commands/default-address-range');
|
||||
var CurrentSelectionAddress = require('command-panel/lib/commands/current-selection-address')
|
||||
var RegexAddress = require('command-panel/lib/commands/regex-address')
|
||||
var SelectAllMatches = require('command-panel/lib/commands/select-all-matches')
|
||||
var SelectAllMatchesInProject = require('command-panel/lib/commands/select-all-matches-in-project')
|
||||
}
|
||||
|
||||
start = _ commands:( selectAllMatchesInProject / textCommand ) {
|
||||
return new CompositeCommand(commands);
|
||||
}
|
||||
|
||||
textCommand = defaultAddress:defaultAddress? expressions:expression* {
|
||||
if (defaultAddress) expressions.unshift(defaultAddress);
|
||||
return expressions;
|
||||
}
|
||||
|
||||
defaultAddress = !address {
|
||||
return new DefaultAddressRange();
|
||||
}
|
||||
|
||||
expression = _ expression:(address / substitution / selectAllMatches) {
|
||||
return expression;
|
||||
}
|
||||
|
||||
address = addressRange / primitiveAddress
|
||||
|
||||
addressRange = start:primitiveAddress? _ ',' _ end:address? {
|
||||
if (!start) start = new ZeroAddress();
|
||||
if (!end) end = new EofAddress();
|
||||
return new AddressRange(start, end);
|
||||
}
|
||||
|
||||
primitiveAddress
|
||||
= '0' { return new ZeroAddress() }
|
||||
/ '$' { return new EofAddress() }
|
||||
/ '.' { return new CurrentSelectionAddress() }
|
||||
/ lineNumber:integer { return new LineAddress(lineNumber) }
|
||||
/ regexAddress
|
||||
|
||||
regexAddress
|
||||
= reverse:'-'? '/' pattern:pattern '/'? { return new RegexAddress(pattern, reverse.length > 0)}
|
||||
|
||||
substitution
|
||||
= "s" _ "/" find:pattern "/" replace:pattern "/" _ options:[g]* {
|
||||
return new Substitution(find, eval("'" + replace + "'"), options);
|
||||
}
|
||||
|
||||
selectAllMatches
|
||||
= 'x' _ '/' pattern:pattern '/'? { return new SelectAllMatches(pattern) }
|
||||
|
||||
selectAllMatchesInProject
|
||||
= 'X' _ 'x' _ '/' pattern:pattern '/'? { return [new SelectAllMatchesInProject(pattern)] }
|
||||
|
||||
pattern
|
||||
= pattern:('\\/' / [^/])* { return pattern.join('') }
|
||||
|
||||
integer
|
||||
= digits:[0-9]+ { return parseInt(digits.join('')); }
|
||||
|
||||
_ = " "*
|
||||
@@ -1,12 +0,0 @@
|
||||
Address = require 'command-panel/lib/commands/address'
|
||||
Range = require 'range'
|
||||
|
||||
module.exports =
|
||||
class AddressRange extends Address
|
||||
constructor: (@startAddress, @endAddress) ->
|
||||
|
||||
getRange: (buffer, range) ->
|
||||
new Range(@startAddress.getRange(buffer, range).start, @endAddress.getRange(buffer, range).end)
|
||||
|
||||
isRelative: ->
|
||||
@startAddress.isRelative() and @endAddress.isRelative()
|
||||
@@ -1,21 +0,0 @@
|
||||
Command = require './command'
|
||||
Operation = require 'command-panel/lib/operation'
|
||||
$ = require 'jquery'
|
||||
|
||||
module.exports =
|
||||
class Address extends Command
|
||||
compile: (project, buffer, ranges) ->
|
||||
deferred = $.Deferred()
|
||||
operations = ranges.map (range) =>
|
||||
newRange = @getRange(buffer, range)
|
||||
|
||||
new Operation
|
||||
project: project
|
||||
buffer: buffer
|
||||
bufferRange: newRange
|
||||
errorMessage: @errorMessage
|
||||
|
||||
deferred.resolve(operations)
|
||||
deferred.promise()
|
||||
|
||||
isAddress: -> true
|
||||
@@ -1,7 +0,0 @@
|
||||
module.exports =
|
||||
class Command
|
||||
isAddress: -> false
|
||||
|
||||
errorMessage: null
|
||||
preserveSelections: false
|
||||
previewOperations: false
|
||||
@@ -1,62 +0,0 @@
|
||||
_ = require 'underscore'
|
||||
$ = require 'jquery'
|
||||
|
||||
module.exports =
|
||||
class CompositeCommand
|
||||
constructor: (@subcommands) ->
|
||||
|
||||
execute: (project, editSession) ->
|
||||
currentRanges = editSession?.getSelectedBufferRanges() ? []
|
||||
@executeCommands(@subcommands, project, editSession, currentRanges)
|
||||
|
||||
executeCommands: (commands, project, editSession, ranges) ->
|
||||
deferred = $.Deferred()
|
||||
[currentCommand, remainingCommands...] = commands
|
||||
|
||||
currentCommand.compile(project, editSession?.buffer, ranges).done (operations) =>
|
||||
if remainingCommands.length
|
||||
errorMessages = @errorMessagesForOperations(operations)
|
||||
nextRanges = operations.map (operation) -> operation.getBufferRange()
|
||||
operations.forEach (operation) -> operation.destroy()
|
||||
|
||||
@executeCommands(remainingCommands, project, editSession, nextRanges).done ({errorMessages: moreErrorMessages})->
|
||||
errorMessages.push(moreErrorMessages...) if moreErrorMessages
|
||||
deferred.resolve({errorMessages})
|
||||
else
|
||||
errorMessages = @errorMessagesForOperations(operations)
|
||||
|
||||
if currentCommand.previewOperations
|
||||
deferred.resolve({operationsToPreview: operations, errorMessages})
|
||||
else
|
||||
bufferRanges = []
|
||||
errorMessages = @errorMessagesForOperations(operations)
|
||||
|
||||
executeOperations = ->
|
||||
for operation in operations
|
||||
bufferRange = operation.execute(editSession)
|
||||
bufferRanges.push(bufferRange) if bufferRange
|
||||
operation.destroy()
|
||||
|
||||
if bufferRanges.length and not currentCommand.preserveSelections
|
||||
editSession.setSelectedBufferRanges(bufferRanges, autoscroll: true)
|
||||
|
||||
operationsWillChangeBuffer = _.detect(operations, (operation) -> operation.newText?)
|
||||
|
||||
if operationsWillChangeBuffer
|
||||
editSession.transact(executeOperations)
|
||||
else
|
||||
executeOperations()
|
||||
|
||||
deferred.resolve({errorMessages})
|
||||
|
||||
deferred.promise()
|
||||
|
||||
errorMessagesForOperations: (operations) ->
|
||||
operationsWithErrorMessages = operations.filter (operation) -> operation.errorMessage?
|
||||
operationsWithErrorMessages.map (operation) -> operation.errorMessage
|
||||
|
||||
reverse: ->
|
||||
new CompositeCommand(@subcommands.map (command) -> command.reverse())
|
||||
|
||||
isRelativeAddress: ->
|
||||
_.all(@subcommands, (command) -> command.isAddress() and command.isRelative())
|
||||
@@ -1,8 +0,0 @@
|
||||
Address = require './address'
|
||||
|
||||
module.exports =
|
||||
class CurrentSelectionAddress extends Address
|
||||
getRange: (buffer, range) ->
|
||||
range
|
||||
|
||||
isRelative: -> true
|
||||
@@ -1,11 +0,0 @@
|
||||
Address = require './address'
|
||||
|
||||
module.exports =
|
||||
class DefaultAddressRange extends Address
|
||||
getRange: (buffer, range)->
|
||||
if range.isEmpty()
|
||||
buffer.getRange()
|
||||
else
|
||||
range
|
||||
|
||||
isRelative: -> false
|
||||
@@ -1,10 +0,0 @@
|
||||
Address = require './address'
|
||||
Range = require 'range'
|
||||
|
||||
module.exports =
|
||||
class EofAddress extends Address
|
||||
getRange: (buffer, range) ->
|
||||
eof = buffer.getEofPosition()
|
||||
new Range(eof, eof)
|
||||
|
||||
isRelative: -> false
|
||||
@@ -1,12 +0,0 @@
|
||||
Address = require './address'
|
||||
Range = require 'range'
|
||||
|
||||
module.exports =
|
||||
class LineAddress extends Address
|
||||
constructor: (lineNumber) ->
|
||||
@row = lineNumber - 1
|
||||
|
||||
getRange: ->
|
||||
new Range([@row, 0], [@row + 1, 0])
|
||||
|
||||
isRelative: -> false
|
||||
@@ -1,47 +0,0 @@
|
||||
Address = require './address'
|
||||
Range = require 'range'
|
||||
|
||||
module.exports =
|
||||
class RegexAddress extends Address
|
||||
regex: null
|
||||
isReversed: false
|
||||
|
||||
constructor: (@pattern, isReversed, options) ->
|
||||
flags = ""
|
||||
pattern = pattern.source if pattern.source
|
||||
|
||||
patternContainsCapitalLetter = /(^|[^\\])[A-Z]/.test(pattern)
|
||||
flags += "i" unless patternContainsCapitalLetter
|
||||
@isReversed = isReversed
|
||||
|
||||
@regex = new RegExp(pattern, flags)
|
||||
|
||||
getRange: (buffer, range) ->
|
||||
rangeBefore = new Range([0, 0], range.start)
|
||||
rangeAfter = new Range(range.end, buffer.getEofPosition())
|
||||
|
||||
rangeToSearch = if @isReversed then rangeBefore else rangeAfter
|
||||
|
||||
rangeToReturn = null
|
||||
scanMethodName = if @isReversed then "backwardsScanInRange" else "scanInRange"
|
||||
buffer[scanMethodName] @regex, rangeToSearch, ({range}) ->
|
||||
rangeToReturn = range
|
||||
|
||||
if not rangeToReturn
|
||||
rangeToSearch = if @isReversed then rangeAfter else rangeBefore
|
||||
buffer[scanMethodName] @regex, rangeToSearch, ({range}) ->
|
||||
rangeToReturn = range
|
||||
|
||||
if not rangeToReturn
|
||||
flags = ""
|
||||
flags += "i" if @regex.ignoreCase
|
||||
flags += "g" if @regex.global
|
||||
flags += "m" if @regex.multiline
|
||||
@errorMessage = "Pattern not found /#{@regex.source}/#{flags}"
|
||||
|
||||
rangeToReturn or range
|
||||
|
||||
isRelative: -> true
|
||||
|
||||
reverse: ->
|
||||
new RegexAddress(@regex, !@isReversed)
|
||||
@@ -1,24 +0,0 @@
|
||||
Command = require './command'
|
||||
Operation = require 'command-panel/lib/operation'
|
||||
$ = require 'jquery'
|
||||
|
||||
module.exports =
|
||||
class SelectAllMatchesInProject extends Command
|
||||
regex: null
|
||||
previewOperations: true
|
||||
|
||||
constructor: (pattern) ->
|
||||
@regex = new RegExp(pattern)
|
||||
|
||||
compile: (project, buffer, range) ->
|
||||
deferred = $.Deferred()
|
||||
operations = []
|
||||
promise = project.scan @regex, ({path, range}) ->
|
||||
operations.push(new Operation(
|
||||
project: project
|
||||
path: path
|
||||
bufferRange: range
|
||||
))
|
||||
|
||||
promise.done -> deferred.resolve(operations)
|
||||
deferred.promise()
|
||||
@@ -1,23 +0,0 @@
|
||||
Command = require './command'
|
||||
Operation = require 'command-panel/lib/operation'
|
||||
$ = require 'jquery'
|
||||
|
||||
module.exports =
|
||||
class SelectAllMatches extends Command
|
||||
regex: null
|
||||
|
||||
constructor: (pattern) ->
|
||||
@regex = new RegExp(pattern, 'g')
|
||||
|
||||
compile: (project, buffer, ranges) ->
|
||||
deferred = $.Deferred()
|
||||
operations = []
|
||||
for scanRange in ranges
|
||||
buffer.scanInRange @regex, scanRange, ({range}) ->
|
||||
operations.push(new Operation(
|
||||
project: project
|
||||
buffer: buffer
|
||||
bufferRange: range
|
||||
))
|
||||
deferred.resolve(operations)
|
||||
deferred.promise()
|
||||
@@ -1,28 +0,0 @@
|
||||
Command = require './command'
|
||||
Operation = require 'command-panel/lib/operation'
|
||||
$ = require 'jquery'
|
||||
|
||||
module.exports =
|
||||
class Substitution extends Command
|
||||
regex: null
|
||||
replacementText: null
|
||||
preserveSelections: true
|
||||
|
||||
constructor: (pattern, replacementText, options) ->
|
||||
@replacementText = replacementText
|
||||
@regex = new RegExp(pattern, options.join(''))
|
||||
|
||||
compile: (project, buffer, ranges) ->
|
||||
deferred = $.Deferred()
|
||||
operations = []
|
||||
for scanRange in ranges
|
||||
buffer.scanInRange @regex, scanRange, ({range}) =>
|
||||
operations.push(new Operation(
|
||||
project: project
|
||||
buffer: buffer
|
||||
bufferRange: range
|
||||
newText: @replacementText
|
||||
preserveSelection: true
|
||||
))
|
||||
deferred.resolve(operations)
|
||||
deferred.promise()
|
||||
@@ -1,9 +0,0 @@
|
||||
Address = require './address'
|
||||
Range = require 'range'
|
||||
|
||||
module.exports =
|
||||
class ZeroAddress extends Address
|
||||
getRange: ->
|
||||
new Range([0, 0], [0, 0])
|
||||
|
||||
isRelative: -> false
|
||||
@@ -1,33 +0,0 @@
|
||||
{View} = require 'space-pen'
|
||||
|
||||
module.exports =
|
||||
class OperationView extends View
|
||||
@content: ({operation} = {}) ->
|
||||
{prefix, suffix, match, range} = operation.preview()
|
||||
@li class: 'operation', =>
|
||||
@span range.start.row + 1, class: 'line-number'
|
||||
@span class: 'preview', =>
|
||||
@span prefix
|
||||
@span match, class: 'match'
|
||||
@span suffix
|
||||
|
||||
initialize: ({@previewList, @operation}) ->
|
||||
@subscribe @previewList, 'core:confirm', =>
|
||||
if @hasClass('selected')
|
||||
@executeOperation()
|
||||
false
|
||||
@on 'mousedown', (e) =>
|
||||
@executeOperation()
|
||||
@previewList.find('.selected').removeClass('selected')
|
||||
@addClass('selected')
|
||||
|
||||
executeOperation: ->
|
||||
editSession = rootView.open(@operation.getPath())
|
||||
bufferRange = @operation.execute(editSession)
|
||||
editSession.setSelectedBufferRange(bufferRange, autoscroll: true) if bufferRange
|
||||
@previewList.focus()
|
||||
|
||||
scrollTo: ->
|
||||
top = @previewList.scrollTop() + @offset().top - @previewList.offset().top
|
||||
bottom = top + @outerHeight()
|
||||
@previewList.scrollTo(top, bottom)
|
||||
@@ -1,36 +0,0 @@
|
||||
module.exports =
|
||||
class Operation
|
||||
constructor: ({@project, @path, @buffer, @bufferRange, @newText, @preserveSelection, @errorMessage}) ->
|
||||
if @buffer?
|
||||
@buffer.retain()
|
||||
@getMarker()
|
||||
|
||||
getMarker: ->
|
||||
@marker ?= @getBuffer().markRange(@bufferRange)
|
||||
|
||||
getBuffer: ->
|
||||
@buffer ?= @project.bufferForPath(@path).retain()
|
||||
|
||||
getPath: ->
|
||||
path = @path ? @getBuffer().getPath()
|
||||
@project.relativize(path)
|
||||
|
||||
getBufferRange: ->
|
||||
@getMarker().getRange()
|
||||
|
||||
execute: (editSession) ->
|
||||
@getBuffer().change(@getBufferRange(), @newText) if @newText?
|
||||
@getBufferRange() unless @preserveSelection
|
||||
|
||||
preview: ->
|
||||
range = @getBufferRange()
|
||||
line = @getBuffer().lineForRow(range.start.row)
|
||||
prefix = line[0...range.start.column]
|
||||
match = line[range.start.column...range.end.column]
|
||||
suffix = line[range.end.column..]
|
||||
|
||||
{prefix, suffix, match, range}
|
||||
|
||||
destroy: ->
|
||||
@marker?.destroy()
|
||||
@buffer?.release()
|
||||
@@ -1,60 +0,0 @@
|
||||
{View} = require 'space-pen'
|
||||
fsUtils = require 'fs-utils'
|
||||
OperationView = require './operation-view'
|
||||
$ = require 'jquery'
|
||||
|
||||
module.exports =
|
||||
class PathView extends View
|
||||
@content: ({path, previewList} = {}) ->
|
||||
classes = ['path']
|
||||
classes.push('readme') if fsUtils.isReadmePath(path)
|
||||
@li class: classes.join(' '), =>
|
||||
@div outlet: 'pathDetails', class: 'path-details', =>
|
||||
@span class: 'path-name', path
|
||||
@span outlet: 'description', class: 'path-match-number'
|
||||
@ul outlet: 'matches', class: 'matches', =>
|
||||
|
||||
initialize: ({@previewList, operationCount}) ->
|
||||
@pathDetails.on 'mousedown', => @toggle(true)
|
||||
@subscribe @previewList, 'command-panel:collapse-result', =>
|
||||
if @isSelected()
|
||||
@collapse()
|
||||
@previewList.renderOperations()
|
||||
@subscribe @previewList, 'command-panel:expand-result', =>
|
||||
@expand() if @isSelected()
|
||||
@subscribe @previewList, 'core:confirm', =>
|
||||
if @hasClass('selected')
|
||||
@toggle(true)
|
||||
false
|
||||
|
||||
@description.text("(#{operationCount})")
|
||||
|
||||
addOperation: (operation) ->
|
||||
@matches.append new OperationView({operation, @previewList})
|
||||
|
||||
isSelected: ->
|
||||
@hasClass('selected') or @find('.selected').length
|
||||
|
||||
setSelected: ->
|
||||
@previewList.find('.selected').removeClass('selected')
|
||||
@addClass('selected')
|
||||
|
||||
toggle: ->
|
||||
if @hasClass('is-collapsed')
|
||||
@expand()
|
||||
else
|
||||
@collapse()
|
||||
|
||||
expand: ->
|
||||
@matches.show()
|
||||
@removeClass 'is-collapsed'
|
||||
|
||||
scrollTo: ->
|
||||
top = @previewList.scrollTop() + @offset().top - @previewList.offset().top
|
||||
bottom = top + @pathDetails.outerHeight()
|
||||
@previewList.scrollTo(top, bottom)
|
||||
|
||||
collapse: ->
|
||||
@matches.hide()
|
||||
@addClass 'is-collapsed'
|
||||
@setSelected() if @isSelected()
|
||||
@@ -1,133 +0,0 @@
|
||||
$ = require 'jquery'
|
||||
ScrollView = require 'scroll-view'
|
||||
_ = require 'underscore'
|
||||
PathView = require './path-view'
|
||||
OperationView = require './operation-view'
|
||||
|
||||
module.exports =
|
||||
class PreviewList extends ScrollView
|
||||
@content: ->
|
||||
@ol class: 'preview-list', tabindex: -1
|
||||
|
||||
operations: null
|
||||
viewsForPath: null
|
||||
pixelOverdraw: 100
|
||||
lastRenderedOperationIndex: null
|
||||
|
||||
initialize: ->
|
||||
super
|
||||
|
||||
@on 'core:move-down', => @selectNextOperation(); false
|
||||
@on 'core:move-up', => @selectPreviousOperation(); false
|
||||
@on 'scroll', =>
|
||||
@renderOperations() if @scrollBottom() >= @prop('scrollHeight')
|
||||
@command 'command-panel:collapse-all', => @collapseAllPaths()
|
||||
@command 'command-panel:expand-all', => @expandAllPaths()
|
||||
|
||||
expandAllPaths: ->
|
||||
@children().each (index, element) -> $(element).view().expand()
|
||||
|
||||
collapseAllPaths: ->
|
||||
@renderOperations(renderAll: true)
|
||||
@children().each (index, element) -> $(element).view().collapse()
|
||||
|
||||
destroy: ->
|
||||
@destroyOperations() if @operations
|
||||
|
||||
hasOperations: -> @operations?
|
||||
|
||||
populate: (operations) ->
|
||||
@destroyOperations() if @operations
|
||||
@operations = operations
|
||||
@lastRenderedOperationIndex = 0
|
||||
@empty()
|
||||
@viewsForPath = {}
|
||||
|
||||
@show()
|
||||
@renderOperations()
|
||||
|
||||
@find('.operation:first').addClass('selected')
|
||||
|
||||
renderOperations: ({renderAll}={}) ->
|
||||
renderAll ?= false
|
||||
startingScrollHeight = @prop('scrollHeight')
|
||||
for operation in @operations[@lastRenderedOperationIndex..]
|
||||
pathView = @pathViewForPath(operation.getPath())
|
||||
pathView.addOperation(operation)
|
||||
@lastRenderedOperationIndex++
|
||||
break if not renderAll and @prop('scrollHeight') >= startingScrollHeight + @pixelOverdraw and @prop('scrollHeight') > @height() + @pixelOverdraw
|
||||
|
||||
pathViewForPath: (path) ->
|
||||
pathView = @viewsForPath[path]
|
||||
if not pathView
|
||||
pathView = new PathView({path: path, previewList: this, operationCount: @getPathOperationCount(path)})
|
||||
@viewsForPath[path] = pathView
|
||||
@append(pathView)
|
||||
pathView
|
||||
|
||||
selectNextOperation: ->
|
||||
selectedView = @find('.selected').view()
|
||||
nextView = selectedView.next().view()
|
||||
|
||||
if selectedView instanceof PathView
|
||||
nextView = selectedView.find('.operation:first').view() unless selectedView.hasClass('is-collapsed')
|
||||
else
|
||||
nextView ?= selectedView.closest('.path').next().view()
|
||||
|
||||
if nextView?
|
||||
selectedView.removeClass('selected')
|
||||
nextView.addClass('selected')
|
||||
nextView.scrollTo()
|
||||
|
||||
selectPreviousOperation: ->
|
||||
selectedView = @find('.selected').view()
|
||||
previousView = selectedView.prev().view()
|
||||
|
||||
if selectedView instanceof PathView
|
||||
if previousView? and not previousView.hasClass('is-collapsed')
|
||||
previousView = previousView.find('.operation:last').view()
|
||||
else
|
||||
previousView ?= selectedView.closest('.path').view()
|
||||
|
||||
if previousView?
|
||||
selectedView.removeClass('selected')
|
||||
previousView.addClass('selected')
|
||||
previousView.scrollTo()
|
||||
|
||||
getPathCount: ->
|
||||
_.keys(_.groupBy(@operations, (operation) -> operation.getPath())).length
|
||||
|
||||
getPathOperationCount: (path) ->
|
||||
@operations.filter((operation) -> path is operation.getPath()).length
|
||||
|
||||
getOperations: ->
|
||||
new Array(@operations...)
|
||||
|
||||
destroyOperations: ->
|
||||
operation.destroy() for operation in @getOperations()
|
||||
@operations = null
|
||||
|
||||
getSelectedOperation: ->
|
||||
@find('.operation.selected').view()?.operation
|
||||
|
||||
scrollTo: (top, bottom) ->
|
||||
@scrollBottom(bottom) if bottom > @scrollBottom()
|
||||
@scrollTop(top) if top < @scrollTop()
|
||||
|
||||
scrollToBottom: ->
|
||||
@renderOperations(renderAll: true)
|
||||
|
||||
super()
|
||||
|
||||
@find('.selected').removeClass('selected')
|
||||
lastPath = @find('.path:last')
|
||||
if lastPath.hasClass('is-collapsed')
|
||||
lastPath.addClass('selected')
|
||||
else
|
||||
lastPath.find('.operation:last').addClass('selected')
|
||||
|
||||
scrollToTop: ->
|
||||
super()
|
||||
|
||||
@find('.selected').removeClass('selected')
|
||||
@find('.path:first').addClass('selected')
|
||||
@@ -1,11 +0,0 @@
|
||||
'main': './lib/command-panel'
|
||||
'description': 'Find and replace text for the current editor or project.'
|
||||
'activationEvents': [
|
||||
'command-panel:toggle'
|
||||
'command-panel:toggle-preview'
|
||||
'command-panel:find-in-file'
|
||||
'command-panel:find-in-project'
|
||||
'command-panel:repeat-relative-address'
|
||||
'command-panel:repeat-relative-address-in-reverse'
|
||||
'command-panel:set-selection-as-regex-address'
|
||||
]
|
||||
@@ -1,465 +0,0 @@
|
||||
CommandInterpreter = require 'command-panel/lib/command-interpreter'
|
||||
Project = require 'project'
|
||||
Buffer = require 'text-buffer'
|
||||
EditSession = require 'edit-session'
|
||||
|
||||
_ = require 'underscore'
|
||||
|
||||
describe "CommandInterpreter", ->
|
||||
[interpreter, editSession, buffer] = []
|
||||
|
||||
beforeEach ->
|
||||
interpreter = new CommandInterpreter(project)
|
||||
editSession = project.open('sample.js')
|
||||
buffer = editSession.buffer
|
||||
|
||||
afterEach ->
|
||||
editSession?.destroy()
|
||||
expect(buffer.getMarkerCount()).toBe 0
|
||||
|
||||
describe "addresses", ->
|
||||
beforeEach ->
|
||||
editSession.addSelectionForBufferRange([[7,0], [7,11]])
|
||||
editSession.addSelectionForBufferRange([[8,0], [8,11]])
|
||||
|
||||
describe "a line address", ->
|
||||
it "selects the specified line", ->
|
||||
waitsForPromise -> interpreter.eval('4', editSession)
|
||||
runs ->
|
||||
expect(editSession.getSelections().length).toBe 1
|
||||
expect(editSession.getSelection().getBufferRange()).toEqual [[3, 0], [4, 0]]
|
||||
|
||||
describe "0", ->
|
||||
it "selects the zero-length string at the start of the file", ->
|
||||
waitsForPromise -> interpreter.eval('0', editSession)
|
||||
runs ->
|
||||
expect(editSession.getSelections().length).toBe 1
|
||||
expect(editSession.getSelection().getBufferRange()).toEqual [[0,0], [0,0]]
|
||||
|
||||
interpreter.eval('0,1', editSession)
|
||||
expect(editSession.getSelections().length).toBe 1
|
||||
expect(editSession.getSelection().getBufferRange()).toEqual [[0,0], [1,0]]
|
||||
|
||||
describe "$", ->
|
||||
it "selects EOF", ->
|
||||
waitsForPromise -> interpreter.eval('$', editSession)
|
||||
runs ->
|
||||
expect(editSession.getSelections().length).toBe 1
|
||||
expect(editSession.getSelection().getBufferRange()).toEqual [[12,2], [12,2]]
|
||||
|
||||
waitsForPromise -> interpreter.eval('1,$', editSession)
|
||||
runs ->
|
||||
expect(editSession.getSelections().length).toBe 1
|
||||
expect(editSession.getSelection().getBufferRange()).toEqual [[0,0], [12,2]]
|
||||
|
||||
describe ".", ->
|
||||
describe "when a single selection", ->
|
||||
it 'maintains the current selection', ->
|
||||
editSession.clearSelections()
|
||||
|
||||
waitsForPromise ->
|
||||
editSession.setSelectedBufferRange([[1,1], [2,2]])
|
||||
interpreter.eval('.', editSession)
|
||||
|
||||
runs ->
|
||||
expect(editSession.getSelection().getBufferRange()).toEqual [[1,1], [2,2]]
|
||||
|
||||
waitsForPromise ->
|
||||
editSession.setSelectedBufferRange([[1,1], [2,2]])
|
||||
interpreter.eval('.,', editSession)
|
||||
|
||||
runs ->
|
||||
expect(editSession.getSelection().getBufferRange()).toEqual [[1,1], [12,2]]
|
||||
|
||||
waitsForPromise ->
|
||||
editSession.setSelectedBufferRange([[1,1], [2,2]])
|
||||
interpreter.eval(',.', editSession)
|
||||
|
||||
runs ->
|
||||
expect(editSession.getSelection().getBufferRange()).toEqual [[0,0], [2,2]]
|
||||
|
||||
describe "with multiple selections", ->
|
||||
it "maintains the current selections", ->
|
||||
preSelections = editSession.getSelections()
|
||||
expect(preSelections.length).toBe 3
|
||||
[preRange1, preRange2, preRange3] = preSelections.map (s) -> s.getScreenRange()
|
||||
|
||||
waitsForPromise -> interpreter.eval('.', editSession)
|
||||
|
||||
runs ->
|
||||
selections = editSession.getSelections()
|
||||
expect(selections.length).toBe 3
|
||||
[selection1, selection2, selection3] = selections
|
||||
expect(selection1.getScreenRange()).toEqual preRange1
|
||||
expect(selection2.getScreenRange()).toEqual preRange2
|
||||
expect(selection3.getScreenRange()).toEqual preRange3
|
||||
|
||||
describe "/regex/", ->
|
||||
beforeEach ->
|
||||
editSession.clearSelections()
|
||||
|
||||
it 'selects text matching regex after current selection', ->
|
||||
waitsForPromise ->
|
||||
editSession.setSelectedBufferRange([[4,16], [4,20]])
|
||||
interpreter.eval('/pivot/', editSession)
|
||||
|
||||
runs ->
|
||||
expect(editSession.getSelection().getBufferRange()).toEqual [[6,16], [6,21]]
|
||||
|
||||
it 'does not require the trailing slash', ->
|
||||
waitsForPromise ->
|
||||
editSession.setSelectedBufferRange([[4,16], [4,20]])
|
||||
interpreter.eval('/pivot', editSession)
|
||||
|
||||
runs ->
|
||||
expect(editSession.getSelection().getBufferRange()).toEqual [[6,16], [6,21]]
|
||||
|
||||
it "searches from the end of each selection in the buffer", ->
|
||||
waitsForPromise ->
|
||||
editSession.clearSelections()
|
||||
editSession.setSelectedBufferRange([[4,16], [4,20]])
|
||||
editSession.addSelectionForBufferRange([[1,16], [2,20]])
|
||||
expect(editSession.getSelections().length).toBe 2
|
||||
interpreter.eval('/pivot', editSession)
|
||||
|
||||
runs ->
|
||||
selections = editSession.getSelections()
|
||||
expect(selections.length).toBe 2
|
||||
expect(selections[0].getBufferRange()).toEqual [[3,8], [3,13]]
|
||||
expect(selections[1].getBufferRange()).toEqual [[6,16], [6,21]]
|
||||
|
||||
it "wraps around to the beginning of the buffer, but doesn't infinitely loop if no matches are found", ->
|
||||
waitsForPromise ->
|
||||
editSession.setSelectedBufferRange([[10, 0], [10,3]])
|
||||
interpreter.eval('/pivot', editSession)
|
||||
|
||||
runs ->
|
||||
expect(editSession.getSelection().getBufferRange()).toEqual [[3,8], [3,13]]
|
||||
|
||||
waitsForPromise ->
|
||||
interpreter.eval('/mike tyson', editSession)
|
||||
|
||||
runs ->
|
||||
expect(editSession.getSelection().getBufferRange()).toEqual [[3,8], [3,13]]
|
||||
|
||||
it "searches in reverse when prefixed with a -", ->
|
||||
waitsForPromise ->
|
||||
editSession.setSelectedBufferRange([[6, 16], [6, 22]])
|
||||
interpreter.eval('-/pivot', editSession)
|
||||
|
||||
runs ->
|
||||
expect(editSession.getSelection().getBufferRange()).toEqual [[3,8], [3,13]]
|
||||
|
||||
it "removes folds that contain the selections", ->
|
||||
waitsForPromise ->
|
||||
editSession.createFold(5, 6)
|
||||
editSession.createFold(10, 11)
|
||||
editSession.setSelectedBufferRange([[4,16], [4,20]])
|
||||
interpreter.eval('/pivot/', editSession)
|
||||
|
||||
runs ->
|
||||
expect(editSession.getSelection().getBufferRange()).toEqual [[6,16], [6,21]]
|
||||
expect(editSession.lineForScreenRow(5).fold).toBeUndefined()
|
||||
expect(editSession.lineForScreenRow(10).fold).toBeDefined()
|
||||
editSession.unfoldAll() # cleanup fold marker for after assertion
|
||||
|
||||
it "is case-insentive when the pattern contains no non-escaped uppercase letters (behavior copied from vim)", ->
|
||||
waitsForPromise ->
|
||||
interpreter.eval('/array', editSession)
|
||||
runs ->
|
||||
expect(interpreter.lastRelativeAddress.subcommands[0].regex.toString()).toEqual "/array/i"
|
||||
|
||||
waitsForPromise ->
|
||||
interpreter.eval('/a\\Sray', editSession)
|
||||
runs ->
|
||||
expect(interpreter.lastRelativeAddress.subcommands[0].regex.toString()).toEqual "/a\\Sray/i"
|
||||
|
||||
it "allows the regex to contain an escaped forward slash", ->
|
||||
buffer.setText "hey/baby"
|
||||
|
||||
waitsForPromise ->
|
||||
interpreter.eval('/y\\/b/', editSession)
|
||||
runs ->
|
||||
expect(editSession.getSelectedText()).toBe "y/b"
|
||||
|
||||
it "does not push to the undo stack (since the buffer is not modified)", ->
|
||||
waitsForPromise ->
|
||||
editSession.setSelectedBufferRange([[4,16], [4,20]])
|
||||
interpreter.eval('/pivot/', editSession)
|
||||
|
||||
runs ->
|
||||
selectedRangeBeforeUndo = editSession.getSelection().getBufferRange()
|
||||
editSession.undo()
|
||||
expect(editSession.getSelection().getBufferRange()).toEqual selectedRangeBeforeUndo
|
||||
|
||||
describe "when no match is found", ->
|
||||
it "it returns an error messages", ->
|
||||
errorMessages = null
|
||||
waitsForPromise ->
|
||||
interpreter.eval('/garbage!', editSession).done (results) -> { errorMessages } = results
|
||||
|
||||
runs ->
|
||||
expect(errorMessages.length).toBe 1
|
||||
|
||||
waitsForPromise ->
|
||||
interpreter.eval('/Array', editSession)
|
||||
runs ->
|
||||
expect(interpreter.lastRelativeAddress.subcommands[0].regex.toString()).toEqual "/Array/"
|
||||
|
||||
describe "when there is no active edit session", ->
|
||||
it "returns no error messages and does not throw an error", ->
|
||||
errorMessages = null
|
||||
|
||||
waitsForPromise ->
|
||||
interpreter.eval('/something').done (results) ->
|
||||
{errorMessages} = results
|
||||
|
||||
runs ->
|
||||
expect(errorMessages.length).toBe 0
|
||||
|
||||
describe "address range", ->
|
||||
describe "when two addresses are specified", ->
|
||||
it "selects from the begining of the left address to the end of the right address", ->
|
||||
waitsForPromise -> interpreter.eval('4,7', editSession)
|
||||
|
||||
runs ->
|
||||
expect(editSession.getSelections().length).toBe 1
|
||||
expect(editSession.getSelection().getBufferRange()).toEqual [[3, 0], [7, 0]]
|
||||
|
||||
describe "when the left address is unspecified", ->
|
||||
it "selects from the begining of buffer to the end of the right address", ->
|
||||
waitsForPromise -> interpreter.eval(',7', editSession)
|
||||
runs ->
|
||||
expect(editSession.getSelections().length).toBe 1
|
||||
expect(editSession.getSelection().getBufferRange()).toEqual [[0, 0], [7, 0]]
|
||||
|
||||
describe "when the right address is unspecified", ->
|
||||
it "selects from the begining of left address to the end file", ->
|
||||
waitsForPromise -> interpreter.eval('4,', editSession)
|
||||
runs ->
|
||||
expect(editSession.getSelections().length).toBe 1
|
||||
expect(editSession.getSelection().getBufferRange()).toEqual [[3, 0], [12, 2]]
|
||||
|
||||
describe "when the neither address is specified", ->
|
||||
it "selects the entire file", ->
|
||||
waitsForPromise -> interpreter.eval(',', editSession)
|
||||
runs ->
|
||||
expect(editSession.getSelections().length).toBe 1
|
||||
expect(editSession.getSelection().getBufferRange()).toEqual [[0, 0], [12, 2]]
|
||||
|
||||
describe "x/regex/", ->
|
||||
it "sets the current selection to every match of the regex in the current selection", ->
|
||||
waitsForPromise -> interpreter.eval('6,7 x/current/', editSession)
|
||||
|
||||
runs ->
|
||||
selections = editSession.getSelections()
|
||||
expect(selections.length).toBe 4
|
||||
|
||||
expect(selections[0].getBufferRange()).toEqual [[5,6], [5,13]]
|
||||
expect(selections[1].getBufferRange()).toEqual [[6,6], [6,13]]
|
||||
expect(selections[2].getBufferRange()).toEqual [[6,34], [6,41]]
|
||||
expect(selections[3].getBufferRange()).toEqual [[6,56], [6,63]]
|
||||
|
||||
describe "when there is no address range is given", ->
|
||||
describe "when there is no text selection", ->
|
||||
it "uses the entire file as the address range", ->
|
||||
waitsForPromise ->
|
||||
editSession.clearSelections()
|
||||
interpreter.eval('x/return', editSession)
|
||||
runs ->
|
||||
expect(editSession.getSelectedBufferRanges()).toEqual [
|
||||
[[2,27],[2,33]]
|
||||
[[8,4], [8,10]]
|
||||
[[11,2],[11,8]]
|
||||
]
|
||||
|
||||
describe "when text is selected", ->
|
||||
it "uses the selection as the address range", ->
|
||||
waitsForPromise ->
|
||||
editSession.setSelectedBufferRange([[2, 0], [9, 0]])
|
||||
interpreter.eval('x/return', editSession)
|
||||
runs ->
|
||||
expect(editSession.getSelectedBufferRanges()).toEqual [
|
||||
[[2,27],[2,33]]
|
||||
[[8,4], [8,10]]
|
||||
]
|
||||
|
||||
describe "when matching /$/", ->
|
||||
it "matches the end of each line in the selected region", ->
|
||||
waitsForPromise -> interpreter.eval('6,8 x/$/', editSession)
|
||||
|
||||
runs ->
|
||||
cursors = editSession.getCursors()
|
||||
expect(cursors.length).toBe 3
|
||||
|
||||
expect(cursors[0].getBufferPosition()).toEqual [5, 30]
|
||||
expect(cursors[1].getBufferPosition()).toEqual [6, 65]
|
||||
expect(cursors[2].getBufferPosition()).toEqual [7, 5]
|
||||
|
||||
describe "when text is initially selected", ->
|
||||
it "loops through current selections and selects text matching the regex", ->
|
||||
waitsForPromise ->
|
||||
editSession.setSelectedBufferRange [[3,0], [3,62]]
|
||||
editSession.addSelectionForBufferRange [[6,0], [6,65]]
|
||||
interpreter.eval('x/current', editSession)
|
||||
|
||||
runs ->
|
||||
selections = editSession.getSelections()
|
||||
expect(selections.length).toBe 4
|
||||
|
||||
expect(selections[0].getBufferRange()).toEqual [[3,31], [3,38]]
|
||||
expect(selections[1].getBufferRange()).toEqual [[6,6], [6,13]]
|
||||
expect(selections[2].getBufferRange()).toEqual [[6,34], [6,41]]
|
||||
expect(selections[3].getBufferRange()).toEqual [[6,56], [6,63]]
|
||||
|
||||
describe "substitution", ->
|
||||
describe "when there is no address range is given", ->
|
||||
describe "when there is no text selection", ->
|
||||
it "uses the entire file as the address range", ->
|
||||
waitsForPromise ->
|
||||
editSession.clearSelections()
|
||||
interpreter.eval('s/current/foo/g', editSession)
|
||||
runs ->
|
||||
expect(buffer.lineForRow(3)).toBe ' var pivot = items.shift(), foo, left = [], right = [];'
|
||||
expect(buffer.lineForRow(6)).toBe ' foo < pivot ? left.push(foo) : right.push(foo);'
|
||||
|
||||
describe "when text is selected", ->
|
||||
it "uses the selection as the address range", ->
|
||||
waitsForPromise ->
|
||||
editSession.setSelectedBufferRange([[6, 0], [6, 44]])
|
||||
interpreter.eval('s/current/foo/g', editSession)
|
||||
runs ->
|
||||
expect(buffer.lineForRow(3)).toBe ' var pivot = items.shift(), current, left = [], right = [];'
|
||||
expect(buffer.lineForRow(6)).toBe ' foo < pivot ? left.push(foo) : right.push(current);'
|
||||
|
||||
describe "when not global", ->
|
||||
describe "when there is a single selection", ->
|
||||
it "performs a single substitution within the current selection", ->
|
||||
waitsForPromise ->
|
||||
editSession.setSelectedBufferRange([[6, 0], [6, 44]])
|
||||
interpreter.eval('s/current/foo/', editSession)
|
||||
runs ->
|
||||
expect(buffer.lineForRow(6)).toBe ' foo < pivot ? left.push(current) : right.push(current);'
|
||||
|
||||
describe "when there are multiple selections", ->
|
||||
it "performs a single substitutions within each of the selections", ->
|
||||
waitsForPromise ->
|
||||
editSession.setSelectedBufferRange([[5, 0], [5, 20]])
|
||||
editSession.addSelectionForBufferRange([[6, 0], [6, 44]])
|
||||
interpreter.eval('s/current/foo/', editSession)
|
||||
|
||||
runs ->
|
||||
expect(buffer.lineForRow(5)).toBe ' foo = items.shift();'
|
||||
expect(buffer.lineForRow(6)).toBe ' foo < pivot ? left.push(current) : right.push(current);'
|
||||
|
||||
describe "when global", ->
|
||||
it "performs a multiple substitutions within the current selection as a batch that can be undone in a single operation", ->
|
||||
waitsForPromise ->
|
||||
editSession.setSelectedBufferRange([[6, 0], [6, 44]])
|
||||
interpreter.eval('s/current/foo/g', editSession)
|
||||
|
||||
runs ->
|
||||
expect(buffer.lineForRow(6)).toBe ' foo < pivot ? left.push(foo) : right.push(current);'
|
||||
buffer.undo()
|
||||
expect(buffer.getText()).not.toContain('foo')
|
||||
|
||||
describe "when prefixed with an address", ->
|
||||
it "only makes substitutions within given lines", ->
|
||||
waitsForPromise -> interpreter.eval('4,6s/ /!/g', editSession)
|
||||
|
||||
runs ->
|
||||
expect(buffer.lineForRow(2)).toBe ' if (items.length <= 1) return items;'
|
||||
expect(buffer.lineForRow(3)).toBe '!!!!var!pivot!=!items.shift(),!current,!left!=![],!right!=![];'
|
||||
expect(buffer.lineForRow(4)).toBe '!!!!while(items.length!>!0)!{'
|
||||
expect(buffer.lineForRow(5)).toBe '!!!!!!current!=!items.shift();'
|
||||
expect(buffer.lineForRow(6)).toBe ' current < pivot ? left.push(current) : right.push(current);'
|
||||
|
||||
describe "when matching $", ->
|
||||
it "matches the end of each line and avoids infinitely looping on a zero-width match", ->
|
||||
waitsForPromise -> interpreter.eval(',s/$/!!!/g', editSession)
|
||||
runs ->
|
||||
expect(buffer.lineForRow(0)).toBe 'var quicksort = function () {!!!'
|
||||
expect(buffer.lineForRow(2)).toBe ' if (items.length <= 1) return items;!!!'
|
||||
expect(buffer.lineForRow(6)).toBe ' current < pivot ? left.push(current) : right.push(current);!!!'
|
||||
expect(buffer.lineForRow(12)).toBe '};!!!'
|
||||
|
||||
describe "when matching ^", ->
|
||||
it "matches the beginning of each line and avoids infinitely looping on a zero-width match", ->
|
||||
waitsForPromise -> interpreter.eval(',s/^/!!!/g', editSession)
|
||||
runs ->
|
||||
expect(buffer.lineForRow(0)).toBe '!!!var quicksort = function () {'
|
||||
expect(buffer.lineForRow(2)).toBe '!!! if (items.length <= 1) return items;'
|
||||
expect(buffer.lineForRow(6)).toBe '!!! current < pivot ? left.push(current) : right.push(current);'
|
||||
expect(buffer.lineForRow(12)).toBe '!!!};'
|
||||
|
||||
describe "when there are multiple selections", ->
|
||||
it "performs a multiple substitutions within each of the selections", ->
|
||||
waitsForPromise ->
|
||||
editSession.setSelectedBufferRange([[5, 0], [5, 20]])
|
||||
editSession.addSelectionForBufferRange([[6, 0], [6, 44]])
|
||||
interpreter.eval('s/current/foo/g', editSession)
|
||||
|
||||
runs ->
|
||||
expect(buffer.lineForRow(5)).toBe ' foo = items.shift();'
|
||||
expect(buffer.lineForRow(6)).toBe ' foo < pivot ? left.push(foo) : right.push(current);'
|
||||
|
||||
describe "when prefixed with an address", ->
|
||||
it "restores the original selections upon completion if it is the last command", ->
|
||||
waitsForPromise ->
|
||||
editSession.setSelectedBufferRanges([[[5, 0], [5, 20]], [[6, 0], [6, 44]]])
|
||||
interpreter.eval(',s/current/foo/g', editSession)
|
||||
|
||||
runs ->
|
||||
expect(editSession.getSelectedBufferRanges()).toEqual [[[5, 0], [5, 16]], [[6, 0], [6, 36]]]
|
||||
|
||||
it "does nothing if there are no matches", ->
|
||||
waitsForPromise ->
|
||||
editSession.setSelectedBufferRange([[6, 0], [6, 44]])
|
||||
interpreter.eval('s/not-in-text/foo/', editSession)
|
||||
|
||||
runs ->
|
||||
expect(buffer.lineForRow(6)).toBe ' current < pivot ? left.push(current) : right.push(current);'
|
||||
|
||||
it "properly handles escaped text in the replacement text", ->
|
||||
waitsForPromise ->
|
||||
interpreter.eval('s/ /\\t/g', editSession)
|
||||
runs ->
|
||||
expect(buffer.lineForRow(6)).toBe '\t\t\tcurrent < pivot ? left.push(current) : right.push(current);'
|
||||
|
||||
it "removes matched text when an empty string is given as the replacement", ->
|
||||
waitsForPromise ->
|
||||
interpreter.eval('s/items//', editSession)
|
||||
runs ->
|
||||
expect(buffer.lineForRow(1)).toBe(' var sort = function() {')
|
||||
|
||||
describe "X x/regex/", ->
|
||||
it "returns selection operations for all regex matches in all the project's files", ->
|
||||
editSession.destroy()
|
||||
project.setPath(project.resolve('dir'))
|
||||
interpreter = new CommandInterpreter(project)
|
||||
|
||||
operationsToPreview = null
|
||||
waitsForPromise ->
|
||||
interpreter.eval("X x/a+/").done (result) -> {operationsToPreview} = result
|
||||
|
||||
runs ->
|
||||
expect(operationsToPreview.length).toBeGreaterThan 3
|
||||
for operation in operationsToPreview
|
||||
editSession = project.open(operation.getPath())
|
||||
editSession.setSelectedBufferRange(operation.execute(editSession))
|
||||
expect(editSession.getSelectedText()).toMatch /a+/
|
||||
editSession.destroy()
|
||||
operation.destroy()
|
||||
|
||||
editSession = null
|
||||
|
||||
describe "nested commands", ->
|
||||
describe "/regex/ /regex", ->
|
||||
it "returns an error message if the last regex has no matches", ->
|
||||
previousSelections = null
|
||||
errorMessages = null
|
||||
waitsForPromise ->
|
||||
previousSelections = editSession.getSelectedBufferRanges()
|
||||
interpreter.eval('/sort/ /no match', editSession).done (results) -> { errorMessages } = results
|
||||
runs ->
|
||||
expect(errorMessages.length).toBe 1
|
||||
@@ -1,597 +0,0 @@
|
||||
RootView = require 'root-view'
|
||||
CommandPanelView = require 'command-panel/lib/command-panel-view'
|
||||
shell = require 'shell'
|
||||
_ = require 'underscore'
|
||||
|
||||
describe "CommandPanel", ->
|
||||
[editSession, buffer, commandPanel] = []
|
||||
|
||||
beforeEach ->
|
||||
window.rootView = new RootView
|
||||
rootView.open('sample.js')
|
||||
rootView.enableKeymap()
|
||||
editSession = rootView.getActivePaneItem()
|
||||
buffer = editSession.buffer
|
||||
commandPanelMain = atom.activatePackage('command-panel', immediate: true).mainModule
|
||||
commandPanel = commandPanelMain.commandPanelView
|
||||
commandPanel.history = []
|
||||
commandPanel.historyIndex = 0
|
||||
|
||||
describe "serialization", ->
|
||||
it "preserves the command panel's history across reloads", ->
|
||||
rootView.attachToDom()
|
||||
rootView.trigger 'command-panel:toggle'
|
||||
expect(commandPanel.miniEditor.isFocused).toBeTruthy()
|
||||
commandPanel.execute('/.')
|
||||
expect(commandPanel.history.length).toBe(1)
|
||||
expect(commandPanel.history[0]).toBe('/.')
|
||||
expect(commandPanel.historyIndex).toBe(1)
|
||||
rootView.trigger 'command-panel:toggle'
|
||||
expect(commandPanel.miniEditor.isFocused).toBeTruthy()
|
||||
|
||||
atom.deactivatePackage('command-panel')
|
||||
atom.activatePackage('command-panel')
|
||||
|
||||
expect(rootView.find('.command-panel')).not.toExist()
|
||||
rootView.trigger 'command-panel:toggle'
|
||||
expect(rootView.find('.command-panel')).toExist()
|
||||
commandPanel = rootView.find('.command-panel').view()
|
||||
expect(commandPanel.history.length).toBe(1)
|
||||
expect(commandPanel.history[0]).toBe('/.')
|
||||
expect(commandPanel.historyIndex).toBe(1)
|
||||
|
||||
it "only retains the configured max serialized history size", ->
|
||||
rootView.attachToDom()
|
||||
|
||||
commandPanel.maxSerializedHistorySize = 2
|
||||
commandPanel.execute('/test1')
|
||||
commandPanel.execute('/test2')
|
||||
commandPanel.execute('/test3')
|
||||
expect(commandPanel.history.length).toBe(3)
|
||||
expect(commandPanel.history[0]).toBe('/test1')
|
||||
expect(commandPanel.history[1]).toBe('/test2')
|
||||
expect(commandPanel.history[2]).toBe('/test3')
|
||||
expect(commandPanel.historyIndex).toBe(3)
|
||||
|
||||
atom.deactivatePackage('command-panel')
|
||||
atom.activatePackage('command-panel')
|
||||
|
||||
rootView.trigger 'command-panel:toggle'
|
||||
commandPanel = rootView.find('.command-panel').view()
|
||||
|
||||
expect(commandPanel.history.length).toBe(2)
|
||||
expect(commandPanel.history[0]).toBe('/test2')
|
||||
expect(commandPanel.history[1]).toBe('/test3')
|
||||
expect(commandPanel.historyIndex).toBe(2)
|
||||
|
||||
describe "when core:close is triggered on the command panel", ->
|
||||
it "detaches the command panel, focuses the RootView and does not bubble the core:close event", ->
|
||||
commandPanel.attach('command')
|
||||
expect(commandPanel.miniEditor.getText()).toBe 'command'
|
||||
rootViewCloseHandler = jasmine.createSpy('rootViewCloseHandler')
|
||||
rootView.on 'core:close', rootViewCloseHandler
|
||||
spyOn(rootView, 'focus')
|
||||
|
||||
commandPanel.trigger('core:close')
|
||||
|
||||
expect(rootView.focus).toHaveBeenCalled()
|
||||
expect(rootViewCloseHandler).not.toHaveBeenCalled()
|
||||
expect(commandPanel.hasParent()).toBeFalsy()
|
||||
expect(commandPanel.miniEditor.getText()).toBe 'command'
|
||||
|
||||
describe "when core:cancel is triggered on the command panel's mini editor", ->
|
||||
it "detaches the command panel, focuses the RootView and does not bubble the core:cancel event", ->
|
||||
commandPanel.attach('command')
|
||||
expect(commandPanel.miniEditor.getText()).toBe 'command'
|
||||
rootViewCancelHandler = jasmine.createSpy('rootViewCancelHandler')
|
||||
rootView.on 'core:cancel', rootViewCancelHandler
|
||||
spyOn(rootView, 'focus')
|
||||
|
||||
commandPanel.miniEditor.trigger('core:cancel')
|
||||
|
||||
expect(rootView.focus).toHaveBeenCalled()
|
||||
expect(rootViewCancelHandler).not.toHaveBeenCalled()
|
||||
expect(commandPanel.hasParent()).toBeFalsy()
|
||||
expect(commandPanel.miniEditor.getText()).toBe 'command'
|
||||
|
||||
describe "when command-panel:toggle is triggered on the root view", ->
|
||||
beforeEach ->
|
||||
rootView.attachToDom()
|
||||
|
||||
describe "when the command panel is visible", ->
|
||||
beforeEach ->
|
||||
commandPanel.attach()
|
||||
|
||||
describe "when the mini editor is focused", ->
|
||||
it "closes the command panel", ->
|
||||
expect(commandPanel.miniEditor.hiddenInput).toMatchSelector ':focus'
|
||||
rootView.trigger 'command-panel:toggle'
|
||||
expect(commandPanel.hasParent()).toBeFalsy()
|
||||
|
||||
describe "when the mini editor is not focused", ->
|
||||
it "focuses the mini editor", ->
|
||||
rootView.focus()
|
||||
expect(commandPanel.miniEditor.hiddenInput).not.toMatchSelector ':focus'
|
||||
rootView.trigger 'command-panel:toggle'
|
||||
expect(commandPanel.hasParent()).toBeTruthy()
|
||||
expect(commandPanel.miniEditor.hiddenInput).toMatchSelector ':focus'
|
||||
|
||||
describe "when the command panel is opened a second time", ->
|
||||
it "displays and selects the previously entered text", ->
|
||||
commandPanel.miniEditor.setText('command1')
|
||||
rootView.trigger 'command-panel:toggle'
|
||||
expect(commandPanel.hasParent()).toBeFalsy()
|
||||
rootView.trigger 'command-panel:toggle'
|
||||
expect(commandPanel.hasParent()).toBeTruthy()
|
||||
expect(commandPanel.miniEditor.getText()).toBe 'command1'
|
||||
expect(commandPanel.miniEditor.getSelectedText()).toBe 'command1'
|
||||
|
||||
describe "when the command panel is not visible", ->
|
||||
it "shows and focuses the command panel", ->
|
||||
expect(commandPanel.hasParent()).toBeFalsy()
|
||||
rootView.trigger 'command-panel:toggle'
|
||||
expect(commandPanel.hasParent()).toBeTruthy()
|
||||
|
||||
describe "when command-panel:toggle-preview is triggered on the root view", ->
|
||||
beforeEach ->
|
||||
rootView.attachToDom()
|
||||
|
||||
describe "when the preview list is/was previously visible", ->
|
||||
beforeEach ->
|
||||
rootView.trigger 'command-panel:toggle'
|
||||
waitsForPromise -> commandPanel.execute('X x/quicksort/')
|
||||
|
||||
describe "when the command panel is visible", ->
|
||||
beforeEach ->
|
||||
expect(commandPanel.hasParent()).toBeTruthy()
|
||||
|
||||
describe "when the preview list is visible", ->
|
||||
beforeEach ->
|
||||
expect(commandPanel.previewList).toBeVisible()
|
||||
|
||||
it "shows the expand and collapse all buttons", ->
|
||||
expect(commandPanel.collapseAll).toBeVisible()
|
||||
expect(commandPanel.expandAll).toBeVisible()
|
||||
|
||||
describe "when the preview list is focused", ->
|
||||
it "hides the command panel", ->
|
||||
expect(commandPanel.previewList).toMatchSelector(':focus')
|
||||
rootView.trigger 'command-panel:toggle-preview'
|
||||
expect(commandPanel.hasParent()).toBeFalsy()
|
||||
|
||||
describe "when the preview list is not focused", ->
|
||||
it "focuses the preview list", ->
|
||||
commandPanel.miniEditor.focus()
|
||||
rootView.trigger 'command-panel:toggle-preview'
|
||||
expect(commandPanel.previewList).toMatchSelector(':focus')
|
||||
|
||||
describe "when the preview list is not visible", ->
|
||||
beforeEach ->
|
||||
commandPanel.miniEditor.focus()
|
||||
rootView.trigger 'command-panel:toggle'
|
||||
rootView.trigger 'command-panel:toggle'
|
||||
expect(commandPanel.hasParent()).toBeTruthy()
|
||||
expect(commandPanel.previewList).toBeHidden()
|
||||
|
||||
it "shows and focuses the preview list", ->
|
||||
rootView.trigger 'command-panel:toggle-preview'
|
||||
expect(commandPanel.previewList).toBeVisible()
|
||||
expect(commandPanel.previewList).toMatchSelector(':focus')
|
||||
|
||||
describe "when the command panel is not visible", ->
|
||||
it "shows the command panel and the preview list, and focuses the preview list", ->
|
||||
commandPanel.miniEditor.focus()
|
||||
rootView.trigger 'command-panel:toggle'
|
||||
expect(commandPanel.hasParent()).toBeFalsy()
|
||||
|
||||
rootView.trigger 'command-panel:toggle-preview'
|
||||
expect(commandPanel.hasParent()).toBeTruthy()
|
||||
expect(commandPanel.previewList).toBeVisible()
|
||||
expect(commandPanel.previewList).toMatchSelector(':focus')
|
||||
|
||||
describe "when the preview list has never been opened", ->
|
||||
describe "when the command panel is visible", ->
|
||||
beforeEach ->
|
||||
rootView.trigger 'command-panel:toggle'
|
||||
expect(commandPanel.hasParent()).toBeTruthy()
|
||||
|
||||
describe "when the mini editor is focused", ->
|
||||
it "retains focus on the mini editor and does not show the preview list or preview header", ->
|
||||
expect(commandPanel.miniEditor.isFocused).toBeTruthy()
|
||||
rootView.trigger 'command-panel:toggle-preview'
|
||||
expect(commandPanel.previewList).toBeHidden()
|
||||
expect(commandPanel.previewHeader).toBeHidden()
|
||||
expect(commandPanel.miniEditor.isFocused).toBeTruthy()
|
||||
|
||||
describe "when the mini editor is not focused", ->
|
||||
it "focuses the mini editor and does not show the preview list or preview header", ->
|
||||
rootView.focus()
|
||||
rootView.trigger 'command-panel:toggle-preview'
|
||||
expect(commandPanel.previewList).toBeHidden()
|
||||
expect(commandPanel.previewHeader).toBeHidden()
|
||||
expect(commandPanel.miniEditor.isFocused).toBeTruthy()
|
||||
|
||||
describe "when the command panel is not visible", ->
|
||||
it "shows the command panel and focuses the mini editor, but does not show the preview list", ->
|
||||
|
||||
describe "when tool-panel:unfocus is triggered on the command panel", ->
|
||||
it "returns focus to the root view but does not hide the command panel", ->
|
||||
rootView.attachToDom()
|
||||
commandPanel.attach()
|
||||
expect(commandPanel.miniEditor.hiddenInput).toMatchSelector ':focus'
|
||||
commandPanel.trigger 'tool-panel:unfocus'
|
||||
expect(commandPanel.hasParent()).toBeTruthy()
|
||||
expect(commandPanel.miniEditor.hiddenInput).not.toMatchSelector ':focus'
|
||||
|
||||
describe "when command-panel:repeat-relative-address is triggered on the root view", ->
|
||||
describe "when there is more than one match", ->
|
||||
it "repeats the last search command if there is one", ->
|
||||
rootView.trigger 'command-panel:repeat-relative-address'
|
||||
|
||||
editSession.setCursorScreenPosition([4, 0])
|
||||
|
||||
commandPanel.execute("/current")
|
||||
expect(editSession.getSelectedBufferRange()).toEqual [[5,6], [5,13]]
|
||||
|
||||
rootView.trigger 'command-panel:repeat-relative-address'
|
||||
expect(editSession.getSelectedBufferRange()).toEqual [[6,6], [6,13]]
|
||||
|
||||
commandPanel.execute('s/r/R/g')
|
||||
|
||||
rootView.trigger 'command-panel:repeat-relative-address'
|
||||
expect(editSession.getSelectedBufferRange()).toEqual [[6,34], [6,41]]
|
||||
|
||||
commandPanel.execute('0')
|
||||
commandPanel.execute('/sort/ s/r/R/') # this contains a substitution... won't be repeated
|
||||
|
||||
rootView.trigger 'command-panel:repeat-relative-address'
|
||||
expect(editSession.getSelectedBufferRange()).toEqual [[3,31], [3,38]]
|
||||
|
||||
describe "when there is only one match and it is selected", ->
|
||||
it "maintains the current selection and plays a beep", ->
|
||||
editSession.setCursorScreenPosition([0, 0])
|
||||
waitsForPromise ->
|
||||
commandPanel.execute("/Array")
|
||||
runs ->
|
||||
expect(editSession.getSelectedBufferRange()).toEqual [[11,14], [11,19]]
|
||||
spyOn(shell, 'beep')
|
||||
rootView.trigger 'command-panel:repeat-relative-address'
|
||||
waitsFor ->
|
||||
shell.beep.callCount > 0
|
||||
runs ->
|
||||
expect(editSession.getSelectedBufferRange()).toEqual [[11,14], [11,19]]
|
||||
|
||||
describe "when command-panel:repeat-relative-address-in-reverse is triggered on the root view", ->
|
||||
describe "when there is more than one match", ->
|
||||
it "it repeats the last relative address in the reverse direction", ->
|
||||
rootView.trigger 'command-panel:repeat-relative-address-in-reverse'
|
||||
|
||||
editSession.setCursorScreenPosition([6, 0])
|
||||
|
||||
commandPanel.execute("/current")
|
||||
expect(editSession.getSelectedBufferRange()).toEqual [[6,6], [6,13]]
|
||||
|
||||
rootView.trigger 'command-panel:repeat-relative-address-in-reverse'
|
||||
expect(editSession.getSelectedBufferRange()).toEqual [[5,6], [5,13]]
|
||||
|
||||
describe "when there is only one match and it is selected", ->
|
||||
it "maintains the current selection and plays a beep", ->
|
||||
editSession.setCursorScreenPosition([0, 0])
|
||||
waitsForPromise ->
|
||||
commandPanel.execute("/Array")
|
||||
runs ->
|
||||
expect(editSession.getSelectedBufferRange()).toEqual [[11,14], [11,19]]
|
||||
spyOn(shell, 'beep')
|
||||
rootView.trigger 'command-panel:repeat-relative-address-in-reverse'
|
||||
waitsFor ->
|
||||
shell.beep.callCount > 0
|
||||
runs ->
|
||||
expect(editSession.getSelectedBufferRange()).toEqual [[11,14], [11,19]]
|
||||
|
||||
describe "when command-panel:set-selection-as-regex-address is triggered on the root view", ->
|
||||
it "sets the @lastRelativeAddress to a RegexAddress of the current selection", ->
|
||||
rootView.open(require.resolve('fixtures/sample.js'))
|
||||
rootView.getActivePaneItem().setSelectedBufferRange([[1,21],[1,28]])
|
||||
|
||||
commandInterpreter = commandPanel.commandInterpreter
|
||||
expect(commandInterpreter.lastRelativeAddress).toBeUndefined()
|
||||
rootView.trigger 'command-panel:set-selection-as-regex-address'
|
||||
expect(commandInterpreter.lastRelativeAddress.subcommands.length).toBe 1
|
||||
expect(commandInterpreter.lastRelativeAddress.subcommands[0].regex.toString()).toEqual "/\\(items\\)/i"
|
||||
|
||||
describe "when command-panel:find-in-file is triggered on an editor", ->
|
||||
describe "when the command panel's editor does not begin with /", ->
|
||||
it "pre-populates the command panel's editor with / and moves the cursor to the last column", ->
|
||||
spyOn(commandPanel, 'attach').andCallThrough()
|
||||
commandPanel.miniEditor.setText("foo")
|
||||
commandPanel.miniEditor.setCursorBufferPosition([0, 0])
|
||||
|
||||
rootView.getActiveView().trigger "command-panel:find-in-file"
|
||||
expect(commandPanel.attach).toHaveBeenCalled()
|
||||
expect(commandPanel.parent).not.toBeEmpty()
|
||||
expect(commandPanel.miniEditor.getText()).toBe "/"
|
||||
expect(commandPanel.miniEditor.getCursorBufferPosition()).toEqual [0, 1]
|
||||
|
||||
describe "when the command panel's editor begins with /", ->
|
||||
it "selects text after the /", ->
|
||||
spyOn(commandPanel, 'attach').andCallThrough()
|
||||
commandPanel.miniEditor.setText("/foo")
|
||||
commandPanel.miniEditor.setCursorBufferPosition([0, 0])
|
||||
|
||||
rootView.getActiveView().trigger "command-panel:find-in-file"
|
||||
expect(commandPanel.attach).toHaveBeenCalled()
|
||||
expect(commandPanel.parent).not.toBeEmpty()
|
||||
expect(commandPanel.miniEditor.getText()).toBe "/foo"
|
||||
expect(commandPanel.miniEditor.getSelectedText()).toBe "foo"
|
||||
|
||||
describe "when command-panel:find-in-project is triggered on the root view", ->
|
||||
describe "when the command panel's editor does not begin with Xx/", ->
|
||||
it "pre-populates the command panel's editor with Xx/ and moves the cursor to the last column", ->
|
||||
spyOn(commandPanel, 'attach').andCallThrough()
|
||||
commandPanel.miniEditor.setText("foo")
|
||||
commandPanel.miniEditor.setCursorBufferPosition([0, 0])
|
||||
|
||||
rootView.trigger "command-panel:find-in-project"
|
||||
expect(commandPanel.attach).toHaveBeenCalled()
|
||||
expect(commandPanel.parent).not.toBeEmpty()
|
||||
expect(commandPanel.miniEditor.getText()).toBe "Xx/"
|
||||
expect(commandPanel.miniEditor.getCursorBufferPosition()).toEqual [0, 3]
|
||||
|
||||
describe "when the command panel's editor begins with Xx/", ->
|
||||
it "selects text after the Xx/", ->
|
||||
spyOn(commandPanel, 'attach').andCallThrough()
|
||||
commandPanel.miniEditor.setText("Xx/foo")
|
||||
commandPanel.miniEditor.setCursorBufferPosition([0, 0])
|
||||
|
||||
rootView.getActiveView().trigger "command-panel:find-in-project"
|
||||
expect(commandPanel.attach).toHaveBeenCalled()
|
||||
expect(commandPanel.parent).not.toBeEmpty()
|
||||
expect(commandPanel.miniEditor.getText()).toBe "Xx/foo"
|
||||
expect(commandPanel.miniEditor.getSelectedText()).toBe "foo"
|
||||
|
||||
describe "when return is pressed on the panel's editor", ->
|
||||
describe "if the command has an immediate effect", ->
|
||||
it "executes it immediately on the current buffer", ->
|
||||
rootView.trigger 'command-panel:toggle'
|
||||
commandPanel.miniEditor.insertText ',s/sort/torta/g'
|
||||
commandPanel.miniEditor.hiddenInput.trigger keydownEvent('enter')
|
||||
|
||||
expect(buffer.lineForRow(0)).toMatch /quicktorta/
|
||||
expect(buffer.lineForRow(1)).toMatch /var torta/
|
||||
|
||||
describe "when the command returns operations to be previewed", ->
|
||||
beforeEach ->
|
||||
rootView.getActivePane().remove()
|
||||
rootView.attachToDom()
|
||||
rootView.trigger 'command-panel:toggle'
|
||||
waitsForPromise -> commandPanel.execute('X x/quicksort/')
|
||||
|
||||
it "displays and focuses the operation preview list", ->
|
||||
expect(commandPanel).toBeVisible()
|
||||
expect(commandPanel.previewList).toBeVisible()
|
||||
expect(commandPanel.previewList).toMatchSelector ':focus'
|
||||
previewItem = commandPanel.previewList.find("li:contains(sample.js):first")
|
||||
expect(previewItem.find('.path-details').text()).toBe "sample.js(1)"
|
||||
expect(previewItem.next().find('.preview').text()).toBe "var quicksort = function () {"
|
||||
expect(previewItem.next().find('.preview > .match').text()).toBe "quicksort"
|
||||
|
||||
rootView.trigger 'command-panel:toggle-preview' # ensure we can close panel without problems
|
||||
expect(commandPanel).toBeHidden()
|
||||
|
||||
it "destroys previously previewed operations if there are any", ->
|
||||
waitsForPromise -> commandPanel.execute('X x/pivot/')
|
||||
# there shouldn't be any dangling operations after this
|
||||
|
||||
describe "if the command is malformed", ->
|
||||
it "adds and removes an error class to the command panel and does not close it or display a loading message", ->
|
||||
rootView.attachToDom()
|
||||
rootView.trigger 'command-panel:toggle'
|
||||
commandPanel.miniEditor.insertText 'garbage-command!!'
|
||||
|
||||
commandPanel.miniEditor.hiddenInput.trigger keydownEvent('enter')
|
||||
expect(commandPanel.parent()).toExist()
|
||||
expect(commandPanel).toHaveClass 'error'
|
||||
expect(commandPanel.loadingMessage).toBeHidden()
|
||||
|
||||
advanceClock 400
|
||||
|
||||
expect(commandPanel).not.toHaveClass 'error'
|
||||
|
||||
describe "if the command returns an error message", ->
|
||||
beforeEach ->
|
||||
rootView.attachToDom()
|
||||
rootView.trigger 'command-panel:toggle'
|
||||
commandPanel.miniEditor.insertText '/garbage'
|
||||
expect(commandPanel.errorMessages).not.toBeVisible()
|
||||
commandPanel.miniEditor.hiddenInput.trigger keydownEvent('enter')
|
||||
|
||||
it "adds and removes an error class to the command panel and displays the error message", ->
|
||||
expect(commandPanel).toBeVisible()
|
||||
expect(commandPanel.errorMessages).toBeVisible()
|
||||
expect(commandPanel).toHaveClass 'error'
|
||||
|
||||
it "removes the error message when the command-panel is toggled", ->
|
||||
rootView.trigger 'command-panel:toggle' # off
|
||||
rootView.trigger 'command-panel:toggle' # on
|
||||
expect(commandPanel).toBeVisible()
|
||||
expect(commandPanel.errorMessages).not.toBeVisible()
|
||||
|
||||
describe "when the command contains an escaped character", ->
|
||||
it "executes the command with the escaped character (instead of as a backslash followed by the character)", ->
|
||||
rootView.trigger 'command-panel:toggle'
|
||||
|
||||
editSession = rootView.open(require.resolve 'fixtures/sample-with-tabs.coffee')
|
||||
commandPanel.miniEditor.setText "/\\tsell"
|
||||
commandPanel.miniEditor.hiddenInput.trigger keydownEvent('enter')
|
||||
expect(editSession.getSelectedBufferRange()).toEqual [[3,1],[3,6]]
|
||||
|
||||
describe "when move-up and move-down are triggerred on the editor", ->
|
||||
it "navigates forward and backward through the command history", ->
|
||||
commandPanel.execute 's/war/peace/g'
|
||||
commandPanel.execute 's/twinkies/wheatgrass/g'
|
||||
|
||||
rootView.trigger 'command-panel:toggle'
|
||||
|
||||
commandPanel.miniEditor.trigger 'core:move-up'
|
||||
expect(commandPanel.miniEditor.getText()).toBe 's/twinkies/wheatgrass/g'
|
||||
commandPanel.miniEditor.trigger 'core:move-up'
|
||||
expect(commandPanel.miniEditor.getText()).toBe 's/war/peace/g'
|
||||
commandPanel.miniEditor.trigger 'core:move-up'
|
||||
expect(commandPanel.miniEditor.getText()).toBe 's/war/peace/g'
|
||||
commandPanel.miniEditor.trigger 'core:move-down'
|
||||
expect(commandPanel.miniEditor.getText()).toBe 's/twinkies/wheatgrass/g'
|
||||
commandPanel.miniEditor.trigger 'core:move-down'
|
||||
expect(commandPanel.miniEditor.getText()).toBe ''
|
||||
|
||||
describe "when the preview list is focused with search operations", ->
|
||||
previewList = null
|
||||
|
||||
beforeEach ->
|
||||
previewList = commandPanel.previewList
|
||||
rootView.trigger 'command-panel:toggle'
|
||||
waitsForPromise -> commandPanel.execute('X x/sort/')
|
||||
|
||||
it "displays the number of files and operations", ->
|
||||
rootView.attachToDom()
|
||||
expect(commandPanel.previewCount.text()).toBe '22 matches in 5 files'
|
||||
|
||||
describe "when move-down and move-up are triggered on the preview list", ->
|
||||
it "selects the next/previous operation (if there is one), and scrolls the list if needed", ->
|
||||
rootView.attachToDom()
|
||||
expect(previewList.find('li.operation:eq(0)')).toHaveClass 'selected'
|
||||
expect(previewList.getSelectedOperation()).toBe previewList.getOperations()[0]
|
||||
|
||||
previewList.trigger 'core:move-down'
|
||||
expect(previewList.find('li.operation:eq(1)')).toHaveClass 'selected'
|
||||
expect(previewList.getSelectedOperation()).toBe previewList.getOperations()[1]
|
||||
|
||||
previewList.trigger 'core:move-down'
|
||||
expect(previewList.find('li.operation:eq(2)')).toHaveClass 'selected'
|
||||
expect(previewList.getSelectedOperation()).toBe previewList.getOperations()[2]
|
||||
|
||||
previewList.trigger 'core:move-up'
|
||||
expect(previewList.find('li.operation:eq(1)')).toHaveClass 'selected'
|
||||
expect(previewList.getSelectedOperation()).toBe previewList.getOperations()[1]
|
||||
|
||||
_.times previewList.getOperations().length + previewList.getPathCount(), -> previewList.trigger 'core:move-down'
|
||||
|
||||
expect(previewList.find("li.operation:last")).toHaveClass 'selected'
|
||||
expect(previewList.getSelectedOperation()).toBe _.last(previewList.getOperations())
|
||||
|
||||
expect(previewList.scrollBottom()).toBeCloseTo previewList.prop('scrollHeight'), -1
|
||||
|
||||
_.times previewList.getOperations().length + previewList.getPathCount(), -> previewList.trigger 'core:move-up'
|
||||
expect(previewList.scrollTop()).toBe 0
|
||||
|
||||
it "doesn't bubble up the event and the command panel text doesn't change", ->
|
||||
rootView.attachToDom()
|
||||
commandPanel.miniEditor.setText "command"
|
||||
previewList.focus()
|
||||
previewList.trigger 'core:move-down'
|
||||
expect(previewList.find('li.operation:eq(1)')).toHaveClass 'selected'
|
||||
expect(commandPanel.miniEditor.getText()).toBe 'command'
|
||||
previewList.trigger 'core:move-up'
|
||||
expect(previewList.find('li.operation:eq(0)')).toHaveClass 'selected'
|
||||
expect(commandPanel.miniEditor.getText()).toBe 'command'
|
||||
|
||||
it "doesn't select collapsed operations", ->
|
||||
rootView.attachToDom()
|
||||
previewList.trigger 'command-panel:collapse-result'
|
||||
expect(previewList.find('li.path:eq(0)')).toHaveClass 'selected'
|
||||
previewList.trigger 'core:move-down'
|
||||
expect(previewList.find('li.path:eq(1)')).toHaveClass 'selected'
|
||||
previewList.trigger 'core:move-up'
|
||||
expect(previewList.find('li.path:eq(0)')).toHaveClass 'selected'
|
||||
|
||||
describe "when move-to-top and move-to-bottom are triggered on the preview list", ->
|
||||
it "selects the first path or last operation", ->
|
||||
rootView.attachToDom()
|
||||
expect(previewList.getOperations().length).toBeGreaterThan 0
|
||||
expect(previewList.find('li.operation:eq(0)')).toHaveClass 'selected'
|
||||
expect(previewList.getSelectedOperation()).toBe previewList.getOperations()[0]
|
||||
|
||||
previewList.trigger 'core:move-to-bottom'
|
||||
expect(previewList.find('li.operation:last')).toHaveClass 'selected'
|
||||
expect(previewList.getSelectedOperation()).toBe _.last(previewList.getOperations())
|
||||
|
||||
previewList.trigger 'core:move-to-top'
|
||||
expect(previewList.find('li.path:eq(0)')).toHaveClass 'selected'
|
||||
expect(previewList.getSelectedOperation()).toBeUndefined()
|
||||
|
||||
describe "when core:confirm is triggered on the preview list", ->
|
||||
it "opens the operation's buffer, selects and scrolls to the search result, and refocuses the preview list", ->
|
||||
rootView.height(200)
|
||||
rootView.attachToDom()
|
||||
|
||||
waitsForPromise -> commandPanel.execute('X x/apply/') # use apply because it is at the end of the file
|
||||
runs ->
|
||||
spyOn(previewList, 'focus')
|
||||
executeHandler = jasmine.createSpy('executeHandler')
|
||||
commandPanel.on 'core:confirm', executeHandler
|
||||
|
||||
_.times 4, -> previewList.trigger 'core:move-down'
|
||||
operation = previewList.getSelectedOperation()
|
||||
|
||||
previewList.trigger 'core:confirm'
|
||||
|
||||
editSession = rootView.getActivePaneItem()
|
||||
expect(editSession.buffer.getPath()).toBe project.resolve(operation.getPath())
|
||||
expect(editSession.getSelectedBufferRange()).toEqual operation.getBufferRange()
|
||||
expect(editSession.getSelectedBufferRange()).toEqual operation.getBufferRange()
|
||||
expect(rootView.getActiveView().isScreenRowVisible(editSession.getCursorScreenRow())).toBeTruthy()
|
||||
expect(previewList.focus).toHaveBeenCalled()
|
||||
|
||||
expect(executeHandler).not.toHaveBeenCalled()
|
||||
|
||||
it "toggles the expansion state when a path is selected", ->
|
||||
rootView.attachToDom()
|
||||
previewList.trigger 'core:move-to-top'
|
||||
expect(previewList.find('li.path:first')).toHaveClass 'selected'
|
||||
expect(previewList.find('li.path:first')).not.toHaveClass 'is-collapsed'
|
||||
previewList.trigger 'core:confirm'
|
||||
expect(previewList.find('li.path:first')).toHaveClass 'selected'
|
||||
expect(previewList.find('li.path:first')).toHaveClass 'is-collapsed'
|
||||
|
||||
describe "when an operation in the preview list is clicked", ->
|
||||
it "opens the operation's buffer, selects the search result, and refocuses the preview list", ->
|
||||
spyOn(previewList, 'focus')
|
||||
operation = previewList.getOperations()[4]
|
||||
|
||||
previewList.find('li.operation:eq(4) span').mousedown()
|
||||
|
||||
expect(previewList.getSelectedOperation()).toBe operation
|
||||
editSession = rootView.getActivePaneItem()
|
||||
expect(editSession.buffer.getPath()).toBe project.resolve(operation.getPath())
|
||||
expect(editSession.getSelectedBufferRange()).toEqual operation.getBufferRange()
|
||||
expect(previewList.focus).toHaveBeenCalled()
|
||||
|
||||
describe "when a path in the preview list is clicked", ->
|
||||
it "shows and hides the matches for that path", ->
|
||||
rootView.attachToDom()
|
||||
expect(previewList.find('li.path:first-child ul.matches')).toBeVisible()
|
||||
previewList.find('li.path:first-child .path-details').mousedown()
|
||||
expect(previewList.find('li.path:first-child ul.matches')).toBeHidden()
|
||||
|
||||
previewList.find('li.path:first-child .path-details').mousedown()
|
||||
expect(previewList.find('li.path:first-child ul.matches')).toBeVisible()
|
||||
|
||||
describe "when command-panel:collapse-result and command-panel:expand-result are triggered", ->
|
||||
it "collapses and selects the path, and then expands the selected path", ->
|
||||
rootView.attachToDom()
|
||||
expect(previewList.find('li.path:first-child ul.matches')).toBeVisible()
|
||||
previewList.trigger 'command-panel:collapse-result'
|
||||
expect(previewList.find('li.path:first-child ul.matches')).toBeHidden()
|
||||
expect(previewList.find('li.path:first-child')).toHaveClass 'selected'
|
||||
previewList.trigger 'command-panel:expand-result'
|
||||
expect(previewList.find('li.path:first-child ul.matches')).toBeVisible()
|
||||
expect(previewList.find('li.path:first-child')).toHaveClass 'selected'
|
||||
|
||||
describe "when the active pane item is not an EditSession", ->
|
||||
it "doesn't throw an error (regression)", ->
|
||||
rootView.open('binary-file.png')
|
||||
rootView.trigger 'command-panel:toggle'
|
||||
|
||||
executePromise = null
|
||||
expect(-> executePromise = commandPanel.execute('Xx/sort/')).not.toThrow()
|
||||
|
||||
waitsForPromise -> executePromise
|
||||
@@ -1,67 +0,0 @@
|
||||
RootView = require 'root-view'
|
||||
CommandPanelView = require 'command-panel/lib/command-panel-view'
|
||||
_ = require 'underscore'
|
||||
|
||||
describe "Preview List", ->
|
||||
[previewList, commandPanelMain, commandPanelView] = []
|
||||
|
||||
beforeEach ->
|
||||
window.rootView = new RootView()
|
||||
rootView.attachToDom()
|
||||
commandPanelMain = atom.activatePackage('command-panel', immediate: true).mainModule
|
||||
commandPanelView = commandPanelMain.commandPanelView
|
||||
previewList = commandPanelView.previewList
|
||||
rootView.trigger 'command-panel:toggle'
|
||||
|
||||
describe "when the list is scrollable", ->
|
||||
it "adds more operations to the DOM when `scrollBottom` nears the `pixelOverdraw`", ->
|
||||
waitsForPromise ->
|
||||
commandPanelView.execute('X x/so/')
|
||||
|
||||
runs ->
|
||||
expect(previewList.prop('scrollHeight')).toBeGreaterThan previewList.height()
|
||||
previousScrollHeight = previewList.prop('scrollHeight')
|
||||
previousOperationCount = previewList.find("li").length
|
||||
|
||||
previewList.scrollTop(previewList.pixelOverdraw / 2)
|
||||
previewList.trigger('scroll') # Not sure why scroll event isn't being triggered on it's own
|
||||
expect(previewList.prop('scrollHeight')).toBe previousScrollHeight
|
||||
expect(previewList.find("li").length).toBe previousOperationCount
|
||||
|
||||
previewList.scrollToBottom()
|
||||
previewList.trigger('scroll') # Not sure why scroll event isn't being triggered on it's own
|
||||
expect(previewList.prop('scrollHeight')).toBeGreaterThan previousScrollHeight
|
||||
expect(previewList.find("li").length).toBeGreaterThan previousOperationCount
|
||||
|
||||
it "renders all operations if the preview items are collapsed", ->
|
||||
waitsForPromise ->
|
||||
commandPanelView.execute('X x/so/')
|
||||
|
||||
runs ->
|
||||
expect(previewList.prop('scrollHeight')).toBeGreaterThan previewList.height()
|
||||
previousScrollHeight = previewList.prop('scrollHeight')
|
||||
previousOperationCount = previewList.find("li").length
|
||||
previewList.collapseAllPaths()
|
||||
expect(previewList.find("li").length).toBeGreaterThan previousOperationCount
|
||||
|
||||
it "renders more operations when a preview item is collapsed", ->
|
||||
waitsForPromise ->
|
||||
commandPanelView.execute('X x/so/')
|
||||
|
||||
runs ->
|
||||
expect(previewList.prop('scrollHeight')).toBeGreaterThan previewList.height()
|
||||
previousScrollHeight = previewList.prop('scrollHeight')
|
||||
previousOperationCount = previewList.find("li").length
|
||||
previewList.trigger 'command-panel:collapse-result'
|
||||
expect(previewList.find("li").length).toBeGreaterThan previousOperationCount
|
||||
|
||||
it "renders all operations when core:move-to-bottom is triggered", ->
|
||||
waitsForPromise ->
|
||||
commandPanelView.execute('X x/so/')
|
||||
|
||||
runs ->
|
||||
expect(previewList.prop('scrollHeight')).toBeGreaterThan previewList.height()
|
||||
previousScrollHeight = previewList.prop('scrollHeight')
|
||||
previewList.trigger 'core:move-to-bottom'
|
||||
liCount = previewList.getPathCount() + previewList.getOperations().length
|
||||
expect(previewList.find("li").length).toBe liCount
|
||||
@@ -1,151 +0,0 @@
|
||||
@import "octicon-utf-codes.less";
|
||||
@import "octicon-mixins.less";
|
||||
|
||||
.command-panel {
|
||||
position: relative;
|
||||
padding: 0;
|
||||
|
||||
.is-loading {
|
||||
margin-bottom: 10px;
|
||||
text-align: center;
|
||||
|
||||
span {
|
||||
padding: 5px 10px 5px 10px;
|
||||
background-color: #111111;
|
||||
text-align: center;
|
||||
border-radius: 3px;
|
||||
border: 1px solid rgba(255,255,255,0.1);
|
||||
border-top: 1px solid rgba(0,0,0,1);
|
||||
border-left: 1px solid rgba(0,0,0,1);
|
||||
|
||||
.octicon(hourglass, 1.1em);
|
||||
|
||||
&:before {
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.preview-count {
|
||||
display: inline-block;
|
||||
margin-top: 4px;
|
||||
font-size: 11px;
|
||||
-webkit-user-select: none;
|
||||
}
|
||||
|
||||
.preview-list {
|
||||
max-height: 300px;
|
||||
overflow: auto;
|
||||
margin: 0 0 10px 0;
|
||||
position: relative;
|
||||
cursor: default;
|
||||
|
||||
.path {
|
||||
position: relative;
|
||||
-webkit-user-select: none;
|
||||
}
|
||||
|
||||
.path-details {
|
||||
.octicon(chevron-down, 12px);
|
||||
|
||||
&:before {
|
||||
margin-right: 5px;
|
||||
margin-left: 5px;
|
||||
position: relative;
|
||||
top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.is-collapsed .path-details:before {
|
||||
content: @chevron-right;
|
||||
}
|
||||
|
||||
.path-name {
|
||||
.octicon(file-text);
|
||||
|
||||
&:before {
|
||||
margin-right: 5px;
|
||||
position: relative;
|
||||
top: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
.path.readme .path-name:before {
|
||||
content: @book;
|
||||
}
|
||||
|
||||
.operation {
|
||||
padding-top: 2px;
|
||||
padding-bottom: 2px;
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
.line-number {
|
||||
margin-right: 1ex;
|
||||
text-align: right;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.path-match-number {
|
||||
padding-left: 8px;
|
||||
}
|
||||
|
||||
.preview {
|
||||
word-break: break-all;
|
||||
|
||||
.match {
|
||||
-webkit-border-radius: 2px;
|
||||
padding: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
.matches {
|
||||
list-style-type: none;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.header:after {
|
||||
content: ".";
|
||||
display: block;
|
||||
visibility: hidden;
|
||||
clear: both;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.expand-collapse {
|
||||
float: right;
|
||||
-webkit-user-select: none;
|
||||
margin: 0;
|
||||
|
||||
li {
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
font-size: 11px;
|
||||
margin-left: 5px;
|
||||
padding: 5px 10px;
|
||||
border-radius: 3px;
|
||||
line-height: normal;
|
||||
}
|
||||
}
|
||||
|
||||
.prompt-and-editor {
|
||||
display: -webkit-flex;
|
||||
|
||||
.editor {
|
||||
position: relative;
|
||||
-webkit-flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.error-messages {
|
||||
list-style-type: none;
|
||||
margin: 0;
|
||||
padding: 5px 1em;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn {
|
||||
margin-left: 5px;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user