2021-03-30 18:49:29 +01:00
const std = @import ( " std " ) ;
2021-10-07 12:34:14 +01:00
const builtin = @import ( " builtin " ) ;
2021-10-01 01:52:15 +01:00
const zinput = @import ( " zinput " ) ;
2021-03-31 12:46:56 +01:00
const known_folders = @import ( " known-folders " ) ;
2021-03-30 18:49:29 +01:00
2021-03-31 15:54:27 +01:00
fn print ( comptime fmt : [ ] const u8 , args : anytype ) void {
const stdout = std . io . getStdOut ( ) . writer ( ) ;
stdout . print ( fmt , args ) catch @panic ( " Could not write to stdout " ) ;
}
fn write ( text : [ ] const u8 ) void {
const stdout = std . io . getStdOut ( ) . writer ( ) ;
stdout . writeAll ( text ) catch @panic ( " Could not write to stdout " ) ;
}
2021-12-02 05:16:15 +00:00
pub fn wizard ( allocator : std . mem . Allocator ) ! void {
2021-03-30 18:49:29 +01:00
@setEvalBranchQuota ( 2500 ) ;
2021-03-31 15:54:27 +01:00
write (
2021-03-30 18:49:29 +01:00
\\Welcome to the ZLS configuration wizard!
\\ *
\\ |\
\\ /* \
\\ | *\
\\ _/_*___|_ x
\\ | @ @ /
\\ @ \ /
\\ \__-/ /
\\
\\
2021-06-24 11:38:01 +01:00
) ;
2021-03-31 15:54:27 +01:00
var local_path = known_folders . getPath ( allocator , . local_configuration ) catch null ;
var global_path = known_folders . getPath ( allocator , . global_configuration ) catch null ;
defer if ( local_path ) | d | allocator . free ( d ) ;
defer if ( global_path ) | d | allocator . free ( d ) ;
if ( global_path = = null and local_path = = null ) {
write ( " Could not open a global or local config directory. \n " ) ;
return ;
}
var config_path : [ ] const u8 = undefined ;
if ( try zinput . askBool ( " Should this configuration be system-wide? " ) ) {
if ( global_path ) | p | {
config_path = p ;
} else {
write ( " Could not find a global config directory. \n " ) ;
return ;
}
} else {
if ( local_path ) | p | {
config_path = p ;
} else {
write ( " Could not find a local config directory. \n " ) ;
return ;
}
}
var dir = std . fs . cwd ( ) . openDir ( config_path , . { } ) catch | err | {
2021-06-24 11:38:01 +01:00
print ( " Could not open {s}: {}. \n " , . { config_path , err } ) ;
2021-03-31 15:54:27 +01:00
return ;
} ;
defer dir . close ( ) ;
var file = dir . createFile ( " zls.json " , . { } ) catch | err | {
2021-06-24 11:38:01 +01:00
print ( " Could not create {s}/zls.json: {}. \n " , . { config_path , err } ) ;
2021-03-31 15:54:27 +01:00
return ;
} ;
defer file . close ( ) ;
const out = file . writer ( ) ;
2021-03-30 18:49:29 +01:00
var zig_exe_path = try findZig ( allocator ) ;
2021-03-31 12:46:56 +01:00
defer if ( zig_exe_path ) | p | allocator . free ( p ) ;
2021-03-30 18:49:29 +01:00
if ( zig_exe_path ) | path | {
2021-03-31 15:54:27 +01:00
print ( " Found zig executable '{s}' in PATH. \n " , . { path } ) ;
2021-03-30 18:49:29 +01:00
} else {
2021-03-31 15:54:27 +01:00
write ( " Could not find 'zig' in PATH \n " ) ;
2021-10-07 12:34:14 +01:00
zig_exe_path = try zinput . askString ( allocator , if ( builtin . os . tag = = . windows )
2021-06-24 11:38:01 +01:00
\\What is the path to the 'zig' executable you would like to use?
\\Note that due to a bug in zig (https://github.com/ziglang/zig/issues/6044),
\\your zig directory cannot contain the '/' character.
else
" What is the path to the 'zig' executable you would like to use? " , std . fs . MAX_PATH_BYTES ) ;
2021-03-30 18:49:29 +01:00
}
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 ) {
2021-03-31 12:46:56 +01:00
. Sublime = > true ,
. VSCode , . Kate , . Neovim , . Vim8 , . Emacs , . Doom = > false ,
else = > try zinput . askBool ( " Should the @ sign be included in completions of builtin functions? \n Change this later if `@inc` completes to `include` or `@@include` " ) ,
2021-03-30 18:49:29 +01:00
} ;
const max_detail_length : usize = switch ( editor ) {
2021-03-31 12:46:56 +01:00
. Sublime = > 256 ,
else = > 1024 * 1024 ,
2021-03-30 18:49:29 +01:00
} ;
2021-11-30 18:25:36 +00:00
std . debug . print ( " Writing config to {s}/zls.json ... " , . { config_path } ) ;
2021-03-31 12:46:56 +01:00
2021-06-24 11:38:01 +01:00
try std . json . stringify ( . {
2021-03-30 18:49:29 +01:00
. 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 ,
2021-10-01 01:52:36 +01:00
} , . { } , out ) ;
2021-03-31 15:54:27 +01:00
write ( " successful. \n \n \n \n " ) ;
2021-03-30 18:49:29 +01:00
// Keep synced with README.md
switch ( editor ) {
. VSCode = > {
2021-03-31 15:54:27 +01:00
write (
2021-03-30 18:49:29 +01:00
\\To use ZLS in Visual Studio Code, install the 'ZLS for VSCode' extension from
\\'https://github.com/zigtools/zls-vscode/releases' or via the extensions menu.
\\Then, open VSCode's 'settings.json' file, and add:
\\
\\"zigLanguageClient.path": "[command_or_path_to_zls]"
2021-03-31 15:54:27 +01:00
) ;
2021-03-30 18:49:29 +01:00
} ,
. Sublime = > {
2021-03-31 15:54:27 +01:00
write (
2021-03-30 18:49:29 +01:00
\\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:
\\
2021-11-02 09:50:14 +00:00
\\For Sublime Text 3:
\\
2021-03-31 15:54:27 +01:00
\\{
\\ "clients": {
\\ "zig": {
2021-03-30 18:49:29 +01:00
\\ "command": ["zls"],
\\ "enabled": true,
\\ "languageId": "zig",
\\ "scopes": ["source.zig"],
2021-11-02 09:50:14 +00:00
\\ "syntaxes": ["Packages/Zig Language/Syntaxes/Zig.tmLanguage"]
\\ }
\\ }
\\}
\\
\\For Sublime Text 4:
\\
\\{
\\ "clients": {
\\ "zig": {
\\ "command": ["zls"],
\\ "enabled": true,
\\ "selector": "source.zig"
2021-03-31 15:54:27 +01:00
\\ }
\\ }
\\}
) ;
2021-03-30 18:49:29 +01:00
} ,
. Kate = > {
2021-03-31 15:54:27 +01:00
write (
2021-03-30 18:49:29 +01:00
\\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)
\\
2021-03-31 15:54:27 +01:00
\\{
\\ "servers": {
\\ "zig": {
2021-03-30 18:49:29 +01:00
\\ "command": ["zls"],
\\ "url": "https://github.com/zigtools/zls",
\\ "highlightingModeRegex": "^Zig$"
2021-03-31 15:54:27 +01:00
\\ }
\\ }
\\}
) ;
2021-03-30 18:49:29 +01:00
} ,
. Neovim , . Vim8 = > {
2021-03-31 15:54:27 +01:00
write (
2021-03-30 18:49:29 +01:00
\\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:
\\
2021-03-31 15:54:27 +01:00
\\{
\\ "languageserver": {
\\ "zls" : {
2021-03-30 18:49:29 +01:00
\\ "command": "command_or_path_to_zls",
\\ "filetypes": ["zig"]
2021-03-31 15:54:27 +01:00
\\ }
\\ }
\\}
) ;
2021-03-30 18:49:29 +01:00
} ,
. Emacs = > {
2021-03-31 15:54:27 +01:00
write (
2021-03-30 18:49:29 +01:00
\\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>")
2021-03-31 15:54:27 +01:00
) ;
2021-03-30 18:49:29 +01:00
} ,
. Doom = > {
2021-03-31 15:54:27 +01:00
write (
2021-03-30 18:49:29 +01:00
\\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))))
2021-03-31 15:54:27 +01:00
) ;
2021-03-30 18:49:29 +01:00
} ,
. Other = > {
2021-03-31 15:54:27 +01:00
write (
2021-03-30 18:49:29 +01:00
\\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!
2021-03-31 15:54:27 +01:00
) ;
2021-03-30 18:49:29 +01:00
} ,
}
2021-03-31 15:54:27 +01:00
write ( " \n \n Thank you for choosing ZLS! \n " ) ;
2021-03-30 18:49:29 +01:00
}
2021-12-02 05:16:15 +00:00
pub fn findZig ( allocator : std . mem . Allocator ) ! ? [ ] const u8 {
2021-03-30 18:49:29 +01:00
const env_path = std . process . getEnvVarOwned ( allocator , " PATH " ) catch | err | switch ( err ) {
error . EnvironmentVariableNotFound = > {
return null ;
} ,
else = > return err ,
} ;
defer allocator . free ( env_path ) ;
2021-10-07 12:34:14 +01:00
const exe_extension = builtin . target . exeFileExt ( ) ;
2021-03-30 18:49:29 +01:00
const zig_exe = try std . fmt . allocPrint ( allocator , " zig{s} " , . { exe_extension } ) ;
defer allocator . free ( zig_exe ) ;
2021-08-09 17:14:20 +01:00
var it = std . mem . tokenize ( u8 , env_path , & [ _ ] u8 { std . fs . path . delimiter } ) ;
2021-03-30 18:49:29 +01:00
while ( it . next ( ) ) | path | {
2021-10-07 12:34:14 +01:00
if ( builtin . os . tag = = . windows ) {
2021-06-24 11:38:01 +01:00
if ( std . mem . indexOfScalar ( u8 , path , '/' ) ! = null ) continue ;
2021-04-03 10:14:52 +01:00
}
2021-10-01 01:52:36 +01:00
const full_path = try std . fs . path . join ( allocator , & [ _ ] [ ] const u8 { path , zig_exe } ) ;
2021-03-30 18:49:29 +01:00
defer allocator . free ( full_path ) ;
if ( ! std . fs . path . isAbsolute ( full_path ) ) continue ;
2021-06-24 11:38:01 +01:00
2021-03-30 18:49:29 +01:00
const file = std . fs . openFileAbsolute ( full_path , . { } ) catch continue ;
defer file . close ( ) ;
const stat = file . stat ( ) catch continue ;
if ( stat . kind = = . Directory ) continue ;
2021-03-31 12:46:56 +01:00
2021-03-30 18:49:29 +01:00
return try allocator . dupe ( u8 , full_path ) ;
}
return null ;
}