Skip to content

Refactor config wizard #274

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

Merged
merged 2 commits into from
Mar 30, 2021
Merged
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
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ zig build config # Configure ZLS

### Configuration Options

You can configure zls by providing a zls.json file.
You can configure zls by running `zls config`.
zls will look for a zls.json configuration file in multiple locations with the following priority:
- In the local configuration folder of your OS (as provided by [known-folders](https://github.com/ziglibs/known-folders#folder-list))
- In the same directory as the executable
Expand All @@ -85,6 +85,8 @@ The following options are currently available.
| `enable_semantic_tokens` | `bool` | `true` | Enables semantic token support when the client also supports it. |
| `operator_completions` | `bool` | `true` | Enables `*` and `?` operators in completion lists. |
| `skip_std_references` | `bool` | `false` | When true, skips searching for references in std. Improves lookup speed for functions in user's code. Renaming and go-to-definition will continue to work as is.
| `include_at_in_builtins` | `bool` | `false` | Most editors (except Sublime Text, it seems) generate a duplicate @ if a completion starts with it.
| `max_detail_length` | `usize` | `1048576` | Completion detail fields are truncated to this length. Decrease if previews lag your editor.

## Features

Expand Down
209 changes: 0 additions & 209 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2,210 +2,10 @@ const std = @import("std");
const builtin = @import("builtin");
// const build_options = @import("build_options")

const zinput = @import("src/zinput/src/main.zig");

var builder: *std.build.Builder = undefined;

pub fn config(step: *std.build.Step) anyerror!void {
@setEvalBranchQuota(2500);
std.debug.warn("Welcome to the ZLS configuration wizard! (insert mage emoji here)\n", .{});

var zig_exe_path: ?[]const u8 = null;
std.debug.print("Looking for 'zig' in PATH...\n", .{});
find_zig: {
const allocator = builder.allocator;
const env_path = std.process.getEnvVarOwned(allocator, "PATH") catch |err| switch (err) {
error.EnvironmentVariableNotFound => {
break :find_zig;
},
else => return err,
};
defer allocator.free(env_path);

const exe_extension = @as(std.zig.CrossTarget, .{}).exeFileExt();
const zig_exe = try std.fmt.allocPrint(allocator, "zig{s}", .{exe_extension});
defer allocator.free(zig_exe);

var it = std.mem.tokenize(env_path, &[_]u8{std.fs.path.delimiter});
while (it.next()) |path| {
const resolved_path = try std.fs.path.resolve(allocator, &[_][]const u8{path});
defer allocator.free(resolved_path);
const full_path = try std.fs.path.join(allocator, &[_][]const u8{
resolved_path,
zig_exe,
});
defer allocator.free(full_path);

if (!std.fs.path.isAbsolute(full_path)) continue;
// Skip folders named zig
const file = std.fs.openFileAbsolute(full_path, .{}) catch continue;
const stat = file.stat() catch continue;
const is_dir = stat.kind == .Directory;
if (is_dir) continue;

var buf: [std.fs.MAX_PATH_BYTES]u8 = undefined;
zig_exe_path = try std.mem.dupe(allocator, u8, std.os.realpath(full_path, &buf) catch continue);
break :find_zig;
}
}

if (zig_exe_path == null) {
std.debug.print("Could not find 'zig' in PATH\n", .{});
zig_exe_path = try zinput.askString(builder.allocator, "What is the path to the 'zig' executable you would like to use?", 512);
} else {
std.debug.print("Found zig executable '{s}'\n", .{zig_exe_path.?});
}

const editor = try zinput.askSelectOne("Which code editor do you use?", enum { VSCode, Sublime, Kate, Neovim, Vim8, Emacs, Doom, Other });
const snippets = try zinput.askBool("Do you want to enable snippets?");
const style = try zinput.askBool("Do you want to enable style warnings?");
const semantic_tokens = try zinput.askBool("Do you want to enable semantic highlighting?");
const operator_completions = try zinput.askBool("Do you want to enable .* and .? completions?");
const include_at_in_builtins = switch (editor) {
.Sublime =>
true,
.VSCode,
.Kate,
.Neovim,
.Vim8,
.Emacs,
.Doom =>
false,
else =>
try zinput.askBool("Should the @ sign be included in completions of builtin functions?\nChange this later if `@inc` completes to `include` or `@@include`")
};
const max_detail_length: usize = switch (editor) {
.Sublime =>
256,
else =>
1024 * 1024
};

var dir = try std.fs.cwd().openDir(builder.exe_dir, .{});
defer dir.close();

var file = try dir.createFile("zls.json", .{});
defer file.close();

const out = file.writer();

std.debug.warn("Writing to config...\n", .{});

const content = std.json.stringify(.{
.zig_exe_path = zig_exe_path,
.enable_snippets = snippets,
.warn_style = style,
.enable_semantic_tokens = semantic_tokens,
.operator_completions = operator_completions,
.include_at_in_builtins = include_at_in_builtins,
.max_detail_length = max_detail_length,
}, std.json.StringifyOptions{}, out);

std.debug.warn("Successfully saved configuration options!\n", .{});
std.debug.warn("\n", .{});

switch (editor) {
.VSCode => {
std.debug.warn(
\\To use ZLS in Visual Studio Code, install the 'ZLS for VSCode' extension.
\\Then, open VSCode's 'settings.json' file, and add `"zigLanguageClient.path": "[command_or_path_to_zls]"`.
, .{});
},
.Sublime => {
std.debug.warn(
\\To use ZLS in Sublime, install the `LSP` package from `https://github.com/sublimelsp/LSP/releases` or via Package Control.
\\Then, add the following snippet to `LSP`'s user settings:
\\
\\{{
\\ "clients": {{
\\ "zig": {{
\\ "command": ["zls"],
\\ "enabled": true,
\\ "languageId": "zig",
\\ "scopes": ["source.zig"],
\\ "syntaxes": ["Packages/Zig/Syntaxes/Zig.tmLanguage"]
\\ }}
\\ }}
\\}}
, .{});
},
.Kate => {
std.debug.warn(
\\To use ZLS in Kate, enable `LSP client` plugin in Kate settings.
\\Then, add the following snippet to `LSP client's` user settings:
\\(or paste it in `LSP client's` GUI settings)
\\
\\{{
\\ "servers": {{
\\ "zig": {{
\\ "command": ["zls"],
\\ "url": "https://github.com/zigtools/zls",
\\ "highlightingModeRegex": "^Zig$"
\\ }}
\\ }}
\\}}
, .{});
},
.Neovim, .Vim8 => {
std.debug.warn(
\\To use ZLS in Neovim/Vim8, we recommend using CoC engine. You can get it from 'https://github.com/neoclide/coc.nvim'.
\\Then, simply issue cmd from Neovim/Vim8 `:CocConfig`, and add this to your CoC config:
\\
\\{{
\\ "languageserver": {{
\\ "zls" : {{
\\ "command": "command_or_path_to_zls",
\\ "filetypes": ["zig"]
\\ }}
\\ }}
\\}}
, .{});
},
.Emacs => {
std.debug.warn(
\\To use ZLS in Emacs, install lsp-mode (https://github.com/emacs-lsp/lsp-mode) from melpa.
\\Zig mode (https://github.com/ziglang/zig-mode) is also useful!
\\Then, add the following to your emacs config:
\\
\\(require 'lsp-mode)
\\(setq lsp-zig-zls-executable "<path to zls>")
, .{});
},
.Doom => {
std.debug.warn(
\\To use ZLS in Doom Emacs, enable the lsp module
\\And install the `zig-mode` (https://github.com/ziglang/zig-mode) package by adding `(package! zig-mode)` to your packages.el file.
\\
\\(use-package! zig-mode
\\ :hook ((zig-mode . lsp-deferred))
\\ :custom (zig-format-on-save nil)
\\ :config
\\ (after! lsp-mode
\\ (add-to-list 'lsp-language-id-configuration '(zig-mode . "zig"))
\\ (lsp-register-client
\\ (make-lsp-client
\\ :new-connection (lsp-stdio-connection "<path to zls>")
\\ :major-modes '(zig-mode)
\\ :server-id 'zls))))
, .{});
},
.Other => {
std.debug.warn(
\\We might not *officially* support your editor, but you can definitely still use ZLS!
\\Simply configure your editor for use with language servers and point it to the ZLS executable!
, .{});
},
}

std.debug.warn("\nYou can find the ZLS executable in the \"zig-cache/bin\" by default.\nNOTE: Make sure that if you move the ZLS executable, you move the `zls.json` config file with it as well!\n\nAnd finally: Thanks for choosing ZLS!\n\n", .{});
}

pub fn build(b: *std.build.Builder) !void {
builder = b;
// Standard target options allows the person running `zig build` to choose
// what target to build for. Here we do not override the defaults, which
// means any target is allowed, and the default is native. Other options
// for restricting supported target set are available.
const target = b.standardTargetOptions(.{});

const mode = b.standardReleaseOptions();
Expand All @@ -225,15 +25,6 @@ pub fn build(b: *std.build.Builder) !void {

b.installFile("src/special/build_runner.zig", "bin/build_runner.zig");

const run_cmd = exe.run();
run_cmd.step.dependOn(b.getInstallStep());

const run_step = b.step("run", "Run the app");
run_step.dependOn(&run_cmd.step);

const configure_step = b.step("config", "Configure zls");
configure_step.makeFn = config;

const test_step = b.step("test", "Run all the tests");
test_step.dependOn(builder.getInstallStep());

Expand Down
50 changes: 19 additions & 31 deletions src/main.zig
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const URI = @import("uri.zig");
const references = @import("references.zig");
const rename = @import("rename.zig");
const offsets = @import("offsets.zig");
const setup = @import("setup.zig");
const semantic_tokens = @import("semantic_tokens.zig");
const known_folders = @import("known-folders");

Expand Down Expand Up @@ -356,6 +357,8 @@ fn nodeToCompletion(
const node_tags = tree.nodes.items(.tag);
const datas = tree.nodes.items(.data);
const token_tags = tree.tokens.items(.tag);
if (tree.errors.len > 0)
return;

const doc_kind: types.MarkupContent.Kind = if (client_capabilities.completion_doc_supports_md)
.Markdown
Expand Down Expand Up @@ -1678,10 +1681,17 @@ fn processJsonRpc(arena: *std.heap.ArenaAllocator, parser: *std.json.Parser, jso
logger.debug("Method without return value not implemented: {s}", .{method});
}

const stack_frames = switch (std.builtin.mode) {
.Debug => 10,
else => 0,
};

fn launchWizard() !void {
const dest =
(try known_folders.getPath(allocator, .local_configuration))
orelse (try known_folders.getPath(allocator, .executable_dir))
orelse return error.NoConfigPathFound;
defer allocator.free(dest);
try setup.wizard(allocator, dest);
}

const stack_frames = switch (std.builtin.mode) { .Debug => 10, else => 0 };
var gpa_state = std.heap.GeneralPurposeAllocator(.{ .stack_trace_frames = stack_frames }){};

pub fn main() anyerror!void {
Expand All @@ -1699,6 +1709,10 @@ pub fn main() anyerror!void {
if (std.mem.eql(u8, arg, "--debug-log")) {
actual_log_level = .debug;
std.debug.print("Enabled debug logging\n", .{});
} else if (std.mem.eql(u8, arg, "config")) {
try launchWizard();
args_it.deinit();
return;
} else {
std.debug.print("Unrecognized argument {s}\n", .{arg});
std.os.exit(1);
Expand Down Expand Up @@ -1746,33 +1760,7 @@ pub fn main() anyerror!void {
logger.debug("zig path `{s}` is not absolute, will look in path", .{exe_path});
allocator.free(exe_path);
}

const env_path = std.process.getEnvVarOwned(allocator, "PATH") catch |err| switch (err) {
error.EnvironmentVariableNotFound => {
logger.warn("Could not get PATH environmental variable", .{});
break :find_zig;
},
else => return err,
};
defer allocator.free(env_path);

const exe_extension = std.Target.current.exeFileExt();
const zig_exe = try std.fmt.allocPrint(allocator, "zig{s}", .{exe_extension});
defer allocator.free(zig_exe);

var it = std.mem.tokenize(env_path, &[_]u8{std.fs.path.delimiter});
while (it.next()) |path| {
const full_path = try std.fs.path.join(allocator, &[_][]const u8{
path,
zig_exe,
});
defer allocator.free(full_path);

var buf: [std.fs.MAX_PATH_BYTES]u8 = undefined;
config.zig_exe_path = try std.mem.dupe(allocator, u8, std.os.realpath(full_path, &buf) catch continue);
logger.info("Found zig in PATH: {s}", .{config.zig_exe_path});
break :find_zig;
}
config.zig_exe_path = try setup.findZig(allocator);
}

if (config.zig_exe_path) |exe_path| {
Expand Down
Loading