From 176ee2a3b3a167f68a82dafd153da09d940f20c0 Mon Sep 17 00:00:00 2001 From: Ashi Krishnan Date: Fri, 20 Jul 2018 16:22:15 -0400 Subject: [PATCH] Change TreeSitterLanguageMode::bufferRangeForScopeAtPosition(position) to bufferRangeForScopeAtPosition(selector, position), to match the TextMateLanguageMode signature, extracting some selector matching logic to do so. Needed for https://github.com/atom/toggle-quotes/issues/57 --- src/selectors.js | 42 ++++++++++++++++++++++++++++++++ src/text-mate-language-mode.js | 9 +------ src/tree-sitter-language-mode.js | 12 ++++++--- 3 files changed, 51 insertions(+), 12 deletions(-) create mode 100644 src/selectors.js diff --git a/src/selectors.js b/src/selectors.js new file mode 100644 index 000000000..fc5d74cb1 --- /dev/null +++ b/src/selectors.js @@ -0,0 +1,42 @@ +module.exports = {selectorMatchesAnyScope, matcherForSelector} + +const _ = require('underscore-plus') + +/** + * Parse a selector into parts. If already parsed, returns the selector + * unmodified. + * + * @param {String|Array} selector + * @returns {Array} selector parts + */ +function parse (selector) { + return typeof selector === 'string' + ? selector.replace(/^\./, '').split('.') + : selector +} + +const always = scope => true + +/** + * Return a matcher function for a selector. + * + * @param {String} selector + * @returns {(scope: String) -> Boolean} a matcher function + */ +function matcherForSelector (selector) { + const parts = parse(selector) + return selector + ? scope => _.isSubset(parts, parse(scope)) + : always +} + +/** + * Return true iff the selector matches any provided scope. + * + * @param {String} selector + * @param {Array} scopes + * @returns {Boolean} true if any scope matches the selector + */ +function selectorMatchesAnyScope (selector, scopes) { + return !selector || scopes.some(matcherForSelector(selector)) +} diff --git a/src/text-mate-language-mode.js b/src/text-mate-language-mode.js index 9abe55ecb..471af9af2 100644 --- a/src/text-mate-language-mode.js +++ b/src/text-mate-language-mode.js @@ -7,6 +7,7 @@ const ScopeDescriptor = require('./scope-descriptor') const NullGrammar = require('./null-grammar') const {OnigRegExp} = require('oniguruma') const {toFirstMateScopeId, fromFirstMateScopeId} = require('./first-mate-helpers') +const {selectorMatchesAnyScope} = require('./selectors') const NON_WHITESPACE_REGEX = /\S/ @@ -726,14 +727,6 @@ class TextMateLanguageMode { TextMateLanguageMode.prototype.chunkSize = 50 -function selectorMatchesAnyScope (selector, scopes) { - const targetClasses = selector.replace(/^\./, '').split('.') - return scopes.some((scope) => { - const scopeClasses = scope.split('.') - return _.isSubset(targetClasses, scopeClasses) - }) -} - class TextMateHighlightIterator { constructor (languageMode) { this.languageMode = languageMode diff --git a/src/tree-sitter-language-mode.js b/src/tree-sitter-language-mode.js index 7d0377f28..a9818b1ad 100644 --- a/src/tree-sitter-language-mode.js +++ b/src/tree-sitter-language-mode.js @@ -5,6 +5,7 @@ const {Emitter, Disposable} = require('event-kit') const ScopeDescriptor = require('./scope-descriptor') const TokenizedLine = require('./tokenized-line') const TextMateLanguageMode = require('./text-mate-language-mode') +const {matcherForSelector} = require('./selectors') let nextId = 0 const MAX_RANGE = new Range(Point.ZERO, Point.INFINITY).freeze() @@ -348,25 +349,28 @@ class TreeSitterLanguageMode { Section - Syntax Tree APIs */ - getRangeForSyntaxNodeContainingRange (range) { + getRangeForSyntaxNodeContainingRange (range, selector) { const startIndex = this.buffer.characterIndexForPosition(range.start) const endIndex = this.buffer.characterIndexForPosition(range.end) const searchEndIndex = Math.max(0, endIndex - 1) + const matches = matcherForSelector(selector) + let smallestNode this._forEachTreeWithRange(range, tree => { let node = tree.rootNode.descendantForIndex(startIndex, searchEndIndex) while (node && !nodeContainsIndices(node, startIndex, endIndex)) { node = node.parent + console.log(node) } - if (nodeIsSmaller(node, smallestNode)) smallestNode = node + if (matches(node.type) && nodeIsSmaller(node, smallestNode)) smallestNode = node }) if (smallestNode) return rangeForNode(smallestNode) } - bufferRangeForScopeAtPosition (position) { - return this.getRangeForSyntaxNodeContainingRange(new Range(position, position)) + bufferRangeForScopeAtPosition (selector, position) { + return this.getRangeForSyntaxNodeContainingRange(new Range(position, position), selector) } /*