Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft: Add actions related to exception handling #254

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions src/codeaction/addthrowsdeclaration.vala
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/* addthrowsdeclaration.vala
*
* Copyright 2022 JCWasmx86 <[email protected]>
*
* 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 3 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 <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: LGPL-3.0-or-later
*/

using Gee;
using Lsp;

class Vls.AddThrowsDeclaration : CodeAction {
public AddThrowsDeclaration (VersionedTextDocumentIdentifier document, string error_name, Vala.CodeNode where) {

var error_types = new Vala.HashSet<Vala.DataType> ();
if (where is Vala.Constructor)
((Vala.Constructor)where).body.get_error_types (error_types, null);
else
where.get_error_types (error_types, null);

var sb = new StringBuilder ();
if (error_types.is_empty) {
sb.append (" throws ");
} else {
sb.append (", ");
}
sb.append (error_name);
sb.append (" ");
var sref = where.source_reference;
var range = new Range ();
for (var i = sref.begin.line; i <= sref.end.line; i++) {
var line = sref.file.get_source_line (i);
if (line.contains ("{")) {
var idx = line.index_of ("{");
range.start = new Lsp.Position () {
line = i - 1,
character = idx - 1,
};
range.end = new Lsp.Position () {
line = i - 1,
character = idx - 1,
};
}
}
var workspace_edit = new WorkspaceEdit ();
var document_edit = new TextDocumentEdit (document);
var text_edit = new TextEdit (range);
text_edit.range.end.character++;
text_edit.newText = sb.str;
document_edit.edits.add (text_edit);
workspace_edit.documentChanges = new ArrayList<TextDocumentEdit> ();
workspace_edit.documentChanges.add (document_edit);
this.edit = workspace_edit;
this.title = "Add to error list";
}
}
68 changes: 68 additions & 0 deletions src/codeaction/addtrycatchassignmentaction.vala
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/* AddTryCatchAssignmentAction.vala
*
* Copyright 2022 JCWasmx86 <[email protected]>
*
* 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 3 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 <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: LGPL-3.0-or-later
*/

using Gee;
using Lsp;

class Vls.AddTryCatchAssignmentAction : CodeAction {
public AddTryCatchAssignmentAction (VersionedTextDocumentIdentifier document, Vala.ArrayList<Vala.Variable> variables, string error_name, string indent, Vala.CodeNode rhs) {
var sref = rhs.source_reference;
var assignment_line = sref.file.get_source_line (sref.begin.line);
var copied_indent = assignment_line.substring (0, assignment_line.length - assignment_line.chug ().length);
var sb = new StringBuilder ();
foreach (var v in variables) {
sb.append (copied_indent).append (v.variable_type.to_string ()).append (" ").append (v.name).append (";\n");
}
sb.append (copied_indent).append ("try {\n");
sb.append (copied_indent).append (indent).append (variables[0].name).append (" = ");
var s1 = variables[0].initializer.source_reference;
for (var i = s1.begin.line; i <= s1.end.line; i++) {
var len = -1;
var offset = 0;
if (i == s1.begin.line && i != s1.end.line) {
offset = s1.begin.column - 1;
} else if (i == s1.end.line && i != s1.begin.line) {
len = s1.end.column;
} else if (i == s1.begin.line && i == s1.end.line) {
offset = s1.begin.column - 1;
len = s1.end.column - s1.begin.column + 1;
}
if (i != s1.begin.line)
sb.append (copied_indent).append (indent);
sb.append (s1.file.get_source_line (i).substring (offset, len).strip ());
sb.append (i == s1.end.line ? ";" : "").append ("\n");
}
// TODO: Deduplicate error name
sb.append (copied_indent).append ("} catch (").append (error_name).append (" e) {\n");
sb.append (copied_indent).append (indent).append ("error (\"Caught error ").append (error_name).append (": %s\", e.message);\n");
sb.append (copied_indent).append ("}\n");
var workspace_edit = new WorkspaceEdit ();
var document_edit = new TextDocumentEdit (document);
var text_edit = new TextEdit (new Range.from_sourceref (sref));
text_edit.range.start.character = 0;
text_edit.range.end.character++;
text_edit.newText = sb.str;
document_edit.edits.add (text_edit);
workspace_edit.documentChanges = new ArrayList<TextDocumentEdit> ();
workspace_edit.documentChanges.add (document_edit);
this.edit = workspace_edit;
this.title = "Wrap with try-catch";
}
}
61 changes: 61 additions & 0 deletions src/codeaction/addtrycatchstatementaction.vala
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/* addtrycatchstatementaction.vala
*
* Copyright 2022 JCWasmx86 <[email protected]>
*
* 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 3 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 <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: LGPL-3.0-or-later
*/

using Gee;
using Lsp;

class Vls.AddTryCatchStatementAction : CodeAction {
public AddTryCatchStatementAction (VersionedTextDocumentIdentifier document, string error_name, string indent, Vala.CodeNode node) {
var sref = node.source_reference;
var sb = new StringBuilder ();
var line = sref.file.get_source_line (sref.begin.line);
var copied_indent = line.substring (0, line.length - line.chug ().length);
sb.append (copied_indent).append ("try {\n");
for (var i = sref.begin.line; i <= sref.end.line; i++) {
var len = -1;
var offset = 0;
if (i == sref.begin.line && i != sref.end.line) {
offset = sref.begin.column - 1;
} else if (i == sref.end.line && i != sref.begin.line) {
len = sref.end.column;
} else if (i == sref.begin.line && i == sref.end.line) {
offset = sref.begin.column - 1;
len = sref.end.column - sref.begin.column + 1;
}
sb.append (copied_indent).append (indent);
sb.append (sref.file.get_source_line (i).substring (offset, len).strip ());
sb.append (i == sref.end.line ? ";" : "").append ("\n");
}
sb.append (copied_indent).append ("} catch (").append (error_name).append (" e) {\n");
sb.append (copied_indent).append (indent).append ("error (\"Caught error ").append (error_name).append (": %s\", e.message);\n");
sb.append (copied_indent).append ("}\n");
var workspace_edit = new WorkspaceEdit ();
var document_edit = new TextDocumentEdit (document);
var text_edit = new TextEdit (new Range.from_sourceref (sref));
text_edit.range.start.character = 0;
text_edit.range.end.character++;
text_edit.newText = sb.str;
document_edit.edits.add (text_edit);
workspace_edit.documentChanges = new ArrayList<TextDocumentEdit> ();
workspace_edit.documentChanges.add (document_edit);
this.edit = workspace_edit;
this.title = "Wrap with try-catch";
}
}
53 changes: 52 additions & 1 deletion src/codehelp/codeaction.vala
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,9 @@ namespace Vls.CodeActions {
* @param file the current document
* @param range the range to show code actions for
* @param uri the document URI
* @param diags the diagnostics to use
*/
Collection<CodeAction> extract (Compilation compilation, TextDocument file, Range range, string uri) {
Collection<CodeAction> extract (Compilation compilation, TextDocument file, Range range, string uri, Gee.List<Diagnostic> diags) {
// search for nodes containing the query range
var finder = new NodeSearch (file, range.start, true, range.end);
var code_actions = new ArrayList<CodeAction> ();
Expand Down Expand Up @@ -60,6 +61,56 @@ namespace Vls.CodeActions {
}
}

finder = new NodeSearch.with_filter (file, null, (a, b) => true, true);
var code_style = compilation.get_analysis_for_file<CodeStyleAnalyzer> (file);
foreach (var d in diags) {
if (d.message.has_prefix ("unhandled error ")) {
foreach (var node in finder.result) {
var tmp_range = new Range.from_sourceref (node.source_reference);
if ((tmp_range.contains (d.range.start) && tmp_range.contains (d.range.end))
&& !(node is Vala.Block)) {
var error_name = d.message.replace ("unhandled error `", "").replace ("'", "").strip ();
if (node is DeclarationStatement) {
var variables = new Vala.ArrayList<Variable> ();
((DeclarationStatement)node).get_defined_variables (variables);
if (variables.size == 1)
code_actions.add (new AddTryCatchAssignmentAction (document, variables, error_name, code_style.indentation, node));
var parent = node.parent_node;
while (parent != null) {
if (parent is Vala.ForeachStatement || parent is Vala.LambdaExpression) {
parent = null;
break;
}
if (parent is Vala.Method || parent is Vala.Constructor)
break;
parent = parent.parent_node;
}
if (parent != null) {
code_actions.add (new AddThrowsDeclaration (document, error_name, parent));
}
} else if (node is ExpressionStatement && !(node.parent_node is Vala.ForeachStatement)) {
var es = (ExpressionStatement) node;
code_actions.add (new AddTryCatchStatementAction (document, error_name, code_style.indentation, es.expression));
} else {
var parent = node.parent_node;
while (parent != null) {
if (parent is Vala.ForeachStatement || parent is Vala.LambdaExpression) {
parent = null;
break;
}
if (parent is Vala.Method || parent is Vala.Constructor)
break;
parent = parent.parent_node;
}
if (parent != null) {
code_actions.add (new AddThrowsDeclaration (document, error_name, parent));
}
}
}
}
}
}

return code_actions;
}

Expand Down
4 changes: 2 additions & 2 deletions src/codehelp/nodesearch.vala
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class Vls.NodeSearch : Vala.CodeVisitor {
private Gee.HashSet<Vala.CodeNode> seen = new Gee.HashSet<Vala.CodeNode> ();

[CCode (has_target = false)]
public delegate bool Filter (Vala.CodeNode needle, Vala.CodeNode hay_node);
public delegate bool Filter (Vala.CodeNode? needle, Vala.CodeNode hay_node);

private Vala.CodeNode? needle;
private Filter? filter;
Expand Down Expand Up @@ -122,7 +122,7 @@ class Vls.NodeSearch : Vala.CodeVisitor {
this.visit_source_file (file);
}

public NodeSearch.with_filter (Vala.SourceFile file, Vala.CodeNode needle, Filter filter_func,
public NodeSearch.with_filter (Vala.SourceFile file, Vala.CodeNode? needle, Filter filter_func,
bool include_declaration = true) {
this.file = file;
this.needle = needle;
Expand Down
3 changes: 3 additions & 0 deletions src/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ vls_src = files([
'analysis/codestyleanalyzer.vala',
'analysis/inlayhintnodes.vala',
'analysis/symbolenumerator.vala',
'codeaction/addtrycatchassignmentaction.vala',
'codeaction/addtrycatchstatementaction.vala',
'codeaction/addthrowsdeclaration.vala',
'codeaction/baseconverteraction.vala',
'codeaction/implementmissingprereqsaction.vala',
'codehelp/callhierarchy.vala',
Expand Down
2 changes: 1 addition & 1 deletion src/server.vala
Original file line number Diff line number Diff line change
Expand Up @@ -1664,7 +1664,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.extract (compilation, (TextDocument) source_file, p.range, Uri.unescape_string (p.textDocument.uri), p.context.diagnostics);
foreach (var action in code_actions)
json_array.add_element (Json.gobject_serialize (action));
Vala.CodeContext.pop ();
Expand Down