Cleaned up LSP types, add InitializeResult, no longer use a hardcoded string

This commit is contained in:
Alexandros Naskos 2020-11-06 10:08:20 +02:00
parent 996deb45fe
commit f7eff6632e
No known key found for this signature in database
GPG Key ID: 02BF2E72B0EA32D2
6 changed files with 307 additions and 230 deletions

View File

@ -29,7 +29,7 @@ pub fn getDocComments(
allocator: *std.mem.Allocator,
tree: *ast.Tree,
node: *ast.Node,
format: types.MarkupKind,
format: types.MarkupContent.Kind,
) !?[]const u8 {
if (getDocCommentNode(tree, node)) |doc_comment_node| {
return try collectDocComments(allocator, tree, doc_comment_node, format);
@ -41,7 +41,7 @@ pub fn collectDocComments(
allocator: *std.mem.Allocator,
tree: *ast.Tree,
doc_comments: *ast.Node.DocComment,
format: types.MarkupKind,
format: types.MarkupContent.Kind,
) ![]const u8 {
var lines = std.ArrayList([]const u8).init(allocator);
defer lines.deinit();

View File

@ -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 semantic_tokens = @import("semantic_tokens.zig");
const logger = std.log.scoped(.main);
@ -50,8 +51,8 @@ pub fn log(
};
send(&arena, types.Notification{
.method = "window/showMessage",
.params = types.NotificationParams{
.ShowMessageParams = .{
.params = types.Notification.Params{
.ShowMessage = .{
.type = message_type,
.message = message,
},
@ -67,8 +68,8 @@ pub fn log(
send(&arena, types.Notification{
.method = "window/logMessage",
.params = types.NotificationParams{
.LogMessageParams = .{
.params = types.Notification.Params{
.LogMessage = .{
.type = message_type,
.message = message,
},
@ -97,12 +98,6 @@ const ClientCapabilities = struct {
var client_capabilities = ClientCapabilities{};
var offset_encoding = offsets.Encoding.utf16;
const initialize_capabilities =
\\"capabilities": {"signatureHelpProvider": {"triggerCharacters": ["(",","]},"textDocumentSync": 1,"renameProvider":true,"completionProvider": {"resolveProvider": false,"triggerCharacters": [".",":","@"]},"documentHighlightProvider": false,"hoverProvider": true,"codeActionProvider": false,"declarationProvider": true,"definitionProvider": true,"typeDefinitionProvider": true,"implementationProvider": false,"referencesProvider": true,"documentSymbolProvider": true,"colorProvider": false,"documentFormattingProvider": true,"documentRangeFormattingProvider": false,"foldingRangeProvider": false,"selectionRangeProvider": false,"workspaceSymbolProvider": false,"rangeProvider": false,"documentProvider": true,"workspace": {"workspaceFolders": {"supported": true,"changeNotifications": true}},"semanticTokensProvider": {"documentProvider": true,"legend": {"tokenTypes": ["namespace","type","struct","enum","union","opaque","parameter","variable","tagField","field","errorTag","function","keyword","comment","string","number","operator","builtin","label","keywordLiteral"],"tokenModifiers": ["definition","async","documentation", "generic"]}}}}}
;
const initialize_response = ",\"result\": {" ++ initialize_capabilities;
const not_implemented_response =
\\,"error":{"code":-32601,"message":"NotImplemented"}}
;
@ -259,7 +254,7 @@ fn publishDiagnostics(arena: *std.heap.ArenaAllocator, handle: DocumentStore.Han
try send(arena, types.Notification{
.method = "textDocument/publishDiagnostics",
.params = .{
.PublishDiagnosticsParams = .{
.PublishDiagnostics = .{
.uri = handle.uri(),
.diagnostics = diagnostics.items,
},
@ -331,7 +326,11 @@ fn nodeToCompletion(
const node = node_handle.node;
const handle = node_handle.handle;
const doc_kind: types.MarkupKind = if (client_capabilities.completion_doc_supports_md) .Markdown else .PlainText;
const doc_kind: types.MarkupContent.Kind = if (client_capabilities.completion_doc_supports_md)
.Markdown
else
.PlainText;
const doc = if (try analysis.getDocComments(
list.allocator,
handle.tree,
@ -563,7 +562,7 @@ fn gotoDefinitionSymbol(id: types.RequestId, arena: *std.heap.ArenaAllocator, de
fn hoverSymbol(id: types.RequestId, arena: *std.heap.ArenaAllocator, decl_handle: analysis.DeclWithHandle) (std.os.WriteError || error{OutOfMemory})!void {
const handle = decl_handle.handle;
const hover_kind: types.MarkupKind = if (client_capabilities.hover_supports_md) .Markdown else .PlainText;
const hover_kind: types.MarkupContent.Kind = if (client_capabilities.hover_supports_md) .Markdown else .PlainText;
const md_string = switch (decl_handle.decl.*) {
.ast_node => |node| ast_node: {
if (try analysis.resolveVarDeclAlias(&document_store, arena, .{ .node = node, .handle = handle })) |result| {
@ -866,7 +865,7 @@ fn declToCompletion(context: DeclToCompletionContext, decl_handle: analysis.Decl
switch (decl_handle.decl.*) {
.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_kind: types.MarkupContent.Kind = if (client_capabilities.completion_doc_supports_md) .Markdown else .PlainText;
const doc = if (param.doc_comments) |doc_comments|
types.MarkupContent{
.kind = doc_kind,
@ -1073,7 +1072,6 @@ fn configFromUriOr(uri: []const u8, default: Config) Config {
}
fn initializeHandler(arena: *std.heap.ArenaAllocator, id: types.RequestId, req: requests.Initialize, config: Config) !void {
var send_encoding = req.params.capabilities.offsetEncoding.value.len != 0;
for (req.params.capabilities.offsetEncoding.value) |encoding| {
if (std.mem.eql(u8, encoding, "utf-8")) {
offset_encoding = .utf8;
@ -1117,15 +1115,77 @@ fn initializeHandler(arena: *std.heap.ArenaAllocator, id: types.RequestId, req:
try loadWorkspaceConfigs();
}
if (!send_encoding) {
try respondGeneric(id, initialize_response);
} else {
const response_str = try std.fmt.allocPrint(&arena.allocator, ",\"result\": {{\"offsetEncoding\":\"{}\",{}", .{
if (offset_encoding == .utf8) @as([]const u8, "utf-8") else @as([]const u8, "utf-16"),
initialize_capabilities,
});
try respondGeneric(id, response_str);
try send(arena, types.Response{
.id = id,
.result = .{
.InitializeResult = .{
.offsetEncoding = if (offset_encoding == .utf8)
@as([]const u8, "utf-8")
else
"utf-16",
.serverInfo = .{
.name = "zls",
.version = "0.1.0",
},
.capabilities = .{
.signatureHelpProvider = .{
.triggerCharacters = &[_][]const u8{ "(", "," },
},
.textDocumentSync = .Full,
.renameProvider = true,
.completionProvider = .{
.resolveProvider = false,
.triggerCharacters = &[_][]const u8{ ".", ":", "@" },
},
.documentHighlightProvider = false,
.hoverProvider = true,
.codeActionProvider = false,
.declarationProvider = true,
.definitionProvider = true,
.typeDefinitionProvider = true,
.implementationProvider = false,
.referencesProvider = true,
.documentSymbolProvider = true,
.colorProvider = false,
.documentFormattingProvider = true,
.documentRangeFormattingProvider = false,
.foldingRangeProvider = false,
.selectionRangeProvider = false,
.workspaceSymbolProvider = false,
.rangeProvider = false,
.documentProvider = true,
.workspace = .{
.workspaceFolders = .{
.supported = true,
.changeNotifications = true,
},
},
.semanticTokensProvider = .{
.documentProvider = true,
.legend = .{
.tokenTypes = comptime block: {
const tokTypeFields = std.meta.fields(semantic_tokens.TokenType);
var names: [tokTypeFields.len][]const u8 = undefined;
for (tokTypeFields) |field, i| {
names[i] = field.name;
}
break :block &names;
},
.tokenModifiers = comptime block: {
const tokModFields = std.meta.fields(semantic_tokens.TokenModifiers);
var names: [tokModFields.len][]const u8 = undefined;
for (tokModFields) |field, i| {
names[i] = field.name;
}
break :block &names;
},
},
},
},
},
},
});
logger.notice("zls initialized", .{});
logger.info("{}\n", .{client_capabilities});
logger.notice("Using offset encoding: {}\n", .{std.meta.tagName(offset_encoding)});
@ -1201,7 +1261,6 @@ fn semanticTokensFullHandler(arena: *std.heap.ArenaAllocator, id: types.RequestI
return try respondGeneric(id, no_semantic_tokens_response);
};
const semantic_tokens = @import("semantic_tokens.zig");
const token_array = try semantic_tokens.writeAllSemanticTokens(arena, &document_store, handle, offset_encoding);
defer allocator.free(token_array);

View File

@ -19,12 +19,12 @@ fn tokenReference(
.uri = handle.uri(),
.range = .{
.start = .{
.line = @intCast(types.Integer, loc.line),
.character = @intCast(types.Integer, loc.column),
.line = @intCast(i64, loc.line),
.character = @intCast(i64, loc.column),
},
.end = .{
.line = @intCast(types.Integer, loc.line),
.character = @intCast(types.Integer, loc.column + offsets.tokenLength(handle.tree, tok, encoding)),
.line = @intCast(i64, loc.line),
.character = @intCast(i64, loc.column + offsets.tokenLength(handle.tree, tok, encoding)),
},
},
});

View File

@ -121,7 +121,7 @@ pub fn fromDynamicTree(arena: *std.heap.ArenaAllocator, comptime T: type, value:
//! Note that the parameter types may be incomplete.
//! We only define what we actually use.
const MaybeStringArray = Default([]const types.String, &[0]types.String{});
const MaybeStringArray = Default([]const []const u8, &[0][]const u8{});
pub const Initialize = struct {
pub const ClientCapabilities = struct {
@ -161,14 +161,14 @@ pub const WorkspaceFoldersChange = struct {
pub const OpenDocument = struct {
params: struct {
textDocument: struct {
uri: types.String,
text: types.String,
uri: []const u8,
text: []const u8,
},
},
};
const TextDocumentIdentifier = struct {
uri: types.String,
uri: []const u8,
};
pub const ChangeDocument = struct {
@ -205,7 +205,7 @@ pub const Rename = struct {
params: struct {
textDocument: TextDocumentIdentifier,
position: types.Position,
newName: types.String,
newName: []const u8,
},
};

View File

@ -4,7 +4,7 @@ const DocumentStore = @import("document_store.zig");
const analysis = @import("analysis.zig");
const ast = std.zig.ast;
const TokenType = enum(u32) {
pub const TokenType = enum(u32) {
namespace,
type,
@"struct",
@ -27,7 +27,7 @@ const TokenType = enum(u32) {
keywordLiteral,
};
const TokenModifiers = packed struct {
pub const TokenModifiers = packed struct {
definition: bool = false,
@"async": bool = false,
documentation: bool = false,
@ -275,7 +275,12 @@ fn writeNodeTokens(builder: *Builder, arena: *std.heap.ArenaAllocator, store: *D
try await @asyncCall(child_frame, {}, writeNodeTokens, .{ builder, arena, store, child });
}
}
if (node.tag == .Root) {
try gap_highlighter.end(handle.tree.token_ids.len - 1);
} else {
try gap_highlighter.end(node.lastToken());
}
},
.VarDecl => {
const var_decl = node.cast(ast.Node.VarDecl).?;

View File

@ -1,25 +1,10 @@
// Collection of JSONRPC and LSP structs, enums, and unions
const std = @import("std");
// LSP types
const json = std.json;
// JSON Types
pub const String = []const u8;
pub const Integer = i64;
pub const Float = f64;
pub const Bool = bool;
pub const Array = json.Array;
pub const Object = json.ObjectMap;
// pub const Any = @TypeOf(var);
// Basic structures
pub const DocumentUri = String;
pub const Position = struct {
line: Integer,
character: Integer,
line: i64,
character: i64,
};
pub const Range = struct {
@ -28,21 +13,15 @@ pub const Range = struct {
};
pub const Location = struct {
uri: DocumentUri, range: Range
uri: []const u8,
range: Range,
};
/// Id of a request
pub const RequestId = union(enum) {
String: String,
Integer: Integer,
Float: Float,
};
/// Params of a request
pub const RequestParams = void;
pub const NotificationParams = union(enum) {
LogMessageParams: LogMessageParams, PublishDiagnosticsParams: PublishDiagnosticsParams, ShowMessageParams: ShowMessageParams
String: []const u8,
Integer: i64,
Float: f64,
};
/// Hover response
@ -60,35 +39,40 @@ pub const ResponseParams = union(enum) {
TextEdits: []TextEdit,
Locations: []Location,
WorkspaceEdit: WorkspaceEdit,
};
/// JSONRPC error
pub const Error = struct {
code: Integer,
message: String,
data: String,
};
/// JSONRPC request
pub const Request = struct {
jsonrpc: String = "2.0", method: String, id: ?RequestId = RequestId{ .Integer = 0 }, params: RequestParams
InitializeResult: InitializeResult,
};
/// JSONRPC notifications
pub const Notification = struct {
jsonrpc: String = "2.0", method: String, params: NotificationParams
pub const Params = union(enum) {
LogMessage: struct {
type: MessageType,
message: []const u8,
},
PublishDiagnostics: struct {
uri: []const u8,
diagnostics: []Diagnostic,
},
ShowMessage: struct {
type: MessageType,
message: []const u8,
},
};
jsonrpc: []const u8 = "2.0",
method: []const u8,
params: Params,
};
/// JSONRPC response
pub const Response = struct {
jsonrpc: String = "2.0",
// @"error": ?Error = null,
jsonrpc: []const u8 = "2.0",
id: RequestId,
result: ResponseParams,
};
/// Type of a debug message
pub const MessageType = enum(Integer) {
pub const MessageType = enum(i64) {
Error = 1,
Warning = 2,
Info = 3,
@ -103,12 +87,7 @@ pub const MessageType = enum(Integer) {
}
};
/// Params for a LogMessage Notification (window/logMessage)
pub const LogMessageParams = struct {
type: MessageType, message: String
};
pub const DiagnosticSeverity = enum(Integer) {
pub const DiagnosticSeverity = enum(i64) {
Error = 1,
Warning = 2,
Information = 3,
@ -126,19 +105,15 @@ pub const DiagnosticSeverity = enum(Integer) {
pub const Diagnostic = struct {
range: Range,
severity: DiagnosticSeverity,
code: String,
source: String,
message: String,
};
pub const PublishDiagnosticsParams = struct {
uri: DocumentUri, diagnostics: []Diagnostic
code: []const u8,
source: []const u8,
message: []const u8,
};
pub const TextDocument = struct {
uri: DocumentUri,
uri: []const u8,
// This is a substring of mem starting at 0
text: String,
text: []const u8,
// This holds the memory that we have actually allocated.
mem: []u8,
};
@ -172,15 +147,16 @@ pub const WorkspaceEdit = struct {
pub const TextEdit = struct {
range: Range,
newText: String,
newText: []const u8,
};
pub const MarkupKind = enum(u1) {
PlainText = 0, // plaintext
Markdown = 1, // markdown
pub const MarkupContent = struct {
pub const Kind = enum(u1) {
PlainText = 0,
Markdown = 1,
pub fn jsonStringify(
value: MarkupKind,
value: Kind,
options: json.StringifyOptions,
out_stream: anytype,
) !void {
@ -192,35 +168,30 @@ pub const MarkupKind = enum(u1) {
}
};
pub const MarkupContent = struct {
kind: MarkupKind = MarkupKind.Markdown,
value: String,
kind: Kind = .Markdown,
value: []const u8,
};
// pub const TextDocumentIdentifier = struct {
// uri: DocumentUri,
// };
// pub const CompletionTriggerKind = enum(Integer) {
// Invoked = 1,
// TriggerCharacter = 2,
// TriggerForIncompleteCompletions = 3,
// pub fn jsonStringify(
// value: CompletionTriggerKind,
// options: json.StringifyOptions,
// out_stream: var,
// ) !void {
// try json.stringify(@enumToInt(value), options, out_stream);
// }
// };
pub const CompletionList = struct {
isIncomplete: Bool,
isIncomplete: bool,
items: []const CompletionItem,
};
pub const CompletionItemKind = enum(Integer) {
pub const InsertTextFormat = enum(i64) {
PlainText = 1,
Snippet = 2,
pub fn jsonStringify(
value: InsertTextFormat,
options: json.StringifyOptions,
out_stream: anytype,
) !void {
try json.stringify(@enumToInt(value), options, out_stream);
}
};
pub const CompletionItem = struct {
const Kind = enum(i64) {
Text = 1,
Method = 2,
Function = 3,
@ -248,7 +219,7 @@ pub const CompletionItemKind = enum(Integer) {
TypeParameter = 25,
pub fn jsonStringify(
value: CompletionItemKind,
value: Kind,
options: json.StringifyOptions,
out_stream: anytype,
) !void {
@ -256,32 +227,18 @@ pub const CompletionItemKind = enum(Integer) {
}
};
pub const InsertTextFormat = enum(Integer) {
PlainText = 1,
Snippet = 2,
pub fn jsonStringify(
value: InsertTextFormat,
options: json.StringifyOptions,
out_stream: anytype,
) !void {
try json.stringify(@enumToInt(value), options, out_stream);
}
};
pub const CompletionItem = struct {
label: String,
kind: CompletionItemKind,
label: []const u8,
kind: Kind,
textEdit: ?TextEdit = null,
filterText: ?String = null,
insertText: ?String = null,
insertTextFormat: ?InsertTextFormat = InsertTextFormat.PlainText,
detail: ?String = null,
filterText: ?[]const u8 = null,
insertText: ?[]const u8 = null,
insertTextFormat: ?InsertTextFormat = .PlainText,
detail: ?[]const u8 = null,
documentation: ?MarkupContent = null,
// filterText: String = .NotDefined,
};
const SymbolKind = enum {
pub const DocumentSymbol = struct {
const Kind = enum {
File = 1,
Module = 2,
Namespace = 3,
@ -310,7 +267,7 @@ const SymbolKind = enum {
TypeParameter = 26,
pub fn jsonStringify(
value: SymbolKind,
value: Kind,
options: json.StringifyOptions,
out_stream: anytype,
) !void {
@ -318,22 +275,78 @@ const SymbolKind = enum {
}
};
pub const DocumentSymbol = struct {
name: String,
detail: ?String = null,
kind: SymbolKind,
name: []const u8,
detail: ?[]const u8 = null,
kind: Kind,
deprecated: bool = false,
range: Range,
selectionRange: Range,
children: []const DocumentSymbol = &[_]DocumentSymbol{},
};
pub const ShowMessageParams = struct {
type: MessageType,
message: String,
pub const WorkspaceFolder = struct {
uri: []const u8,
name: []const u8,
};
pub const WorkspaceFolder = struct {
uri: DocumentUri,
name: String,
// Only includes options we set in our initialize result.
const InitializeResult = struct {
capabilities: struct {
signatureHelpProvider: struct {
triggerCharacters: []const []const u8,
},
textDocumentSync: enum {
None = 0,
Full = 1,
Incremental = 2,
pub fn jsonStringify(
value: @This(),
options: json.StringifyOptions,
out_stream: anytype,
) !void {
try json.stringify(@enumToInt(value), options, out_stream);
}
},
renameProvider: bool,
completionProvider: struct {
resolveProvider: bool,
triggerCharacters: []const []const u8,
},
documentHighlightProvider: bool,
hoverProvider: bool,
codeActionProvider: bool,
declarationProvider: bool,
definitionProvider: bool,
typeDefinitionProvider: bool,
implementationProvider: bool,
referencesProvider: bool,
documentSymbolProvider: bool,
colorProvider: bool,
documentFormattingProvider: bool,
documentRangeFormattingProvider: bool,
foldingRangeProvider: bool,
selectionRangeProvider: bool,
workspaceSymbolProvider: bool,
rangeProvider: bool,
documentProvider: bool,
workspace: struct {
workspaceFolders: struct {
supported: bool,
changeNotifications: bool,
},
},
semanticTokensProvider: struct {
documentProvider: bool,
legend: struct {
tokenTypes: []const []const u8,
tokenModifiers: []const []const u8,
},
},
},
serverInfo: struct {
name: []const u8,
version: ?[]const u8 = null,
},
offsetEncoding: []const u8,
};