Skip to content
This repository has been archived by the owner on Jun 1, 2023. It is now read-only.

Commit

Permalink
Make scry faster and lightweight (#53)
Browse files Browse the repository at this point in the history
* Implements lightweight features

* Avoid heavy compiler dependencies

* Use external process to free up memory

* Remove LLVM dependency
  • Loading branch information
faustinoaq committed Mar 6, 2018
1 parent 6790b10 commit 8653d63
Show file tree
Hide file tree
Showing 10 changed files with 57 additions and 41 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Expand Up @@ -15,7 +15,7 @@ matrix:
- DEPLOY_DIR=bin/darwin

install:
- bin/ci prepare_build
- bin/ci prepare_build

script:
- bin/ci build
Expand Down
1 change: 0 additions & 1 deletion Dockerfile
@@ -1,7 +1,6 @@
FROM crystallang/crystal:0.24.1

RUN apt-get update
RUN apt-get install -y llvm-4.0-dev
RUN apt-get clean
RUN rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*

Expand Down
4 changes: 2 additions & 2 deletions README.md
Expand Up @@ -17,7 +17,7 @@ The server is implemented in Crystal.

To install scry download it from [releases page](https://github.com/crystal-lang-tools/scry/releases) or compile latest version using:

> **Note:** crystal and LLVM are required in order to compile scry.
> **Note:** crystal is required in order to compile scry.
```
git clone https://github.com/crystal-lang-tools/scry.git
Expand All @@ -32,7 +32,7 @@ Then setup `scry` binary path on your LSP client.
- Completion feature is still experimental.
- Unexpected diagnostics can appear on files using implicit `require`, try using explicit `require` at top of your files.
- Go to definition only work for some methods and variables. Go to classes or macros definition is not supported yet.
- Symbol listing is per file, listing workspace symbols isn't supported yet.
- Symbol listing works per file, searching for workspace symbols is not supported yet.

## Roadmap

Expand Down
5 changes: 2 additions & 3 deletions bin/ci
Expand Up @@ -65,8 +65,7 @@ prepare_build() {
on_linux docker build -t crystal/scry-image .

on_osx brew update
on_osx brew install llvm crystal-lang
on_osx brew link --overwrite --force llvm
on_osx brew install crystal-lang
}


Expand Down Expand Up @@ -139,4 +138,4 @@ case $command in
exit 1
fi
;;
esac
esac
2 changes: 1 addition & 1 deletion spec/scry/initialize_spec.cr
Expand Up @@ -6,7 +6,7 @@ module Scry
initer = Initializer.new(
InitializeParams.from_json({
processId: 1,
rootPath: "/homa/main/Projects/Experiment",
rootPath: "/home/main/Projects/Experiment",
capabilities: {} of String => String,
trace: "off",
}.to_json),
Expand Down
28 changes: 16 additions & 12 deletions src/scry/analyzer.cr
@@ -1,4 +1,3 @@
require "compiler/crystal/**"
require "./workspace"
require "./text_document"
require "./publish_diagnostic"
Expand Down Expand Up @@ -26,17 +25,22 @@ module Scry

# NOTE: compiler is a bit heavy in some projects.
private def analyze(source)
source = Crystal::Compiler::Source.new(@text_document.filename, source)
compiler = Crystal::Compiler.new
compiler.color = false
compiler.no_codegen = true
compiler.debug = Crystal::Debug::None
compiler.compile(source, source.filename + ".out")
[@diagnostic.clean]
rescue ex : Crystal::Exception
@diagnostic.from(ex)
ensure
GC.collect
response = crystal_build(@text_document.filename, source)
if response.empty?
[@diagnostic.clean]
else
@diagnostic.from(response)
end
rescue ex
Log.logger.error("A error was found while searching diagnostics\n#{ex}")
nil
end

private def crystal_build(filename, source)
code = IO::Memory.new(source)
String.build do |io|
Process.run("crystal", ["build", "--no-codegen", "--no-color", "--error-trace", "-f", "json", "--stdin-filename", filename], output: io, error: io, input: code)
end
end
end
end
30 changes: 13 additions & 17 deletions src/scry/implementations.cr
@@ -1,5 +1,3 @@
require "compiler/crystal/**"

require "./log"
require "./protocol/location"

Expand All @@ -21,29 +19,27 @@ module Scry

# NOTE: compiler is a bit heavy in some projects.
def search(filename, source, position)
source = Crystal::Compiler::Source.new(filename, source)
compiler = Crystal::Compiler.new
compiler.color = false
compiler.no_codegen = true
compiler.debug = Crystal::Debug::None
result = compiler.compile(source, filename + ".out")
loc = Crystal::Location.new(filename, position.line + 1, position.character + 1)
res = Crystal::ImplementationsVisitor.new(loc).process(result)
Log.logger.debug(res)
impls = res.implementations
if res.status == "ok" && impls
response = crystal_tool(filename, position)
parsed_response = JSON.parse(response)
impls = parsed_response["implementations"]?
if impls
locations = impls.map do |impl|
pos = Position.new(impl.line, impl.column)
pos = Position.new(impl["line"].as_i, impl["column"].as_i)
range = Range.new(pos, pos)
Location.new("file://" + impl.filename, range)
Location.new("file://" + impl["filename"].as_s, range)
end
ResponseMessage.new(@text_document.id, locations)
end
rescue ex
Log.logger.error("A error was found while searching definitions\n#{ex}")
nil
ensure
GC.collect
end

private def crystal_tool(filename, position)
location = "#{filename}:#{position.line + 1}:#{position.character + 1}"
String.build do |io|
Process.run("crystal", ["tool", "implementations", "--no-color", "--error-trace", "-f", "json", "-c", "#{location}", filename], output: io, error: io)
end
end
end
end
17 changes: 17 additions & 0 deletions src/scry/missing_methods.cr
@@ -0,0 +1,17 @@
# HACK: to avoid requiring the whole compiler
module Crystal
struct CrystalPath
class Error
def to_s_with_source(source, io)
end
end
end

class Exception
def to_json_single(json)
json.object do
json.field "message", @message
end
end
end
end
7 changes: 4 additions & 3 deletions src/scry/parse_analyzer.cr
@@ -1,8 +1,9 @@
require "compiler/crystal/**"

require "compiler/crystal/syntax"
require "compiler/crystal/crystal_path"
require "./workspace"
require "./text_document"
require "./publish_diagnostic"
require "./missing_methods"

module Scry
struct ParseAnalyzer
Expand All @@ -20,7 +21,7 @@ module Scry
parser.parse
[@diagnostic.clean]
rescue ex : Crystal::Exception
@diagnostic.from(ex)
@diagnostic.from(ex.to_json)
end
end
end
2 changes: 1 addition & 1 deletion src/scry/publish_diagnostic.cr
Expand Up @@ -26,7 +26,7 @@ module Scry
end

def from(ex)
build_failures = Array(BuildFailure).from_json(ex.to_json)
build_failures = Array(BuildFailure).from_json(ex)
build_failures
.uniq
.first(@workspace.max_number_of_problems)
Expand Down

0 comments on commit 8653d63

Please sign in to comment.