Added *, ? completions, improved readme
This commit is contained in:
parent
f55d5a25aa
commit
5454883d2d
30
README.md
30
README.md
@ -21,14 +21,14 @@ Zig Language Server, or `zls`, is a language server for Zig. The Zig wiki states
|
||||
|
||||
## Installation
|
||||
|
||||
Installing `zls` is pretty simple. You will need [a build of Zig master](https://ziglang.org/download/) (or >0.6) to build ZLS.
|
||||
Installing `zls` is pretty simple. You will need [a build of Zig master](https://ziglang.org/download/) to build zls.
|
||||
|
||||
```bash
|
||||
git clone --recurse-submodules https://github.com/zigtools/zls
|
||||
cd zls
|
||||
zig build
|
||||
|
||||
# To configure ZLS:
|
||||
# To configure zls:
|
||||
zig build config
|
||||
```
|
||||
|
||||
@ -36,7 +36,9 @@ zig build config
|
||||
|
||||
| Option | Type | Default Value | What it Does |
|
||||
| --- | --- | --- | --- |
|
||||
| `-Ddata_version` | `string` (master or 0.6.0) | 0.6.0 | The data file version. This selects the files in the `src/data` folder that correspond to the Zig version being served.
|
||||
| `-Ddata_version` | `string` (master or 0.6.0) | 0.6.0 | The data file version. This selects the files in the `src/data` folder that correspond to the Zig version being served.|
|
||||
| `-Dallocation_info` | `bool` | `false` | Enable the use of the debug allocator that will print out information in debug mode and track memory leaks.|
|
||||
| `-Dmax_bytes_allocated` | `usize` | `0` | When `allocation_info` is true, enables a maximum allowed allocation size (excluding stacktraces) before the program panics.|
|
||||
|
||||
Then, you can use the `zls` executable in an editor of your choice that has a Zig language server client!
|
||||
|
||||
@ -57,15 +59,29 @@ The following options are currently available.
|
||||
| `zig_exe_path` | `?[]const u8` | `null` | zig executable path, e.g. `/path/to/zig/zig`, used to run the custom build runner. If `null`, zig is looked up in `PATH`. Will be used to infer the zig standard library path if none is provided. |
|
||||
| `warn_style` | `bool` | `false` | Enables warnings for style *guideline* mismatches |
|
||||
| `build_runner_path` | `?[]const u8` | `null` | Path to the build_runner.zig file provided by zls. This option must be present in one of the global configuration files to have any effect. `null` is equivalent to `${executable_directory}/build_runner.zig` |
|
||||
| `enable_semantic_tokens` | `bool` | false | Enables semantic token support when the client also supports it. |
|
||||
| `enable_semantic_tokens` | `bool` | `false` | Enables semantic token support when the client also supports it. |
|
||||
| `operator_completions` | `bool` | `true` | Enables `*` and `?` operators in completion lists. |
|
||||
|
||||
## Usage
|
||||
## Features
|
||||
|
||||
`zls` will supercharge your Zig programming experience with autocomplete, function documentation, and more! Follow the instructions for your specific editor below:
|
||||
`zls` supports most language features, including simple type function support, usingnamespace, payload capture type resolution, custom packages and others.
|
||||
Notable language features that are not currently implemented include `@cImport` as well as most forms of compile time evaluation.
|
||||
|
||||
The following LSP features are supported:
|
||||
- Completions
|
||||
- Hover
|
||||
- Goto definition/declaration
|
||||
- Document symbols
|
||||
- Find references
|
||||
- Rename symbol
|
||||
- Formatting using `zig fmt`
|
||||
- Semantic token highlighting (LSP 3.16 proposed feature, implemented by a few clients including VSCode, kak and emacs lsp-mode)
|
||||
|
||||
You can install `zls` using the instuctions for your text editor below:
|
||||
|
||||
### VSCode
|
||||
|
||||
Install the `zls-vscode` extension from [here](https://github.com/zigtools/zls-vscode/releases).
|
||||
Install the `zls-vscode` extension from [here](https://github.com/zigtools/zls-vscode/releases) and provide a path to the build `zls` executable.
|
||||
|
||||
### Sublime Text 3
|
||||
|
||||
|
@ -1012,13 +1012,18 @@ pub const NodeWithHandle = struct {
|
||||
handle: *DocumentStore.Handle,
|
||||
};
|
||||
|
||||
pub const FieldAccessReturn = struct {
|
||||
original: TypeWithHandle,
|
||||
unwrapped: ?TypeWithHandle = null,
|
||||
};
|
||||
|
||||
pub fn getFieldAccessType(
|
||||
store: *DocumentStore,
|
||||
arena: *std.heap.ArenaAllocator,
|
||||
handle: *DocumentStore.Handle,
|
||||
source_index: usize,
|
||||
tokenizer: *std.zig.Tokenizer,
|
||||
) !?TypeWithHandle {
|
||||
) !?FieldAccessReturn {
|
||||
var current_type = TypeWithHandle.typeVal(.{
|
||||
.node = &handle.tree.root_node.base,
|
||||
.handle = handle,
|
||||
@ -1030,7 +1035,10 @@ pub fn getFieldAccessType(
|
||||
while (true) {
|
||||
const tok = tokenizer.next();
|
||||
switch (tok.id) {
|
||||
.Eof => return try resolveFieldAccessLhsType(store, arena, current_type, &bound_type_params),
|
||||
.Eof => return FieldAccessReturn{
|
||||
.original = current_type,
|
||||
.unwrapped = try resolveDerefType(store, arena, current_type, &bound_type_params),
|
||||
},
|
||||
.Identifier => {
|
||||
if (try lookupSymbolGlobal(store, arena, current_type.handle, tokenizer.buffer[tok.loc.start..tok.loc.end], source_index)) |child| {
|
||||
current_type = (try child.resolveType(store, arena, &bound_type_params)) orelse return null;
|
||||
@ -1039,10 +1047,17 @@ pub fn getFieldAccessType(
|
||||
.Period => {
|
||||
const after_period = tokenizer.next();
|
||||
switch (after_period.id) {
|
||||
.Eof => return try resolveFieldAccessLhsType(store, arena, current_type, &bound_type_params),
|
||||
.Eof => return FieldAccessReturn{
|
||||
.original = current_type,
|
||||
.unwrapped = try resolveDerefType(store, arena, current_type, &bound_type_params),
|
||||
},
|
||||
.Identifier => {
|
||||
if (after_period.loc.end == tokenizer.buffer.len)
|
||||
return try resolveFieldAccessLhsType(store, arena, current_type, &bound_type_params);
|
||||
if (after_period.loc.end == tokenizer.buffer.len) {
|
||||
return FieldAccessReturn{
|
||||
.original = current_type,
|
||||
.unwrapped = try resolveDerefType(store, arena, current_type, &bound_type_params),
|
||||
};
|
||||
}
|
||||
|
||||
current_type = try resolveFieldAccessLhsType(store, arena, current_type, &bound_type_params);
|
||||
const current_type_node = switch (current_type.type.data) {
|
||||
@ -1121,7 +1136,10 @@ pub fn getFieldAccessType(
|
||||
}
|
||||
}
|
||||
|
||||
return try resolveFieldAccessLhsType(store, arena, current_type, &bound_type_params);
|
||||
return FieldAccessReturn{
|
||||
.original = current_type,
|
||||
.unwrapped = try resolveDerefType(store, arena, current_type, &bound_type_params),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn isNodePublic(tree: *ast.Tree, node: *ast.Node) bool {
|
||||
@ -1428,7 +1446,7 @@ fn getDocumentSymbolsInternal(allocator: *std.mem.Allocator, tree: *ast.Tree, no
|
||||
pub fn getDocumentSymbols(allocator: *std.mem.Allocator, tree: *ast.Tree, encoding: offsets.Encoding) ![]types.DocumentSymbol {
|
||||
var symbols = try std.ArrayList(types.DocumentSymbol).initCapacity(allocator, tree.root_node.decls_len);
|
||||
|
||||
var context = GetDocumentSymbolsContext {
|
||||
var context = GetDocumentSymbolsContext{
|
||||
.symbols = &symbols,
|
||||
.encoding = encoding,
|
||||
};
|
||||
|
@ -20,3 +20,6 @@ build_runner_path: ?[]const u8 = null,
|
||||
|
||||
/// Semantic token support
|
||||
enable_semantic_tokens: bool = false,
|
||||
|
||||
/// Whether to enable `*` and `?` operators in completion lists
|
||||
operator_completions: bool = true,
|
||||
|
45
src/main.zig
45
src/main.zig
@ -262,10 +262,11 @@ fn publishDiagnostics(arena: *std.heap.ArenaAllocator, handle: DocumentStore.Han
|
||||
fn typeToCompletion(
|
||||
arena: *std.heap.ArenaAllocator,
|
||||
list: *std.ArrayList(types.CompletionItem),
|
||||
type_handle: analysis.TypeWithHandle,
|
||||
field_access: analysis.FieldAccessReturn,
|
||||
orig_handle: *DocumentStore.Handle,
|
||||
config: Config,
|
||||
) error{OutOfMemory}!void {
|
||||
const type_handle = field_access.original;
|
||||
switch (type_handle.type.data) {
|
||||
.slice => {
|
||||
if (!type_handle.type.is_type_val) {
|
||||
@ -284,6 +285,7 @@ fn typeToCompletion(
|
||||
arena,
|
||||
list,
|
||||
.{ .node = n, .handle = type_handle.handle },
|
||||
field_access.unwrapped,
|
||||
orig_handle,
|
||||
type_handle.type.is_type_val,
|
||||
config,
|
||||
@ -296,6 +298,7 @@ fn nodeToCompletion(
|
||||
arena: *std.heap.ArenaAllocator,
|
||||
list: *std.ArrayList(types.CompletionItem),
|
||||
node_handle: analysis.NodeWithHandle,
|
||||
unwrapped: ?analysis.TypeWithHandle,
|
||||
orig_handle: *DocumentStore.Handle,
|
||||
is_type_val: bool,
|
||||
config: Config,
|
||||
@ -417,15 +420,40 @@ fn nodeToCompletion(
|
||||
},
|
||||
.PrefixOp => {
|
||||
const prefix_op = node.cast(std.zig.ast.Node.PrefixOp).?;
|
||||
|
||||
switch (prefix_op.op) {
|
||||
.ArrayType, .SliceType => {},
|
||||
.PtrType => {
|
||||
if (config.operator_completions) {
|
||||
try list.append(.{
|
||||
.label = "*",
|
||||
.kind = .Operator,
|
||||
});
|
||||
}
|
||||
|
||||
if (prefix_op.rhs.cast(std.zig.ast.Node.PrefixOp)) |child_pop| {
|
||||
switch (child_pop.op) {
|
||||
.ArrayType => {},
|
||||
else => return,
|
||||
.ArrayType => {
|
||||
try list.append(.{
|
||||
.label = "len",
|
||||
.kind = .Field,
|
||||
});
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
} else return;
|
||||
} else if (unwrapped) |actual_type| {
|
||||
try typeToCompletion(arena, list, .{ .original = actual_type }, orig_handle, config);
|
||||
}
|
||||
return;
|
||||
},
|
||||
.OptionalType => {
|
||||
if (config.operator_completions) {
|
||||
try list.append(.{
|
||||
.label = "?",
|
||||
.kind = .Operator,
|
||||
});
|
||||
}
|
||||
return;
|
||||
},
|
||||
else => return,
|
||||
}
|
||||
@ -638,7 +666,8 @@ fn getSymbolFieldAccess(
|
||||
if (name.len == 0) return null;
|
||||
var tokenizer = std.zig.Tokenizer.init(position.line[range.start..range.end]);
|
||||
|
||||
if (try analysis.getFieldAccessType(&document_store, arena, handle, position.absolute_index, &tokenizer)) |container_handle| {
|
||||
if (try analysis.getFieldAccessType(&document_store, arena, handle, position.absolute_index, &tokenizer)) |result| {
|
||||
const container_handle = result.original;
|
||||
const container_handle_node = switch (container_handle.type.data) {
|
||||
.other => |n| n,
|
||||
else => return null,
|
||||
@ -799,7 +828,7 @@ fn declToCompletion(context: DeclToCompletionContext, decl_handle: analysis.Decl
|
||||
const tree = decl_handle.handle.tree;
|
||||
|
||||
switch (decl_handle.decl.*) {
|
||||
.ast_node => |node| try nodeToCompletion(context.arena, context.completions, .{ .node = node, .handle = decl_handle.handle }, context.orig_handle, false, context.config.*),
|
||||
.ast_node => |node| try nodeToCompletion(context.arena, context.completions, .{ .node = node, .handle = decl_handle.handle }, null, context.orig_handle, false, context.config.*),
|
||||
.param_decl => |param| {
|
||||
const doc_kind: types.MarkupKind = if (client_capabilities.completion_doc_supports_md) .Markdown else .PlainText;
|
||||
const doc = if (param.doc_comments) |doc_comments|
|
||||
@ -898,8 +927,8 @@ fn completeFieldAccess(
|
||||
) !void {
|
||||
var completions = std.ArrayList(types.CompletionItem).init(&arena.allocator);
|
||||
var tokenizer = std.zig.Tokenizer.init(position.line[range.start..range.end]);
|
||||
if (try analysis.getFieldAccessType(&document_store, arena, handle, position.absolute_index, &tokenizer)) |node| {
|
||||
try typeToCompletion(arena, &completions, node, handle, config);
|
||||
if (try analysis.getFieldAccessType(&document_store, arena, handle, position.absolute_index, &tokenizer)) |result| {
|
||||
try typeToCompletion(arena, &completions, result, handle, config);
|
||||
}
|
||||
|
||||
try send(arena, types.Response{
|
||||
|
Loading…
Reference in New Issue
Block a user