mirror of
https://github.com/atom/atom.git
synced 2026-01-24 14:28:14 -05:00
Add bracket matcher that highlights pair
(), {}, and [] pairs are now highlighted when
after or before the cursor
This commit is contained in:
@@ -307,6 +307,9 @@ class EditSession
|
||||
fold.destroy()
|
||||
@setCursorBufferPosition([fold.startRow, 0])
|
||||
|
||||
isFoldedAtCursorRow: ->
|
||||
@isFoldedAtScreenRow(@getCursorScreenRow())
|
||||
|
||||
isFoldedAtBufferRow: (bufferRow) ->
|
||||
screenRow = @screenPositionForBufferPosition([bufferRow]).row
|
||||
@isFoldedAtScreenRow(screenRow)
|
||||
|
||||
@@ -277,6 +277,7 @@ class Editor extends View
|
||||
destroyFoldsContainingBufferRow: (bufferRow) -> @activeEditSession.destroyFoldsContainingBufferRow(bufferRow)
|
||||
isFoldedAtScreenRow: (screenRow) -> @activeEditSession.isFoldedAtScreenRow(screenRow)
|
||||
isFoldedAtBufferRow: (bufferRow) -> @activeEditSession.isFoldedAtBufferRow(bufferRow)
|
||||
isFoldedAtCursorRow: -> @activeEditSession.isFoldedAtCursorRow()
|
||||
|
||||
lineForScreenRow: (screenRow) -> @activeEditSession.lineForScreenRow(screenRow)
|
||||
linesForScreenRows: (start, end) -> @activeEditSession.linesForScreenRows(start, end)
|
||||
|
||||
94
src/packages/bracket-matcher/index.coffee
Normal file
94
src/packages/bracket-matcher/index.coffee
Normal file
@@ -0,0 +1,94 @@
|
||||
AtomPackage = require 'atom-package'
|
||||
_ = require 'underscore'
|
||||
{$$} = require 'space-pen'
|
||||
Range = require 'range'
|
||||
|
||||
module.exports =
|
||||
class BracketMatcher extends AtomPackage
|
||||
startPairMatches:
|
||||
'(': ')'
|
||||
'[': ']'
|
||||
'{': '}'
|
||||
|
||||
endPairMatches:
|
||||
')': '('
|
||||
']': '['
|
||||
'}': '{'
|
||||
|
||||
pairHighlighted: false
|
||||
|
||||
activate: (rootView) ->
|
||||
rootView.eachEditor (editor) => @subscribeToEditor(editor) if editor.attached
|
||||
|
||||
subscribeToEditor: (editor) ->
|
||||
editor.on 'cursor:moved', => @updateMatch(editor)
|
||||
|
||||
createView: (editor, bufferPosition) ->
|
||||
pixelPosition = editor.pixelPositionForBufferPosition(bufferPosition)
|
||||
view = $$ -> @div class: 'bracket-matcher'
|
||||
view.css('top', pixelPosition.top).css('left', pixelPosition.left)
|
||||
view.width(editor.charWidth).height(editor.charHeight)
|
||||
|
||||
findCurrentPair: (editor, buffer, matches) ->
|
||||
position = editor.getCursorBufferPosition()
|
||||
currentPair = buffer.getTextInRange(Range.fromPointWithDelta(position, 0, 1))
|
||||
unless matches[currentPair]
|
||||
position = position.translate([0, -1])
|
||||
currentPair = buffer.getTextInRange(Range.fromPointWithDelta(position, 0, 1))
|
||||
matchingPair = matches[currentPair]
|
||||
if matchingPair
|
||||
{position, currentPair, matchingPair}
|
||||
else
|
||||
{}
|
||||
|
||||
findMatchingEndPair: (buffer, startPairPosition, startPair, endPair) ->
|
||||
scanRange = new Range(startPairPosition.translate([0, 1]), buffer.getEofPosition())
|
||||
regex = new RegExp("[#{_.escapeRegExp(startPair + endPair)}]", 'g')
|
||||
endPairPosition = null
|
||||
unpairedCount = 0
|
||||
buffer.scanInRange regex, scanRange, (match, range, {stop}) =>
|
||||
if match[0] is startPair
|
||||
unpairedCount++
|
||||
else if match[0] is endPair
|
||||
unpairedCount--
|
||||
endPairPosition = range.start
|
||||
stop() if unpairedCount < 0
|
||||
endPairPosition
|
||||
|
||||
findMatchingStartPair: (buffer, endPairPosition, startPair, endPair) ->
|
||||
scanRange = new Range([0, 0], endPairPosition)
|
||||
regex = new RegExp("[#{_.escapeRegExp(startPair + endPair)}]", 'g')
|
||||
startPairPosition = null
|
||||
unpairedCount = 0
|
||||
scanner = (match, range, {stop}) =>
|
||||
if match[0] is endPair
|
||||
unpairedCount++
|
||||
else if match[0] is startPair
|
||||
unpairedCount--
|
||||
startPairPosition = range.start
|
||||
stop() if unpairedCount < 0
|
||||
buffer.scanInRange(regex, scanRange, scanner, true)
|
||||
startPairPosition
|
||||
|
||||
updateMatch: (editor) ->
|
||||
return unless underlayer = editor.pane()?.find('.underlayer')
|
||||
|
||||
underlayer.find('.bracket-matcher').remove() if @pairHighlighted
|
||||
@pairHighlighted = false
|
||||
|
||||
return unless editor.getSelection().isEmpty()
|
||||
return if editor.isFoldedAtCursorRow()
|
||||
|
||||
buffer = editor.getBuffer()
|
||||
{position, currentPair, matchingPair} = @findCurrentPair(editor, buffer, @startPairMatches)
|
||||
if position
|
||||
matchPosition = @findMatchingEndPair(buffer, position, currentPair, matchingPair)
|
||||
else
|
||||
{position, currentPair, matchingPair} = @findCurrentPair(editor, buffer, @endPairMatches)
|
||||
if position
|
||||
matchPosition = @findMatchingStartPair(buffer, position, matchingPair, currentPair)
|
||||
|
||||
if position? and matchPosition?
|
||||
underlayer.append(@createView(editor, position))
|
||||
underlayer.append(@createView(editor, matchPosition))
|
||||
@pairHighlighted = true
|
||||
@@ -0,0 +1,60 @@
|
||||
RootView = require 'root-view'
|
||||
|
||||
describe "bracket matching", ->
|
||||
[rootView, editor] = []
|
||||
|
||||
beforeEach ->
|
||||
rootView = new RootView(require.resolve('fixtures/sample.js'))
|
||||
atom.loadPackage('bracket-matcher')
|
||||
rootView.attachToDom()
|
||||
editor = rootView.getActiveEditor()
|
||||
|
||||
afterEach ->
|
||||
rootView.deactivate()
|
||||
|
||||
describe "when the cursor is before a starting pair", ->
|
||||
it "highlights the starting pair and ending pair", ->
|
||||
editor.moveCursorToEndOfLine()
|
||||
editor.moveCursorLeft()
|
||||
expect(editor.underlayer.find('.bracket-matcher').length).toBe 2
|
||||
expect(editor.underlayer.find('.bracket-matcher:first').position()).toEqual editor.pixelPositionForBufferPosition([0,28])
|
||||
expect(editor.underlayer.find('.bracket-matcher:last').position()).toEqual editor.pixelPositionForBufferPosition([12,0])
|
||||
|
||||
describe "when the cursor is after a starting pair", ->
|
||||
it "highlights the starting pair and ending pair", ->
|
||||
editor.moveCursorToEndOfLine()
|
||||
expect(editor.underlayer.find('.bracket-matcher').length).toBe 2
|
||||
expect(editor.underlayer.find('.bracket-matcher:first').position()).toEqual editor.pixelPositionForBufferPosition([0,28])
|
||||
expect(editor.underlayer.find('.bracket-matcher:last').position()).toEqual editor.pixelPositionForBufferPosition([12,0])
|
||||
|
||||
describe "when the cursor is before an ending pair", ->
|
||||
it "highlights the starting pair and ending pair", ->
|
||||
editor.moveCursorToBottom()
|
||||
editor.moveCursorLeft()
|
||||
editor.moveCursorLeft()
|
||||
expect(editor.underlayer.find('.bracket-matcher').length).toBe 2
|
||||
expect(editor.underlayer.find('.bracket-matcher:first').position()).toEqual editor.pixelPositionForBufferPosition([12,0])
|
||||
expect(editor.underlayer.find('.bracket-matcher:last').position()).toEqual editor.pixelPositionForBufferPosition([0,28])
|
||||
|
||||
describe "when the cursor is after an ending pair", ->
|
||||
it "highlights the starting pair and ending pair", ->
|
||||
editor.moveCursorToBottom()
|
||||
editor.moveCursorLeft()
|
||||
expect(editor.underlayer.find('.bracket-matcher').length).toBe 2
|
||||
expect(editor.underlayer.find('.bracket-matcher:first').position()).toEqual editor.pixelPositionForBufferPosition([12,0])
|
||||
expect(editor.underlayer.find('.bracket-matcher:last').position()).toEqual editor.pixelPositionForBufferPosition([0,28])
|
||||
|
||||
describe "when the cursor is moved off a pair", ->
|
||||
it "removes the starting pair and ending pair highlights", ->
|
||||
editor.moveCursorToEndOfLine()
|
||||
expect(editor.underlayer.find('.bracket-matcher').length).toBe 2
|
||||
editor.moveCursorToBeginningOfLine()
|
||||
expect(editor.underlayer.find('.bracket-matcher').length).toBe 0
|
||||
|
||||
describe "pair balancing", ->
|
||||
describe "when a second starting pair preceeds the first ending pair", ->
|
||||
it "advances to the second ending pair", ->
|
||||
editor.setCursorBufferPosition([8,42])
|
||||
expect(editor.underlayer.find('.bracket-matcher').length).toBe 2
|
||||
expect(editor.underlayer.find('.bracket-matcher:first').position()).toEqual editor.pixelPositionForBufferPosition([8,42])
|
||||
expect(editor.underlayer.find('.bracket-matcher:last').position()).toEqual editor.pixelPositionForBufferPosition([8,54])
|
||||
@@ -0,0 +1,3 @@
|
||||
.bracket-matcher {
|
||||
position: absolute;
|
||||
}
|
||||
5
themes/atom-dark-ui/bracket-matcher.css
Normal file
5
themes/atom-dark-ui/bracket-matcher.css
Normal file
@@ -0,0 +1,5 @@
|
||||
.bracket-matcher {
|
||||
border-bottom: 1px solid #f8de7e;
|
||||
margin-top: -1px;
|
||||
opacity: .7;
|
||||
}
|
||||
@@ -8,6 +8,7 @@
|
||||
"status-bar.css",
|
||||
"markdown-preview.css",
|
||||
"command-panel.css",
|
||||
"command-logger.css"
|
||||
"command-logger.css",
|
||||
"bracket-matcher.css"
|
||||
]
|
||||
}
|
||||
|
||||
5
themes/atom-light-ui/bracket-matcher.css
Normal file
5
themes/atom-light-ui/bracket-matcher.css
Normal file
@@ -0,0 +1,5 @@
|
||||
.bracket-matcher {
|
||||
border-bottom: 1px solid #f8de7e;
|
||||
margin-top: -1px;
|
||||
opacity: .7;
|
||||
}
|
||||
@@ -8,6 +8,7 @@
|
||||
"status-bar.css",
|
||||
"markdown-preview.css",
|
||||
"command-panel.css",
|
||||
"command-logger.css"
|
||||
"command-logger.css",
|
||||
"bracket-matcher.css"
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user