diff --git a/src/codeaction/implementconstructoraction.vala b/src/codeaction/implementconstructoraction.vala new file mode 100644 index 00000000..1716cc0b --- /dev/null +++ b/src/codeaction/implementconstructoraction.vala @@ -0,0 +1,62 @@ +/* implementconstructoraction.vala + * + * Copyright 2022 JCWasmx86 + * + * This file is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program. If not, see . + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +using Lsp; +using Gee; + +class Vls.ImplementConstructorAction : CodeAction { + public ImplementConstructorAction (Vala.ObjectCreationExpression oce, Vala.Symbol to_be_created) { + var target_file = to_be_created.source_reference.file; + var insertion_line = to_be_created.source_reference.end.line + 1; + var sb = new StringBuilder (); + var indent = to_be_created.source_reference.begin.column == 1 ? "" : "\t"; + sb.append (indent).append (indent).append ("public ").append (oce.member_name.to_string ()).append (" ("); + var args = oce.get_argument_list (); + var idx = 0; + for (var i = 0; i < args.size - 1; i++) { + sb.append (args[i].value_type.to_string ()).append (" arg%d".printf (idx++)).append (", "); + } + if (args.size != 0) + sb.append (args[args.size - 1].value_type.to_string ()).append (" arg%d".printf (idx)); + sb.append (") {}\n "); + try { + var target_uri = Filename.to_uri (target_file.filename); + var target_document = new VersionedTextDocumentIdentifier () { + uri = target_uri, + version = 1 + }; + var workspace_edit = new WorkspaceEdit (); + var document_edit = new TextDocumentEdit (target_document); + var r = new Range.from_sourceref ( + new Vala.SourceReference (target_file, + Vala.SourceLocation (null, insertion_line, 1), + Vala.SourceLocation (null, insertion_line, 1))); + var text_edit = new TextEdit (r); + document_edit.edits.add (text_edit); + workspace_edit.documentChanges = new Gee.ArrayList (); + workspace_edit.documentChanges.add (document_edit); + this.edit = workspace_edit; + this.title = "Add constructor"; + text_edit.newText = sb.str; + } catch (ConvertError ce) { + error ("Should not happen: %s", ce.message); + } + } +} diff --git a/src/codehelp/codeaction.vala b/src/codehelp/codeaction.vala index 4cb47f61..35876712 100644 --- a/src/codehelp/codeaction.vala +++ b/src/codehelp/codeaction.vala @@ -23,13 +23,13 @@ using Vala; namespace Vls.CodeActions { /** - * Extracts a list of code actions for the given document and range. + * Extracts a list of code actions for the given document and range using the AST and the diagnostics. * * @param file the current document * @param range the range to show code actions for * @param uri the document URI */ - Collection extract (Compilation compilation, TextDocument file, Range range, string uri) { + Collection generate_codeactions (Compilation compilation, TextDocument file, Range range, string uri, Reporter reporter) { // search for nodes containing the query range var finder = new NodeSearch (file, range.start, true, range.end); var code_actions = new ArrayList (); @@ -41,13 +41,14 @@ namespace Vls.CodeActions { // add code actions foreach (CodeNode code_node in finder.result) { + critical ("%s", code_node.type_name); if (code_node is IntegerLiteral) { - var lit = (IntegerLiteral)code_node; + var lit = (IntegerLiteral) code_node; var lit_range = new Range.from_sourceref (lit.source_reference); if (lit_range.contains (range.start) && lit_range.contains (range.end)) code_actions.add (new BaseConverterAction (lit, document)); } else if (code_node is Class) { - var csym = (Class)code_node; + var csym = (Class) code_node; var clsdef_range = compute_class_def_range (csym, class_ranges); var cls_range = new Range.from_sourceref (csym.source_reference); if (cls_range.contains (range.start) && cls_range.contains (range.end)) { @@ -57,6 +58,29 @@ namespace Vls.CodeActions { code_actions.add (new ImplementMissingPrereqsAction (csym, missing.first, missing.second, clsdef_range.end, code_style, document)); } } + } else if (code_node is ObjectCreationExpression) { + var oce = (ObjectCreationExpression) code_node; + foreach (var diag in reporter.messages) { + if (file.filename != diag.loc.file.filename) + continue; + if (!(oce.source_reference.contains (diag.loc.begin) || oce.source_reference.contains (diag.loc.end))) + continue; + if (diag.message.contains (" extra arguments for ")) { + var to_be_created = oce.type_reference.symbol; + if (!(to_be_created is Vala.Class)) { + continue; + } + var constr = ((Vala.Class) to_be_created).constructor; + if (constr != null) { + continue; + } + var target_file = to_be_created.source_reference.file; + // We can't just edit, e.g. some external vapi + if (!compilation.get_project_files ().contains (target_file)) + continue; + code_actions.add (new ImplementConstructorAction (oce, to_be_created)); + } + } } } @@ -72,11 +96,11 @@ namespace Vls.CodeActions { // otherwise compute the result and cache it // csym.source_reference must be non-null otherwise NodeSearch wouldn't have found csym var pos = new Position.from_libvala (csym.source_reference.end); - var offset = csym.source_reference.end.pos - (char *)csym.source_reference.file.content; + var offset = csym.source_reference.end.pos - (char*) csym.source_reference.file.content; var dl = 0; var dc = 0; - while (offset < csym.source_reference.file.content.length && csym.source_reference.file.content[(long)offset] != '{') { - if (Util.is_newline (csym.source_reference.file.content[(long)offset])) { + while (offset < csym.source_reference.file.content.length && csym.source_reference.file.content[(long) offset] != '{') { + if (Util.is_newline (csym.source_reference.file.content[(long) offset])) { dl++; dc = 0; } else { @@ -93,8 +117,8 @@ namespace Vls.CodeActions { if (member.source_reference == null) continue; range = range.union (new Range.from_sourceref (member.source_reference)); - if (member is Method && ((Method)member).body != null && ((Method)member).body.source_reference != null) - range = range.union (new Range.from_sourceref (((Method)member).body.source_reference)); + if (member is Method && ((Method) member).body != null && ((Method) member).body.source_reference != null) + range = range.union (new Range.from_sourceref (((Method) member).body.source_reference)); } class_ranges[csym] = range; return range; diff --git a/src/meson.build b/src/meson.build index 60226922..77361d19 100644 --- a/src/meson.build +++ b/src/meson.build @@ -4,6 +4,7 @@ vls_src = files([ 'analysis/codestyleanalyzer.vala', 'analysis/symbolenumerator.vala', 'codeaction/baseconverteraction.vala', + 'codeaction/implementconstructoraction.vala', 'codeaction/implementmissingprereqsaction.vala', 'codehelp/callhierarchy.vala', 'codehelp/codeaction.vala', diff --git a/src/server.vala b/src/server.vala index 2134420f..108bdf96 100644 --- a/src/server.vala +++ b/src/server.vala @@ -1645,7 +1645,7 @@ class Vls.Server : Jsonrpc.Server { var json_array = new Json.Array (); Vala.CodeContext.push (compilation.code_context); - var code_actions = CodeActions.extract (compilation, (TextDocument) source_file, p.range, Uri.unescape_string (p.textDocument.uri)); + var code_actions = CodeActions.generate_codeactions (compilation, (TextDocument) source_file, p.range, Uri.unescape_string (p.textDocument.uri), compilation.reporter); foreach (var action in code_actions) json_array.add_element (Json.gobject_serialize (action)); Vala.CodeContext.pop ();