Merge pull request #435 - move to WASM version of Oniguruma

Using "second-mate"
This commit is contained in:
Maurício Szabo 2023-03-28 21:57:16 -03:00 committed by GitHub
commit c3bcd06059
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
44 changed files with 3753 additions and 208 deletions

View File

@ -6,6 +6,9 @@
## [Unreleased]
- Migrated away from `node-oniguruma` in favor of `vscode-oniguruma` (WASM
version). This fixes issues with Electron 21
## 1.103.0
- Added a new feature to Search for Pulsar's settings

View File

@ -45,7 +45,7 @@
"base16-tomorrow-dark-theme": "file:packages/base16-tomorrow-dark-theme",
"base16-tomorrow-light-theme": "file:packages/base16-tomorrow-light-theme",
"bookmarks": "file:packages/bookmarks",
"bracket-matcher": "https://github.com/pulsar-edit/bracket-matcher.git#c877977",
"bracket-matcher": "file:packages/bracket-matcher",
"chai": "4.3.4",
"clear-cut": "^2.0.2",
"coffeescript": "1.12.7",
@ -62,7 +62,7 @@
"exception-reporting": "file:packages/exception-reporting",
"find-and-replace": "https://github.com/atom-community/find-and-replace/archive/refs/tags/v0.220.1.tar.gz",
"find-parent-dir": "^0.3.0",
"first-mate": "7.4.3",
"second-mate": "https://github.com/pulsar-edit/second-mate.git#14aa7bd",
"focus-trap": "6.3.0",
"fs-admin": "0.19.0",
"fs-plus": "^3.1.1",

View File

@ -0,0 +1 @@
[See how you can contribute](https://github.com/pulsar-edit/.github/blob/main/CONTRIBUTING.md)

View File

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

View File

@ -0,0 +1,52 @@
# Bracket Matcher package
Highlights and jumps between `[]`, `()`, and `{}`. Also highlights matching XML
and HTML tags.
Autocompletes `[]`, `()`, `{}`, `""`, `''`, `“”`, ``, `«»`, ``, and
backticks by default.
Use <kbd>ctrl-m</kbd> to jump to the bracket matching the one adjacent to the cursor.
It jumps to the nearest enclosing bracket when there's no adjacent bracket,
Use <kbd>ctrl-cmd-m</kbd> to select all the text inside the current brackets.
Use <kbd>alt-cmd-.</kbd> to close the current XML/HTML tag.
---
### Configuration
Matching brackets and quotes are sensibly inserted for you. If you dislike this
functionality, you can disable it from the Bracket Matcher section of the
Settings View.
#### Custom Pairs
You can customize matching pairs in Bracket Matcher at any time. You can do so either globally via the Settings View or at the scope level via your `config.cson`. Changes take effect immediately.
* **Autocomplete Characters** - Comma-separated pairs that the editor will treat as brackets / quotes. Entries in this field override the package defaults.
* For example: `<>, (), []`
* **Pairs With Extra Newline** - Comma-separated pairs that enhance the editor's auto indent feature. When used, a newline is automatically added between the pair when enter is pressed between them. Note: This feature is meant to be used in combination with brackets defined for indentation by the active language package (`increaseIndentPattern` / `decreaseIndentPattern`).
Example:
```
fn main() {
| <---- Cursor positioned at one indent level higher
}
```
#### Scoped settings
In addition to the global settings, you are also able to add scope-specific modifications to Pulsar in your `config.cson`. This is especially useful for editor rule changes specific to each language. Scope-specific settings override package defaults _and_ global settings.
Example:
```cson
".rust.source":
"bracket-matcher":
autocompleteCharacters: [
"()"
"[]"
"{}"
"<>"
"\"\""
"``"
]
```

View File

@ -0,0 +1,18 @@
'atom-text-editor':
'ctrl-m': 'bracket-matcher:go-to-matching-bracket'
'ctrl-]': 'bracket-matcher:remove-brackets-from-selection'
'.platform-darwin atom-text-editor':
'ctrl-cmd-m': 'bracket-matcher:select-inside-brackets'
'alt-cmd-.': 'bracket-matcher:close-tag'
'ctrl-backspace': 'bracket-matcher:remove-matching-brackets'
'.platform-linux atom-text-editor':
'ctrl-alt-,': 'bracket-matcher:select-inside-brackets'
'ctrl-alt-.': 'bracket-matcher:close-tag'
'ctrl-alt-backspace': 'bracket-matcher:remove-matching-brackets'
'.platform-win32 atom-text-editor':
'ctrl-alt-,': 'bracket-matcher:select-inside-brackets'
'ctrl-alt-.': 'bracket-matcher:close-tag'
'ctrl-alt-backspace': 'bracket-matcher:remove-matching-brackets'

View File

@ -0,0 +1,585 @@
const {CompositeDisposable} = require('atom')
const _ = require('underscore-plus')
const {Range, Point} = require('atom')
const TagFinder = require('./tag-finder')
const MAX_ROWS_TO_SCAN = 10000
const ONE_CHAR_FORWARD_TRAVERSAL = Object.freeze(Point(0, 1))
const ONE_CHAR_BACKWARD_TRAVERSAL = Object.freeze(Point(0, -1))
const TWO_CHARS_BACKWARD_TRAVERSAL = Object.freeze(Point(0, -2))
const MAX_ROWS_TO_SCAN_FORWARD_TRAVERSAL = Object.freeze(Point(MAX_ROWS_TO_SCAN, 0))
const MAX_ROWS_TO_SCAN_BACKWARD_TRAVERSAL = Object.freeze(Point(-MAX_ROWS_TO_SCAN, 0))
module.exports =
class BracketMatcherView {
constructor (editor, editorElement, matchManager) {
this.destroy = this.destroy.bind(this)
this.updateMatch = this.updateMatch.bind(this)
this.editor = editor
this.matchManager = matchManager
this.gutter = this.editor.gutterWithName('line-number')
this.subscriptions = new CompositeDisposable()
this.tagFinder = new TagFinder(this.editor)
this.pairHighlighted = false
this.tagHighlighted = false
// ranges for possible selection
this.bracket1Range = null
this.bracket2Range = null
this.subscriptions.add(
this.editor.onDidTokenize(this.updateMatch),
this.editor.getBuffer().onDidChangeText(this.updateMatch),
this.editor.onDidChangeGrammar(this.updateMatch),
this.editor.onDidChangeSelectionRange(this.updateMatch),
this.editor.onDidAddCursor(this.updateMatch),
this.editor.onDidRemoveCursor(this.updateMatch),
atom.commands.add(editorElement, 'bracket-matcher:go-to-matching-bracket', () =>
this.goToMatchingBracket()
),
atom.commands.add(editorElement, 'bracket-matcher:go-to-enclosing-bracket', () =>
this.gotoPrecedingStartBracket()
),
atom.commands.add(editorElement, 'bracket-matcher:select-inside-brackets', () =>
this.selectInsideBrackets()
),
atom.commands.add(editorElement, 'bracket-matcher:close-tag', () =>
this.closeTag()
),
atom.commands.add(editorElement, 'bracket-matcher:remove-matching-brackets', () =>
this.removeMatchingBrackets()
),
atom.commands.add(editorElement, 'bracket-matcher:select-matching-brackets', () =>
this.selectMatchingBrackets()
),
this.editor.onDidDestroy(this.destroy)
)
this.updateMatch()
}
destroy () {
this.subscriptions.dispose()
}
updateMatch () {
if (this.pairHighlighted) {
this.editor.destroyMarker(this.startMarker.id)
this.editor.destroyMarker(this.endMarker.id)
}
this.pairHighlighted = false
this.tagHighlighted = false
if (!this.editor.getLastSelection().isEmpty()) return
const {position, matchPosition} = this.findCurrentPair()
let startRange = null
let endRange = null
let highlightTag = false
let highlightPair = false
if (position && matchPosition) {
this.bracket1Range = (startRange = Range(position, position.traverse(ONE_CHAR_FORWARD_TRAVERSAL)))
this.bracket2Range = (endRange = Range(matchPosition, matchPosition.traverse(ONE_CHAR_FORWARD_TRAVERSAL)))
highlightPair = true
} else {
this.bracket1Range = null
this.bracket2Range = null
if (this.hasSyntaxTree()) {
({startRange, endRange} = this.findMatchingTagNameRangesWithSyntaxTree())
} else {
({startRange, endRange} = this.tagFinder.findMatchingTags())
if (this.isCursorOnCommentOrString()) return
}
if (startRange) {
highlightTag = true
highlightPair = true
}
}
if (!highlightTag && !highlightPair) return
this.startMarker = this.createMarker(startRange)
this.endMarker = this.createMarker(endRange)
this.pairHighlighted = highlightPair
this.tagHighlighted = highlightTag
}
selectMatchingBrackets () {
if (!this.bracket1Range && !this.bracket2Range) return
this.editor.setSelectedBufferRanges([this.bracket1Range, this.bracket2Range])
this.matchManager.changeBracketsMode = true
}
removeMatchingBrackets () {
if (this.editor.hasMultipleCursors()) {
this.editor.backspace()
return
}
this.editor.transact(() => {
if (this.editor.getLastSelection().isEmpty()) {
this.editor.selectLeft()
}
const text = this.editor.getSelectedText()
this.editor.moveRight()
// check if the character to the left is part of a pair
if (
this.matchManager.pairedCharacters.hasOwnProperty(text) ||
this.matchManager.pairedCharactersInverse.hasOwnProperty(text)
) {
let {position, matchPosition, bracket} = this.findCurrentPair()
if (position && matchPosition) {
this.editor.setCursorBufferPosition(matchPosition)
this.editor.delete()
// if on the same line and the cursor is in front of an end pair
// offset by one to make up for the missing character
if (position.row === matchPosition.row && this.matchManager.pairedCharactersInverse.hasOwnProperty(bracket)) {
position = position.traverse(ONE_CHAR_BACKWARD_TRAVERSAL)
}
this.editor.setCursorBufferPosition(position)
this.editor.delete()
} else {
this.editor.backspace()
}
} else {
this.editor.backspace()
}
})
}
findMatchingEndBracket (startBracketPosition, startBracket, endBracket) {
if (startBracket === endBracket) return
if (this.hasSyntaxTree()) {
return this.findMatchingEndBracketWithSyntaxTree(startBracketPosition, startBracket, endBracket)
} else {
const scopeDescriptor = this.editor.scopeDescriptorForBufferPosition(startBracketPosition)
if (this.isScopeCommentedOrString(scopeDescriptor.getScopesArray())) return
return this.findMatchingEndBracketWithRegexSearch(startBracketPosition, startBracket, endBracket)
}
}
findMatchingStartBracket (endBracketPosition, startBracket, endBracket) {
if (startBracket === endBracket) return
if (this.hasSyntaxTree()) {
return this.findMatchingStartBracketWithSyntaxTree(endBracketPosition, startBracket, endBracket)
} else {
const scopeDescriptor = this.editor.scopeDescriptorForBufferPosition(endBracketPosition)
if (this.isScopeCommentedOrString(scopeDescriptor.getScopesArray())) return
return this.findMatchingStartBracketWithRegexSearch(endBracketPosition, startBracket, endBracket)
}
}
findMatchingEndBracketWithSyntaxTree (bracketPosition, startBracket, endBracket) {
let result
const bracketEndPosition = bracketPosition.traverse([0, startBracket.length])
this.editor.buffer.getLanguageMode().getSyntaxNodeContainingRange(
new Range(bracketPosition, bracketEndPosition),
node => {
if (bracketEndPosition.isGreaterThan(node.startPosition) && bracketEndPosition.isLessThan(node.endPosition)) {
const matchNode = node.children.find(child =>
bracketEndPosition.isLessThanOrEqual(child.startPosition) &&
child.type === endBracket
)
if (matchNode) result = Point.fromObject(matchNode.startPosition)
return true
}
}
)
return result
}
findMatchingStartBracketWithSyntaxTree (bracketPosition, startBracket, endBracket) {
let result
const bracketEndPosition = bracketPosition.traverse([0, startBracket.length])
this.editor.buffer.getLanguageMode().getSyntaxNodeContainingRange(
new Range(bracketPosition, bracketEndPosition),
node => {
if (bracketPosition.isGreaterThan(node.startPosition)) {
const matchNode = node.children.find(child =>
bracketPosition.isGreaterThanOrEqual(child.endPosition) &&
child.type === startBracket
)
if (matchNode) result = Point.fromObject(matchNode.startPosition)
return true
}
}
)
return result
}
findMatchingTagNameRangesWithSyntaxTree () {
const position = this.editor.getCursorBufferPosition()
const {startTag, endTag} = this.findContainingTagsWithSyntaxTree(position)
if (startTag && (startTag.range.containsPoint(position) || endTag.range.containsPoint(position))) {
if (startTag === endTag) {
const {range} = startTag.child(1)
return {startRange: range, endRange: range}
} else if (endTag.firstChild.type === '</') {
return {
startRange: startTag.child(1).range,
endRange: endTag.child(1).range
}
} else {
return {
startRange: startTag.child(1).range,
endRange: endTag.child(2).range
}
}
} else {
return {}
}
}
findMatchingTagsWithSyntaxTree () {
const position = this.editor.getCursorBufferPosition()
const {startTag, endTag} = this.findContainingTagsWithSyntaxTree(position)
if (startTag) {
return {startRange: startTag.range, endRange: endTag.range}
} else {
return {}
}
}
findContainingTagsWithSyntaxTree (position) {
let startTag, endTag
if (position.column === this.editor.buffer.lineLengthForRow(position.row)) position.column--;
this.editor.buffer.getLanguageMode().getSyntaxNodeAtPosition(position, node => {
if (node.type.includes('element') && node.childCount > 0) {
const {firstChild, lastChild} = node
if (
firstChild.childCount > 2 &&
firstChild.firstChild.type === '<'
) {
if (lastChild === firstChild && firstChild.lastChild.type === '/>') {
startTag = firstChild
endTag = firstChild
} else if (
lastChild.childCount > 2 &&
(lastChild.firstChild.type === '</' ||
lastChild.firstChild.type === '<' && lastChild.child(1).type === '/')
) {
startTag = firstChild
endTag = lastChild
}
}
return true
}
})
return {startTag, endTag}
}
findMatchingEndBracketWithRegexSearch (startBracketPosition, startBracket, endBracket) {
const scanRange = new Range(
startBracketPosition.traverse(ONE_CHAR_FORWARD_TRAVERSAL),
startBracketPosition.traverse(MAX_ROWS_TO_SCAN_FORWARD_TRAVERSAL)
)
let endBracketPosition = null
let unpairedCount = 0
this.editor.scanInBufferRange(this.matchManager.pairRegexes[startBracket], scanRange, result => {
if (this.isRangeCommentedOrString(result.range)) return
switch (result.match[0]) {
case startBracket:
unpairedCount++
break
case endBracket:
unpairedCount--
if (unpairedCount < 0) {
endBracketPosition = result.range.start
result.stop()
}
break
}
})
return endBracketPosition
}
findMatchingStartBracketWithRegexSearch (endBracketPosition, startBracket, endBracket) {
const scanRange = new Range(
endBracketPosition.traverse(MAX_ROWS_TO_SCAN_BACKWARD_TRAVERSAL),
endBracketPosition
)
let startBracketPosition = null
let unpairedCount = 0
this.editor.backwardsScanInBufferRange(this.matchManager.pairRegexes[startBracket], scanRange, result => {
if (this.isRangeCommentedOrString(result.range)) return
switch (result.match[0]) {
case startBracket:
unpairedCount--
if (unpairedCount < 0) {
startBracketPosition = result.range.start
result.stop()
break
}
break
case endBracket:
unpairedCount++
}
})
return startBracketPosition
}
findPrecedingStartBracket (cursorPosition) {
if (this.hasSyntaxTree()) {
return this.findPrecedingStartBracketWithSyntaxTree(cursorPosition)
} else {
return this.findPrecedingStartBracketWithRegexSearch(cursorPosition)
}
}
findPrecedingStartBracketWithSyntaxTree (cursorPosition) {
let result
this.editor.buffer.getLanguageMode().getSyntaxNodeAtPosition(cursorPosition, node => {
for (const child of node.children) {
if (cursorPosition.isLessThanOrEqual(child.startPosition)) break
if (
child.type in this.matchManager.pairedCharacters ||
child.type in this.matchManager.pairedCharactersInverse
) {
result = Point.fromObject(child.startPosition)
return true
}
}
})
return result
}
findPrecedingStartBracketWithRegexSearch (cursorPosition) {
const scanRange = new Range(Point.ZERO, cursorPosition)
const startBracket = _.escapeRegExp(_.keys(this.matchManager.pairedCharacters).join(''))
const endBracket = _.escapeRegExp(_.keys(this.matchManager.pairedCharactersInverse).join(''))
const combinedRegExp = new RegExp(`[${startBracket}${endBracket}]`, 'g')
const startBracketRegExp = new RegExp(`[${startBracket}]`, 'g')
const endBracketRegExp = new RegExp(`[${endBracket}]`, 'g')
let startPosition = null
let unpairedCount = 0
this.editor.backwardsScanInBufferRange(combinedRegExp, scanRange, result => {
if (this.isRangeCommentedOrString(result.range)) return
if (result.match[0].match(endBracketRegExp)) {
unpairedCount++
} else if (result.match[0].match(startBracketRegExp)) {
unpairedCount--
if (unpairedCount < 0) {
startPosition = result.range.start
result.stop()
}
}
})
return startPosition
}
createMarker (bufferRange) {
const marker = this.editor.markBufferRange(bufferRange)
this.editor.decorateMarker(marker, {type: 'highlight', class: 'bracket-matcher', deprecatedRegionClass: 'bracket-matcher'})
if (atom.config.get('bracket-matcher.highlightMatchingLineNumber', {scope: this.editor.getRootScopeDescriptor()}) && this.gutter) {
this.gutter.decorateMarker(marker, {type: 'highlight', class: 'bracket-matcher', deprecatedRegionClass: 'bracket-matcher'})
}
return marker
}
findCurrentPair () {
const currentPosition = this.editor.getCursorBufferPosition()
const previousPosition = currentPosition.traverse(ONE_CHAR_BACKWARD_TRAVERSAL)
const nextPosition = currentPosition.traverse(ONE_CHAR_FORWARD_TRAVERSAL)
const currentCharacter = this.editor.getTextInBufferRange(new Range(currentPosition, nextPosition))
const previousCharacter = this.editor.getTextInBufferRange(new Range(previousPosition, currentPosition))
let position, matchPosition, currentBracket, matchingBracket
if ((matchingBracket = this.matchManager.pairedCharacters[currentCharacter])) {
position = currentPosition
currentBracket = currentCharacter
matchPosition = this.findMatchingEndBracket(position, currentBracket, matchingBracket)
} else if ((matchingBracket = this.matchManager.pairedCharacters[previousCharacter])) {
position = previousPosition
currentBracket = previousCharacter
matchPosition = this.findMatchingEndBracket(position, currentBracket, matchingBracket)
} else if ((matchingBracket = this.matchManager.pairedCharactersInverse[previousCharacter])) {
position = previousPosition
currentBracket = previousCharacter
matchPosition = this.findMatchingStartBracket(position, matchingBracket, currentBracket)
} else if ((matchingBracket = this.matchManager.pairedCharactersInverse[currentCharacter])) {
position = currentPosition
currentBracket = currentCharacter
matchPosition = this.findMatchingStartBracket(position, matchingBracket, currentBracket)
}
return {position, matchPosition, bracket: currentBracket}
}
goToMatchingBracket () {
if (!this.pairHighlighted) return this.gotoPrecedingStartBracket()
const position = this.editor.getCursorBufferPosition()
if (this.tagHighlighted) {
let tagCharacterOffset
let startRange = this.startMarker.getBufferRange()
const tagLength = startRange.end.column - startRange.start.column
let endRange = this.endMarker.getBufferRange()
if (startRange.compare(endRange) > 0) {
[startRange, endRange] = [endRange, startRange]
}
// include the <
startRange = new Range(startRange.start.traverse(ONE_CHAR_BACKWARD_TRAVERSAL), endRange.end.traverse(ONE_CHAR_BACKWARD_TRAVERSAL))
// include the </
endRange = new Range(endRange.start.traverse(TWO_CHARS_BACKWARD_TRAVERSAL), endRange.end.traverse(TWO_CHARS_BACKWARD_TRAVERSAL))
if (position.isLessThan(endRange.start)) {
tagCharacterOffset = position.column - startRange.start.column
if (tagCharacterOffset > 0) { tagCharacterOffset++ }
tagCharacterOffset = Math.min(tagCharacterOffset, tagLength + 2) // include </
this.editor.setCursorBufferPosition(endRange.start.traverse([0, tagCharacterOffset]))
} else {
tagCharacterOffset = position.column - endRange.start.column
if (tagCharacterOffset > 1) { tagCharacterOffset-- }
tagCharacterOffset = Math.min(tagCharacterOffset, tagLength + 1) // include <
this.editor.setCursorBufferPosition(startRange.start.traverse([0, tagCharacterOffset]))
}
} else {
const previousPosition = position.traverse(ONE_CHAR_BACKWARD_TRAVERSAL)
const startPosition = this.startMarker.getStartBufferPosition()
const endPosition = this.endMarker.getStartBufferPosition()
if (position.isEqual(startPosition)) {
this.editor.setCursorBufferPosition(endPosition.traverse(ONE_CHAR_FORWARD_TRAVERSAL))
} else if (previousPosition.isEqual(startPosition)) {
this.editor.setCursorBufferPosition(endPosition)
} else if (position.isEqual(endPosition)) {
this.editor.setCursorBufferPosition(startPosition.traverse(ONE_CHAR_FORWARD_TRAVERSAL))
} else if (previousPosition.isEqual(endPosition)) {
this.editor.setCursorBufferPosition(startPosition)
}
}
}
gotoPrecedingStartBracket () {
if (this.pairHighlighted) return
const matchPosition = this.findPrecedingStartBracket(this.editor.getCursorBufferPosition())
if (matchPosition) {
this.editor.setCursorBufferPosition(matchPosition)
} else {
let startRange, endRange
if (this.hasSyntaxTree()) {
({startRange, endRange} = this.findMatchingTagsWithSyntaxTree())
} else {
({startRange, endRange} = this.tagFinder.findStartEndTags())
}
if (startRange) {
if (startRange.compare(endRange) > 0) {
[startRange, endRange] = [endRange, startRange]
}
this.editor.setCursorBufferPosition(startRange.start)
}
}
}
multiCursorSelect() {
this.editor.getCursorBufferPositions().forEach(position => {
let startPosition = this.findPrecedingStartBracket(position)
if(startPosition) {
const startBracket = this.editor.getTextInRange(Range.fromPointWithDelta(startPosition, 0, 1))
const endPosition = this.findMatchingEndBracket(startPosition, startBracket, this.matchManager.pairedCharacters[startBracket])
startPosition = startPosition.traverse([0, 1])
if (startPosition && endPosition) {
const rangeToSelect = new Range(startPosition, endPosition)
this.editor.addSelectionForBufferRange(rangeToSelect)
}
} else {
let startRange, endRange;
if (this.hasSyntaxTree()) {
({startRange, endRange} = this.findMatchingTagsWithSyntaxTree())
} else {
({startRange, endRange} = this.tagFinder.findStartEndTags(true))
if (startRange && startRange.compare(endRange) > 0) {
[startRange, endRange] = [endRange, startRange]
}
}
if (startRange) {
const startPosition = startRange.end
const endPosition = endRange.start
const rangeToSelect = new Range(startPosition, endPosition)
this.editor.setSelectedBufferRange(rangeToSelect)
}
}
})
}
selectInsideBrackets () {
let endPosition, endRange, startPosition, startRange
if (this.pairHighlighted) {
startRange = this.startMarker.getBufferRange()
endRange = this.endMarker.getBufferRange()
if (this.tagHighlighted) {
if (this.hasSyntaxTree()) {
({startRange, endRange} = this.findMatchingTagsWithSyntaxTree())
} else {
({startRange, endRange} = this.tagFinder.findStartEndTags(true))
if (startRange && startRange.compare(endRange) > 0) {
[startRange, endRange] = [endRange, startRange]
}
}
}
startPosition = startRange.end
endPosition = endRange.start
const rangeToSelect = new Range(startPosition, endPosition)
this.editor.setSelectedBufferRange(rangeToSelect)
} else {
this.multiCursorSelect();
}
}
// Insert at the current cursor position a closing tag if there exists an
// open tag that is not closed afterwards.
closeTag () {
const cursorPosition = this.editor.getCursorBufferPosition()
const preFragment = this.editor.getTextInBufferRange([Point.ZERO, cursorPosition])
const postFragment = this.editor.getTextInBufferRange([cursorPosition, Point.INFINITY])
const tag = this.tagFinder.closingTagForFragments(preFragment, postFragment)
if (tag) {
this.editor.insertText(`</${tag}>`)
}
}
isCursorOnCommentOrString () {
return this.isScopeCommentedOrString(this.editor.getLastCursor().getScopeDescriptor().getScopesArray())
}
isRangeCommentedOrString (range) {
return this.isScopeCommentedOrString(this.editor.scopeDescriptorForBufferPosition(range.start).getScopesArray())
}
isScopeCommentedOrString (scopesArray) {
for (let scope of scopesArray.reverse()) {
scope = scope.split('.')
if (scope.includes('embedded') && scope.includes('source')) return false
if (scope.includes('comment') || scope.includes('string')) return true
}
return false
}
hasSyntaxTree () {
return this.editor.buffer.getLanguageMode().getSyntaxNodeAtPosition
}
}

View File

@ -0,0 +1,299 @@
const _ = require('underscore-plus')
const {CompositeDisposable} = require('atom')
const SelectorCache = require('./selector-cache')
module.exports =
class BracketMatcher {
constructor (editor, editorElement, matchManager) {
this.insertText = this.insertText.bind(this)
this.insertNewline = this.insertNewline.bind(this)
this.backspace = this.backspace.bind(this)
this.editor = editor
this.matchManager = matchManager
this.subscriptions = new CompositeDisposable()
this.bracketMarkers = []
this.origEditorInsertText = this.editor.insertText.bind(this.editor)
_.adviseBefore(this.editor, 'insertText', this.insertText)
_.adviseBefore(this.editor, 'insertNewline', this.insertNewline)
_.adviseBefore(this.editor, 'backspace', this.backspace)
this.subscriptions.add(
atom.commands.add(editorElement, 'bracket-matcher:remove-brackets-from-selection', event => {
if (!this.removeBrackets()) event.abortKeyBinding()
}),
this.editor.onDidDestroy(() => this.unsubscribe())
)
}
insertText (text, options) {
if (!text) return true
if ((options && options.select) || (options && options.undo === 'skip')) return true
let autoCompleteOpeningBracket, bracketMarker, pair
if (this.matchManager.changeBracketsMode) {
this.matchManager.changeBracketsMode = false
if (this.isClosingBracket(text)) {
text = this.matchManager.pairedCharactersInverse[text]
}
if (this.isOpeningBracket(text)) {
this.editor.mutateSelectedText(selection => {
const selectionText = selection.getText()
if (this.isOpeningBracket(selectionText)) {
selection.insertText(text)
}
if (this.isClosingBracket(selectionText)) {
selection.insertText(this.matchManager.pairedCharacters[text])
}
})
return false
}
}
if (this.wrapSelectionInBrackets(text)) return false
if (this.editor.hasMultipleCursors()) return true
const cursorBufferPosition = this.editor.getCursorBufferPosition()
const previousCharacters = this.editor.getTextInBufferRange([[cursorBufferPosition.row, 0], cursorBufferPosition])
const nextCharacter = this.editor.getTextInBufferRange([cursorBufferPosition, cursorBufferPosition.traverse([0, 1])])
const previousCharacter = previousCharacters.slice(-1)
const hasWordAfterCursor = /\w/.test(nextCharacter)
const hasWordBeforeCursor = /\w/.test(previousCharacter)
const hasQuoteBeforeCursor = this.isQuote(previousCharacter) && (previousCharacter === text[0])
const hasEscapeCharacterBeforeCursor = endsWithEscapeCharacter(previousCharacters)
const hasEscapeSequenceBeforeCursor = endsWithEscapeSequence(previousCharacters)
if (text === '#' && this.isCursorOnInterpolatedString()) {
autoCompleteOpeningBracket = this.getScopedSetting('bracket-matcher.autocompleteBrackets') && !hasEscapeCharacterBeforeCursor
text += '{'
pair = '}'
} else {
autoCompleteOpeningBracket = (
this.isOpeningBracket(text) &&
!hasWordAfterCursor &&
this.getScopedSetting('bracket-matcher.autocompleteBrackets') &&
!(this.isQuote(text) && (hasWordBeforeCursor || hasQuoteBeforeCursor || hasEscapeSequenceBeforeCursor)) &&
!hasEscapeCharacterBeforeCursor
)
pair = this.matchManager.pairedCharacters[text]
}
let skipOverExistingClosingBracket = false
if (this.isClosingBracket(text) && (nextCharacter === text) && !hasEscapeCharacterBeforeCursor) {
bracketMarker = this.bracketMarkers.find(marker => marker.isValid() && marker.getBufferRange().end.isEqual(cursorBufferPosition))
if (bracketMarker || this.getScopedSetting('bracket-matcher.alwaysSkipClosingPairs')) {
skipOverExistingClosingBracket = true
}
}
if (skipOverExistingClosingBracket) {
if (bracketMarker) bracketMarker.destroy()
_.remove(this.bracketMarkers, bracketMarker)
this.editor.moveRight()
return false
} else if (autoCompleteOpeningBracket) {
this.editor.transact(() => {
this.origEditorInsertText(text + pair)
this.editor.moveLeft()
})
const range = [cursorBufferPosition, cursorBufferPosition.traverse([0, text.length])]
this.bracketMarkers.push(this.editor.markBufferRange(range))
return false
}
}
insertNewline () {
if (this.editor.hasMultipleCursors()) return
if (!this.editor.getLastSelection().isEmpty()) return
const cursorBufferPosition = this.editor.getCursorBufferPosition()
const previousCharacters = this.editor.getTextInBufferRange([[cursorBufferPosition.row, 0], cursorBufferPosition])
const nextCharacter = this.editor.getTextInBufferRange([cursorBufferPosition, cursorBufferPosition.traverse([0, 1])])
const previousCharacter = previousCharacters.slice(-1)
const hasEscapeCharacterBeforeCursor = endsWithEscapeCharacter(previousCharacters)
if (
this.matchManager.pairsWithExtraNewline[previousCharacter] === nextCharacter &&
!hasEscapeCharacterBeforeCursor
) {
this.editor.transact(() => {
this.origEditorInsertText('\n\n')
this.editor.moveUp()
if (this.getScopedSetting('editor.autoIndent')) {
const cursorRow = this.editor.getCursorBufferPosition().row
this.editor.autoIndentBufferRows(cursorRow, cursorRow + 1)
}
})
return false
}
}
backspace () {
if (this.editor.hasMultipleCursors()) return
if (!this.editor.getLastSelection().isEmpty()) return
const cursorBufferPosition = this.editor.getCursorBufferPosition()
const previousCharacters = this.editor.getTextInBufferRange([[cursorBufferPosition.row, 0], cursorBufferPosition])
const nextCharacter = this.editor.getTextInBufferRange([cursorBufferPosition, cursorBufferPosition.traverse([0, 1])])
const previousCharacter = previousCharacters.slice(-1)
const hasEscapeCharacterBeforeCursor = endsWithEscapeCharacter(previousCharacters)
if (
this.matchManager.pairedCharacters[previousCharacter] === nextCharacter &&
!hasEscapeCharacterBeforeCursor &&
this.getScopedSetting('bracket-matcher.autocompleteBrackets')
) {
this.editor.transact(() => {
this.editor.moveLeft()
this.editor.delete()
this.editor.delete()
})
return false
}
}
removeBrackets () {
let bracketsRemoved = false
this.editor.mutateSelectedText(selection => {
let selectionEnd
if (!this.selectionIsWrappedByMatchingBrackets(selection)) return
const range = selection.getBufferRange()
const options = {reversed: selection.isReversed()}
const selectionStart = range.start
if (range.start.row === range.end.row) {
selectionEnd = range.end.traverse([0, -2])
} else {
selectionEnd = range.end.traverse([0, -1])
}
const text = selection.getText()
selection.insertText(text.substring(1, text.length - 1))
selection.setBufferRange([selectionStart, selectionEnd], options)
bracketsRemoved = true
})
return bracketsRemoved
}
wrapSelectionInBrackets (bracket) {
let pair
if (bracket === '#') {
if (!this.isCursorOnInterpolatedString()) return false
bracket = '#{'
pair = '}'
} else {
if (!this.isOpeningBracket(bracket)) return false
pair = this.matchManager.pairedCharacters[bracket]
}
if (!this.editor.selections.some(s => !s.isEmpty())) return false
if (!this.getScopedSetting('bracket-matcher.wrapSelectionsInBrackets')) return false
let selectionWrapped = false
this.editor.mutateSelectedText(selection => {
let selectionEnd
if (selection.isEmpty()) return
// Don't wrap in #{} if the selection spans more than one line
if ((bracket === '#{') && !selection.getBufferRange().isSingleLine()) return
selectionWrapped = true
const range = selection.getBufferRange()
const options = {reversed: selection.isReversed()}
selection.insertText(`${bracket}${selection.getText()}${pair}`)
const selectionStart = range.start.traverse([0, bracket.length])
if (range.start.row === range.end.row) {
selectionEnd = range.end.traverse([0, bracket.length])
} else {
selectionEnd = range.end
}
selection.setBufferRange([selectionStart, selectionEnd], options)
})
return selectionWrapped
}
isQuote (string) {
return /['"`]/.test(string)
}
isCursorOnInterpolatedString () {
const cursor = this.editor.getLastCursor()
const languageMode = this.editor.getBuffer().getLanguageMode()
if (languageMode.getSyntaxNodeAtPosition) {
const node = languageMode.getSyntaxNodeAtPosition(
cursor.getBufferPosition(),
(node, grammar) => grammar.scopeName === 'source.ruby' && /string|symbol/.test(node.type)
)
if (node) {
const {firstChild} = node
if (firstChild) {
return ['"', ':"', '%('].includes(firstChild.text)
}
}
return false
} else {
if (this.interpolatedStringSelector == null) {
const segments = [
'*.*.*.interpolated.ruby',
'string.interpolated.ruby',
'string.regexp.interpolated.ruby',
'string.quoted.double.coffee',
'string.unquoted.heredoc.ruby',
'string.quoted.double.livescript',
'string.quoted.double.heredoc.livescript',
'string.quoted.double.elixir',
'string.quoted.double.heredoc.elixir',
'comment.documentation.heredoc.elixir'
]
this.interpolatedStringSelector = SelectorCache.get(segments.join(' | '))
}
return this.interpolatedStringSelector.matches(this.editor.getLastCursor().getScopeDescriptor().getScopesArray())
}
}
isOpeningBracket (string) {
return this.matchManager.pairedCharacters.hasOwnProperty(string)
}
isClosingBracket (string) {
return this.matchManager.pairedCharactersInverse.hasOwnProperty(string)
}
selectionIsWrappedByMatchingBrackets (selection) {
if (selection.isEmpty()) return false
const selectedText = selection.getText()
const firstCharacter = selectedText[0]
const lastCharacter = selectedText[selectedText.length - 1]
return this.matchManager.pairedCharacters[firstCharacter] === lastCharacter
}
unsubscribe () {
this.subscriptions.dispose()
}
getScopedSetting (key) {
return atom.config.get(key, {scope: this.editor.getRootScopeDescriptor()})
}
}
const BACKSLASHES_REGEX = /(\\+)$/
const ESCAPE_SEQUENCE_REGEX = /(\\+)[^\\]$/
// odd number of backslashes
function endsWithEscapeCharacter (string) {
const backslashesMatch = string.match(BACKSLASHES_REGEX)
return backslashesMatch && backslashesMatch[1].length % 2 === 1
}
// even number of backslashes or odd number of backslashes followed by another character
function endsWithEscapeSequence (string) {
const backslashesMatch = string.match(BACKSLASHES_REGEX)
const escapeSequenceMatch = string.match(ESCAPE_SEQUENCE_REGEX)
return (
(escapeSequenceMatch && escapeSequenceMatch[1].length % 2 === 1) ||
(backslashesMatch && backslashesMatch[1].length % 2 === 0)
)
}

View File

@ -0,0 +1,20 @@
const MatchManager = require('./match-manager')
const BracketMatcherView = require('./bracket-matcher-view')
const BracketMatcher = require('./bracket-matcher')
module.exports = {
activate () {
const watchedEditors = new WeakSet()
atom.workspace.observeTextEditors(editor => {
if (watchedEditors.has(editor)) return
const editorElement = atom.views.getView(editor)
const matchManager = new MatchManager(editor, editorElement)
new BracketMatcherView(editor, editorElement, matchManager)
new BracketMatcher(editor, editorElement, matchManager)
watchedEditors.add(editor)
editor.onDidDestroy(() => watchedEditors.delete(editor))
})
}
}

View File

@ -0,0 +1,60 @@
const _ = require('underscore-plus')
const {CompositeDisposable} = require('atom')
module.exports =
class MatchManager {
appendPair (pairList, [itemLeft, itemRight]) {
const newPair = {}
newPair[itemLeft] = itemRight
pairList = _.extend(pairList, newPair)
}
processAutoPairs (autocompletePairs, pairedList, dataFun) {
if (autocompletePairs.length) {
for (let autocompletePair of autocompletePairs) {
const pairArray = autocompletePair.split('')
this.appendPair(pairedList, dataFun(pairArray))
}
}
}
updateConfig () {
this.pairedCharacters = {}
this.pairedCharactersInverse = {}
this.pairRegexes = {}
this.pairsWithExtraNewline = {}
this.processAutoPairs(this.getScopedSetting('bracket-matcher.autocompleteCharacters'), this.pairedCharacters, x => [x[0], x[1]])
this.processAutoPairs(this.getScopedSetting('bracket-matcher.autocompleteCharacters'), this.pairedCharactersInverse, x => [x[1], x[0]])
this.processAutoPairs(this.getScopedSetting('bracket-matcher.pairsWithExtraNewline'), this.pairsWithExtraNewline, x => [x[0], x[1]])
for (let startPair in this.pairedCharacters) {
const endPair = this.pairedCharacters[startPair]
this.pairRegexes[startPair] = new RegExp(`[${_.escapeRegExp(startPair + endPair)}]`, 'g')
}
}
getScopedSetting (key) {
return atom.config.get(key, {scope: this.editor.getRootScopeDescriptor()})
}
constructor (editor, editorElement) {
this.destroy = this.destroy.bind(this)
this.editor = editor
this.subscriptions = new CompositeDisposable()
this.updateConfig()
// Subscribe to config changes
const scope = this.editor.getRootScopeDescriptor()
this.subscriptions.add(
atom.config.observe('bracket-matcher.autocompleteCharacters', {scope}, () => this.updateConfig()),
atom.config.observe('bracket-matcher.pairsWithExtraNewline', {scope}, () => this.updateConfig()),
this.editor.onDidDestroy(this.destroy)
)
this.changeBracketsMode = false
}
destroy () {
this.subscriptions.dispose()
}
}

View File

@ -0,0 +1,11 @@
const {ScopeSelector} = require('second-mate')
const cache = {}
exports.get = function (selector) {
let scopeSelector = cache[selector]
if (!scopeSelector) {
scopeSelector = new ScopeSelector(selector)
cache[selector] = scopeSelector
}
return scopeSelector
}

View File

@ -0,0 +1,18 @@
[
"area",
"base",
"br",
"col",
"command",
"embed",
"hr",
"img",
"input",
"keygen",
"link",
"meta",
"param",
"source",
"track",
"wbr"
]

View File

@ -0,0 +1,258 @@
const {Range} = require('atom')
const _ = require('underscore-plus')
const SelfClosingTags = require('./self-closing-tags')
const TAG_SELECTOR_REGEX = /(\b|\.)(meta\.tag|punctuation\.definition\.tag)/
const COMMENT_SELECTOR_REGEX = /(\b|\.)comment/
// Creates a regex to match opening tag with match[1] and closing tags with match[2]
//
// * tagNameRegexStr - a regex string describing how to match the tagname.
// Should not contain capturing match groups.
//
// Returns a {RegExp}.
const generateTagStartOrEndRegex = function (tagNameRegexStr) {
const notSelfClosingTagEnd = "(?:[^>\\/\"']|\"[^\"]*\"|'[^']*')*>"
return new RegExp(`<(${tagNameRegexStr})${notSelfClosingTagEnd}|<\\/(${tagNameRegexStr})>`)
}
const tagStartOrEndRegex = generateTagStartOrEndRegex('\\w[-\\w]*(?:(?:\\:|\\.)\\w[-\\w]*)*')
// Helper to find the matching start/end tag for the start/end tag under the
// cursor in XML, HTML, etc. editors.
module.exports =
class TagFinder {
constructor (editor) {
// 1. Tag prefix
// 2. Closing tag (optional)
// 3. Tag name
// 4. Attributes (ids, classes, etc. - optional)
// 5. Tag suffix
// 6. Self-closing tag (optional)
this.editor = editor
this.tagPattern = /(<(\/)?)(.+?)(\s+.*?)?((\/)?>|$)/
this.wordRegex = /.*?(>|$)/
}
patternForTagName (tagName) {
tagName = _.escapeRegExp(tagName)
// 1. Start tag
// 2. Tag name
// 3. Attributes (optional)
// 4. Tag suffix
// 5. Self-closing tag (optional)
// 6. End tag
return new RegExp(`(<(${tagName})(\\s+[^>]*?)?((/)?>))|(</${tagName}[^>]*>)`, 'gi')
}
isRangeCommented (range) {
return this.scopesForPositionMatchRegex(range.start, COMMENT_SELECTOR_REGEX)
}
isCursorOnTag () {
return this.scopesForPositionMatchRegex(this.editor.getCursorBufferPosition(), TAG_SELECTOR_REGEX)
}
scopesForPositionMatchRegex (position, regex) {
const {tokenizedBuffer, buffer} = this.editor
const {grammar} = tokenizedBuffer
let column = 0
const line = tokenizedBuffer.tokenizedLineForRow(position.row)
if (line == null) { return false }
const lineLength = buffer.lineLengthForRow(position.row)
const scopeIds = line.openScopes.slice()
for (let i = 0; i < line.tags.length; i++) {
const tag = line.tags[i]
if (tag >= 0) {
const nextColumn = column + tag
if ((nextColumn > position.column) || (nextColumn === lineLength)) { break }
column = nextColumn
} else if ((tag & 1) === 1) {
scopeIds.push(tag)
} else {
scopeIds.pop()
}
}
return scopeIds.some(scopeId => regex.test(grammar.scopeForId(scopeId)))
}
findStartTag (tagName, endPosition, fullRange = false) {
const scanRange = new Range([0, 0], endPosition)
const pattern = this.patternForTagName(tagName)
let startRange = null
let unpairedCount = 0
this.editor.backwardsScanInBufferRange(pattern, scanRange, ({match, range, stop}) => {
if (this.isRangeCommented(range)) return
const [entireMatch, isStartTag, tagName, attributes, suffix, isSelfClosingTag, isEndTag] = match
if (isSelfClosingTag) return
if (isStartTag) {
unpairedCount--
if (unpairedCount < 0) {
stop()
startRange = range
if (!fullRange) {
// Move the start past the initial <
startRange.start = startRange.start.translate([0, 1])
// End right after the tag name
startRange.end = startRange.start.translate([0, tagName.length])
}
}
} else {
unpairedCount++
}
})
return startRange
}
findEndTag (tagName, startPosition, fullRange = false) {
const scanRange = new Range(startPosition, this.editor.buffer.getEndPosition())
const pattern = this.patternForTagName(tagName)
let endRange = null
let unpairedCount = 0
this.editor.scanInBufferRange(pattern, scanRange, ({match, range, stop}) => {
if (this.isRangeCommented(range)) return
const [entireMatch, isStartTag, tagName, attributes, suffix, isSelfClosingTag, isEndTag] = match
if (isSelfClosingTag) return
if (isStartTag) {
unpairedCount++
} else {
unpairedCount--
if (unpairedCount < 0) {
stop()
endRange = range
if (!fullRange) {
// Subtract </ and > from range
endRange = range.translate([0, 2], [0, -1])
}
}
}
})
return endRange
}
findStartEndTags (fullRange = false) {
let ranges = {}
const endPosition = this.editor.getLastCursor().getCurrentWordBufferRange({wordRegex: this.wordRegex}).end
this.editor.backwardsScanInBufferRange(this.tagPattern, [[0, 0], endPosition], ({match, range, stop}) => {
stop()
const [entireMatch, prefix, isClosingTag, tagName, attributes, suffix, isSelfClosingTag] = Array.from(match)
let startRange = range
if (!fullRange) {
if (range.start.row === range.end.row) {
// Move the start past the initial <
startRange.start = startRange.start.translate([0, prefix.length])
// End right after the tag name
startRange.end = startRange.start.translate([0, tagName.length])
} else {
startRange = Range.fromObject([range.start.translate([0, prefix.length]), [range.start.row, Infinity]])
}
}
let endRange
if (isSelfClosingTag) {
endRange = startRange
} else if (isClosingTag) {
endRange = this.findStartTag(tagName, startRange.start, fullRange)
} else {
endRange = this.findEndTag(tagName, startRange.end, fullRange)
}
if (startRange && endRange) ranges = {startRange, endRange}
})
return ranges
}
findMatchingTags () {
return (this.isCursorOnTag() && this.findStartEndTags()) || {}
}
// Parses a fragment of html returning the stack (i.e., an array) of open tags
//
// fragment - the fragment of html to be analysed
// stack - an array to be populated (can be non-empty)
// matchExpr - a RegExp describing how to match opening/closing tags
// the opening/closing descriptions must be captured subexpressions
// so that the code can refer to match[1] to check if an opening
// tag has been found, and to match[2] to check if a closing tag
// has been found
// cond - a condition to be checked at each iteration. If the function
// returns false the processing is immediately interrupted. When
// called the current stack is provided to the function.
//
// Returns an array of strings. Each string is a tag that is still to be closed
// (the most recent non closed tag is at the end of the array).
parseFragment (fragment, stack, matchExpr, cond) {
let match = fragment.match(matchExpr)
while (match && cond(stack)) {
if (SelfClosingTags.indexOf(match[1]) === -1) {
const topElem = stack[stack.length - 1]
if (match[2] && (topElem === match[2])) {
stack.pop()
} else if (match[1]) {
stack.push(match[1])
}
}
fragment = fragment.substr(match.index + match[0].length)
match = fragment.match(matchExpr)
}
return stack
}
// Parses the given fragment of html code returning the last unclosed tag.
//
// fragment - a string containing a fragment of html code.
//
// Returns an array of strings. Each string is a tag that is still to be closed
// (the most recent non closed tag is at the end of the array).
tagsNotClosedInFragment (fragment) {
return this.parseFragment(fragment, [], tagStartOrEndRegex, () => true)
}
// Parses the given fragment of html code and returns true if the given tag
// has a matching closing tag in it. If tag is reopened and reclosed in the
// given fragment then the end point of that pair does not count as a matching
// closing tag.
tagDoesNotCloseInFragment (tags, fragment) {
if (tags.length === 0) { return false }
let stack = tags
const stackLength = stack.length
const tag = tags[tags.length - 1]
const escapedTag = _.escapeRegExp(tag)
stack = this.parseFragment(fragment, stack, generateTagStartOrEndRegex(escapedTag), s =>
s.length >= stackLength || s[s.length - 1] === tag
)
return (stack.length > 0) && (stack[stack.length - 1] === tag)
}
// Parses preFragment and postFragment returning the last open tag in
// preFragment that is not closed in postFragment.
//
// Returns a tag name or null if it can't find it.
closingTagForFragments (preFragment, postFragment) {
const tags = this.tagsNotClosedInFragment(preFragment)
const tag = tags[tags.length - 1]
if (this.tagDoesNotCloseInFragment(tags, postFragment)) {
return tag
} else {
return null
}
}
}

View File

@ -0,0 +1,22 @@
'menu': [
{
'label': 'Packages'
'submenu': [
'label': 'Bracket Matcher'
'submenu': [
{ 'label': 'Go To Matching Bracket', 'command': 'bracket-matcher:go-to-matching-bracket' }
{ 'label': 'Select Inside Brackets', 'command': 'bracket-matcher:select-inside-brackets' }
{ 'label': 'Remove Brackets From Selection', 'command': 'bracket-matcher:remove-brackets-from-selection' }
{ 'label': 'Close Current Tag', 'command': 'bracket-matcher:close-tag' }
{ 'label': 'Remove Matching Brackets', 'command': 'bracket-matcher:remove-matching-brackets' }
{ 'label': 'Select Matching Brackets', 'command': 'bracket-matcher:select-matching-brackets' }
]
]
},
{
'label': 'Selection'
'submenu': [
{ 'label': 'Select Inside Brackets', 'command': 'bracket-matcher:select-inside-brackets' }
]
}
]

View File

@ -0,0 +1,67 @@
{
"name": "bracket-matcher",
"version": "0.92.0",
"main": "./lib/main",
"description": "Highlight the matching bracket for the `(){}[]` character under the cursor. Move the cursor to the matching bracket with `ctrl-m`.",
"repository": "https://github.com/pulsar-edit/bracket-matcher",
"license": "MIT",
"engines": {
"atom": "*"
},
"dependencies": {
"underscore-plus": "1.x"
},
"configSchema": {
"autocompleteCharacters": {
"description": "Autocompleted characters treated as matching pairs, such as `()`, and `{}`.",
"type": "array",
"default": [
"()",
"[]",
"{}",
"\"\"",
"''",
"``",
"“”",
"",
"«»",
""
],
"items": {
"type": "string"
}
},
"pairsWithExtraNewline": {
"description": "Automatically add a newline between the pair when enter is pressed.",
"type": "array",
"default": [
"()",
"[]",
"{}"
],
"items": {
"type": "string"
}
},
"autocompleteBrackets": {
"type": "boolean",
"default": true,
"description": "Autocomplete bracket and quote characters, such as `(` and `)`, and `\"`."
},
"wrapSelectionsInBrackets": {
"type": "boolean",
"default": true,
"description": "Wrap selected text in brackets or quotes when the editor contains selections and the opening bracket or quote is typed."
},
"highlightMatchingLineNumber": {
"type": "boolean",
"default": false,
"description": "Highlight the line number of the matching bracket."
},
"alwaysSkipClosingPairs": {
"type": "boolean",
"default": false,
"description": "Always skip closing pairs in front of the cursor."
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,142 @@
const TagFinder = require('../lib/tag-finder')
const tagFinder = new TagFinder()
describe('closeTag', () => {
describe('TagFinder::parseFragment', () => {
let fragment = ''
beforeEach(() => fragment = '<html><head><body></body>')
it('returns the last not closed elem in fragment, matching a given pattern', () => {
const stack = tagFinder.parseFragment(fragment, [], /<(\w+)|<\/(\w*)/, () => true)
expect(stack[stack.length - 1]).toBe('head')
})
it('stops when cond become true', () => {
const stack = tagFinder.parseFragment(fragment, [], /<(\w+)|<\/(\w*)/, () => false)
expect(stack.length).toBe(0)
})
it('uses the given match expression to match tags', () => {
const stack = tagFinder.parseFragment(fragment, [], /<(body)|(notag)/, () => true)
expect(stack[stack.length - 1]).toBe('body')
})
})
describe('TagFinder::tagsNotClosedInFragment', () => {
it('returns the outermost tag not closed in an HTML fragment', () => {
const fragment = '<html><head></head><body><h1><p></p>'
const tags = tagFinder.tagsNotClosedInFragment(fragment)
expect(tags).toEqual(['html', 'body', 'h1'])
})
it('is not confused by tag attributes', () => {
const fragment = '<html><head></head><body class="c"><h1 class="p"><p></p>'
const tags = tagFinder.tagsNotClosedInFragment(fragment)
expect(tags).toEqual(['html', 'body', 'h1'])
})
it('is not confused by namespace prefixes', () => {
const fragment = '<xhtml:html><xhtml:body><xhtml:h1>'
const tags = tagFinder.tagsNotClosedInFragment(fragment)
expect(tags).toEqual(['xhtml:html', 'xhtml:body', 'xhtml:h1'])
})
})
describe('TagFinder::tagDoesNotCloseInFragment', () => {
it('returns true if the given tag is not closed in the given fragment', () => {
const fragment = '</other1></other2></html>'
expect(tagFinder.tagDoesNotCloseInFragment('body', fragment)).toBe(true)
})
it('returns false if the given tag is closed in the given fragment', () => {
const fragment = '</other1></body></html>'
expect(tagFinder.tagDoesNotCloseInFragment(['body'], fragment)).toBe(false)
})
it('returns true even if the given tag is re-opened and re-closed', () => {
const fragment = '<other> </other><body></body><html>'
expect(tagFinder.tagDoesNotCloseInFragment(['body'], fragment)).toBe(true)
})
it('returns false even if the given tag is re-opened and re-closed before closing', () => {
const fragment = '<other> </other><body></body></body><html>'
expect(tagFinder.tagDoesNotCloseInFragment(['body'], fragment)).toBe(false)
})
})
describe('TagFinder::closingTagForFragments', () => {
it('returns the last opened in preFragment tag that is not closed in postFragment', () => {
const preFragment = '<html><head></head><body><h1></h1><p>'
const postFragment = '</body></html>'
expect(tagFinder.closingTagForFragments(preFragment, postFragment)).toBe('p')
})
it('correctly handles empty postFragment', () => {
const preFragment = '<html><head></head><body><h1></h1><p>'
const postFragment = ''
expect(tagFinder.closingTagForFragments(preFragment, postFragment)).toBe('p')
})
it('correctly handles malformed tags', () => {
const preFragment = '<html><head></head></htm'
const postFragment = ''
expect(tagFinder.closingTagForFragments(preFragment, postFragment)).toBe('html')
})
it('returns null if there is no open tag to be closed', () => {
const preFragment = '<html><head></head><body><h1></h1><p>'
const postFragment = '</p></body></html>'
expect(tagFinder.closingTagForFragments(preFragment, postFragment)).toBe(null)
})
it('correctly closes tags containing hyphens', () => {
const preFragment = '<html><head></head><body><h1></h1><my-element>'
const postFragment = '</body></html>'
expect(tagFinder.closingTagForFragments(preFragment, postFragment)).toBe('my-element')
})
it('correctly closes tags containing attributes', () => {
const preFragment = '<html><head></head><body class="foo bar"><div>'
const postFragment = '</body></html>'
expect(tagFinder.closingTagForFragments(preFragment, postFragment)).toBe('div')
})
it('correctly closes tags containing an XML namespace', () => {
const preFragment = '<html><head></head><body><custom:tag>'
const postFragment = '</body></html>'
expect(tagFinder.closingTagForFragments(preFragment, postFragment)).toBe('custom:tag')
})
it('correctly closes tags containing multiple XML namespaces', () => {
// This is not exactly valid syntax but it can't hurt to support it
const preFragment = '<html><head></head><body><custom:custom2:tag>'
const postFragment = '</body></html>'
expect(tagFinder.closingTagForFragments(preFragment, postFragment)).toBe('custom:custom2:tag')
})
it('correctly closes tags in the present of JSX tags containing member accesses', () => {
const preFragment = '<Foo><Bar.Baz></Bar.Baz>'
const postFragment = ''
expect(tagFinder.closingTagForFragments(preFragment, postFragment)).toBe('Foo')
})
it('correctly closes JSX tags containing member accesses', () => {
const preFragment = '<Foo.Bar><div></div>'
const postFragment = ''
expect(tagFinder.closingTagForFragments(preFragment, postFragment)).toBe('Foo.Bar')
})
it('correctly closes JSX tags containing deep member accesses', () => {
const preFragment = '<Foo.Bar.Baz><div></div>'
const postFragment = ''
expect(tagFinder.closingTagForFragments(preFragment, postFragment)).toBe('Foo.Bar.Baz')
})
it('correctly closes tags when there are other tags with the same prefix', () => {
const preFragment = '<thead><th>'
const postFragment = '</thead>'
expect(tagFinder.closingTagForFragments(preFragment, postFragment)).toBe('th')
})
})
})

View File

@ -0,0 +1,6 @@
[link-1](http://example.com/)
[another-link-2](http://example.com/)
[yet-another-link-3](http://example.com/)
[final-link-4](http://example.com/)
(different-type)
not anywhere

View File

@ -0,0 +1,15 @@
<html>
<body>
<div>
<div>
<div>
<p><a>
</div>
</div>
</div>
<div>
<div>
<div>
</div>
</div>

View File

@ -0,0 +1,13 @@
var quicksort = function () {
var sort = function(items) {
if (items.length <= 1) return items;
var pivot = items.shift(), current, left = [], right = [];
while(items.length > 0) {
current = items.shift();
current < pivot ? left.push(current) : right.push(current);
}
return sort(left).concat(pivot).concat(sort(right));
};
return sort(Array.apply(this, arguments));
};

View File

@ -0,0 +1,17 @@
<html>
<body id="bod">
<div>
<div>
<div>
hello
</div>
</div>
</div>
<div>
<div>
world
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,11 @@
@import "syntax-variables";
.bracket-matcher .region {
border-bottom: 1px dotted lime;
position: absolute;
}
.line-number.bracket-matcher.bracket-matcher {
color: @syntax-gutter-text-color-selected;
background-color: @syntax-gutter-background-color-selected;
}

View File

@ -295,7 +295,7 @@ describe "Clojure grammar", ->
#!/usr/bin/env boot
"""
for line in valid.split /\n/
expect(grammar.firstLineRegex.scanner.findNextMatchSync(line)).not.toBeNull()
expect(grammar.firstLineRegex.findNextMatchSync(line)).not.toBeNull()
invalid = """
\x20#!/usr/sbin/boot
@ -306,7 +306,7 @@ describe "Clojure grammar", ->
#!\t/usr/bin/env --boot=bar
"""
for line in invalid.split /\n/
expect(grammar.firstLineRegex.scanner.findNextMatchSync(line)).toBeNull()
expect(grammar.firstLineRegex.findNextMatchSync(line)).toBeNull()
it "recognises Emacs modelines", ->
valid = """
@ -325,7 +325,7 @@ describe "Clojure grammar", ->
"-*- font:x;foo : bar ; mode : ClojureScript ; bar : foo ; foooooo:baaaaar;fo:ba-*-";
"""
for line in valid.split /\n/
expect(grammar.firstLineRegex.scanner.findNextMatchSync(line)).not.toBeNull()
expect(grammar.firstLineRegex.findNextMatchSync(line)).not.toBeNull()
invalid = """
/* --*clojure-*- */
@ -343,7 +343,7 @@ describe "Clojure grammar", ->
// -*-font:mode;mode:clojure--*-
"""
for line in invalid.split /\n/
expect(grammar.firstLineRegex.scanner.findNextMatchSync(line)).toBeNull()
expect(grammar.firstLineRegex.findNextMatchSync(line)).toBeNull()
it "recognises Vim modelines", ->
valid = """
@ -370,7 +370,7 @@ describe "Clojure grammar", ->
# vim:noexpandtab titlestring=hi\|there\\\\ ft=clojure ts=4
"""
for line in valid.split /\n/
expect(grammar.firstLineRegex.scanner.findNextMatchSync(line)).not.toBeNull()
expect(grammar.firstLineRegex.findNextMatchSync(line)).not.toBeNull()
invalid = """
ex: se filetype=clojure:
@ -388,4 +388,4 @@ describe "Clojure grammar", ->
# vim:noexpandtab titlestring=hi\\|there\\\\\\ ft=clojure ts=4
"""
for line in invalid.split /\n/
expect(grammar.firstLineRegex.scanner.findNextMatchSync(line)).toBeNull()
expect(grammar.firstLineRegex.findNextMatchSync(line)).toBeNull()

View File

@ -29,7 +29,7 @@ describe "CoffeeScript (Literate) grammar", ->
#!/usr/local/bin/env coffee --literate -w
"""
for line in valid.split /\n/
expect(grammar.firstLineRegex.scanner.findNextMatchSync(line)).not.toBeNull()
expect(grammar.firstLineRegex.findNextMatchSync(line)).not.toBeNull()
invalid = """
#!/usr/local/bin/coffee --no-head -literate -w
@ -37,7 +37,7 @@ describe "CoffeeScript (Literate) grammar", ->
#!/usr/local/bin/env coffee --illiterate -w=l
"""
for line in invalid.split /\n/
expect(grammar.firstLineRegex.scanner.findNextMatchSync(line)).toBeNull()
expect(grammar.firstLineRegex.findNextMatchSync(line)).toBeNull()
it "recognises Emacs modelines", ->
valid = """
@ -56,7 +56,7 @@ describe "CoffeeScript (Literate) grammar", ->
"-*- font:x;foo : bar ; mode : LiTcOFFEe ; bar : foo ; foooooo:baaaaar;fo:ba-*-";
"""
for line in valid.split /\n/
expect(grammar.firstLineRegex.scanner.findNextMatchSync(line)).not.toBeNull()
expect(grammar.firstLineRegex.findNextMatchSync(line)).not.toBeNull()
invalid = """
/* --*litcoffee-*- */
@ -74,7 +74,7 @@ describe "CoffeeScript (Literate) grammar", ->
// -*-font:mode;mode:litcoffee--*-
"""
for line in invalid.split /\n/
expect(grammar.firstLineRegex.scanner.findNextMatchSync(line)).toBeNull()
expect(grammar.firstLineRegex.findNextMatchSync(line)).toBeNull()
it "recognises Vim modelines", ->
valid = """
@ -101,7 +101,7 @@ describe "CoffeeScript (Literate) grammar", ->
# vim:noexpandtab titlestring=hi\|there\\\\ ft=litcoffee ts=4
"""
for line in valid.split /\n/
expect(grammar.firstLineRegex.scanner.findNextMatchSync(line)).not.toBeNull()
expect(grammar.firstLineRegex.findNextMatchSync(line)).not.toBeNull()
invalid = """
ex: se filetype=litcoffee:
@ -119,4 +119,4 @@ describe "CoffeeScript (Literate) grammar", ->
# vim:noexpandtab titlestring=hi\\|there\\\\\\ ft=litcoffee ts=4
"""
for line in invalid.split /\n/
expect(grammar.firstLineRegex.scanner.findNextMatchSync(line)).toBeNull()
expect(grammar.firstLineRegex.findNextMatchSync(line)).toBeNull()

View File

@ -1393,7 +1393,7 @@ describe "CoffeeScript grammar", ->
#!/usr/bin/env coffee
"""
for line in valid.split /\n/
expect(grammar.firstLineRegex.scanner.findNextMatchSync(line)).not.toBeNull()
expect(grammar.firstLineRegex.findNextMatchSync(line)).not.toBeNull()
invalid = """
\x20#!/usr/sbin/coffee
@ -1404,7 +1404,7 @@ describe "CoffeeScript grammar", ->
#!\t/usr/bin/env --coffee=bar
"""
for line in invalid.split /\n/
expect(grammar.firstLineRegex.scanner.findNextMatchSync(line)).toBeNull()
expect(grammar.firstLineRegex.findNextMatchSync(line)).toBeNull()
it "recognises Emacs modelines", ->
valid = """
@ -1423,7 +1423,7 @@ describe "CoffeeScript grammar", ->
"-*- font:x;foo : bar ; mode : Coffee ; bar : foo ; foooooo:baaaaar;fo:ba-*-";
"""
for line in valid.split /\n/
expect(grammar.firstLineRegex.scanner.findNextMatchSync(line)).not.toBeNull()
expect(grammar.firstLineRegex.findNextMatchSync(line)).not.toBeNull()
invalid = """
/* --*coffee-*- */
@ -1441,7 +1441,7 @@ describe "CoffeeScript grammar", ->
// -*-font:mode;mode:coffee--*-
"""
for line in invalid.split /\n/
expect(grammar.firstLineRegex.scanner.findNextMatchSync(line)).toBeNull()
expect(grammar.firstLineRegex.findNextMatchSync(line)).toBeNull()
it "recognises Vim modelines", ->
valid = """
@ -1468,7 +1468,7 @@ describe "CoffeeScript grammar", ->
# vim:noexpandtab titlestring=hi\|there\\\\ ft=cOFFEe ts=4
"""
for line in valid.split /\n/
expect(grammar.firstLineRegex.scanner.findNextMatchSync(line)).not.toBeNull()
expect(grammar.firstLineRegex.findNextMatchSync(line)).not.toBeNull()
invalid = """
ex: se filetype=coffee:
@ -1486,4 +1486,4 @@ describe "CoffeeScript grammar", ->
# vim:noexpandtab titlestring=hi\\|there\\\\\\ ft=coffee ts=4
"""
for line in invalid.split /\n/
expect(grammar.firstLineRegex.scanner.findNextMatchSync(line)).toBeNull()
expect(grammar.firstLineRegex.findNextMatchSync(line)).toBeNull()

View File

@ -3508,7 +3508,7 @@ describe 'CSS grammar', ->
"-*- font:x;foo : bar ; mode : cSS ; bar : foo ; foooooo:baaaaar;fo:ba-*-";
"""
for line in valid.split /\n/
expect(grammar.firstLineRegex.scanner.findNextMatchSync(line)).not.toBeNull()
expect(grammar.firstLineRegex.findNextMatchSync(line)).not.toBeNull()
invalid = """
/* --*css-*- */
@ -3526,7 +3526,7 @@ describe 'CSS grammar', ->
// -*-font:mode;mode:css--*-
"""
for line in invalid.split /\n/
expect(grammar.firstLineRegex.scanner.findNextMatchSync(line)).toBeNull()
expect(grammar.firstLineRegex.findNextMatchSync(line)).toBeNull()
it "recognises Vim modelines", ->
valid = """
@ -3553,7 +3553,7 @@ describe 'CSS grammar', ->
# vim:noexpandtab titlestring=hi\|there\\\\ ft=css ts=4
"""
for line in valid.split /\n/
expect(grammar.firstLineRegex.scanner.findNextMatchSync(line)).not.toBeNull()
expect(grammar.firstLineRegex.findNextMatchSync(line)).not.toBeNull()
invalid = """
ex: se filetype=css:
@ -3571,7 +3571,7 @@ describe 'CSS grammar', ->
# vim:noexpandtab titlestring=hi\\|there\\\\\\ ft=CSS ts=4
"""
for line in invalid.split /\n/
expect(grammar.firstLineRegex.scanner.findNextMatchSync(line)).toBeNull()
expect(grammar.firstLineRegex.findNextMatchSync(line)).toBeNull()
describe "Missing supported properties regressions", ->
it "recognises place-items property as supported", ->

View File

@ -18,45 +18,45 @@ describe 'Go settings', ->
it 'matches lines correctly using the increaseIndentPattern', ->
increaseIndentRegex = languageMode.increaseIndentRegexForScopeDescriptor(['source.go'])
expect(increaseIndentRegex.testSync(' case true:')).toBeTruthy()
expect(increaseIndentRegex.testSync(' default:')).toBeTruthy()
expect(increaseIndentRegex.testSync('func something() {')).toBeTruthy()
expect(increaseIndentRegex.testSync(' if true {')).toBeTruthy()
expect(increaseIndentRegex.testSync(' else {')).toBeTruthy()
expect(increaseIndentRegex.testSync(' switch {')).toBeTruthy()
expect(increaseIndentRegex.testSync(' switch true {')).toBeTruthy()
expect(increaseIndentRegex.testSync(' select {')).toBeTruthy()
expect(increaseIndentRegex.testSync(' select true {')).toBeTruthy()
expect(increaseIndentRegex.testSync(' for v := range val {')).toBeTruthy()
expect(increaseIndentRegex.testSync(' for i := 0; i < 10; i++ {')).toBeTruthy()
expect(increaseIndentRegex.testSync(' for i := 0; i < 10; i++ {')).toBeTruthy()
expect(increaseIndentRegex.testSync(' type something struct {')).toBeTruthy()
expect(increaseIndentRegex.testSync(' fmt.Printf("some%s",')).toBeTruthy()
expect(increaseIndentRegex.testSync(' aSlice := []string{}{')).toBeTruthy()
expect(increaseIndentRegex.findNextMatchSync(' case true:')).toBeTruthy()
expect(increaseIndentRegex.findNextMatchSync(' default:')).toBeTruthy()
expect(increaseIndentRegex.findNextMatchSync('func something() {')).toBeTruthy()
expect(increaseIndentRegex.findNextMatchSync(' if true {')).toBeTruthy()
expect(increaseIndentRegex.findNextMatchSync(' else {')).toBeTruthy()
expect(increaseIndentRegex.findNextMatchSync(' switch {')).toBeTruthy()
expect(increaseIndentRegex.findNextMatchSync(' switch true {')).toBeTruthy()
expect(increaseIndentRegex.findNextMatchSync(' select {')).toBeTruthy()
expect(increaseIndentRegex.findNextMatchSync(' select true {')).toBeTruthy()
expect(increaseIndentRegex.findNextMatchSync(' for v := range val {')).toBeTruthy()
expect(increaseIndentRegex.findNextMatchSync(' for i := 0; i < 10; i++ {')).toBeTruthy()
expect(increaseIndentRegex.findNextMatchSync(' for i := 0; i < 10; i++ {')).toBeTruthy()
expect(increaseIndentRegex.findNextMatchSync(' type something struct {')).toBeTruthy()
expect(increaseIndentRegex.findNextMatchSync(' fmt.Printf("some%s",')).toBeTruthy()
expect(increaseIndentRegex.findNextMatchSync(' aSlice := []string{}{')).toBeTruthy()
it 'matches lines correctly using the decreaseIndentPattern', ->
decreaseIndentRegex = languageMode.decreaseIndentRegexForScopeDescriptor(['source.go'])
expect(decreaseIndentRegex.testSync(' case true:')).toBeTruthy()
expect(decreaseIndentRegex.testSync(' default:')).toBeTruthy()
expect(decreaseIndentRegex.testSync(' }')).toBeTruthy()
expect(decreaseIndentRegex.testSync(' },')).toBeTruthy()
expect(decreaseIndentRegex.testSync(' )')).toBeTruthy()
expect(decreaseIndentRegex.testSync(' ),')).toBeTruthy()
expect(decreaseIndentRegex.findNextMatchSync(' case true:')).toBeTruthy()
expect(decreaseIndentRegex.findNextMatchSync(' default:')).toBeTruthy()
expect(decreaseIndentRegex.findNextMatchSync(' }')).toBeTruthy()
expect(decreaseIndentRegex.findNextMatchSync(' },')).toBeTruthy()
expect(decreaseIndentRegex.findNextMatchSync(' )')).toBeTruthy()
expect(decreaseIndentRegex.findNextMatchSync(' ),')).toBeTruthy()
it 'matches lines correctly using the decreaseNextIndentPattern', ->
decreaseNextIndentRegex = languageMode.decreaseNextIndentRegexForScopeDescriptor(['source.go'])
expect(decreaseNextIndentRegex.testSync(' fmt.Println("something"))')).toBeTruthy()
expect(decreaseNextIndentRegex.testSync(' fmt.Println("something")),')).toBeTruthy()
expect(decreaseNextIndentRegex.testSync(' fmt.Println("something"), "x"),')).toBeTruthy()
expect(decreaseNextIndentRegex.testSync(' fmt.Println(fmt.Sprint("something"))),')).toBeTruthy()
expect(decreaseNextIndentRegex.testSync(' fmt.Println(fmt.Sprint("something"), "x")),')).toBeTruthy()
expect(decreaseNextIndentRegex.findNextMatchSync(' fmt.Println("something"))')).toBeTruthy()
expect(decreaseNextIndentRegex.findNextMatchSync(' fmt.Println("something")),')).toBeTruthy()
expect(decreaseNextIndentRegex.findNextMatchSync(' fmt.Println("something"), "x"),')).toBeTruthy()
expect(decreaseNextIndentRegex.findNextMatchSync(' fmt.Println(fmt.Sprint("something"))),')).toBeTruthy()
expect(decreaseNextIndentRegex.findNextMatchSync(' fmt.Println(fmt.Sprint("something"), "x")),')).toBeTruthy()
expect(decreaseNextIndentRegex.testSync(' fmt.Println("something")')).toBeFalsy()
expect(decreaseNextIndentRegex.testSync(' fmt.Println("something"),')).toBeFalsy()
expect(decreaseNextIndentRegex.findNextMatchSync(' fmt.Println("something")')).toBeFalsy()
expect(decreaseNextIndentRegex.findNextMatchSync(' fmt.Println("something"),')).toBeFalsy()
# a line with many (), testing for catastrophic backtracking.
# see https://github.com/atom/language-go/issues/78
longLine = 'first.second().third().fourth().fifth().sixth().seventh().eighth().ninth().tenth()'
expect(decreaseNextIndentRegex.testSync(longLine)).toBeFalsy()
expect(decreaseNextIndentRegex.findNextMatchSync(longLine)).toBeFalsy()

View File

@ -601,8 +601,8 @@ describe 'TextMate HTML grammar', ->
describe 'firstLineMatch', ->
it 'recognises HTML5 doctypes', ->
expect(grammar.firstLineRegex.scanner.findNextMatchSync('<!DOCTYPE html>')).not.toBeNull()
expect(grammar.firstLineRegex.scanner.findNextMatchSync('<!doctype HTML>')).not.toBeNull()
expect(grammar.firstLineRegex.findNextMatchSync('<!DOCTYPE html>')).not.toBeNull()
expect(grammar.firstLineRegex.findNextMatchSync('<!doctype HTML>')).not.toBeNull()
it 'recognises Emacs modelines', ->
valid = '''
@ -621,7 +621,7 @@ describe 'TextMate HTML grammar', ->
"-*- font:x;foo : bar ; mode : HtML ; bar : foo ; foooooo:baaaaar;fo:ba-*-";
'''
for line in valid.split /\n/
expect(grammar.firstLineRegex.scanner.findNextMatchSync(line)).not.toBeNull()
expect(grammar.firstLineRegex.findNextMatchSync(line)).not.toBeNull()
invalid = '''
/* --*html-*- */
@ -640,7 +640,7 @@ describe 'TextMate HTML grammar', ->
// -*-font:mode;mode:html--*-
'''
for line in invalid.split /\n/
expect(grammar.firstLineRegex.scanner.findNextMatchSync(line)).toBeNull()
expect(grammar.firstLineRegex.findNextMatchSync(line)).toBeNull()
it 'recognises Vim modelines', ->
valid = '''
@ -667,7 +667,7 @@ describe 'TextMate HTML grammar', ->
# vim:noexpandtab titlestring=hi\|there\\\\ ft=html ts=4
'''
for line in valid.split /\n/
expect(grammar.firstLineRegex.scanner.findNextMatchSync(line)).not.toBeNull()
expect(grammar.firstLineRegex.findNextMatchSync(line)).not.toBeNull()
invalid = '''
ex: se filetype=html:
@ -685,7 +685,7 @@ describe 'TextMate HTML grammar', ->
# vim:noexpandtab titlestring=hi\\|there\\\\\\ ft=HTML ts=4
'''
for line in invalid.split /\n/
expect(grammar.firstLineRegex.scanner.findNextMatchSync(line)).toBeNull()
expect(grammar.firstLineRegex.findNextMatchSync(line)).toBeNull()
describe 'tags', ->
it 'tokenizes style tags as such', ->

View File

@ -2400,7 +2400,7 @@ describe "JavaScript grammar", ->
#!/usr/bin/env node
"""
for line in valid.split /\n/
expect(grammar.firstLineRegex.scanner.findNextMatchSync(line)).not.toBeNull()
expect(grammar.firstLineRegex.findNextMatchSync(line)).not.toBeNull()
invalid = """
\x20#!/usr/sbin/node
@ -2411,7 +2411,7 @@ describe "JavaScript grammar", ->
#!\t/usr/bin/env --node=bar
"""
for line in invalid.split /\n/
expect(grammar.firstLineRegex.scanner.findNextMatchSync(line)).toBeNull()
expect(grammar.firstLineRegex.findNextMatchSync(line)).toBeNull()
it "recognises Emacs modelines", ->
valid = """
@ -2430,7 +2430,7 @@ describe "JavaScript grammar", ->
"-*- font:x;foo : bar ; mode : jS ; bar : foo ; foooooo:baaaaar;fo:ba-*-";
"""
for line in valid.split /\n/
expect(grammar.firstLineRegex.scanner.findNextMatchSync(line)).not.toBeNull()
expect(grammar.firstLineRegex.findNextMatchSync(line)).not.toBeNull()
invalid = """
/* --*js-*- */
@ -2448,7 +2448,7 @@ describe "JavaScript grammar", ->
// -*-font:mode;mode:js--*-
"""
for line in invalid.split /\n/
expect(grammar.firstLineRegex.scanner.findNextMatchSync(line)).toBeNull()
expect(grammar.firstLineRegex.findNextMatchSync(line)).toBeNull()
it "recognises Vim modelines", ->
valid = """
@ -2475,7 +2475,7 @@ describe "JavaScript grammar", ->
# vim:noexpandtab titlestring=hi\|there\\\\ ft=javascript ts=4
"""
for line in valid.split /\n/
expect(grammar.firstLineRegex.scanner.findNextMatchSync(line)).not.toBeNull()
expect(grammar.firstLineRegex.findNextMatchSync(line)).not.toBeNull()
invalid = """
ex: se filetype=javascript:
@ -2493,4 +2493,4 @@ describe "JavaScript grammar", ->
# vim:noexpandtab titlestring=hi\\|there\\\\\\ ft=javascript ts=4
"""
for line in invalid.split /\n/
expect(grammar.firstLineRegex.scanner.findNextMatchSync(line)).toBeNull()
expect(grammar.firstLineRegex.findNextMatchSync(line)).toBeNull()

View File

@ -721,9 +721,6 @@
'match': '^\\s*(package)\\s+([^\\s;]+)'
'name': 'meta.class.perl'
}
{
'include: "#sub'
}
{
'captures':
'1':

View File

@ -183,7 +183,7 @@ describe "Perl 6 grammar", ->
#!/usr/bin/env perl6
"""
for line in valid.split /\n/
expect(grammar.firstLineRegex.scanner.findNextMatchSync(line)).not.toBeNull()
expect(grammar.firstLineRegex.findNextMatchSync(line)).not.toBeNull()
invalid = """
#! pearl6
@ -200,11 +200,11 @@ describe "Perl 6 grammar", ->
#!\t/usr/bin/env --perl6=bar
"""
for line in invalid.split /\n/
expect(grammar.firstLineRegex.scanner.findNextMatchSync(line)).toBeNull()
expect(grammar.firstLineRegex.findNextMatchSync(line)).toBeNull()
it "recognises the Perl6 pragma", ->
line = "use v6;"
expect(grammar.firstLineRegex.scanner.findNextMatchSync(line)).not.toBeNull()
expect(grammar.firstLineRegex.findNextMatchSync(line)).not.toBeNull()
it "recognises Emacs modelines", ->
modelines = """
@ -222,7 +222,7 @@ describe "Perl 6 grammar", ->
"-*- font:x;foo : bar ; mode : pErL6 ; bar : foo ; foooooo:baaaaar;fo:ba-*-";
"""
for line in modelines.split /\n/
expect(grammar.firstLineRegex.scanner.findNextMatchSync(line)).not.toBeNull()
expect(grammar.firstLineRegex.findNextMatchSync(line)).not.toBeNull()
invalid = """
/* --*perl6-*- */
@ -241,7 +241,7 @@ describe "Perl 6 grammar", ->
"""
for line in invalid.split /\n/
expect(grammar.firstLineRegex.scanner.findNextMatchSync(line)).toBeNull()
expect(grammar.firstLineRegex.findNextMatchSync(line)).toBeNull()
it "recognises Vim modelines", ->
valid = """
@ -268,7 +268,7 @@ describe "Perl 6 grammar", ->
# vim:noexpandtab titlestring=hi\|there\\\\ ft=perl6 ts=4
"""
for line in valid.split /\n/
expect(grammar.firstLineRegex.scanner.findNextMatchSync(line)).not.toBeNull()
expect(grammar.firstLineRegex.findNextMatchSync(line)).not.toBeNull()
invalid = """
ex: se filetype=perl6:
@ -286,7 +286,7 @@ describe "Perl 6 grammar", ->
# vim:noexpandtab titlestring=hi\\|there\\\\\\ ft=perl6 ts=4
"""
for line in invalid.split /\n/
expect(grammar.firstLineRegex.scanner.findNextMatchSync(line)).toBeNull()
expect(grammar.firstLineRegex.findNextMatchSync(line)).toBeNull()
# Local variables:
# mode: CoffeeScript

View File

@ -1439,7 +1439,7 @@ Assigned to: @<<<<<<<<<<<<<<<<<<<<<< ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<
#!/usr/bin/env perl
"""
for line in valid.split /\n/
expect(grammar.firstLineRegex.scanner.findNextMatchSync(line)).not.toBeNull()
expect(grammar.firstLineRegex.findNextMatchSync(line)).not.toBeNull()
invalid = """
#! pearl
@ -1456,7 +1456,7 @@ Assigned to: @<<<<<<<<<<<<<<<<<<<<<< ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<
#!\t/usr/bin/env --perl=bar
"""
for line in invalid.split /\n/
expect(grammar.firstLineRegex.scanner.findNextMatchSync(line)).toBeNull()
expect(grammar.firstLineRegex.findNextMatchSync(line)).toBeNull()
it "recognises Emacs modelines", ->
valid = """
@ -1474,7 +1474,7 @@ Assigned to: @<<<<<<<<<<<<<<<<<<<<<< ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<
"-*- font:x;foo : bar ; mode : pErL ; bar : foo ; foooooo:baaaaar;fo:ba-*-";
"""
for line in valid.split /\n/
expect(grammar.firstLineRegex.scanner.findNextMatchSync(line)).not.toBeNull()
expect(grammar.firstLineRegex.findNextMatchSync(line)).not.toBeNull()
invalid = """
/* --*perl-*- */
@ -1491,7 +1491,7 @@ Assigned to: @<<<<<<<<<<<<<<<<<<<<<< ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<
// -*-font:mode;mode:perl--*-
"""
for line in invalid.split /\n/
expect(grammar.firstLineRegex.scanner.findNextMatchSync(line)).toBeNull()
expect(grammar.firstLineRegex.findNextMatchSync(line)).toBeNull()
it "recognises Vim modelines", ->
valid = """
@ -1518,7 +1518,7 @@ Assigned to: @<<<<<<<<<<<<<<<<<<<<<< ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<
# vim:noexpandtab titlestring=hi\|there\\\\ ft=perl ts=4
"""
for line in valid.split /\n/
expect(grammar.firstLineRegex.scanner.findNextMatchSync(line)).not.toBeNull()
expect(grammar.firstLineRegex.findNextMatchSync(line)).not.toBeNull()
invalid = """
ex: se filetype=perl:
@ -1536,7 +1536,7 @@ Assigned to: @<<<<<<<<<<<<<<<<<<<<<< ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<
# vim:noexpandtab titlestring=hi\\|there\\\\\\ ft=perl ts=4
"""
for line in invalid.split /\n/
expect(grammar.firstLineRegex.scanner.findNextMatchSync(line)).toBeNull()
expect(grammar.firstLineRegex.findNextMatchSync(line)).toBeNull()
# Local variables:
# mode: CoffeeScript

View File

@ -161,11 +161,11 @@ describe 'PHP in HTML', ->
<?php namespace foo;
'''
for line in valid.split /\n/
expect(grammar.firstLineRegex.scanner.findNextMatchSync(line)).not.toBeNull()
expect(grammar.firstLineRegex.findNextMatchSync(line)).not.toBeNull()
# Do not allow matching XML declaration until the grammar scoring system takes into account
# the length of the first line match so that longer matches get the priority over shorter matches.
expect(grammar.firstLineRegex.scanner.findNextMatchSync('<?xml version="1.0" encoding="UTF-8"?>')).toBeNull()
expect(grammar.firstLineRegex.findNextMatchSync('<?xml version="1.0" encoding="UTF-8"?>')).toBeNull()
it 'recognises interpreter directives', ->
valid = '''
@ -184,7 +184,7 @@ describe 'PHP in HTML', ->
#!/usr/bin/env php
'''
for line in valid.split /\n/
expect(grammar.firstLineRegex.scanner.findNextMatchSync(line)).not.toBeNull()
expect(grammar.firstLineRegex.findNextMatchSync(line)).not.toBeNull()
invalid = '''
\x20#!/usr/sbin/php
@ -196,7 +196,7 @@ describe 'PHP in HTML', ->
#!\t/usr/bin/env --php=bar
'''
for line in invalid.split /\n/
expect(grammar.firstLineRegex.scanner.findNextMatchSync(line)).toBeNull()
expect(grammar.firstLineRegex.findNextMatchSync(line)).toBeNull()
it 'recognises Emacs modelines', ->
valid = '''
@ -215,7 +215,7 @@ describe 'PHP in HTML', ->
"-*- font:x;foo : bar ; mode : php ; bar : foo ; foooooo:baaaaar;fo:ba-*-";
'''
for line in valid.split /\n/
expect(grammar.firstLineRegex.scanner.findNextMatchSync(line)).not.toBeNull()
expect(grammar.firstLineRegex.findNextMatchSync(line)).not.toBeNull()
invalid = '''
/* --*php-*- */
@ -233,7 +233,7 @@ describe 'PHP in HTML', ->
// -*-font:mode;mode:php--*-
'''
for line in invalid.split /\n/
expect(grammar.firstLineRegex.scanner.findNextMatchSync(line)).toBeNull()
expect(grammar.firstLineRegex.findNextMatchSync(line)).toBeNull()
it 'recognises Vim modelines', ->
valid = '''
@ -260,7 +260,7 @@ describe 'PHP in HTML', ->
# vim:noexpandtab titlestring=hi\|there\\\\ ft=phtml ts=4
'''
for line in valid.split /\n/
expect(grammar.firstLineRegex.scanner.findNextMatchSync(line)).not.toBeNull()
expect(grammar.firstLineRegex.findNextMatchSync(line)).not.toBeNull()
invalid = '''
ex: se filetype=php:
@ -278,7 +278,7 @@ describe 'PHP in HTML', ->
# vim:noexpandtab titlestring=hi\\|there\\\\\\ ft=php ts=4
'''
for line in invalid.split /\n/
expect(grammar.firstLineRegex.scanner.findNextMatchSync(line)).toBeNull()
expect(grammar.firstLineRegex.findNextMatchSync(line)).toBeNull()
it 'should tokenize <?php use Some\\Name ?>', ->
lines = grammar.tokenizeLines '''

View File

@ -18,66 +18,66 @@ describe 'Python settings', ->
it 'matches lines correctly using the increaseIndentPattern', ->
increaseIndentRegex = languageMode.increaseIndentRegexForScopeDescriptor(['source.python'])
expect(increaseIndentRegex.testSync('for i in range(n):')).toBeTruthy()
expect(increaseIndentRegex.testSync(' for i in range(n):')).toBeTruthy()
expect(increaseIndentRegex.testSync('async for i in range(n):')).toBeTruthy()
expect(increaseIndentRegex.testSync(' async for i in range(n):')).toBeTruthy()
expect(increaseIndentRegex.testSync('class TheClass(Object):')).toBeTruthy()
expect(increaseIndentRegex.testSync(' class TheClass(Object):')).toBeTruthy()
expect(increaseIndentRegex.testSync('def f(x):')).toBeTruthy()
expect(increaseIndentRegex.testSync(' def f(x):')).toBeTruthy()
expect(increaseIndentRegex.testSync('async def f(x):')).toBeTruthy()
expect(increaseIndentRegex.testSync(' async def f(x):')).toBeTruthy()
expect(increaseIndentRegex.testSync('if this_var == that_var:')).toBeTruthy()
expect(increaseIndentRegex.testSync(' if this_var == that_var:')).toBeTruthy()
expect(increaseIndentRegex.testSync('elif this_var == that_var:')).toBeTruthy()
expect(increaseIndentRegex.testSync(' elif this_var == that_var:')).toBeTruthy()
expect(increaseIndentRegex.testSync('else:')).toBeTruthy()
expect(increaseIndentRegex.testSync(' else:')).toBeTruthy()
expect(increaseIndentRegex.testSync('except Exception:')).toBeTruthy()
expect(increaseIndentRegex.testSync(' except Exception:')).toBeTruthy()
expect(increaseIndentRegex.testSync('except Exception as e:')).toBeTruthy()
expect(increaseIndentRegex.testSync(' except Exception as e:')).toBeTruthy()
expect(increaseIndentRegex.testSync('finally:')).toBeTruthy()
expect(increaseIndentRegex.testSync(' finally:')).toBeTruthy()
expect(increaseIndentRegex.testSync('with open("filename") as f:')).toBeTruthy()
expect(increaseIndentRegex.testSync(' with open("filename") as f:')).toBeTruthy()
expect(increaseIndentRegex.testSync('async with open("filename") as f:')).toBeTruthy()
expect(increaseIndentRegex.testSync(' async with open("filename") as f:')).toBeTruthy()
expect(increaseIndentRegex.testSync('while True:')).toBeTruthy()
expect(increaseIndentRegex.testSync(' while True:')).toBeTruthy()
expect(increaseIndentRegex.testSync('\t\t while True:')).toBeTruthy()
expect(increaseIndentRegex.findNextMatchSync('for i in range(n):')).toBeTruthy()
expect(increaseIndentRegex.findNextMatchSync(' for i in range(n):')).toBeTruthy()
expect(increaseIndentRegex.findNextMatchSync('async for i in range(n):')).toBeTruthy()
expect(increaseIndentRegex.findNextMatchSync(' async for i in range(n):')).toBeTruthy()
expect(increaseIndentRegex.findNextMatchSync('class TheClass(Object):')).toBeTruthy()
expect(increaseIndentRegex.findNextMatchSync(' class TheClass(Object):')).toBeTruthy()
expect(increaseIndentRegex.findNextMatchSync('def f(x):')).toBeTruthy()
expect(increaseIndentRegex.findNextMatchSync(' def f(x):')).toBeTruthy()
expect(increaseIndentRegex.findNextMatchSync('async def f(x):')).toBeTruthy()
expect(increaseIndentRegex.findNextMatchSync(' async def f(x):')).toBeTruthy()
expect(increaseIndentRegex.findNextMatchSync('if this_var == that_var:')).toBeTruthy()
expect(increaseIndentRegex.findNextMatchSync(' if this_var == that_var:')).toBeTruthy()
expect(increaseIndentRegex.findNextMatchSync('elif this_var == that_var:')).toBeTruthy()
expect(increaseIndentRegex.findNextMatchSync(' elif this_var == that_var:')).toBeTruthy()
expect(increaseIndentRegex.findNextMatchSync('else:')).toBeTruthy()
expect(increaseIndentRegex.findNextMatchSync(' else:')).toBeTruthy()
expect(increaseIndentRegex.findNextMatchSync('except Exception:')).toBeTruthy()
expect(increaseIndentRegex.findNextMatchSync(' except Exception:')).toBeTruthy()
expect(increaseIndentRegex.findNextMatchSync('except Exception as e:')).toBeTruthy()
expect(increaseIndentRegex.findNextMatchSync(' except Exception as e:')).toBeTruthy()
expect(increaseIndentRegex.findNextMatchSync('finally:')).toBeTruthy()
expect(increaseIndentRegex.findNextMatchSync(' finally:')).toBeTruthy()
expect(increaseIndentRegex.findNextMatchSync('with open("filename") as f:')).toBeTruthy()
expect(increaseIndentRegex.findNextMatchSync(' with open("filename") as f:')).toBeTruthy()
expect(increaseIndentRegex.findNextMatchSync('async with open("filename") as f:')).toBeTruthy()
expect(increaseIndentRegex.findNextMatchSync(' async with open("filename") as f:')).toBeTruthy()
expect(increaseIndentRegex.findNextMatchSync('while True:')).toBeTruthy()
expect(increaseIndentRegex.findNextMatchSync(' while True:')).toBeTruthy()
expect(increaseIndentRegex.findNextMatchSync('\t\t while True:')).toBeTruthy()
it 'does not match lines incorrectly using the increaseIndentPattern', ->
increaseIndentRegex = languageMode.increaseIndentRegexForScopeDescriptor(['source.python'])
expect(increaseIndentRegex.testSync('for i in range(n)')).toBeFalsy()
expect(increaseIndentRegex.testSync('class TheClass(Object)')).toBeFalsy()
expect(increaseIndentRegex.testSync('def f(x)')).toBeFalsy()
expect(increaseIndentRegex.testSync('if this_var == that_var')).toBeFalsy()
expect(increaseIndentRegex.testSync('"for i in range(n):"')).toBeFalsy()
expect(increaseIndentRegex.findNextMatchSync('for i in range(n)')).toBeFalsy()
expect(increaseIndentRegex.findNextMatchSync('class TheClass(Object)')).toBeFalsy()
expect(increaseIndentRegex.findNextMatchSync('def f(x)')).toBeFalsy()
expect(increaseIndentRegex.findNextMatchSync('if this_var == that_var')).toBeFalsy()
expect(increaseIndentRegex.findNextMatchSync('"for i in range(n):"')).toBeFalsy()
it 'matches lines correctly using the decreaseIndentPattern', ->
decreaseIndentRegex = languageMode.decreaseIndentRegexForScopeDescriptor(['source.python'])
expect(decreaseIndentRegex.testSync('elif this_var == that_var:')).toBeTruthy()
expect(decreaseIndentRegex.testSync(' elif this_var == that_var:')).toBeTruthy()
expect(decreaseIndentRegex.testSync('else:')).toBeTruthy()
expect(decreaseIndentRegex.testSync(' else:')).toBeTruthy()
expect(decreaseIndentRegex.testSync('except Exception:')).toBeTruthy()
expect(decreaseIndentRegex.testSync(' except Exception:')).toBeTruthy()
expect(decreaseIndentRegex.testSync('except Exception as e:')).toBeTruthy()
expect(decreaseIndentRegex.testSync(' except Exception as e:')).toBeTruthy()
expect(decreaseIndentRegex.testSync('finally:')).toBeTruthy()
expect(decreaseIndentRegex.testSync(' finally:')).toBeTruthy()
expect(decreaseIndentRegex.testSync('\t\t finally:')).toBeTruthy()
expect(decreaseIndentRegex.findNextMatchSync('elif this_var == that_var:')).toBeTruthy()
expect(decreaseIndentRegex.findNextMatchSync(' elif this_var == that_var:')).toBeTruthy()
expect(decreaseIndentRegex.findNextMatchSync('else:')).toBeTruthy()
expect(decreaseIndentRegex.findNextMatchSync(' else:')).toBeTruthy()
expect(decreaseIndentRegex.findNextMatchSync('except Exception:')).toBeTruthy()
expect(decreaseIndentRegex.findNextMatchSync(' except Exception:')).toBeTruthy()
expect(decreaseIndentRegex.findNextMatchSync('except Exception as e:')).toBeTruthy()
expect(decreaseIndentRegex.findNextMatchSync(' except Exception as e:')).toBeTruthy()
expect(decreaseIndentRegex.findNextMatchSync('finally:')).toBeTruthy()
expect(decreaseIndentRegex.findNextMatchSync(' finally:')).toBeTruthy()
expect(decreaseIndentRegex.findNextMatchSync('\t\t finally:')).toBeTruthy()
it 'does not match lines incorrectly using the decreaseIndentPattern', ->
decreaseIndentRegex = languageMode.decreaseIndentRegexForScopeDescriptor(['source.python'])
# NOTE! This first one is different from most other rote tests here.
expect(decreaseIndentRegex.testSync('else: expression()')).toBeFalsy()
expect(decreaseIndentRegex.testSync('elif this_var == that_var')).toBeFalsy()
expect(decreaseIndentRegex.testSync(' elif this_var == that_var')).toBeFalsy()
expect(decreaseIndentRegex.testSync('else')).toBeFalsy()
expect(decreaseIndentRegex.testSync(' "finally:"')).toBeFalsy()
expect(decreaseIndentRegex.findNextMatchSync('else: expression()')).toBeFalsy()
expect(decreaseIndentRegex.findNextMatchSync('elif this_var == that_var')).toBeFalsy()
expect(decreaseIndentRegex.findNextMatchSync(' elif this_var == that_var')).toBeFalsy()
expect(decreaseIndentRegex.findNextMatchSync('else')).toBeFalsy()
expect(decreaseIndentRegex.findNextMatchSync(' "finally:"')).toBeFalsy()

View File

@ -14,8 +14,8 @@ describe "Python grammar", ->
grammar = atom.grammars.grammarForScopeName("source.python")
it "recognises shebang on firstline", ->
expect(grammar.firstLineRegex.scanner.findNextMatchSync("#!/usr/bin/env python")).not.toBeNull()
expect(grammar.firstLineRegex.scanner.findNextMatchSync("#! /usr/bin/env python")).not.toBeNull()
expect(grammar.firstLineRegex.findNextMatchSync("#!/usr/bin/env python")).not.toBeNull()
expect(grammar.firstLineRegex.findNextMatchSync("#! /usr/bin/env python")).not.toBeNull()
it "parses the grammar", ->
expect(grammar).toBeDefined()

View File

@ -964,7 +964,7 @@ describe "TextMate Ruby grammar", ->
#!/usr/bin/env ruby
"""
for line in valid.split /\n/
expect(grammar.firstLineRegex.scanner.findNextMatchSync(line)).not.toBeNull()
expect(grammar.firstLineRegex.findNextMatchSync(line)).not.toBeNull()
invalid = """
\x20#!/usr/sbin/ruby
@ -975,7 +975,7 @@ describe "TextMate Ruby grammar", ->
#!\t/usr/bin/env --ruby=bar
"""
for line in invalid.split /\n/
expect(grammar.firstLineRegex.scanner.findNextMatchSync(line)).toBeNull()
expect(grammar.firstLineRegex.findNextMatchSync(line)).toBeNull()
it "recognises Emacs modelines", ->
valid = """
@ -994,7 +994,7 @@ describe "TextMate Ruby grammar", ->
"-*- font:x;foo : bar ; mode : RUBY ; bar : foo ; foooooo:baaaaar;fo:ba-*-";
"""
for line in valid.split /\n/
expect(grammar.firstLineRegex.scanner.findNextMatchSync(line)).not.toBeNull()
expect(grammar.firstLineRegex.findNextMatchSync(line)).not.toBeNull()
invalid = """
/* --*ruby-*- */
@ -1012,7 +1012,7 @@ describe "TextMate Ruby grammar", ->
// -*-font:mode;mode:ruby--*-
"""
for line in invalid.split /\n/
expect(grammar.firstLineRegex.scanner.findNextMatchSync(line)).toBeNull()
expect(grammar.firstLineRegex.findNextMatchSync(line)).toBeNull()
it "recognises Vim modelines", ->
valid = """
@ -1039,7 +1039,7 @@ describe "TextMate Ruby grammar", ->
# vim:noexpandtab titlestring=hi\|there\\\\ ft=ruby ts=4
"""
for line in valid.split /\n/
expect(grammar.firstLineRegex.scanner.findNextMatchSync(line)).not.toBeNull()
expect(grammar.firstLineRegex.findNextMatchSync(line)).not.toBeNull()
invalid = """
ex: se filetype=ruby:
@ -1057,4 +1057,4 @@ describe "TextMate Ruby grammar", ->
# vim:noexpandtab titlestring=hi\\|there\\\\\\ ft=ruby ts=4
"""
for line in invalid.split /\n/
expect(grammar.firstLineRegex.scanner.findNextMatchSync(line)).toBeNull()
expect(grammar.firstLineRegex.findNextMatchSync(line)).toBeNull()

View File

@ -361,7 +361,7 @@ describe "Shell script grammar", ->
#!/usr/bin/env bash
"""
for line in valid.split /\n/
expect(grammar.firstLineRegex.scanner.findNextMatchSync(line)).not.toBeNull()
expect(grammar.firstLineRegex.findNextMatchSync(line)).not.toBeNull()
invalid = """
\x20#!/usr/sbin/bash
@ -373,7 +373,7 @@ describe "Shell script grammar", ->
#!\t/usr/bin/env --bash=bar
"""
for line in invalid.split /\n/
expect(grammar.firstLineRegex.scanner.findNextMatchSync(line)).toBeNull()
expect(grammar.firstLineRegex.findNextMatchSync(line)).toBeNull()
it "recognises Emacs modelines", ->
valid = """
@ -392,7 +392,7 @@ describe "Shell script grammar", ->
"-*- font:x;foo : bar ; mode : sH ; bar : foo ; foooooo:baaaaar;fo:ba-*-";
"""
for line in valid.split /\n/
expect(grammar.firstLineRegex.scanner.findNextMatchSync(line)).not.toBeNull()
expect(grammar.firstLineRegex.findNextMatchSync(line)).not.toBeNull()
invalid = """
/* --*sh-*- */
@ -410,7 +410,7 @@ describe "Shell script grammar", ->
// -*-font:mode;mode:sh--*-
"""
for line in invalid.split /\n/
expect(grammar.firstLineRegex.scanner.findNextMatchSync(line)).toBeNull()
expect(grammar.firstLineRegex.findNextMatchSync(line)).toBeNull()
it "recognises Vim modelines", ->
valid = """
@ -437,7 +437,7 @@ describe "Shell script grammar", ->
# vim:noexpandtab titlestring=hi\|there\\\\ ft=sh ts=4
"""
for line in valid.split /\n/
expect(grammar.firstLineRegex.scanner.findNextMatchSync(line)).not.toBeNull()
expect(grammar.firstLineRegex.findNextMatchSync(line)).not.toBeNull()
invalid = """
ex: se filetype=sh:
@ -455,4 +455,4 @@ describe "Shell script grammar", ->
# vim:noexpandtab titlestring=hi\\|there\\\\\\ ft=sh ts=4
"""
for line in invalid.split /\n/
expect(grammar.firstLineRegex.scanner.findNextMatchSync(line)).toBeNull()
expect(grammar.firstLineRegex.findNextMatchSync(line)).toBeNull()

View File

@ -122,7 +122,7 @@ attrName="attrValue">
"-*- font:x;foo : bar ; mode : xMl ; bar : foo ; foooooo:baaaaar;fo:ba-*-";
"""
for line in valid.split /\n/
expect(grammar.firstLineRegex.scanner.findNextMatchSync(line)).not.toBeNull()
expect(grammar.firstLineRegex.findNextMatchSync(line)).not.toBeNull()
invalid = """
/* --*XML-*- */
@ -140,7 +140,7 @@ attrName="attrValue">
// -*-font:mode;mode:xml--*-
"""
for line in invalid.split /\n/
expect(grammar.firstLineRegex.scanner.findNextMatchSync(line)).toBeNull()
expect(grammar.firstLineRegex.findNextMatchSync(line)).toBeNull()
it "recognises Vim modelines", ->
valid = """
@ -167,7 +167,7 @@ attrName="attrValue">
# vim:noexpandtab titlestring=hi\|there\\\\ ft=xml ts=4
"""
for line in valid.split /\n/
expect(grammar.firstLineRegex.scanner.findNextMatchSync(line)).not.toBeNull()
expect(grammar.firstLineRegex.findNextMatchSync(line)).not.toBeNull()
invalid = """
ex: se filetype=xml:
@ -185,7 +185,7 @@ attrName="attrValue">
# vim:noexpandtab titlestring=hi\\|there\\\\\\ ft=xml ts=4
"""
for line in invalid.split /\n/
expect(grammar.firstLineRegex.scanner.findNextMatchSync(line)).toBeNull()
expect(grammar.firstLineRegex.findNextMatchSync(line)).toBeNull()
it "recognises a valid XML declaration", ->
valid = """
@ -196,7 +196,7 @@ attrName="attrValue">
<?xml version="1.0" encoding='UTF-8' standalone='no' ?>
"""
for line in valid.split /\n/
expect(grammar.firstLineRegex.scanner.findNextMatchSync(line)).not.toBeNull()
expect(grammar.firstLineRegex.findNextMatchSync(line)).not.toBeNull()
invalid = """
<?XML version="1.0"?>
@ -209,4 +209,4 @@ attrName="attrValue">
<?xml version="1.0">
"""
for line in invalid.split /\n/
expect(grammar.firstLineRegex.scanner.findNextMatchSync(line)).toBeNull()
expect(grammar.firstLineRegex.findNextMatchSync(line)).toBeNull()

View File

@ -5,13 +5,14 @@ const temp = require('temp').track();
const TextBuffer = require('text-buffer');
const GrammarRegistry = require('../src/grammar-registry');
const TreeSitterGrammar = require('../src/tree-sitter-grammar');
const FirstMate = require('first-mate');
const { OnigRegExp } = require('oniguruma');
const SecondMate = require('second-mate');
const { OnigScanner } = SecondMate;
describe('GrammarRegistry', () => {
let grammarRegistry;
beforeEach(() => {
beforeEach(async () => {
await SecondMate.ready
grammarRegistry = new GrammarRegistry({ config: atom.config });
expect(subscriptionCount(grammarRegistry)).toBe(1);
});
@ -102,7 +103,7 @@ describe('GrammarRegistry', () => {
);
const grammar = grammarRegistry.grammarForId('source.js');
expect(grammar instanceof FirstMate.Grammar).toBe(true);
expect(grammar instanceof SecondMate.Grammar).toBe(true);
expect(grammar.scopeName).toBe('source.js');
grammarRegistry.removeGrammar(grammar);
@ -127,7 +128,7 @@ describe('GrammarRegistry', () => {
grammarRegistry.removeGrammar(grammar);
expect(
grammarRegistry.grammarForId('source.js') instanceof FirstMate.Grammar
grammarRegistry.grammarForId('source.js') instanceof SecondMate.Grammar
).toBe(true);
});
});
@ -560,7 +561,7 @@ describe('GrammarRegistry', () => {
const grammar = grammarRegistry.selectGrammar('test.js');
expect(grammar.scopeName).toBe('source.js');
expect(grammar instanceof FirstMate.Grammar).toBe(true);
expect(grammar instanceof SecondMate.Grammar).toBe(true);
});
it('favors a tree-sitter grammar over a text-mate grammar when `core.useTreeSitterParsers` is true', () => {
@ -766,7 +767,7 @@ describe('GrammarRegistry', () => {
grammarRegistry.addGrammar(grammar1);
const grammar2 = {
name: 'foo++',
contentRegex: new OnigRegExp('.*bar'),
contentRegex: new OnigScanner(['.*bar']),
fileTypes: ['foo']
};
grammarRegistry.addGrammar(grammar2);

View File

@ -1,7 +1,7 @@
const _ = require('underscore-plus');
const Grim = require('grim');
const CSON = require('season');
const FirstMate = require('first-mate');
const SecondMate = require('second-mate');
const { Disposable, CompositeDisposable } = require('event-kit');
const TextMateLanguageMode = require('./text-mate-language-mode');
const TreeSitterLanguageMode = require('./tree-sitter-language-mode');
@ -20,7 +20,7 @@ module.exports = class GrammarRegistry {
constructor({ config } = {}) {
this.config = config;
this.subscriptions = new CompositeDisposable();
this.textmateRegistry = new FirstMate.GrammarRegistry({
this.textmateRegistry = new SecondMate.GrammarRegistry({
maxTokensPerLine: 100,
maxLineLength: 1000
});
@ -264,7 +264,7 @@ module.exports = class GrammarRegistry {
if (grammar.contentRegex) {
const contentMatch = isTreeSitter
? grammar.contentRegex.test(contents)
: grammar.contentRegex.testSync(contents);
: grammar.contentRegex.findNextMatchSync(contents);
if (contentMatch) {
score += 0.05;
} else {
@ -339,8 +339,8 @@ module.exports = class GrammarRegistry {
.split('\n')
.slice(0, numberOfNewlinesInRegex + 1)
.join('\n');
if (grammar.firstLineRegex.testSync) {
return grammar.firstLineRegex.testSync(prefix);
if (grammar.firstLineRegex.findNextMatchSync) {
return grammar.firstLineRegex.findNextMatchSync(prefix);
} else {
return grammar.firstLineRegex.test(prefix);
}

View File

@ -13,7 +13,7 @@ const NullGrammar = require('./null-grammar');
const TextMateLanguageMode = require('./text-mate-language-mode');
const ScopeDescriptor = require('./scope-descriptor');
const TextMateScopeSelector = require('first-mate').ScopeSelector;
const TextMateScopeSelector = require('second-mate').ScopeSelector;
const GutterContainer = require('./gutter-container');
let TextEditorComponent = null;
let TextEditorElement = null;

View File

@ -5,7 +5,7 @@ const TokenizedLine = require('./tokenized-line');
const TokenIterator = require('./token-iterator');
const ScopeDescriptor = require('./scope-descriptor');
const NullGrammar = require('./null-grammar');
const { OnigRegExp } = require('oniguruma');
const { OnigScanner } = require('second-mate');
const {
toFirstMateScopeId,
fromFirstMateScopeId
@ -144,7 +144,7 @@ class TextMateLanguageMode {
);
if (!decreaseIndentRegex) return;
if (!decreaseIndentRegex.testSync(line)) return;
if (!decreaseIndentRegex.findNextMatchSync(line)) return;
const precedingRow = this.buffer.previousNonBlankRow(bufferRow);
if (precedingRow == null) return;
@ -156,14 +156,14 @@ class TextMateLanguageMode {
scopeDescriptor
);
if (increaseIndentRegex) {
if (!increaseIndentRegex.testSync(precedingLine)) desiredIndentLevel -= 1;
if (!increaseIndentRegex.findNextMatchSync(precedingLine)) desiredIndentLevel -= 1;
}
const decreaseNextIndentRegex = this.decreaseNextIndentRegexForScopeDescriptor(
scopeDescriptor
);
if (decreaseNextIndentRegex) {
if (decreaseNextIndentRegex.testSync(precedingLine))
if (decreaseNextIndentRegex.findNextMatchSync(precedingLine))
desiredIndentLevel -= 1;
}
@ -203,17 +203,17 @@ class TextMateLanguageMode {
if (!increaseIndentRegex) return desiredIndentLevel;
if (!this.isRowCommented(precedingRow)) {
if (increaseIndentRegex && increaseIndentRegex.testSync(precedingLine))
if (increaseIndentRegex && increaseIndentRegex.findNextMatchSync(precedingLine))
desiredIndentLevel += 1;
if (
decreaseNextIndentRegex &&
decreaseNextIndentRegex.testSync(precedingLine)
decreaseNextIndentRegex.findNextMatchSync(precedingLine)
)
desiredIndentLevel -= 1;
}
if (!this.buffer.isRowBlank(precedingRow)) {
if (decreaseIndentRegex && decreaseIndentRegex.testSync(line))
if (decreaseIndentRegex && decreaseIndentRegex.findNextMatchSync(line))
desiredIndentLevel -= 1;
}
@ -812,7 +812,7 @@ class TextMateLanguageMode {
if (indentation < startIndentLevel) {
break;
} else if (indentation === startIndentLevel) {
if (foldEndRegex && foldEndRegex.searchSync(line)) foldEndRow = nextRow;
if (foldEndRegex && foldEndRegex.findNextMatchSync(line)) foldEndRow = nextRow;
break;
}
foldEndRow = nextRow;
@ -848,7 +848,7 @@ class TextMateLanguageMode {
regexForPattern(pattern) {
if (pattern) {
if (!this.regexesByPattern[pattern]) {
this.regexesByPattern[pattern] = new OnigRegExp(pattern);
this.regexesByPattern[pattern] = new OnigScanner([pattern]);
}
return this.regexesByPattern[pattern];
}

View File

@ -19,10 +19,11 @@
}
StartupTime.addMarker('window:start', startWindowTime);
window.onload = function() {
window.onload = async function() {
try {
StartupTime.addMarker('window:onload:start');
const startTime = Date.now();
await require('second-mate').ready
process.on('unhandledRejection', function(error, promise) {
console.error(

View File

@ -2659,11 +2659,9 @@ braces@~3.0.2:
dependencies:
fill-range "^7.0.1"
"bracket-matcher@https://github.com/pulsar-edit/bracket-matcher.git#c877977":
"bracket-matcher@file:packages/bracket-matcher":
version "0.92.0"
resolved "https://github.com/pulsar-edit/bracket-matcher.git#c877977ac7e9b7fe43c2100a1880c7ffc119280b"
dependencies:
first-mate "^7.4.1"
underscore-plus "1.x"
browser-stdout@1.3.1:
@ -3991,7 +3989,7 @@ electron@12.2.3:
"@types/node" "^14.6.2"
extract-zip "^1.0.3"
emissary@^1, emissary@^1.0.0, emissary@^1.1.0, emissary@^1.2.0, emissary@^1.3.2:
emissary@^1.0.0, emissary@^1.1.0, emissary@^1.2.0, emissary@^1.3.2, emissary@^1.3.3:
version "1.3.3"
resolved "https://registry.yarnpkg.com/emissary/-/emissary-1.3.3.tgz#a618d92d682b232d31111dc3625a5df661799606"
integrity sha512-pD6FWNBSlEOzSJDCTcSGVLgNnGw5fnCvvGMdQ/TN43efeXZ/QTq8+hZoK3OOEXPRNjMmSJmeOnEJh+bWT5O8rQ==
@ -4390,7 +4388,7 @@ etch@^0.12.2, etch@^0.12.6:
resolved "https://registry.yarnpkg.com/etch/-/etch-0.12.8.tgz#c24bc9bd3a6148f62204ce8643d2e899b9ecb9de"
integrity sha512-dFLRe4wLroVtwzyy1vGlE3BSDZHiL0kZME5XgNGzZIULcYTvVno8vbiIleAesoKJmwWaxDTzG+4eppg2zk14JQ==
event-kit@2.5.3, event-kit@^2.0.0, event-kit@^2.1.0, event-kit@^2.2.0, event-kit@^2.4.0, event-kit@^2.5.3:
event-kit@2.5.3, event-kit@^2.0.0, event-kit@^2.1.0, event-kit@^2.4.0, event-kit@^2.5.3:
version "2.5.3"
resolved "https://registry.yarnpkg.com/event-kit/-/event-kit-2.5.3.tgz#d47e4bc116ec0aacd00263791fa1a55eb5e79ba1"
integrity sha512-b7Qi1JNzY4BfAYfnIRanLk0DOD1gdkWHT4GISIn8Q2tAf3LpU8SP2CMwWaq40imYoKWbtN4ZhbSRxvsnikooZQ==
@ -4605,19 +4603,6 @@ find-up@^5.0.0:
locate-path "^6.0.0"
path-exists "^4.0.0"
first-mate@7.4.3, first-mate@^7.4.1:
version "7.4.3"
resolved "https://registry.yarnpkg.com/first-mate/-/first-mate-7.4.3.tgz#058b9b6d2f43e38a5f0952669338cff2c46ae2dd"
integrity sha512-PtZUpaPmcV5KV4Rw5TfwczEnExN+X1o3Q/G82E4iRJ0tW91fm3Yi7pa5t4cBH8r3D6EyoBKvfpG2jKE+TZ0/nw==
dependencies:
emissary "^1"
event-kit "^2.2.0"
fs-plus "^3.0.0"
grim "^2.0.1"
oniguruma "^7.2.3"
season "^6.0.2"
underscore-plus "^1"
flat-cache@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11"
@ -5194,7 +5179,7 @@ graphql@14.5.8:
dependencies:
iterall "^1.2.2"
grim@2.0.3, grim@^2.0.1, grim@^2.0.2:
grim@2.0.3, grim@^2.0.1, grim@^2.0.2, grim@^2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/grim/-/grim-2.0.3.tgz#66e575efc4577981d959da0764926b4aaded4b0d"
integrity sha512-FM20Ump11qYLK9k9DbL8yzVpy+YBieya1JG15OeH8s+KbHq8kL4SdwRtURwIUHniSxb24EoBUpwKfFjGNVi4/Q==
@ -7422,13 +7407,6 @@ onetime@^5.1.0:
dependencies:
mimic-fn "^2.1.0"
oniguruma@^7.2.3:
version "7.2.3"
resolved "https://registry.yarnpkg.com/oniguruma/-/oniguruma-7.2.3.tgz#e0b0b415302de8cdd6564e57a1a822ac0ab57012"
integrity sha512-PZZcE0yfg8Q1IvaJImh21RUTHl8ep0zwwyoE912KqlWVrsGByjjj29sdACcD1BFyX2bLkfuOJeP+POzAGVWtbA==
dependencies:
nan "^2.14.0"
"open-on-github@file:packages/open-on-github":
version "1.3.2"
@ -8503,6 +8481,18 @@ season@^6.0.2:
fs-plus "^3.0.0"
yargs "^3.23.0"
"second-mate@https://github.com/pulsar-edit/second-mate.git#14aa7bd":
version "8.0.0"
resolved "https://github.com/pulsar-edit/second-mate.git#14aa7bd94b90c47aa99f000394301b9573b8898b"
dependencies:
emissary "^1.3.3"
event-kit "^2.5.3"
fs-plus "^3.0.0"
grim "^2.0.3"
season "^6.0.2"
underscore-plus "^1"
vscode-oniguruma "^1.7.0"
selector-kit@^0.1:
version "0.1.0"
resolved "https://registry.yarnpkg.com/selector-kit/-/selector-kit-0.1.0.tgz#304338fceccea35ec28ffaddb792ab7715633e6f"
@ -9810,6 +9800,11 @@ verror@^1.10.0:
core-util-is "1.0.2"
extsprintf "^1.2.0"
vscode-oniguruma@^1.7.0:
version "1.7.0"
resolved "https://registry.yarnpkg.com/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz#439bfad8fe71abd7798338d1cd3dc53a8beea94b"
integrity sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==
vscode-ripgrep@1.9.0:
version "1.9.0"
resolved "https://registry.yarnpkg.com/vscode-ripgrep/-/vscode-ripgrep-1.9.0.tgz#d6cdea4d290f3c2919472cdcfe2440d5fb1f99db"