From 8a250ae8992bac660105133ff89e2ef5dbb47a38 Mon Sep 17 00:00:00 2001 From: Sergey Gnuskov Date: Fri, 12 Jun 2020 18:37:02 +0300 Subject: [PATCH] Added scopes for many kernel and special methods --- grammars/tree-sitter-ruby.cson | 34 +++---- spec/tree-sitter-gemfile-spec.js | 65 ++++++++++++++ spec/tree-sitter-spec.js | 150 ++++++++++++++++++++++++++++++- 3 files changed, 232 insertions(+), 17 deletions(-) create mode 100644 spec/tree-sitter-gemfile-spec.js diff --git a/grammars/tree-sitter-ruby.cson b/grammars/tree-sitter-ruby.cson index cf7ad54..30bc9e6 100644 --- a/grammars/tree-sitter-ruby.cson +++ b/grammars/tree-sitter-ruby.cson @@ -130,6 +130,7 @@ scopes: 'interpolation > "}"': 'punctuation.section.embedded' 'constant': [ + {exact: 'ENV', scopes: 'support.variable'} {match: '^[A-Z_0-9]+$', scopes: 'variable.constant'} 'entity.name.type.class' ] @@ -137,18 +138,10 @@ scopes: 'superclass > constant': 'entity.other.inherited-class' 'identifier': [ - { - match: '^__(FILE|LINE|ENCODING)__$', - scopes: 'support.variable' - } - { - match: '^(public|protected|private)$' - scopes: 'keyword.other.special-method' - } - { - match: '^(block_given\?|iterator\?|alias_method)' - scopes: 'keyword.control' - } + {match: '^__(FILE|LINE|ENCODING)__$', scopes: 'support.variable'} + {match: '^(caller|binding|__dir__)$', scopes: 'support.function.kernel'} + {match: '^(public|protected|private|module_function|raise|caller|binding)$', scopes: 'keyword.other.special-method'} + {match: '^(block_given\?|iterator\?|alias_method)', scopes: 'keyword.control'} ] 'escape_sequence': 'constant.character.escape' @@ -165,15 +158,22 @@ scopes: 'singleton_method > identifier:nth-child(3)': 'entity.name.function' 'setter > identifier': 'entity.name.function' - 'call > identifier:nth-child(2)': 'entity.name.function' + 'call > identifier:nth-child(2)': [ + {exact: 'new', scopes: 'keyword.other.special-method'} + 'entity.name.function' + ] 'method_call > identifier:nth-child(0)': [ - {exact: 'require', scopes: 'support.function'} - {match: '^(public|protected|private)$', scopes: 'keyword.other.special-method'} + # Gemile specific TODO: move to separate grammar? + {match: '^(gem|git|group|platforms|ruby|source|eval_gemfile)$', scopes: 'keyword.other.special-method'} + + {match: '^(require|require_relative|loop|eval|lambda|catch|raise|exit|throw|autoload|puts)$', scopes: 'support.function.kernel'} + {match: '^(public|protected|private|module_function|new|include|extend|inspect)$', scopes: 'keyword.other.special-method'} + {match: '^(attr_accessor|attr_reader|attr_writer)$', scopes: 'keyword.other.special-method'} {match: '^(block_given\?|iterator\?|alias_method)', scopes: 'keyword.control'} 'entity.name.function' ] - 'block_parameters > identifier': 'variable.other.block' + 'block_parameters > identifier, lambda_parameters > identifier': 'variable.other.block' 'method_parameters > identifier, optional_parameter > identifier': 'variable.parameter.function' 'keyword_parameter > identifier:nth-child(0)': 'constant.other.symbol' 'class_variable': 'variable.other.object.property' @@ -254,3 +254,5 @@ scopes: 'nil': 'constant.language.nil' 'true': 'constant.language.true' 'false': 'constant.language.false' + + 'lambda > "->"': 'support.function.kernel' diff --git a/spec/tree-sitter-gemfile-spec.js b/spec/tree-sitter-gemfile-spec.js new file mode 100644 index 0000000..42a725b --- /dev/null +++ b/spec/tree-sitter-gemfile-spec.js @@ -0,0 +1,65 @@ +const dedent = require('dedent') + +describe('Tree-sitter Gemfile grammar', () => { + beforeEach(async () => { + atom.config.set('core.useTreeSitterParsers', true) + await atom.packages.activatePackage('language-ruby') + }) + + it('tokenizes ruby code', async () => { + const editor = await atom.workspace.open('Gemfile') + + editor.setText(dedent` + :foo + %i(foo) + `) + + expect(editor.scopeDescriptorForBufferPosition([0, 1]).toString()).toBe( + '.source.ruby .constant.other.symbol' + ) + + expect(editor.scopeDescriptorForBufferPosition([1, 3]).toString()).toBe( + '.source.ruby .constant.other.symbol' + ) + }) + + it('tokenizes Gemfile specific code', async () => { + const editor = await atom.workspace.open('Gemfile') + + editor.setText(dedent` + source 'https://rubygems.org' + ruby '2.5.1' + gem 'rails', '~> 5.2', '< 6.0' + + group :development, :test do + gem 'pry' + end + + eval_gemfile 'Gemfile.local' + `) + + expect(editor.scopeDescriptorForBufferPosition([0, 1]).toString()).toBe( + '.source.ruby .keyword.other.special-method' + ) + + expect(editor.scopeDescriptorForBufferPosition([1, 1]).toString()).toBe( + '.source.ruby .keyword.other.special-method' + ) + + expect(editor.scopeDescriptorForBufferPosition([2, 1]).toString()).toBe( + '.source.ruby .keyword.other.special-method' + ) + + expect(editor.scopeDescriptorForBufferPosition([4, 1]).toString()).toBe( + '.source.ruby .keyword.other.special-method' + ) + + expect(editor.scopeDescriptorForBufferPosition([5, 3]).toString()).toBe( + '.source.ruby .keyword.other.special-method' + ) + + expect(editor.scopeDescriptorForBufferPosition([8, 1]).toString()).toBe( + '.source.ruby .keyword.other.special-method' + ) + }) +}) diff --git a/spec/tree-sitter-spec.js b/spec/tree-sitter-spec.js index c51a940..7760e5f 100644 --- a/spec/tree-sitter-spec.js +++ b/spec/tree-sitter-spec.js @@ -23,6 +23,82 @@ describe('Tree-sitter Ruby grammar', () => { ) }) + it('tokenizes constants', async () => { + const editor = await atom.workspace.open('foo.rb') + + editor.setText(dedent` + HELLO = __FILE__.dirname + ClassName::HELLO + ENV["ABC"] + `) + + expect(editor.scopeDescriptorForBufferPosition([0, 1]).toString()).toBe( + '.source.ruby .variable.constant' + ) + + expect(editor.scopeDescriptorForBufferPosition([0, 10]).toString()).toBe( + '.source.ruby .support.variable' + ) + + expect(editor.scopeDescriptorForBufferPosition([1, 12]).toString()).toBe( + '.source.ruby .variable.constant' + ) + + expect(editor.scopeDescriptorForBufferPosition([2, 1]).toString()).toBe( + '.source.ruby .support.variable' + ) + }) + + + it('tokenizes kernel methods', async () => { + const editor = await atom.workspace.open('foo.rb') + editor.setText(dedent` + require 'hello' + require_relative '.world' + + catch :hello do + throw :hello + end + + raise StandardError + binding.pry + caller + puts 'asfd' + `) + + expect(editor.scopeDescriptorForBufferPosition([0, 1]).toString()).toBe( + '.source.ruby .support.function.kernel' + ) + + expect(editor.scopeDescriptorForBufferPosition([1, 1]).toString()).toBe( + '.source.ruby .support.function.kernel' + ) + + expect(editor.scopeDescriptorForBufferPosition([3, 1]).toString()).toBe( + '.source.ruby .support.function.kernel' + ) + + expect(editor.scopeDescriptorForBufferPosition([4, 3]).toString()).toBe( + '.source.ruby .support.function.kernel' + ) + + expect(editor.scopeDescriptorForBufferPosition([7, 1]).toString()).toBe( + '.source.ruby .support.function.kernel' + ) + + expect(editor.scopeDescriptorForBufferPosition([8, 1]).toString()).toBe( + '.source.ruby .support.function.kernel' + ) + + expect(editor.scopeDescriptorForBufferPosition([8, 1]).toString()).toBe( + '.source.ruby .support.function.kernel' + ) + + expect(editor.scopeDescriptorForBufferPosition([9, 1]).toString()).toBe( + '.source.ruby .support.function.kernel' + ) + }) + it('tokenizes visibility modifiers', async () => { const editor = await atom.workspace.open('foo.rb') @@ -30,10 +106,12 @@ describe('Tree-sitter Ruby grammar', () => { public protected private + module_function public def foo; end protected def bar; end private def baz; end + module_function def quux; end `) expect(editor.scopeDescriptorForBufferPosition([0, 0]).toString()).toBe( @@ -45,7 +123,7 @@ describe('Tree-sitter Ruby grammar', () => { expect(editor.scopeDescriptorForBufferPosition([2, 0]).toString()).toBe( '.source.ruby .keyword.other.special-method' ) - expect(editor.scopeDescriptorForBufferPosition([4, 0]).toString()).toBe( + expect(editor.scopeDescriptorForBufferPosition([3, 0]).toString()).toBe( '.source.ruby .keyword.other.special-method' ) expect(editor.scopeDescriptorForBufferPosition([5, 0]).toString()).toBe( @@ -54,6 +132,12 @@ describe('Tree-sitter Ruby grammar', () => { expect(editor.scopeDescriptorForBufferPosition([6, 0]).toString()).toBe( '.source.ruby .keyword.other.special-method' ) + expect(editor.scopeDescriptorForBufferPosition([7, 0]).toString()).toBe( + '.source.ruby .keyword.other.special-method' + ) + expect(editor.scopeDescriptorForBufferPosition([8, 0]).toString()).toBe( + '.source.ruby .keyword.other.special-method' + ) }) it('tokenizes keyword predicates', async () => { @@ -130,4 +214,68 @@ describe('Tree-sitter Ruby grammar', () => { '.source.ruby .variable' ) }) + + it('tokenizes lambdas', async () => { + const editor = await atom.workspace.open('foo.rb') + editor.setText(dedent` + foo 'bar', ->(hello) { puts hello } + + lambda { |hello| + puts hello + } + `) + + expect(editor.scopeDescriptorForBufferPosition([0, 12]).toString()).toBe( + '.source.ruby .support.function.kernel' + ) + + expect(editor.scopeDescriptorForBufferPosition([0, 15]).toString()).toBe( + '.source.ruby .variable.other.block' + ) + + expect(editor.scopeDescriptorForBufferPosition([2, 1]).toString()).toBe( + '.source.ruby .support.function.kernel' + ) + + expect(editor.scopeDescriptorForBufferPosition([2, 11]).toString()).toBe( + '.source.ruby .variable.other.block' + ) + }) + + it('tokenizes special methods', async () => { + const editor = await atom.workspace.open('foo.rb') + editor.setText(dedent` + include HelloWorld + extend FooBar + `) + + expect(editor.scopeDescriptorForBufferPosition([0, 1]).toString()).toBe( + '.source.ruby .keyword.other.special-method' + ) + + expect(editor.scopeDescriptorForBufferPosition([1, 1]).toString()).toBe( + '.source.ruby .keyword.other.special-method' + ) + }) + + it('tokenizes attr accessors', async () => { + const editor = await atom.workspace.open('foo.rb') + editor.setText(dedent` + attr_accessor :hello + attr_reader :foo + attr_writer :bar + `) + + expect(editor.scopeDescriptorForBufferPosition([0, 1]).toString()).toBe( + '.source.ruby .keyword.other.special-method' + ) + + expect(editor.scopeDescriptorForBufferPosition([1, 1]).toString()).toBe( + '.source.ruby .keyword.other.special-method' + ) + + expect(editor.scopeDescriptorForBufferPosition([2, 1]).toString()).toBe( + '.source.ruby .keyword.other.special-method' + ) + }) })