RSense is a tool for doing static analysis of Ruby source code. Rsense is used in conjunction with an editor plugin.
RSense is currently under heavy development and ready for testing. Currently we need to improve the homepage and develop plugins for code_completion. In the near future we'll also be ready to implement some of the other basic features like find-definition
. After that, there's plenty to do in the long term. See the waffle link below to find out where you can pitch in. It would be awesome if you helped get things done.
RSense is installed via RubyGems. It works well installed via Bundler as that gives it access to your application's LOAD_PATH very easily, and ensures it's installed into the same Ruby as your other dependencies if you happen to be using a version management tool like rbenv. Otherwise, be sure to install it with the proper version of Ruby.
Add this line to your application's Gemfile:
gem 'rsense'
And then execute:
$ bundle
Or install it yourself as:
$ gem install rsense
Install one of these plugins:
The Rsense server is started from the commandline with:
$ rsense start
It takes two potential options: --port
and --path
.
Currently there are plugins for Atom, Sublime Text and Textmate2. If the community does not contribute Vim and Emacs plugins, we will eventually do so.
When using Rsense, please be patient. After starting the server, Rsense can take some time to warm up and to load your projects dependencies into it's graph. It's quite fast after returning the initial completion and we will continue working to optimize this.
Rsense plugins are easy to implement. First your plugin may want to ensure the Rsense server has been started. It can do this by shelling out to the command line with rsense start
. Alternately, you can instruct your users to start it from the command line. The server can optionally take a port number like this: rsense start --port 12345
. The default port is 47367
. It also takes a project path, in case the user has a .rsense
config file there. For now, this config file is not very useful, but it may become so in the future.
The rsense server will be running at http://localhost:47367
(or an alternate port if you specify one). It communicates via json. You need to send it json like the following example, via POST:
{
"command": "code_completion",
"project": "spec/fixtures/test_gem",
"file": "spec/fixtures/test_gem/lib/sample.rb",
"code": "require \"sample/version\"\n\nmodule Sample\n class Sample\n attr_accessor :simple\n\n def initialize\n @simple = \"simple\"\n end\n\n def another\n \"another\"\n end\n end\nend\n\nsample = Sample::Sample.new\nsample",
"location": {
"row": 18,
"column": 7
}
}
For now, code_completion
is the only command available, but this will change in the future.
Use absolute paths for both paths as there's no way to be sure where the user may have rsense installed. Project is the root dir of the user's project. This is needed for finding information about project dependencies. Rsense uses project path to get information from RubyGems and Bundler about your dependencies so it can do accurate type inference. RSense uses the filepath for tracking SourcePosition. This is only slightly important for code-completion and most completions will work without it. HOWEVER, when we begin looking into source-rewriting for refactorings, or even exposing the already written Find Definition tool, it will be necessary.
code is the text from the file where a completion is being triggered. We send it with the command because it is unlikely the user will have saved the file. Many tools which act on source code resolve this by writing to a tempfile, but I find this to be a hacky, and unnecessary solution. Rsense takes the code directly.
Location is tricky. Editor's measure cursor position in different ways. Rsense expects 1-based numbers here, with the first row, and first column being (1, 1). With each editor I've worked with, I've found it necessary to play around until it works. But you can examine spec/fixtures/test_gem/lib/sample.rb for the file from which the above json was generated for use as a test-case.
Rsense will return json that looks like the below:
{
"completions":
[
{
"name":"taint",
"qualified_name":"Object#taint",
"base_name":"Object",
"kind":"METHOD"
},
{
"name":"methods",
"qualified_name":"Object#methods",
"base_name":"Object",
"kind":"METHOD"
}
]
}
Here is a working curl example:
curl -XPOST -H "Content-Type: application/json" \
-d '{"command": "code_completion", "project": "~/code/rsense/rsense-server/spec/fixtures/test_gem", "file": "~/code/rsense/rsense-server/spec/fixtures/test_gem/lib/sample.rb", "code": "require \"sample/version\"\n\nmodule Sample\n class Sample\n attr_accessor :simple\n\n def initialize\n @simple = \"simple\"\n end\n\n def another\n \"another\"\n end\n end\nend\n\nsample = Sample::Sample.new\nsample", "location": { "row": 18, "column": 7 }}' \
http://localhost:47367
Rsense currently only does completions at callsites, or points in the code where you would type '.' or '::'. Bear that in mind when designing an editor plugin.
Rsense now comes with a secondary executable- _rsense_commandline.rb. If forming json and sending commands via http is difficult from your editor, _rsense_commandline.rb can do it for you. Just shell out to it with the needed info, like this:
_rsense_commandline.rb --project=/home/projects/myproject --filepath=/home/projects/myproject/lib/test_case.rb --text='class TestCase
def junk
"Hello"
end
end
tt = TestCase.new
tt.
' --location='7:3'
Contributions can only be accepted if they include tests.
git checkout -b my-new-feature
)git commit -am 'Add some feature'
)git push origin my-new-feature
)RSense is distributed under the term of GPLv3+.
Rsense was originally designed and implemented by Matsuyama Tomohiro(@m2ym), and his hard work is still at the core of rsense today. All of the algorithms for type-detection were implemented by him, with inspiration from multiple places. You can read about his original version at Rsense: A Ruby Development tools for Emacs, Vim and Others
In 2013, a major undertaking by @edubkendo to bring it current and improve its usefulness to rubyists was sponsored by the @jruby organization as a Google Summer of Code project.
Special thanks belongs to Tom Enebo (@enebo) who provided excellent mentorship, code, architectural suggestions and more throughout the course of the update.