Skip to content

Commit

Permalink
Add documentation tool
Browse files Browse the repository at this point in the history
Useful for getting the documentation of a method or path, borrowing heavily from how the implementations tool works
  • Loading branch information
nobodywasishere committed Jun 12, 2024
1 parent c141700 commit 331a669
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 0 deletions.
4 changes: 4 additions & 0 deletions src/compiler/crystal/command.cr
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ class Crystal::Command
dependencies show file dependency tree
implementations show implementations for given call in location
unreachable show methods that are never called
documentation show doc comments for a given location
types show type of main variables
--help, -h show this help
USAGE
Expand Down Expand Up @@ -193,6 +194,9 @@ class Crystal::Command
when "implementations".starts_with?(tool)
options.shift
implementations
when "documentation".starts_with?(tool)
options.shift
documentation
when "types".starts_with?(tool)
options.shift
types
Expand Down
6 changes: 6 additions & 0 deletions src/compiler/crystal/command/cursor.cr
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ class Crystal::Command
end
end

private def documentation
cursor_command("tool documentation", no_cleanup: true, wants_doc: true) do |location, config, result|
DocumentationVisitor.new(location).process(result)
end
end

private def cursor_command(command, no_cleanup = false, wants_doc = false, &)
config, result = compile_no_codegen command,
cursor_command: true,
Expand Down
82 changes: 82 additions & 0 deletions src/compiler/crystal/tools/documentation.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
require "../syntax/ast"
require "../compiler"
require "../semantic/*"
require "./typed_def_processor"
require "json"

module Crystal
class DocumentationResult
include JSON::Serializable

property status : String
property message : String

property documentations : Array({String, String})?

def initialize(@status, @message)
end

def to_text(io : IO)
io.puts message
documentations.try do |arr|
arr.each do |doc, loc|
io.puts "#{loc}\n#{doc}\n"
end
end
end
end

class DocumentationVisitor < Visitor
include TypedDefProcessor

getter documentations : Array({String, String})

def initialize(@target_location : Location)
@documentations = [] of {String, String}
end

def process(result : Compiler::Result)
process_result result

result.node.accept(self)

if @documentations.empty?
DocumentationResult.new("failed", "no doc comment or method call found")
else
res = DocumentationResult.new("ok", "#{@documentations.size} doc comment#{@documentations.size > 1 ? "s" : ""} found")
res.documentations = @documentations
res
end
end

def visit(node : Call)
return contains_target(node) unless node.location && @target_location.between?(node.name_location, node.name_end_location)

if target_defs = node.target_defs
target_defs.each do |target_def|
if doc = target_def.doc
@documentations << {doc, target_def.location.not_nil!.to_s}
end
end
end
false
end

def visit(node : Path)
return contains_target(node) unless (loc = node.location) && (end_loc = node.end_location) && @target_location.between?(loc, end_loc)

target = node.target_const || node.target_type
target.try &.locations.try &.each do |loc|
if doc = target.try(&.doc)
@documentations << {doc, loc.to_s}
end
end

false
end

def visit(node)
contains_target(node)
end
end
end

0 comments on commit 331a669

Please sign in to comment.