Skip to content

Commit

Permalink
Add initial version
Browse files Browse the repository at this point in the history
  • Loading branch information
davidanthoff committed Sep 13, 2018
0 parents commit dd445ab
Show file tree
Hide file tree
Showing 10 changed files with 324 additions and 0 deletions.
1 change: 1 addition & 0 deletions .codecov.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
comment: false
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
*.jl.cov
*.jl.*.cov
*.jl.mem
20 changes: 20 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
language: julia
os:
- linux
- osx
julia:
- 0.7
- 1.0
- nightly
notifications:
email: false
branches:
only:
- master
- /release-.*/
- /v(\d+)\.(\d+)\.(\d+)/
matrix:
allow_failures:
- julia: nightly
after_success:
- julia -e 'using Pkg; Pkg.add("Coverage"); using Coverage Codecov.submit(process_folder())'
40 changes: 40 additions & 0 deletions LICENSE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
The SymbolServer.jl package is licensed under the MIT "Expat" License:

> Copyright (c) 2018: David Anthoff.
>
>
> Permission is hereby granted, free of charge, to any person obtaining a copy
>
> of this software and associated documentation files (the "Software"), to deal
>
> in the Software without restriction, including without limitation the rights
>
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
>
> copies of the Software, and to permit persons to whom the Software is
>
> furnished to do so, subject to the following conditions:
>
>
>
> The above copyright notice and this permission notice shall be included in all
>
> copies or substantial portions of the Software.
>
>
>
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
>
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
>
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
>
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
>
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
>
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
>
> SOFTWARE.
>
>
45 changes: 45 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# SymbolServer

[![Project Status: WIP – Initial development is in progress, but there has not yet been a stable, usable release suitable for the public.](https://www.repostatus.org/badges/latest/wip.svg)](https://www.repostatus.org/#wip)
[![Build Status](https://travis-ci.org/JuliaEditorSupport/SymbolServer.jl.svg?branch=master)](https://travis-ci.org/JuliaEditorSupport/SymbolServer.jl)
[![codecov.io](http://codecov.io/github/JuliaEditorSupport/SymbolServer.jl/coverage.svg?branch=master)](http://codecov.io/github/JuliaEditorSupport/SymbolServer.jl?branch=master)

SymbolServer is a helper package for LanguageServer.jl.

## Overview

You can start a new symbol server for a given julia environment like this:

````julia
using SymbolServer

path_to_julia_env = "/foo/bar"

s = SymbolServer.SymbolServerProcess(path_to_julia_env)
````

You can also start a symbol server for the default julia environment if you don't pass any path:

````julia
using SymbolServer

s = SymbolServer.SymbolServerProcess()
````

You can then call a number of functions that extract information about packages and other information for that environment.

``get_packages_in_env`` returns all the packages in that environment:

````julia
pkgs = get_packages_in_env(s)
````

``import_module`` loads a given package into the symbol server process and returns a structure with information about the symbols in that module:

````julia
mod_info = import_module(s, :MyPackage)
````

``get_doc`` should return doc information for a given symbol (but seems broken right now).

Once you are done with a given symbol server, you need to kill it with ``kill(s)`` to free the resources associated with that symbol server.
1 change: 1 addition & 0 deletions REQUIRE
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
julia 0.7
42 changes: 42 additions & 0 deletions appveyor.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
environment:
matrix:
- julia_version: 0.7
- julia_version: 1
- julia_version: nightly

platform:
- x86 # 32-bit
- x64 # 64-bit

matrix:
allow_failures:
- julia_version: nightly

branches:
only:
- master
- /release-.*/
- /v(\d+)\.(\d+)\.(\d+)/

notifications:
- provider: Email
on_build_success: false
on_build_failure: false
on_build_status_changed: false

install:
- ps: iex ((new-object net.webclient).DownloadString("https://raw.githubusercontent.com/JuliaCI/Appveyor.jl/version-1/bin/install.ps1"))

build_script:
- echo "%JL_BUILD_SCRIPT%"
- C:\julia\bin\julia -e "%JL_BUILD_SCRIPT%"

test_script:
- echo "%JL_TEST_SCRIPT%"
- C:\julia\bin\julia -e "%JL_TEST_SCRIPT%"

# # Uncomment to support code coverage upload. Should only be enabled for packages
# # which would have coverage gaps without running on Windows
# on_success:
# - echo "%JL_CODECOV_SCRIPT%"
# - C:\julia\bin\julia -e "%JL_CODECOV_SCRIPT%"
73 changes: 73 additions & 0 deletions src/SymbolServer.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
module SymbolServer

export SymbolServerProcess
export get_packages_in_env, get_doc, import_module

using Serialization

mutable struct SymbolServerProcess
process::Base.Process

function SymbolServerProcess(environment=nothing)
jl_cmd = joinpath(Sys.BINDIR, Base.julia_exename())
client_process_script = joinpath(@__DIR__, "clientprocess", "clientprocess_main.jl")

p = if environment===nothing
open(Cmd(`$jl_cmd $client_process_script`), read=true, write=true)
else
open(Cmd(`$jl_cmd --project=$environment $client_process_script`, dir=environment), read=true, write=true)
end

return new(p)
end
end

function request(server::SymbolServerProcess, message::Symbol, payload)
serialize(server.process, (message, payload))
ret_val = deserialize(server.process)
return ret_val
end

# Public API

function Base.kill(s::SymbolServerProcess)
kill(s.process)
end

function get_packages_in_env(server::SymbolServerProcess)
status, payload = request(server, :get_packages_in_env, nothing)
if status == :success
return payload
else
error(payload)
end
end

function get_doc(server::SymbolServerProcess, mod::Symbol)
status, payload = request(server, :get_module_doc, mod)
if status == :success
return payload
else
error(payload)
end
end

function get_doc(server::SymbolServerProcess, mod::Symbol, name::Symbol)
status, payload = request(server, :get_doc, (mod=mod, name=name))
if status == :success
return payload
else
error(payload)
end
end

function import_module(server::SymbolServerProcess, name::Symbol)
status, payload = request(server, :import, name)
if status == :success
return payload
else
error(payload)
end
end

end # module
33 changes: 33 additions & 0 deletions src/clientprocess/clientprocess_main.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using Serialization, Pkg

include("from_static_lint.jl")

while true
message, payload = deserialize(stdin)

try
if message == :debugmessage
@info(payload)
serialize(stdout, (:success, nothing))
elseif message == :get_packages_in_env
pkgs = collect(Symbol.(keys(Pkg.API.installed())))

serialize(stdout, (:success, pkgs))
elseif message == :get_module_doc
docs = string(Docs.doc(getfield(Main, payload)))

serialize(stdout, (:success, docs))
elseif message == :get_doc
docs = string(Docs.doc(getfield(Main, payload.mod), payload.name))

serialize(stdout, (:success, docs))
elseif message == :import
@eval import $payload

mod_names = read_module(getfield(Main, payload))
serialize(stdout, (:success, mod_names))
end
catch err
serialize(stdout, (:failure, err))
end
end
66 changes: 66 additions & 0 deletions src/clientprocess/from_static_lint.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
function read_methods(x)
map(methods(x)) do m
Dict("type" => "method",
"file" => String(m.file),
"line" => m.line,
"args" => Base.arg_decl_parts(m)[2][2:end])
end
end

function collect_params(t, params = [])
if t isa UnionAll
push!(params, t.var)
return collect_params(t.body, params)
else
return t, params
end
end

function read_module(m)
out = Dict()
out[".type"] = "module"
out[".exported"] = names(m)
for n in names(m, all = true)
!isdefined(m, n) && continue
startswith(string(n), "#") && continue
if false #Base.isdeprecated(m, n)
else
x = getfield(m, n)
if x isa Function
out[String(n)] = Dict(
".type" => "Function",
".methods" => read_methods(x))
elseif x isa DataType
t, p = collect_params(x)
if t.abstract
out[String(n)] = Dict(
".type" => "abstract",
".params" => string.(p))
elseif t.isbitstype
out[String(n)] = Dict(
".type" => "primitive",
".params" => string.(p))
elseif !(isempty(t.types) || Base.isvatuple(t))
out[String(n)] = Dict(
".type" => "struct",
".params" => string.(p),
".fields" => collect(string.(fieldnames(t))),
".types" => string.(collect(t.types)),
".methods" => read_methods(x))
else
out[String(n)] = Dict(
".type" => "DataType",
".params" => string.(p))
end
elseif x isa Module && x != m
if parentmodule(x) == m
out[string(n)] = read_module(x)
end
else
out[String(n)] = Dict(
".type" => string(typeof(x)))
end
end
end
out
end

0 comments on commit dd445ab

Please sign in to comment.