Sublime Forum

SublimePeek - quick access to help for functions

#1

Hi,

I put together a small plugin with the intention to provide quick access to the documentation or help files for functions. A single key-stroke in ST2 will bring up the documentation for the currently selected function or the function at the current cursor position. The plugin can also show an overview of all available help topics based on the familiar ST2 quick select panel (the same as the command panel) so that the user can quickly browse the different help topics.

**Supported Languages: ** HTML, CSS, JavaScript, PHP, Python, Ruby, R, and Stata
(The plugin can be extended to support other languages)

Supported Platforms
Currently, the plugin only runs on Mac OS but it can be extended to work on other unix systems using gloobus-preview as well as Windows using maComfort. I personally don’t have a unix or windows system but it should be pretty easy to add support. Drop me a line and we can make it work.

How to install
github.com/jlegewie/SublimePeek

Illustration

‘subprocess’ is selected in ST2…

…simply pressing command+shift+h opens this window with the documentation for ‘subprocess’

Selecting ‘text-align’ in a CSS file and pressing command+shift+h opens this window with the documentation for ‘text-align’


0 Likes

Plugin Tooltips
#2

I have added support for HTML and CSS…

0 Likes

#3

Great plugin :smile:
Any chance to see it directly integrated in Sublime text pane/window ? :mrgreen:

Cheers
Francois

0 Likes

#4

No, I don’t think that is possible, Francois. The idea of the plugin (at least partly) is to circumvent ST2 limitations to display function tooltips. I think the only way to actually show the help file in ST2 would be to convert the html files to txt and put the content in a new tab. I think that is way to disruptive for the programming flow…

0 Likes

#5

SublimePeek is now available through Package Control…

0 Likes

#6

It would be very cool to have Ruby/RoR support ! :smile:

Very nice job :wink:

0 Likes

#7

i’m trying implementing support for Ruby docs, but for now i can have it working only for classes and not methods since rdoc is stored in html files named after the class ex: String.html.
Also it doesn’t work for subclasses since docs for these are stored in subfolders ie: class Enumerable::Enumerator maps to enumerable\enumerator.html

0 Likes

#8

I’m also trying an approach similar to they python one generating doc on the fly using the ruby ‘ri’ command, but i keep getting no help file found for…
is there a way to debug the plugin to see what’s going wrong?

0 Likes

#9

Hi, I am not familiar with Ruby so can you give some concrete examples? What would be the selection and how are help files called?
Below are the three ways I currently use to access the help files. I think you can solve the subfolder problem using the mapping file. For ri, can you send me a syntax example? Just to generate a help file from the console in a certain location for some function.

Three ways to access the help files.

  1. The help file is named like the function. E.g. round -> round.html
  2. There is a mapping file called [LANG]-mapping.json with this structure:

{"from": "SELECTION","to": "NAME OF HELP FILE"} ]
3) The help files are generated in Python e.g. using the command line tool pydoc for Python.

ps: I might face a similar problems with javascript. For javascript, I have a lot of methods like Array.length, and Array.split but also String.length. One problem is that ST2 does not know whether a certain for is a string or an array. So which help should I call for var.length?

0 Likes

#10

Greatest plugun!! Awesome job!

0 Likes

#11

so, ruby has a command similar to pydoc and it’s called ri, the syntax is like this:

ri --gems --system --no-pager --format html (this is the command i’m trying to use in sublime peek)

explanation of the command options:

–system Include documentation from Ruby’s standard library

–gems Include documentation from RubyGems

–no-pager Send output directly to stdout

if the names match more than one entity, a list will be shown, otherwise details on that entity will be displayed

0 Likes

#12

Can you provide some more information? Some concrete examples? How would you handle the ‘ri length’ case? I assume [1,2,3].length and “dsds”.length (without knowing ruby syntax) both valid. What help file should pop up without knowing whether length is the method for a string or a array?
Would the mapping file be a solution to your other approach not based on ri?

I am happy to help out with ruby support but I won’t do it alone and someone who is familiar with ruby and interested in ruby support should also work on it.

0 Likes

#13

i think the only way to manage the problem of multiple methods with the same name is to find a way to have a 2 step selection, first you ask documentation for a method, second step, if more than one occurence is found, you ask the user exactly which method he wants to see documentation for. For example when you ask ri the documentation for the method length, you have this output:

More than one method matched your request. You can refine
your search by asking for information on one of:

     Gem::SourceIndex#length, Net::SSH::Buffer#length,
     Net::SSH::Transport::CipherFactory::get_lengths,
     Net::SSH::Transport::HMAC::key_length
....
0 Likes

#14

francescob and nollo, I have made some major improvements to SublimePeek including basic Ruby support. It’s currently in the dev branch on github. You can download the development version here: github.com/jlegewie/SublimePeek/tree/dev
Note: You will have to manually install this AND change back to the released version once I push the changes to the master branch (I will post here when I do that).

Can you try out the new feature and particularly the support for Ruby. Does it work for an actual Ruby user?

Changes include:

  • Javascript, Ruby, and PHP support
  • overview of help files: If no help file is found, the user can browse all available help files (does not work for Python and Ruby)
  • threading so that the download of help files is now happing in the background
0 Likes

#15

I am quite busy this week but I’ll give a try ! Thanks for your work ! :smiley:

0 Likes

#16

hi, sorry for the late answer but yesterday was holiday here in italy so i didn’t even turn the computer on :smile:
I’ve tried the dev version of the plugin and i must say the implementation of ruby docs really rules, i especially like how you managed to handle multiple occurence of a method.
Two things comes to my mind, the first is really easy, the second seems more like a problem that i hope you have a solution for :wink:
First: Would be useful to add this 2 flags to the ri command you’re invoking : --system and --gems so that more libraries are searched for docs (including ruby on rails)
The second is tricky: me, and most rubyst, use a tool called rvm (rvm.io/) which basically is a command line tool which allow to have different rubies (and libraries and gems) versions installed on your system, and to easily switch between them. The problem is that calling ri from sublimetext always call the system default ri, and not the one of rvm, so it basically find docs only for the system ruby and not for the current ruby one is using and also can’t find docs for all the gems installed with rvm. I hope i was able to explain myself :smile:

0 Likes

#17

two more little things:
i’ve replaced your css with the one generated by rdoc (pasted at the end of the post) and also the plugin seems to not recognize when no results are found, it basically show an empty window with the string searched

/*
 * "Darkfish" Rdoc CSS
 * $Id: rdoc.css 54 2009-01-27 01:09:48Z deveiant $
 *
 * Author: Michael Granger <ged@FaerieMUD.org>
 *
 */

/* Base Green is: #6C8C22 */

* { padding: 0; margin: 0; }

body {
  background: #efefef;
  font: 14px "Helvetica Neue", Helvetica, Tahoma, sans-serif;
  margin-left: 40px;
}
body.file-popup {
  font-size: 90%;
  margin-left: 0;
}

h1 {
  font-size: 300%;
  text-shadow: rgba(135,145,135,0.65) 2px 2px 3px;
  color: #6C8C22;
}
h2,h3,h4 { margin-top: 1.5em; }

:link,
:visited {
  color: #6C8C22;
  text-decoration: none;
}
:link:hover,
:visited:hover {
  border-bottom: 1px dotted #6C8C22;
}

pre {
  background: #ddd;
  padding: 0.5em 0;
}

/* @group Generic Classes */

.initially-hidden {
  display: none;
}

#search-field {
  width: 98%;
  background: #eee;
  border: none;
  height: 1.5em;
  -webkit-border-radius: 4px;
}
#search-field:focus {
  background: #f1edba;
}
#search-field:-moz-placeholder,
#search-field::-webkit-input-placeholder {
  font-weight: bold;
  color: #666;
}

.missing-docs {
  font-size: 120%;
  background: white url(images/wrench_orange.png) no-repeat 4px center;
  color: #ccc;
  line-height: 2em;
  border: 1px solid #d00;
  opacity: 1;
  padding-left: 20px;
  text-indent: 24px;
  letter-spacing: 3px;
  font-weight: bold;
  -webkit-border-radius: 5px;
  -moz-border-radius: 5px;
}

.target-section {
  border: 2px solid #dcce90;
  border-left-width: 8px;
  padding: 0 1em;
  background: #fff3c2;
}

/* @end */

/* @group Index Page, Standalone file pages */
.indexpage ul {
  line-height: 160%;
  list-style: none;
}
.indexpage ul :link,
.indexpage ul :visited {
  font-size: 16px;
}

.indexpage li {
  padding-left: 20px;
}

.indexpage ul > li {
  background: url(images/bullet_black.png) no-repeat left 4px;
}
.indexpage li.method {
  background: url(images/plugin.png) no-repeat left 4px;
}
.indexpage li.module {
  background: url(images/package.png) no-repeat left 4px;
}
.indexpage li.class {
  background: url(images/ruby.png) no-repeat left 4px;
}
.indexpage li.file {
  background: url(images/page_white_text.png) no-repeat left 4px;
}
.indexpage li li {
  background: url(images/tag_blue.png) no-repeat left 4px;
}
.indexpage li .toc-toggle {
  width: 16px;
  height: 16px;
  background: url(images/add.png) no-repeat;
}

.indexpage li .toc-toggle.open {
  background: url(images/delete.png) no-repeat;
}

/* @end */

/* @group Top-Level Structure */

#metadata {
  float: left;
  width: 260px;
}

#documentation {
  margin: 2em 1em 5em 300px;
  min-width: 340px;
}

#validator-badges {
  clear: both;
  margin: 1em 1em 2em;
  font-size: smaller;
}

/* @end */

/* @group Metadata Section */
#metadata .section {
  background-color: #dedede;
  -moz-border-radius: 5px;
  -webkit-border-radius: 5px;
  border: 1px solid #aaa;
  margin: 0 8px 8px;
  font-size: 90%;
  overflow: hidden;
}
#metadata h3.section-header {
  margin: 0;
  padding: 2px 8px;
  background: #ccc;
  color: #666;
  -moz-border-radius-topleft: 4px;
  -moz-border-radius-topright: 4px;
  -webkit-border-top-left-radius: 4px;
  -webkit-border-top-right-radius: 4px;
  border-bottom: 1px solid #aaa;
}
#metadata #home-section h3.section-header {
  border-bottom: 0;
}

#metadata ul,
#metadata dl,
#metadata p {
  padding:  8px;
  list-style: none;
}

#file-metadata {
  margin-top: 2em;
}

#file-metadata ul {
  padding-left: 28px;
  list-style-image: url(images/page_green.png);
}

dl.svninfo {
  color: #666;
  margin: 0;
}
dl.svninfo dt {
  font-weight: bold;
}

ul.link-list li {
  white-space: nowrap;
}
ul.link-list .type {
  font-size: 8px;
  text-transform: uppercase;
  color: white;
  background: #969696;
  padding: 2px 4px;
  -webkit-border-radius: 5px;
}

/* @end */

/* @group Class Metadata Section */
#class-metadata {
  margin-top: 2em;
}
/* @end */

/* @group Project Metadata Section */
#project-metadata {
  margin-top: 2em;
}

#project-metadata .section {
  border: 1px solid #aaa;
}
#project-metadata h3.section-header {
  border-bottom: 1px solid #aaa;
  position: relative;
}

#project-metadata form {
  color: #777;
  background: #ccc;
}

/* @end */

/* @group Documentation Section */
.description {
  font-size: 100%;
  color: #333;
}

.description p {
  margin: 1em 0.4em;
}

.description li p {
  margin: 0;
}

.description ol,
.description ul {
  margin-left: 1.5em;
}
.description ol li,
.description ul li {
  line-height: 1.4em;
}

.note-list {
  margin: 8px 0;
}

.label-list {
  margin: 8px 1.5em;
  border: 1px solid #ccc;
}
.description .label-list {
  font-size: 14px;
}

.note-list dt {
  font-weight: bold;
}
.note-list dd {
  padding: 0 12px;
}

.label-list dt {
  padding: 2px 4px;
  font-weight: bold;
  background: #ddd;
}
.label-list dd {
  padding: 2px 12px;
}
.label-list dd + dt,
.note-list dd + dt {
  margin-top: 0.7em;
}

#documentation .section {
  font-size: 90%;
}

#documentation h2.section-header {
  margin-top: 1em;
  padding: 0.25em 0.5em;
  background: #ccc;
  color: #333;
  font-size: 175%;
  border: 1px solid #bbb;
  -moz-border-radius: 3px;
  -webkit-border-radius: 3px;
}

.documentation-section-title {
  position: relative;
}
.documentation-section-title .section-click-top {
  position: absolute;
  top: 6px;
  right: 12px;
  font-size: 10px;
  color: #9b9877;
  visibility: hidden;
  padding-right: 0.5px;
}

.documentation-section-title:hover .section-click-top {
  visibility: visible;
}

#documentation h3.section-header {
  margin-top: 1em;
  padding: 0.25em 0.5em;
  background-color: #dedede;
  color: #333;
  font-size: 150%;
  border: 1px solid #bbb;
  -moz-border-radius: 3px;
  -webkit-border-radius: 3px;
}

#constants-list > dl,
#attributes-list > dl {
  margin: 1em 0 2em;
  border: 0;
}
#constants-list > dl dt,
#attributes-list > dl dt {
  padding-left: 0;
  font-weight: bold;
  font-family: Monaco, "Andale Mono";
  background: inherit;
}
#constants-list > dl dt a,
#attributes-list > dl dt a {
  color: inherit;
}
#constants-list > dl dd,
#attributes-list > dl dd {
  margin: 0 0 1em 0;
  padding: 0;
  color: #666;
}

.documentation-section h2 {
  position: relative;
}

.documentation-section h2 a {
  position: absolute;
  top: 8px;
  right: 10px;
  font-size: 12px;
  color: #9b9877;
  visibility: hidden;
}

.documentation-section h2:hover a {
  visibility: visible;
}

/* @group Method Details */

#documentation .method-source-code {
  display: none;
}

#documentation .method-detail {
  margin: 0.5em 0;
  padding: 0.5em 0;
  cursor: pointer;
}
#documentation .method-detail:hover {
  background-color: #f1edba;
}
#documentation .method-heading {
  position: relative;
  padding: 2px 4px 0 20px;
  font-size: 125%;
  font-weight: bold;
  color: #333;
  background: url(images/brick.png) no-repeat left bottom;
}
#documentation .method-heading :link,
#documentation .method-heading :visited {
  color: inherit;
}
#documentation .method-click-advice {
  position: absolute;
  top: 2px;
  right: 5px;
  font-size: 10px;
  color: #9b9877;
  visibility: hidden;
  padding-right: 20px;
  line-height: 20px;
  background: url(images/zoom.png) no-repeat right top;
}
#documentation .method-heading:hover .method-click-advice {
  visibility: visible;
}

#documentation .method-alias .method-heading {
  color: #666;
  background: url(images/brick_link.png) no-repeat left bottom;
}

#documentation .method-description,
#documentation .aliases {
  margin: 0 20px;
  color: #666;
}

#documentation .method-description p,
#documentation .aliases p {
  line-height: 1.2em;
}

#documentation .aliases {
  padding-top: 4px;
  font-style: italic;
  cursor: default;
}
#documentation .method-description p {
  margin-bottom: 0.5em;
}
#documentation .method-description ul {
  margin-left: 1.5em;
}
pre {
  margin: 0.5em 0;
}

#documentation .attribute-method-heading {
  background: url(images/tag_green.png) no-repeat left bottom;
}
#documentation #attribute-method-details .method-detail:hover {
  background-color: transparent;
  cursor: default;
}
#documentation .attribute-access-type {
  font-size: 60%;
  text-transform: uppercase;
  vertical-align: super;
  padding: 0 2px;
}
/* @end */

/* @end */

/* @group Source Code */

pre {
  overflow: auto;
  background: #262626;
  color: white;
  border: 1px dashed #999;
  padding: 0.5em;
}

.description pre {
  margin: 0 0.4em;
}

.ruby-constant   { color: #7fffd4; background: transparent; }
.ruby-keyword    { color: #00ffff; background: transparent; }
.ruby-ivar       { color: #eedd82; background: transparent; }
.ruby-operator   { color: #00ffee; background: transparent; }
.ruby-identifier { color: #ffdead; background: transparent; }
.ruby-node       { color: #ffa07a; background: transparent; }
.ruby-comment    { color: #dc0000; font-weight: bold; background: transparent; }
.ruby-regexp     { color: #ffa07a; background: transparent; }
.ruby-value      { color: #7fffd4; background: transparent; }

/* @end */


/* @group search results */
#search-results h1 {
  font-size: 1em;
  font-weight: normal;
  text-shadow: none;
}

#search-results .current {
  background: #ccc;
  border-bottom: 1px solid transparent;
}

#search-results li {
  list-style: none;
  border-bottom: 1px solid #aaa;
  -moz-border-radius: 4px;
  -webkit-border-radius: 4px;
  border-radius: 4px;
  margin-bottom: 0.5em;
}

#search-results li:last-child {
  border-bottom: none;
  margin-bottom: 0;
}

#search-results li p {
  padding: 0;
  margin: 0.5em;
}

#search-results .search-namespace {
  font-weight: bold;
}

#search-results li em {
  background: yellow;
  font-style: normal;
}

#search-results pre {
  margin: 0.5em;
}

/* @end */

0 Likes

#18

I have added the two options, fixed the ‘no results’ bug, and updated the CSS style file. I didn’t use the one you posted but updated my own style file to clearly indicate code sections. There are a lot of other style definitions in your file, which I am not sure whether they are used at all. Let me know if I am missing something.
About the tricky problem: I run ri on the console from ST2. So if you know a way how I can call a different ri from the console, I can easily implement that in ST2. The second question is whether I can detect the current ruby (probably not?). The alternative would be to add an option that allows the user to choose, which ri is called.

0 Likes

#19

i don’t know if is it possibile, but can python run the command as it was calling it in the bash shell?
basically rvm works by adding these lines in .bashrc:


PATH=$PATH:$HOME/.rvm/bin # Add RVM to PATH for scripting
 -s "$HOME/.rvm/scripts/rvm" ]] && . "$HOME/.rvm/scripts/rvm"

so in my terminal when i call ri, it always get called the one for the current ruby version set by rvm. Examples:

MacBook-Pro-di-francesco:~ francesco$ which ri
/Users/francesco/.rvm/rubies/ruby-1.8.7-head/bin/ri
MacBook-Pro-di-francesco:~ francesco$ rvm use 1.9.2
Using /Users/francesco/.rvm/gems/ruby-1.9.2-p320
MacBook-Pro-di-francesco:~ francesco$ which ri
/Users/francesco/.rvm/gems/ruby-1.9.2-p320/bin/ri
MacBook-Pro-di-francesco:~ francesco$ rvm use system
Now using system ruby.
MacBook-Pro-di-francesco:~ francesco$ which ri
/usr/bin/ri
0 Likes

#20

mmmh, I made a small change that might fix the problem. Can you give it a try?
I am not really satisfied with the solution though because the Python call to subprocess now uses shell=True as an argument, which is less secure for untrusted arguments (not a problem, I think) and requires manual escaping of certain characters. I am not sure about the escaping details but it might be the case that some help topics fail. Let me know if you come across something.

Edit: Some of the problems with shell=True are discussed here: stackoverflow.com/questions/3172 … subprocess
I would prefer not using this possible solution. An alternative would be to provide a user option for the ri path. But that only makes sense when people do not change their Ruby version all the time…

0 Likes