Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[READY] JavaScript completer using Tern.js #272

Merged
merged 24 commits into from
Dec 14, 2015

Conversation

puremourning
Copy link
Member

Overview

Adds support for a JavaScript completer using Tern.js.

The completer supports:

  • Intelligent auto-completion
  • Go to definition, references (GoTo, etc.)
  • Semantic type information for identifiers (GetType)
  • View documentation comments for identifiers (GetDoc)
  • Management of tern server instance

This provides (for Vim at least) a replacement, for tern_for_vim automagically in ycmd.

Demo

Completions:

ycm-tern-demo

Subcommands:

ycm-tern-demo-subcommands

Installation

Run install.py --tern-completer

Caveats and differences vs. tern_for_vim

  • Currently, to use it there must be a .tern-project configuration file somewhere in the directory tree above Vim's working directory when the first JavaScript file is opened (i.e. when the server starts). I believe this is also true for tern_for_vim.
  • Does not support refactoring (rename) which tern_for_vim does
  • Methods from Object.prototype are always included. Tern has the ability to only include them when they match some number of typed characters, as they are otherwise "noise". This is incompatible with ycmd's cache and LCS matching.

Status

I believe this to be fully working with the above caveats. I think the first one is the most frustrating, but it isn't obvious how to avoid it.

Review on Reviewable

Prototype supports:
 - completion
 - GetType
 - GetDoc
 - GoTo and GoToDefinition
 - automatic server management
…gging. Write logs to files to work with test suite
This also seems to resolve issues around completions with a query string. \o/

Add more tests for completions and a basic test for subcommands (before rebasing
on master where this approach has been changed)

Improve some of the test utilities to allow specifying unsaved buffer contents
and to check documentation, etc. on completions.

Tidy up documentation output for completions and for GetDoc. Mimic tern_for_vim
for the former, but use our own style for the latter.
Fix a bug where these would return spurious JSON error when run
without an identifier - catch the HTTP error that tern returns.

Also add GoToReferences which returns a list of the refs
The "installation" requires running 'npm install --production' in
the third-party/tern directory. We detect the lack of the
tern_modules directory and disable our completer. This ensures
that anyone not specifying '--tern-completer' doesn't get it and
therefore it won't conflict with existing users' tern_for_vim
installations.
@puremourning
Copy link
Member Author

HMMMMMM.

The appveyor builds are failing, unable to find npm in build.py.

I've tried two approaches:

@micbou any clues why that might be?

I can see that npm must be there because appveyor.yaml has uses it

@puremourning
Copy link
Member Author

I think I got it. it's npm.cmd not npm.exe

@puremourning
Copy link
Member Author

Grumble. Very grumble.

The problem is https://bugs.python.org/issue2200

i.e. the extension .cmd cannot be used with distutils.spawn.find_executable so we have to do its job for it.

@micbou
Copy link
Collaborator

micbou commented Dec 12, 2015

Welcome to the joy of Windows.

The problem is https://bugs.python.org/issue2200
i.e. the extension .cmd cannot be used with distutils.spawn.find_executable so we have to do its job for it.

You can reuse the FindExecutable function in ycmd/utils.py.

I am not reviewing the tests because they will change with PR #270.


Review status: 0 of 27 files reviewed at latest revision, 3 unresolved discussions, some commit checks failed.


ycmd/completers/javascript/tern_completer.py, line 164 [r1] (raw file):
Using localhost on Windows makes the request to the server very slow (at least on my configuration). Replace it by 127.0.0.1. Should always work.


ycmd/completers/javascript/tern_completer.py, line 199 [r1] (raw file):
Same comment as above. Maybe add a method to obtain the target (_ServerLocation for example).


ycmd/completers/javascript/tern_completer.py, line 280 [r1] (raw file):
Unfortunately, this will fail on Windows. You need to specifically call the Tern binary with the node executable.


Comments from the review on Reviewable.io

@puremourning
Copy link
Member Author

Thanks @micbou 'preciate it!

I've fired up my Windows VM to get this to work. :)


Review status: 0 of 27 files reviewed at latest revision, 3 unresolved discussions, some commit checks failed.


Comments from the review on Reviewable.io

@puremourning
Copy link
Member Author

So strange.

Now i'm getting strange EPERM errors when tern is started by my completer, but not when run manually.

The completer runs it with the following command line:

2015-12-12 21:17:10,700 - DEBUG - Starting tern with the following command: C:\Program Files\nodejs\node.exe C:\Users\Ben\vimfiles\bundle\YOUCOM~1\THIRD_~1\ycmd\ycmd\completers\javascript\..\..\..\third_party\tern\bin\tern --port 49867 --host localhost --persistent --no-port-file --verbose

But the process dies immediately with:

events.js:142
      throw er; // Unhandled 'error' event
      ^

Error: EPERM: operation not permitted, read
    at Error (native)

If I run the same command from the command line, it works. If I run it in python interactive shell, it works too.

>>> node
'C:\\Program Files\\nodejs\\node.exe'
>>> tern = os.path.dirname( 'ycmd\\completers\\javascript\\tern_completer.py' )
>>> tern
'C:\\Users\\Ben\\vimfiles\\bundle\\YouCompleteMe-Clean\\third_party\\ycmd\\third_party\\tern\\bin\\tern'
>>> command = [ node, tern, '--host', 'localhost', '--persistent', '--verbose' ]
>>> import subprocess
>>> subprocess.Popen( command )
<subprocess.Popen object at 0x000000000241AF98>
>>> Listening on port 49881

The only difference seems to be the "shortened" path name with the tilde ~ in it. Could that be related?

Clues? Tips? ;_;

@puremourning
Copy link
Member Author

Even with the same path in command line python it works:

C:\Users\Ben\vimfiles\bundle\YouCompleteMe-Clean\third_party\ycmd>python
Python 2.7.10 (default, May 23 2015, 09:44:00) [MSC v.1500 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import os, subprocess
>>> import ycmd.utils
>>> node = ycmd.utils.FindExecutable( 'node' )
>>> tern = 'C:\\Users\\Ben\\vimfiles\\bundle\\YOUCOM~1\\THIRD_~1\\ycmd\\ycmd\\completers\\javascript\\..\\..\\..\\third_party\\tern\\bin\\tern'
>>> tern
'C:\\Users\\Ben\\vimfiles\\bundle\\YOUCOM~1\\THIRD_~1\\ycmd\\ycmd\\completers\\javascript\\..\\..\\..\\third_party\\tern\\bin\\tern'
>>> command = [ node, tern, '--verbose' ]
>>> subprocess.Popen( command )
<subprocess.Popen object at 0x00000000024842B0>
>>> Listening on port 49907

^C
C:\Users\Ben\vimfiles\bundle\YouCompleteMe-Clean\third_party\ycmd>

I must be going mad. I even tried with raw Popen rather than utils.SafePopen - no joy.

More grumble :p

@micbou
Copy link
Collaborator

micbou commented Dec 12, 2015

Let me try your tern-completer-locking-windows branch.


Review status: 0 of 27 files reviewed at latest revision, 3 unresolved discussions, some commit checks failed.


Comments from the review on Reviewable.io

@puremourning
Copy link
Member Author

i pushed the fixed to build.py just now

@puremourning
Copy link
Member Author

thanks, btw. sorry to be a pain :(

@micbou
Copy link
Collaborator

micbou commented Dec 12, 2015

Redirecting stdout and stderr to the logfiles makes the server crash. Need to understand why.


Review status: 0 of 27 files reviewed at latest revision, 3 unresolved discussions, some commit checks failed.


Comments from the review on Reviewable.io

@puremourning
Copy link
Member Author

Interesting. It works if i reduce it all into one file and run it manually:

import os
import sys

sys.path.append( os.path.join( os.path.dirname( __file__ ), '..', '..', '..' ) )

print sys.path

from ycmd.utils import ( PathToFirstExistingExecutable, SafePopen,
        GetUnusedLocalhostPort, PathToTempDir )


PATH_TO_TERNJS_BINARY = os.path.abspath( 
    os.path.join(
      os.path.dirname( __file__ ),
      '..',
      '..',
      '..',
      'third_party',
      'tern',
      'bin',
      'tern' ) )


node_path = PathToFirstExistingExecutable( [ 'node' ] )

if not node_path:
  raise RuntimeError( 'Unable to find node executable' )
else:
  print ( 'Using node binary from: ' + node_path )


server_port = GetUnusedLocalhostPort()

if True:
  extra_args = [ '--verbose' ]
else:
  extra_args = []

command = [ node_path,
            PATH_TO_TERNJS_BINARY,
            '--port',
            str( server_port ),
            '--host',
            '127.0.0.1',
            '--persistent',
            '--no-port-file' ] + extra_args

print( 'Starting tern with the following command: ' + ' '.join( command ) )

logfile_format = os.path.join( PathToTempDir(), u'tern_{port}_{std}.log' )

server_stdout = logfile_format.format(
    port = server_port,
    std = 'stdout' )

server_stderr = logfile_format.format(
    port = server_port,
    std = 'stderr' )

try:
  with open( server_stdout, 'w' ) as stdout:
    with open( server_stderr, 'w' ) as stderr:
      server_handle = SafePopen( command,
                                 stdout = stdout,
                                 stderr = stderr )
except Exception:
  print( 'Unable to start Tern.js server' )
  server_port = 0


if ( server_port > 0 and server_handle is not None 
    and server_handle.poll() is None ):
  print ( 'Tern.js Server started with pid: ' +
                str( server_handle.pid ) +
                ' listening on port ' +
                str( server_port ) )
  print ( 'Tern.js Server log files are: ' +
                server_stdout +
                ' and ' +
                server_stderr )

server_handle.wait()

Then i can use postman in chrome to talk to the server.

@puremourning
Copy link
Member Author

Hmmm. Yes. Removing stdout= and stderr= from Popen call fixes it, but the output from the server is lost in the ether. subprocess.PIPE also causes it to crash (and also loses the output).

@puremourning
Copy link
Member Author

I even hacked something within waitress/bottle and that works, so it isn't that either.

@Valloric
Copy link
Member

Reviewed 1 of 2 files at r4, 2 of 2 files at r5.
Review status: all files reviewed at latest revision, 2 unresolved discussions.


build.py, line 30 [r3] (raw file):
I remember there was a good reason why we didn't want to import stuff in the scripts, though I can't remember why.

The duplication is shitty but we'll live with it.


Comments from the review on Reviewable.io

@puremourning puremourning changed the title [RFC] JavaScript completer using Tern.js [READY] JavaScript completer using Tern.js Dec 13, 2015
@puremourning
Copy link
Member Author

I've marked this as [READY], though AppVeyor is my nemesis today - seemingly impossibly slow. I'll see if I can massage it.

Also, do you want me to rebase this into one commit ?


Review status: all files reviewed at latest revision, 2 unresolved discussions, some commit checks broke.


Comments from the review on Reviewable.io

@micbou
Copy link
Collaborator

micbou commented Dec 13, 2015

I think there is an issue with AppVeyor. All tests pass but the run does not finish. It could be that the Tern server is still running.


Review status: all files reviewed at latest revision, 2 unresolved discussions, some commit checks broke.


Comments from the review on Reviewable.io

@puremourning
Copy link
Member Author

that's what i was just thinking. I will check it out.

@micbou
Copy link
Collaborator

micbou commented Dec 13, 2015

Review status: all files reviewed at latest revision, 3 unresolved discussions, some commit checks broke.


ycmd/completers/javascript/tern_completer.py, line 412 [r5] (raw file):
Not sure but I think you need to use terminate instead of kill.


Comments from the review on Reviewable.io

@puremourning
Copy link
Member Author

Review status: all files reviewed at latest revision, 3 unresolved discussions, some commit checks broke.


ycmd/completers/javascript/tern_completer.py, line 412 [r5] (raw file):
I think you might be right. There's a utils.TerminateProcess which looks much more aggressive and has windows-specifics.

I also have tons of node processes on my windows system, so it is obviously the problem!


Comments from the review on Reviewable.io

@micbou
Copy link
Collaborator

micbou commented Dec 13, 2015

Review status: all files reviewed at latest revision, 3 unresolved discussions, some commit checks broke.


ycmd/completers/javascript/tern_completer.py, line 412 [r5] (raw file):
Yes, I was writing a comment about this function. Certainly a good idea to use it.


Comments from the review on Reviewable.io

@Valloric
Copy link
Member

Review status: all files reviewed at latest revision, 3 unresolved discussions, some commit checks broke.


ycmd/completers/javascript/tern_completer.py, line 412 [r5] (raw file):
Yep, we've fought this battle before. Use utils.TerminateProcess.


Comments from the review on Reviewable.io

@Valloric
Copy link
Member

Since this is likely caused by us not killing the Tern server properly, this is great instance of AppVeyor saving our butt by catching a nasty issue before it went out to users! :)


Review status: all files reviewed at latest revision, 3 unresolved discussions, some commit checks broke.


Comments from the review on Reviewable.io

@puremourning
Copy link
Member Author

There seems to be a wider issue. Basically the @atexit stuff isn't actually firing, so .kill() or not, Shutdown() isn't being called. I'll see what I can find out.

I think this project has increased my respect for the folk who develop for windows (or actually cross-platform stuff) in a more general capacity. It takes a special type of genius to work in this environment 😀


Review status: all files reviewed at latest revision, 3 unresolved discussions, some commit checks broke.


ycmd/completers/javascript/tern_completer.py, line 412 [r5] (raw file):
I think this project has increased my respect for the poor folk who develop for windows (or actually cross-platform stuff) in a more general capacity. It takes a special type of genius to use cmd.exe and use the \ key all the time. And a special type of patience required to do development without so many of the creature comforts of POSIX-like systems. Like a shell. Did I mention the shell? I hate the shell.


Comments from the review on Reviewable.io

@puremourning puremourning changed the title [READY] JavaScript completer using Tern.js [WIP] JavaScript completer using Tern.js Dec 13, 2015
@puremourning
Copy link
Member Author

status changed back to [WIP]


Review status: all files reviewed at latest revision, 3 unresolved discussions, some commit checks broke.


Comments from the review on Reviewable.io

@micbou
Copy link
Collaborator

micbou commented Dec 13, 2015

There seems to be a wider issue. Basically the @atexit stuff isn't actually firing, so .kill() or not, Shutdown() isn't being called. I'll see what I can find out.

Yes, I know about this issue. It was discussed in ycm-core/YouCompleteMe#876 but no satisfactory solution was found. This is on my list of things to improve on Windows.

Anyway, I see that you decided to manually stop the server and this is the right thing to do. Even without this issue, you don't want to pile up all those Tern servers and kill them in one go at the end.


Review status: 22 of 28 files reviewed at latest revision, 1 unresolved discussion.


Comments from the review on Reviewable.io

@puremourning
Copy link
Member Author

I see, so we have 2 known issues on windows:

  • downstream logging is disabled
  • the tern server is left running on vim exit.

The former is not (yet) well-understood and the latter is a "known" issue on windows.

It also looks like the AppVeyor tests are now passing (woo!). So based on that, switching this back to [READY]


Review status: 22 of 28 files reviewed at latest revision, 1 unresolved discussion.


Comments from the review on Reviewable.io

@puremourning puremourning changed the title [WIP] JavaScript completer using Tern.js [READY] JavaScript completer using Tern.js Dec 13, 2015
@Valloric
Copy link
Member

I'm fine with merging this with the tern server still running after vim exit on Windows because we have the same issue with OmniSharp, but that's a really nasty problem. It was sorta fine before because we didn't officially support Windows, but now that we do, it's just looks bad.

With that, @homu r+


Review status: 22 of 28 files reviewed at latest revision, all discussions resolved.


Comments from the review on Reviewable.io

@homu
Copy link
Contributor

homu commented Dec 14, 2015

📌 Commit 1a24c89 has been approved by Valloric

@homu
Copy link
Contributor

homu commented Dec 14, 2015

⌛ Testing commit 1a24c89 with merge 1caabf5...

homu added a commit that referenced this pull request Dec 14, 2015
[READY] JavaScript completer using Tern.js

# Overview

Adds support for a JavaScript completer using [Tern.js](http://ternjs.net).

The completer supports:

* Intelligent auto-completion
* Go to definition, references (`GoTo`, etc.)
* Semantic type information for identifiers (`GetType`)
* View documentation comments for identifiers (`GetDoc`)
* Management of `tern` server instance

This provides (for Vim at least) a replacement, for [tern_for_vim](https://github.com/ternjs/tern_for_vim) automagically in ycmd.

## Demo

Completions:

![ycm-tern-demo](https://cloud.githubusercontent.com/assets/10584846/11763113/035f748c-a0f6-11e5-8961-77139d4e6f45.gif)

Subcommands:

![ycm-tern-demo-subcommands](https://cloud.githubusercontent.com/assets/10584846/11763115/0523467c-a0f6-11e5-8552-357bcdda2e6a.gif)

## Installation

Run `install.py --tern-completer`

## Caveats and differences vs. tern_for_vim

* Currently, to use it there must be a `.tern-project` configuration file somewhere in the directory tree above Vim's working directory when the first JavaScript file is opened (i.e. when the server starts). I believe this is also true for `tern_for_vim`.
* Does not support refactoring (rename) which `tern_for_vim` does
* Methods from `Object.prototype` are always included. Tern has the ability to only include them when they match some number of typed characters, as they are otherwise "noise". This is incompatible with ycmd's cache and LCS matching.

## Status

I believe this to be fully working with the above caveats. I think the first one is the most frustrating, but it isn't obvious how to avoid it.

<!-- Reviewable:start -->
[<img src="https://reviewable.io/review_button.png" height=40 alt="Review on Reviewable"/>](https://reviewable.io/reviews/valloric/ycmd/272)
<!-- Reviewable:end -->
@homu
Copy link
Contributor

homu commented Dec 14, 2015

☀️ Test successful - status

@vheon
Copy link
Contributor

vheon commented Dec 14, 2015

I'm fine with merging this with the tern server still running after vim exit on Windows because we have the same issue with OmniSharp, but that's a really nasty problem. It was sorta fine before because we didn't officially support Windows, but now that we do, it's just looks bad.

Should we write it somewhere?

@puremourning puremourning deleted the tern-completer-locking branch December 14, 2015 23:20
vheon referenced this pull request in vheon/ycmd Dec 16, 2015
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants