diff --git a/.ameba.yml b/.ameba.yml index a9dc723..953cdd4 100644 --- a/.ameba.yml +++ b/.ameba.yml @@ -2,49 +2,14 @@ Metrics/CyclomaticComplexity: Enabled: false Style/ParenthesesAroundCondition: - Description: Disallows redundant parentheses around control expressions ExcludeTernary: false AllowSafeAssignment: true - Enabled: true - Severity: Convention - -Documentation/DocumentationAdmonition: - Enabled: false - Severity: Warning - -Lint/NotNil: - Description: Identifies usage of `not_nil!` calls - Excluded: - - src/crystalline/analysis/analysis.cr - - src/crystalline/result_cache.cr - - src/crystalline/workspace.cr - Enabled: true - Severity: Warning Naming/BlockParameterName: Enabled: false - Severity: Convention - -Lint/RedundantStringCoercion: - Description: Disallows redundant string conversions in interpolation - Enabled: true - Severity: Warning - -Lint/ShadowingOuterLocalVar: - Description: - Disallows the usage of the same name as outer local variables for block - or proc arguments - Excluded: - - src/crystalline/ext/compiler.cr - Enabled: true - Severity: Warning Naming/QueryBoolMethods: - Description: Reports boolean properties without the `?` suffix Enabled: false - Severity: Convention Style/WhileTrue: - Description: Disallows while statements with a true literal as condition Enabled: false - Severity: Convention diff --git a/.github/workflows/crystal-ameba.yml b/.github/workflows/crystal-ameba.yml index 2cd87bd..eb92100 100644 --- a/.github/workflows/crystal-ameba.yml +++ b/.github/workflows/crystal-ameba.yml @@ -2,47 +2,18 @@ name: Crystal Ameba on: push: - branches: - - master - paths: - - "**/*.cr" pull_request: - branches: - - master - paths: - - "**/*.cr" + +permissions: + contents: read jobs: lint: runs-on: ubuntu-latest - strategy: - matrix: - ameba-version: [v1.6.4] steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Set up Crystal - uses: crystal-lang/install-crystal@v1 - - - name: Cache Ameba binary - id: cache-ameba - uses: actions/cache@v3 - with: - path: bin/ameba - key: ${{ runner.os }}-ameba-${{ matrix.ameba-version }} - - - name: Build Ameba - if: steps.cache-ameba.outputs.cache-hit != 'true' - run: | - git clone --branch ${{ matrix.ameba-version }} --single-branch https://github.com/crystal-ameba/ameba.git - cd ameba - make bin/ameba CRFLAGS='-Dpreview_mt --no-debug' - mkdir -p ../bin - mv bin/ameba ../bin/ameba - cd .. - rm -rf ameba + - name: Download source + uses: actions/checkout@v6 - name: Run Ameba Linter - run: bin/ameba -c .ameba.yml + uses: crystal-ameba/github-action@master diff --git a/spec/crystalline_spec.cr b/spec/crystalline_spec.cr index 4145c38..1acbd20 100644 --- a/spec/crystalline_spec.cr +++ b/spec/crystalline_spec.cr @@ -1,5 +1,5 @@ require "spec" it "works" do - true.should eq(true) + true.should be_true end diff --git a/src/crystalline/analysis/analysis.cr b/src/crystalline/analysis/analysis.cr index c49f8df..b095136 100644 --- a/src/crystalline/analysis/analysis.cr +++ b/src/crystalline/analysis/analysis.cr @@ -25,7 +25,7 @@ module Crystalline::Analysis Crystal::Compiler::Source.new(file_uri.decoded_path, file.gets_to_end), ] file.close - self.compile(server, sources, lib_path: lib_path, file_overrides: file_overrides, ignore_diagnostics: ignore_diagnostics, wants_doc: wants_doc, top_level: top_level, compiler_flags: compiler_flags) + compile(server, sources, lib_path: lib_path, file_overrides: file_overrides, ignore_diagnostics: ignore_diagnostics, wants_doc: wants_doc, top_level: top_level, compiler_flags: compiler_flags) end end @@ -119,18 +119,18 @@ module Crystalline::Analysis # Return the possible definition for the node at the given *location*. def self.definitions_at_cursor(result : Crystal::Compiler::Result, location : Crystal::Location) : Definitions? nodes, context = CursorVisitor.new(location).process(result) - nodes.last?.try { |node| + nodes.last?.try do |node| LSP::Log.debug { "Class of node at cursor: #{node.class} " } locations = begin if node.is_a? Crystal::Call if (defs = node.target_defs) - defs.compact_map { |d| - start_location = d.location.try { |loc| loc.expanded_location || loc }.not_nil! - end_location = d.end_location.try { |loc| loc.expanded_location || loc }.not_nil! + defs.compact_map do |d| + start_location = d.location.try { |loc| loc.expanded_location || loc }.not_nil! # ameba:disable Lint/NotNil! + end_location = d.end_location.try { |loc| loc.expanded_location || loc }.not_nil! # ameba:disable Lint/NotNil! {start_location, end_location} - } + end elsif (expanded_macro = node.expanded_macro) - start_location = expanded_macro.location.try { |loc| loc.expanded_location || loc }.not_nil! + start_location = expanded_macro.location.try { |loc| loc.expanded_location || loc }.not_nil! # ameba:disable Lint/NotNil! end_location = expanded_macro.end_location.try { |loc| loc.expanded_location || loc } || start_location [{start_location, end_location}] end @@ -139,14 +139,14 @@ module Crystalline::Analysis filename = node.string relative_to = location.try &.original_filename filenames = result.program.find_in_path(filename, relative_to) - filenames.try &.map { |path| + filenames.try &.map do |path| location = Crystal::Location.new( path, line_number: 1, column_number: 1 ) {location, location} - } + end elsif node.is_a? Crystal::Path Utils.locations_from_path(node, nodes) elsif node.is_a? Crystal::Union @@ -172,15 +172,15 @@ module Crystalline::Analysis end Definitions.new(node: node, locations: locations) - } + end end def self.all_defs(type, *, accumulator = [] of {String, Crystal::Def, Crystal::Type, Int32}, nesting = 0) if type.is_a? Crystal::UnionType # TODO: intersection instead of union - type.union_types.each { |t| + type.union_types.each do |t| all_defs(t, accumulator: accumulator, nesting: nesting) - } + end return accumulator.uniq &.[1] end @@ -200,7 +200,7 @@ module Crystalline::Analysis if type.responds_to? :instance_type extends_self = type.instance_type == parent end - self.all_defs(parent, accumulator: accumulator, nesting: extends_self ? nesting : nesting + 1) + all_defs(parent, accumulator: accumulator, nesting: extends_self ? nesting : nesting + 1) end accumulator @@ -208,9 +208,9 @@ module Crystalline::Analysis def self.all_macros(type, *, accumulator = [] of {String, Crystal::Macro, Crystal::Type, Int32}, nesting = 0) if type.is_a? Crystal::UnionType - type.union_types.each { |t| + type.union_types.each do |t| all_macros(t, accumulator: accumulator, nesting: nesting) - } + end return accumulator.uniq &.[0] end @@ -221,7 +221,7 @@ module Crystalline::Analysis end type.parents.try &.each do |parent| - self.all_macros(parent, accumulator: accumulator, nesting: nesting + 1) + all_macros(parent, accumulator: accumulator, nesting: nesting + 1) end accumulator diff --git a/src/crystalline/analysis/cursor_visitor.cr b/src/crystalline/analysis/cursor_visitor.cr index e828fbf..f4fee54 100644 --- a/src/crystalline/analysis/cursor_visitor.cr +++ b/src/crystalline/analysis/cursor_visitor.cr @@ -33,9 +33,9 @@ module Crystalline::Analysis private def nearest_end_location(node) return node.end_location if node.end_location - @nodes.reverse.find { |elt| + @nodes.reverse.find do |elt| elt.responds_to?(:end_location) && elt.end_location - }.try &.end_location + end.try &.end_location end def visit_any(node : Crystal::Def | Crystal::Assign | Crystal::Block) diff --git a/src/crystalline/analysis/submodule_visitor.cr b/src/crystalline/analysis/submodule_visitor.cr index 9ddf926..c78c88d 100644 --- a/src/crystalline/analysis/submodule_visitor.cr +++ b/src/crystalline/analysis/submodule_visitor.cr @@ -9,9 +9,9 @@ module Crystalline::Analysis def process_result(result : Crystal::Compiler::Result) result.node.accept(self) - result.program.file_modules.each_value { |file_module| + result.program.file_modules.each_value do |file_module| process(file_module) - } + end @submodules end diff --git a/src/crystalline/broken_source_fixer.cr b/src/crystalline/broken_source_fixer.cr index 2c2cbb3..648b52b 100644 --- a/src/crystalline/broken_source_fixer.cr +++ b/src/crystalline/broken_source_fixer.cr @@ -120,8 +120,6 @@ class Crystalline::BrokenSourceFixer "rescue" elsif line.matches?(/\s*ensure\s*$/) "ensure" - else - nil end end diff --git a/src/crystalline/controller.cr b/src/crystalline/controller.cr index 7ff74db..0c2356f 100644 --- a/src/crystalline/controller.cr +++ b/src/crystalline/controller.cr @@ -34,8 +34,8 @@ class Crystalline::Controller @pending_requests << message.id case message when LSP::DocumentFormattingRequest - @documents_lock.synchronize { - workspace.format_document(message.params).try { |(formatted_document, document)| + @documents_lock.synchronize do + workspace.format_document(message.params).try do |(formatted_document, document)| range = LSP::Range.new( start: LSP::Position.new(line: 0, character: 0), end: LSP::Position.new(line: document.lines_nb + 1, character: 0), @@ -46,34 +46,34 @@ class Crystalline::Controller new_text: formatted_document, ), ] - } - } + end + end when LSP::DocumentRangeFormattingRequest - @documents_lock.synchronize { - workspace.format_document(message.params).try { |(formatted_document, document)| + @documents_lock.synchronize do + workspace.format_document(message.params).try do |(formatted_document, document)| [ LSP::TextEdit.new( range: message.params.range, new_text: formatted_document, ), ] - } - } + end + end when LSP::HoverRequest @compiler_lock.synchronize do - return nil unless @pending_requests.includes? message.id + return unless @pending_requests.includes? message.id file_uri = URI.parse message.params.text_document.uri workspace.hover(@server, file_uri, message.params.position) end when LSP::DefinitionRequest @compiler_lock.synchronize do - return nil unless @pending_requests.includes? message.id + return unless @pending_requests.includes? message.id file_uri = URI.parse message.params.text_document.uri workspace.definitions(@server, file_uri, message.params.position) end when LSP::CompletionRequest @compiler_lock.synchronize do - return nil unless @pending_requests.includes? message.id + return unless @pending_requests.includes? message.id file_uri = URI.parse message.params.text_document.uri workspace.completion(@server, file_uri, message.params.position, message.params.context.try &.trigger_character) end @@ -85,13 +85,11 @@ class Crystalline::Controller if @server.client_capabilities.text_document.try &.document_symbol.try &.hierarchical_document_symbol_support document_symbols else - document_symbols.try &.reduce([] of LSP::SymbolInformation) { |acc, document_symbol| + document_symbols.try &.reduce([] of LSP::SymbolInformation) do |acc, document_symbol| acc.concat(document_symbol.to_symbol_information_array(message.params.text_document.uri)) - } + end end end - else - nil end rescue e : Crystal::TypeException LSP::Log.warn(exception: e) { e.to_s } @@ -106,29 +104,29 @@ class Crystalline::Controller def on_notification(message : LSP::NotificationMessage) : Nil case message when LSP::DidOpenNotification - @documents_lock.synchronize { + @documents_lock.synchronize do workspace.open_document(message.params) - } + end when LSP::DidChangeNotification - @documents_lock.synchronize { + @documents_lock.synchronize do workspace.update_document(@server, message.params) - } + end when LSP::DidCloseNotification - @documents_lock.synchronize { + @documents_lock.synchronize do workspace.close_document(@server, message.params) - } + end when LSP::DidSaveNotification - @documents_lock.synchronize { + @documents_lock.synchronize do workspace.save_document(@server, message.params) - } - @compiler_lock.synchronize { + end + @compiler_lock.synchronize do file_uri = message.params.text_document.uri workspace.compile( @server, URI.parse(file_uri), discard_nil_cached_result: true, ) - } + end when LSP::CancelNotification @pending_requests.delete message.params.id end diff --git a/src/crystalline/diagnostics.cr b/src/crystalline/diagnostics.cr index 05f33a3..e761e9c 100644 --- a/src/crystalline/diagnostics.cr +++ b/src/crystalline/diagnostics.cr @@ -8,7 +8,7 @@ class Crystalline::Diagnostics def append(diagnostic : LSP::Diagnostic) key = "file://#{diagnostic.source}" - self.init_value(key) + init_value(key) @diagnostics[key] << diagnostic end @@ -33,7 +33,7 @@ class Crystalline::Diagnostics related_information = [] of LSP::DiagnosticRelatedInformation - error_stack.each_with_index { |err, i| + error_stack.each_with_index do |err, i| bottom_error = i == error_stack.size - 1 if err.filename.is_a? Crystal::VirtualFile && (expanded_source = err.filename.as(Crystal::VirtualFile).expanded_location) line = expanded_source.line_number || 1 @@ -44,7 +44,7 @@ class Crystalline::Diagnostics end if bottom_error - self.append(LSP::Diagnostic.new( + append(LSP::Diagnostic.new( line: line, column: column, size: err.size || 0, @@ -61,14 +61,14 @@ class Crystalline::Diagnostics filename: err.true_filename, ) end - } + end end def publish(server : LSP::Server) - @diagnostics.each { |key, value| + @diagnostics.each do |key, value| server.try &.send(LSP::PublishDiagnosticsNotification.new( params: LSP::PublishDiagnosticsParams.new(uri: key, diagnostics: value), )) - } + end end end diff --git a/src/crystalline/ext/compiler.cr b/src/crystalline/ext/compiler.cr index b4e84fb..cf34c0e 100644 --- a/src/crystalline/ext/compiler.cr +++ b/src/crystalline/ext/compiler.cr @@ -28,13 +28,13 @@ module Crystal end location = node.location - filename = node.string + node_filename = node.string relative_to = location.try &.original_filename # Remember that the program depends on this require - @program.record_require(filename, relative_to) + @program.record_require(node_filename, relative_to) - filenames = @program.find_in_path(filename, relative_to) + filenames = @program.find_in_path(node_filename, relative_to) if filenames nodes = Array(ASTNode).new(filenames.size) filenames.each do |filename| @@ -71,10 +71,10 @@ module Crystal end else notes << <<-NOTE - If you're trying to require a shard: - - Did you remember to run `shards install`? - - Did you make sure you're running the compiler in the same directory as your shard.yml? - NOTE + If you're trying to require a shard: + - Did you remember to run `shards install`? + - Did you make sure you're running the compiler in the same directory as your shard.yml? + NOTE end node.raise "#{message}\n\n#{notes.join("\n")}" @@ -121,7 +121,7 @@ module Crystal # give an error otherwise processor.check_non_nilable_class_vars_without_initializers - result = @progress_tracker.stage("Semantic (main)") do + @progress_tracker.stage("Semantic (main)") do visit_main(node, process_finished_hooks: true, cleanup: cleanup) end @@ -134,7 +134,7 @@ module Crystal RecursiveStructChecker.new(self).run end - {result, self.error_stack.to_a} + node rescue e : Crystal::CodeError program.error_stack << e # Returns a partially typed ast. @@ -143,8 +143,6 @@ module Crystal # Returns a partially typed ast. node end - - node end end @@ -193,9 +191,9 @@ module Crystal getter program : Crystal::Program def visit(node : MacroExpression) - previous_def.tap { + previous_def.tap do node.expanded = @last - } + end end end @@ -203,7 +201,7 @@ module Crystal # Adds functionality to get the CRYSTAL_PATH value, but without the default # library directory. def self.default_path_without_lib - parts = self.default_path.split(Process::PATH_DELIMITER) + parts = default_path.split(Process::PATH_DELIMITER) parts.select(&.!=(DEFAULT_LIB_PATH)).join(Process::PATH_DELIMITER) end end diff --git a/src/crystalline/progress.cr b/src/crystalline/progress.cr index 847893b..058c8c9 100644 --- a/src/crystalline/progress.cr +++ b/src/crystalline/progress.cr @@ -15,15 +15,15 @@ class Crystalline::Progress ), ) - create_request.on_response { + create_request.on_response do if async - spawn { + spawn do report_callback(server, &cb) - } + end else report_callback(server, &cb) end - } + end server.send(create_request) end diff --git a/src/crystalline/result_cache.cr b/src/crystalline/result_cache.cr index 4907fd3..a5f6814 100644 --- a/src/crystalline/result_cache.cr +++ b/src/crystalline/result_cache.cr @@ -20,7 +20,7 @@ class Crystalline::ResultCache return false unless exists?(entry) invalidation_time = @cache[entry][1] if since - !invalidation_time || invalidation_time.not_nil! > since + !invalidation_time || invalidation_time > since else !invalidation_time.nil? end diff --git a/src/crystalline/text_document.cr b/src/crystalline/text_document.cr index 411a50e..4db22d0 100644 --- a/src/crystalline/text_document.cr +++ b/src/crystalline/text_document.cr @@ -26,9 +26,9 @@ class Crystalline::TextDocument end def update_contents(content_changes : Array({String, LSP::Range?}), version : Number? = nil) - content_changes.each { |change| + content_changes.each do |change| update_contents(*change, version: version) - } + end # Check for pending changes loop do @@ -65,9 +65,9 @@ class Crystalline::TextDocument private def partial_update(contents : String, range : LSP::Range, version : Number? = nil) prefix = @inner_contents[range.start.line]?.try &.[...range.start.character].chomp || "" suffix = @inner_contents[range.end.line]?.try &.[range.end.character..]? || @inner_contents[range.end.line]? || "" - replacement_lines = String.build { |str| + replacement_lines = String.build do |str| str << prefix << contents << suffix - }.lines(chomp: false) + end.lines(chomp: false) @inner_contents = (@inner_contents[...range.start.line]? || [] of String) + replacement_lines + (@inner_contents[range.end.line + 1...]? || [] of String) @version = version if version end diff --git a/src/crystalline/utils.cr b/src/crystalline/utils.cr index 2c3ff99..561f463 100644 --- a/src/crystalline/utils.cr +++ b/src/crystalline/utils.cr @@ -19,7 +19,7 @@ module Crystalline::Utils end def self.locations_from_path(path : Crystal::Path, nodes : Array(Crystal::ASTNode)) : Array({Crystal::Location, Crystal::Location})? - target = self.resolve_path(path, nodes) + target = resolve_path(path, nodes) target.as?(Crystal::Const | Crystal::Type).try &.locations.try &.map do |location| end_location = Crystal::Location.new( location.filename, @@ -31,18 +31,18 @@ module Crystalline::Utils end def self.locations_from_union(union : Crystal::Union, nodes : Array(Crystal::ASTNode), *, locations = [] of {Crystal::Location, Crystal::Location}) : Array({Crystal::Location, Crystal::Location}) - union.types.each { |type| + union.types.each do |type| if type.is_a? Crystal::Path - locations_from_path(type, nodes).try { |locs| + locations_from_path(type, nodes).try do |locs| locations.concat locs - } + end elsif type.is_a? Crystal::Union - self.locations_from_union(type, nodes, locations: locations) + locations_from_union(type, nodes, locations: locations) elsif (location = type.location) end_location = type.end_location || location locations << {location, end_location} end - } + end locations end @@ -83,7 +83,7 @@ module Crystalline::Utils # Format a method definition or macro. def self.format_def(d : Crystal::Def | Crystal::Macro, *, short = false) - String.build { |str| + String.build do |str| unless short str << d.visibility.to_s.downcase str << ' ' @@ -122,7 +122,7 @@ module Crystalline::Utils str << " forall " free_vars.join(str, ", ") end - } + end rescue e # LSP::Log.error(exception: e) { e.to_s } d.to_s diff --git a/src/crystalline/workspace.cr b/src/crystalline/workspace.cr index c02f195..53f6399 100644 --- a/src/crystalline/workspace.cr +++ b/src/crystalline/workspace.cr @@ -18,14 +18,15 @@ class Crystalline::Workspace def initialize(server : LSP::Server, root_uri : String?) if (@root_uri = root_uri.try &->URI.parse(String)) + # ameba:disable Lint/NotNil! @projects = Project.find_in_workspace_root @root_uri.not_nil! if @projects.size > 0 - LSP::Log.info { + LSP::Log.info do <<-LOG - "[workspace] Found projects: - #{@projects.map(&.root_uri.decoded_path).join('\n')} - LOG - } + [workspace] Found projects: + #{@projects.map(&.root_uri.decoded_path).join('\n')} + LOG + end end end end @@ -40,16 +41,16 @@ class Crystalline::Workspace def update_document(server : LSP::Server, params : LSP::DidChangeTextDocumentParams) file_uri = params.text_document.uri - @opened_documents[file_uri]?.try { |document| - content_changes = params.content_changes.map { |change| + @opened_documents[file_uri]?.try do |document| + content_changes = params.content_changes.map do |change| {change.text, change.range} - } + end document.update_contents(content_changes, version: params.text_document.version) - document.project?.try(&.entry_point?).try { |entry| + document.project?.try(&.entry_point?).try do |entry| @result_cache.invalidate(entry.to_s) - } - } + end + end @result_cache.invalidate(file_uri) # spawn self.compile(server, URI.parse(file_uri), in_memory: true ) end @@ -67,21 +68,21 @@ class Crystalline::Workspace end def format_document(params : LSP::DocumentFormattingParams) : {String, TextDocument}? - @opened_documents[params.text_document.uri]?.try { |document| + @opened_documents[params.text_document.uri]?.try do |document| {Crystal.format(document.contents), document} - } + end rescue e # swallow exceptions silently end def format_document(params : LSP::DocumentRangeFormattingParams) : {String, TextDocument}? - @opened_documents[params.text_document.uri]?.try { |document| + @opened_documents[params.text_document.uri]?.try do |document| range = params.range contents_lines = document.contents.lines(chomp: false)[range.start.line..range.end.line] contents_lines[-1] = contents_lines.last[...range.end.character] if range.end.character > 0 contents_lines[0] = contents_lines.first[range.start.character...] {Crystal.format(contents_lines.join), document} - } + end rescue e # swallow exceptions silently end @@ -91,9 +92,9 @@ class Crystalline::Workspace return unless (target = project.entry_point?) lib_path = project.default_lib_path - Analysis.compile(server, target, lib_path: lib_path, ignore_diagnostics: true, wants_doc: false, top_level: true, compiler_flags: project.flags).try { |result| + Analysis.compile(server, target, lib_path: lib_path, ignore_diagnostics: true, wants_doc: false, top_level: true, compiler_flags: project.flags).try do |result| project.dependencies = result.program.requires - } + end rescue nil end @@ -136,6 +137,7 @@ class Crystalline::Workspace ) else # The file is not a project dependency. + # ameba:disable Lint/NotNil! target = file_uri.not_nil! progress = Progress.new( token: "workspace/compile", @@ -169,7 +171,7 @@ class Crystalline::Workspace if in_memory # Tell the compiler to load the opened files from memory, not from the filesystem. file_overrides = Hash(String, String).new - @opened_documents.each { |uri_str, text_document| + @opened_documents.each do |uri_str, text_document| contents = text_overrides.try(&.[uri_str]?) || text_document.contents contents = fix_source(contents) @@ -181,7 +183,7 @@ class Crystalline::Workspace end file_path = URI.parse(uri_str).decoded_path file_overrides[file_path] = contents - } + end end lib_path = project.try(&.default_lib_path) @@ -196,6 +198,7 @@ class Crystalline::Workspace if result if project.try(&.entry_point?) # Store the project dependencies. + # ameba:disable Lint/NotNil! project.not_nil!.dependencies = result.program.requires end "Completed successfully." @@ -221,33 +224,33 @@ class Crystalline::Workspace if doc contents << "----------" contents << <<-MARKDOWN - #{doc} - MARKDOWN + #{doc} + MARKDOWN end end private def code_markdown(str : String?, *, language = "") : String if str <<-MARKDOWN - ```#{language} - #{str} - ``` - MARKDOWN + ```#{language} + #{str} + ``` + MARKDOWN else "" end end def hover(server : LSP::Server, file_uri : URI, position : LSP::Position) - result = self.compile(server, file_uri, in_memory: true, wants_doc: true) + result = compile(server, file_uri, in_memory: true, wants_doc: true) location = Crystal::Location.new( file_uri.decoded_path, line_number: position.line + 1, column_number: position.character + 1 ) - result.try { |r| + result.try do |r| Analysis.nodes_at_cursor(r, location) - }.try do |nodes, _context| + end.try do |nodes, _context| n = nodes.last? contents = [] of String @@ -311,19 +314,20 @@ class Crystalline::Workspace end def definitions(server : LSP::Server, file_uri : URI, position : LSP::Position) - result = self.compile(server, file_uri, in_memory: true, wants_doc: true) + result = compile(server, file_uri, in_memory: true, wants_doc: true) location = Crystal::Location.new( file_uri.decoded_path, line_number: position.line + 1, column_number: position.character + 1 ) - result.try { |r| + result.try do |r| Analysis.definitions_at_cursor(r, location) - }.try do |definitions| + end.try do |definitions| node = definitions.node - definitions.locations.try &.map { |start_loc, end_loc| + definitions.locations.try &.map do |start_loc, end_loc| if node.is_a? Crystal::Path || node.is_a? Crystal::Require target_uri = "file://#{start_loc.original_filename}" + # ameba:disable Lint/NotNil! origin_location = node.location.not_nil! origin_end_location = definitions.node.end_location || Crystal::Location.new( file_uri.decoded_path, @@ -355,7 +359,7 @@ class Crystalline::Workspace ), ) end - } + end end rescue nil @@ -379,25 +383,25 @@ class Crystalline::Workspace left_offset = position.character - prefix.size else # We need to determine which character (and by extension - autocompletion kind) is best suited depending on the location and its surroundings. - document_lines[position.line][0...position.character].each_char_with_index { |char, index| + document_lines[position.line][0...position.character].each_char_with_index do |char, index| unless char.ascii_alphanumeric? || char == '_' || char == '?' || char == '!' trigger_character = char.to_s left_offset = position.character - index end - } + end prefix = document_lines[position.line][0...(position.character - left_offset)] end suffix = document_lines[position.line][(position.character)..]? # Remove the rest of the line (or part of it) to please the parser. - suffix.try &.each_char_with_index { |char, index| + suffix.try &.each_char_with_index do |char, index| unless char.ascii_alphanumeric? || char == '_' || char == '?' || char == '!' || char == ':' truncate_line = true if char == '(' || char == '{' || char == '[' right_offset = index break end - } + end suffix = suffix.try &.[right_offset...]? # LSP::Log.info { "prefix(left offset #{left_offset}): #{prefix}"} @@ -417,7 +421,7 @@ class Crystalline::Workspace ) # Trigger a compilation that will not fail fast. - result = self.compile( + result = compile( server, file_uri, in_memory: true, @@ -452,7 +456,8 @@ class Crystalline::Workspace # We are looking for methods… if node_type.responds_to? :defs - Analysis.all_defs(node_type.not_nil!).each { |def_name, definition, owner_type, nesting| + # ameba:disable Lint/NotNil! + Analysis.all_defs(node_type.not_nil!).each do |def_name, definition, owner_type, nesting| owner_prefix = "*Inherited from: #{owner_type.name}*\n\n" if owner_type.responds_to? :name && owner_type != n.type owner_prefix ||= "" documentation = (owner_prefix + (definition.doc || "")) @@ -470,16 +475,16 @@ class Crystalline::Workspace detail: Utils.format_def(definition), text_edit: text_edit, sort_text: (nesting + 1).chr.to_s + def_name, - documentation: documentation.try { |doc| + documentation: documentation.try do |doc| LSP::MarkupContent.new( kind: LSP::MarkupKind::MarkDown, value: doc, ) - }, + end, ) - } + end - Analysis.all_macros(n.type).each { |macro_name, macro_def, owner_type, nesting| + Analysis.all_macros(n.type).each do |macro_name, macro_def, owner_type, nesting| owner_prefix = "*Inherited from: #{owner_type.name}*\n\n" if owner_type.responds_to? :name && owner_type != n.type owner_prefix ||= "" documentation = (owner_prefix + (macro_def.doc || "")) @@ -497,14 +502,14 @@ class Crystalline::Workspace detail: Utils.format_def(macro_def), text_edit: text_edit, sort_text: (nesting + 1).chr.to_s + macro_name, - documentation: documentation.try { |doc| + documentation: documentation.try do |doc| LSP::MarkupContent.new( kind: LSP::MarkupKind::MarkDown, value: doc, ) - }, + end, ) - } + end end when ":" # We are looking for module types… @@ -517,7 +522,7 @@ class Crystalline::Workspace if node_type.is_a? Crystal::MetaclassType node_type = node_type.instance_type - Analysis.all_submodules(result, node_type).uniq(&.to_s).each { |type| + Analysis.all_submodules(result, node_type).uniq(&.to_s).each do |type| type_string = type.to_s text_edit = LSP::TextEdit.new( @@ -529,14 +534,14 @@ class Crystalline::Workspace label: type_string, text_edit: text_edit, kind: Crystalline::Utils.map_completion_kind(type, default: LSP::CompletionItemKind::Module), - documentation: type.doc.try { |doc| + documentation: type.doc.try do |doc| LSP::MarkupContent.new( kind: LSP::MarkupKind::MarkDown, value: doc, ) - }, + end, ) - } + end end else # Context autocompletion. @@ -544,7 +549,7 @@ class Crystalline::Workspace if trigger_character == "@" context.try &.select!(&.starts_with?("@")) end - context.try &.each { |name, type| + context.try &.each do |name, type| label = "#{name} : #{type}" text_edit = LSP::TextEdit.new( range: range, @@ -554,14 +559,14 @@ class Crystalline::Workspace label: label, text_edit: text_edit, kind: LSP::CompletionItemKind::Variable, - documentation: type.doc.try { |doc| + documentation: type.doc.try do |doc| LSP::MarkupContent.new( kind: LSP::MarkupKind::MarkDown, value: doc, ) - }, + end, ) - } + end end selected_element_index = nil @@ -590,15 +595,15 @@ class Crystalline::Workspace end def document_symbols(server : LSP::Server, file_uri : URI) - @opened_documents[file_uri.to_s]?.try { |text_document| + @opened_documents[file_uri.to_s]?.try do |text_document| parser = Crystal::Parser.new(fix_source(text_document.contents)) parser.filename = file_uri.decoded_path parser.wants_doc = false - Analysis::DocumentSymbolsVisitor.new.tap { |visitor| + Analysis::DocumentSymbolsVisitor.new.tap do |visitor| parser.parse.accept(visitor) - }.symbols - } + end.symbols + end end private def fix_source(source : String) : String