Allow tabs for the indentation added by code actions. (#684)

* Allow tabs for the indentation added by the code actions feature (#673).

The previous implementation always assumed spaces for indentation.
But in stage2 code may contain tabs for indentation as well.

* Fix some edge cases, properly detect indentation(any amount of spaces) and move the indentation code inside `createDiscardText()`.
This commit is contained in:
IntegratedQuantum 2022-10-13 04:41:27 +02:00 committed by GitHub
parent 19fd17ff76
commit 62861193a6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -18,7 +18,7 @@ pub const Builder = struct {
pub fn generateCodeAction( pub fn generateCodeAction(
builder: *Builder, builder: *Builder,
diagnostic: types.Diagnostic, diagnostic: types.Diagnostic,
actions: *std.ArrayListUnmanaged(types.CodeAction), actions: *std.ArrayListUnmanaged(types.CodeAction)
) error{OutOfMemory}!void { ) error{OutOfMemory}!void {
const kind = DiagnosticKind.parse(diagnostic.message) orelse return; const kind = DiagnosticKind.parse(diagnostic.message) orelse return;
@ -113,8 +113,7 @@ fn handleUnusedFunctionParameter(builder: *Builder, actions: *std.ArrayListUnman
const block = node_datas[payload.func].rhs; const block = node_datas[payload.func].rhs;
const indent = offsets.lineSliceUntilIndex(builder.handle.text, token_starts[node_tokens[payload.func]]).len; const new_text = try createDiscardText(builder, identifier_name, token_starts[node_tokens[payload.func]], true);
const new_text = try createDiscardText(builder.arena.allocator(), identifier_name, indent + 4);
const index = token_starts[node_tokens[block]] + 1; const index = token_starts[node_tokens[block]] + 1;
@ -159,11 +158,9 @@ fn handleUnusedVariableOrConstant(builder: *Builder, actions: *std.ArrayListUnma
const first_token = tree.firstToken(node); const first_token = tree.firstToken(node);
const last_token = ast.lastToken(tree, node) + 1; const last_token = ast.lastToken(tree, node) + 1;
const indent = offsets.lineSliceUntilIndex(builder.handle.text, token_starts[first_token]).len;
if (token_tags[last_token] != .semicolon) return; if (token_tags[last_token] != .semicolon) return;
const new_text = try createDiscardText(builder.arena.allocator(), identifier_name, indent); const new_text = try createDiscardText(builder, identifier_name, token_starts[first_token], false);
const index = token_starts[last_token] + 1; const index = token_starts[last_token] + 1;
@ -243,6 +240,30 @@ fn handlePointlessDiscard(builder: *Builder, actions: *std.ArrayListUnmanaged(ty
}); });
} }
fn detectIndentation(source: []const u8) []const u8 {
// Essentially I'm looking for the first indentation in the file.
var i: usize = 0;
var len = source.len - 1; // I need 1 look-ahead
while(i < len) : (i += 1) {
if(source[i] != '\n') continue;
i += 1;
if(source[i] == '\t') return "\t";
var space_count: usize = 0;
while(i < source.len and source[i] == ' ') : (i += 1) {
space_count += 1;
}
if(source[i] == '\n') { // Some editors mess up indentation of empty lines
i -= 1;
continue;
}
if(space_count == 0) continue;
if(source[i] == '/') continue; // Comments sometimes have additional alignment.
if(source[i] == '\\') continue; // multi-line strings might as well.
return source[i - space_count .. i];
}
return " " ** 4; // recommended style
}
// attempts to converts a slice of text into camelcase 'FUNCTION_NAME' -> 'functionName' // attempts to converts a slice of text into camelcase 'FUNCTION_NAME' -> 'functionName'
fn createCamelcaseText(allocator: std.mem.Allocator, identifier: []const u8) ![]const u8 { fn createCamelcaseText(allocator: std.mem.Allocator, identifier: []const u8) ![]const u8 {
// skip initial & ending underscores // skip initial & ending underscores
@ -274,13 +295,26 @@ fn createCamelcaseText(allocator: std.mem.Allocator, identifier: []const u8) ![]
} }
// returns a discard string `\n{indent}_ = identifier_name;` // returns a discard string `\n{indent}_ = identifier_name;`
fn createDiscardText(allocator: std.mem.Allocator, identifier_name: []const u8, indent: usize) ![]const u8 { fn createDiscardText(builder: *Builder, identifier_name: []const u8, declaration_start: usize, add_block_indentation: bool) ![]const u8 {
const new_text_len = 1 + indent + "_ = ;".len + identifier_name.len; const indent = find_indent: {
const line = offsets.lineSliceUntilIndex(builder.handle.text, declaration_start);
for(line) |char, i| {
if(!std.ascii.isSpace(char)) {
break :find_indent line[0..i];
}
}
break :find_indent line;
};
const additional_indent = if(add_block_indentation) detectIndentation(builder.handle.text) else "";
const allocator = builder.arena.allocator();
const new_text_len = 1 + indent.len + additional_indent.len + "_ = ;".len + identifier_name.len;
var new_text = try std.ArrayListUnmanaged(u8).initCapacity(allocator, new_text_len); var new_text = try std.ArrayListUnmanaged(u8).initCapacity(allocator, new_text_len);
errdefer new_text.deinit(allocator); errdefer new_text.deinit(allocator);
new_text.appendAssumeCapacity('\n'); new_text.appendAssumeCapacity('\n');
new_text.appendNTimesAssumeCapacity(' ', indent); new_text.appendSliceAssumeCapacity(indent);
new_text.appendSliceAssumeCapacity(additional_indent);
new_text.appendSliceAssumeCapacity("_ = "); new_text.appendSliceAssumeCapacity("_ = ");
new_text.appendSliceAssumeCapacity(identifier_name); new_text.appendSliceAssumeCapacity(identifier_name);
new_text.appendAssumeCapacity(';'); new_text.appendAssumeCapacity(';');