Skip to content

Commit cf451fe

Browse files
committed
Merge branch 'master' into newuri
2 parents 1891f6d + fccc5b8 commit cf451fe

File tree

17 files changed

+540
-168
lines changed

17 files changed

+540
-168
lines changed

src/document.jl

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ get_offset(doc, p::Position) = get_offset(doc, p.line, p.character)
102102
get_offset(doc, r::Range) = get_offset(doc, r.start):get_offset(doc, r.stop)
103103

104104
# 1-based. Basically the index at which (line, character) can be found in the document.
105+
get_offset2(doc::Document, p::Position, forgiving_mode=false) = get_offset2(doc, p.line, p.character, forgiving_mode)
105106
function get_offset2(doc::Document, line::Integer, character::Integer, forgiving_mode=false)
106107
line_offsets = get_line_offsets2!(doc)
107108
text = get_text(doc)
@@ -134,9 +135,12 @@ function get_offset2(doc::Document, line::Integer, character::Integer, forgiving
134135
pos = nextind(text, pos)
135136
end
136137

137-
return pos
138+
return pos
138139
end
139140

141+
# get_offset, but correct
142+
get_offset3(args...) = get_offset2(args...) - 1
143+
140144
# Note: to be removed
141145
function obscure_text(s)
142146
i = 1

src/languageserverinstance.jl

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,9 @@ function trigger_symbolstore_reload(server::LanguageServerInstance)
237237
end
238238
end
239239

240+
# Set to true to reload request handler functions with Revise (requires Revise loaded in Main)
241+
const USE_REVISE = Ref(false)
242+
240243
function request_wrapper(func, server::LanguageServerInstance)
241244
return function (conn, params)
242245
if server.shutdown_requested
@@ -248,7 +251,16 @@ function request_wrapper(func, server::LanguageServerInstance)
248251
nothing
249252
)
250253
end
251-
func(params, server, conn)
254+
if USE_REVISE[] && isdefined(Main, :Revise)
255+
try
256+
Main.Revise.revise()
257+
catch e
258+
@warn "Reloading with Revise failed" exception = e
259+
end
260+
Base.invokelatest(func, params, server, conn)
261+
else
262+
func(params, server, conn)
263+
end
252264
end
253265
end
254266

@@ -333,7 +345,6 @@ function Base.run(server::LanguageServerInstance)
333345
msg_dispatcher[initialize_request_type] = request_wrapper(initialize_request, server)
334346
msg_dispatcher[initialized_notification_type] = request_wrapper(initialized_notification, server)
335347
msg_dispatcher[shutdown_request_type] = request_wrapper(shutdown_request, server)
336-
msg_dispatcher[exit_notification_type] = request_wrapper(exit_notification, server)
337348
msg_dispatcher[cancel_notification_type] = request_wrapper(cancel_notification, server)
338349
msg_dispatcher[setTrace_notification_type] = request_wrapper(setTrace_notification, server)
339350
msg_dispatcher[setTraceNotification_notification_type] = request_wrapper(setTraceNotification_notification, server)
@@ -352,6 +363,12 @@ function Base.run(server::LanguageServerInstance)
352363
msg_dispatcher[julia_refreshLanguageServer_notification_type] = request_wrapper(julia_refreshLanguageServer_notification, server)
353364
msg_dispatcher[julia_getDocFromWord_request_type] = request_wrapper(julia_getDocFromWord_request, server)
354365
msg_dispatcher[textDocument_selectionRange_request_type] = request_wrapper(textDocument_selectionRange_request, server)
366+
msg_dispatcher[textDocument_documentLink_request_type] = request_wrapper(textDocument_documentLink_request, server)
367+
368+
# The exit notification message should not be wrapped in request_wrapper (which checks
369+
# if the server have been requested to be shut down). Instead, this message needs to be
370+
# handled directly.
371+
msg_dispatcher[exit_notification_type] = (conn, params) -> exit_notification(params, server, conn)
355372

356373
@debug "starting main loop"
357374
@debug "Starting event listener loop at $(round(Int, time()))"

src/protocol/completion.jl

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,8 +108,7 @@ struct CompletionItem <: Outbound
108108
data::Union{Any,Missing}
109109
end
110110
CompletionItem(label, kind, documentation, textEdit) = CompletionItem(label, kind, missing, missing, documentation, missing, missing, missing, missing, missing, InsertTextFormats.PlainText, textEdit, missing, missing, missing, missing)
111-
112-
111+
CompletionItem(label, kind, detail, documentation, textEdit) = CompletionItem(label, kind, missing, detail, documentation, missing, missing, missing, missing, missing, InsertTextFormats.PlainText, textEdit, missing, missing, missing, missing)
113112

114113
struct CompletionList <: Outbound
115114
isIncomplete::Bool

src/protocol/features.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,8 @@ end
114114

115115
@dict_readable struct DocumentLinkParams
116116
textDocument::TextDocumentIdentifier
117+
workDoneToken::Union{Int,String,Missing}
118+
partialResultToken::Union{Int,String,Missing}
117119
end
118120

119121
struct DocumentLink <: Outbound

src/protocol/messagedefs.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ const textDocument_formatting_request_type = JSONRPC.RequestType("textDocument/f
66
const textDocument_range_formatting_request_type = JSONRPC.RequestType("textDocument/rangeFormatting", DocumentRangeFormattingParams, Union{Vector{TextEdit}, Nothing})
77
const textDocument_references_request_type = JSONRPC.RequestType("textDocument/references", ReferenceParams, Union{Vector{Location}, Nothing})
88
const textDocument_rename_request_type = JSONRPC.RequestType("textDocument/rename", RenameParams, Union{WorkspaceEdit, Nothing})
9-
const textDocument_prepareRename_request_type = JSONRPC.RequestType("textDocument/prepareRename", PrepareRenameParams, Range)
9+
const textDocument_prepareRename_request_type = JSONRPC.RequestType("textDocument/prepareRename", PrepareRenameParams, Union{Range,Nothing})
1010
const textDocument_documentSymbol_request_type = JSONRPC.RequestType("textDocument/documentSymbol", DocumentSymbolParams, Union{Vector{DocumentSymbol}, Vector{SymbolInformation}, Nothing})
1111
const textDocument_documentHighlight_request_type = JSONRPC.RequestType("textDocument/documentHighlight", DocumentHighlightParams, Union{Vector{DocumentHighlight}, Nothing})
1212
const textDocument_hover_request_type = JSONRPC.RequestType("textDocument/hover", TextDocumentPositionParams, Union{Hover, Nothing})
@@ -18,6 +18,7 @@ const textDocument_willSave_notification_type = JSONRPC.NotificationType("textDo
1818
const textDocument_willSaveWaitUntil_request_type = JSONRPC.RequestType("textDocument/willSaveWaitUntil", WillSaveTextDocumentParams, Union{Vector{TextEdit}, Nothing})
1919
const textDocument_publishDiagnostics_notification_type = JSONRPC.NotificationType("textDocument/publishDiagnostics", PublishDiagnosticsParams)
2020
const textDocument_selectionRange_request_type = JSONRPC.RequestType("textDocument/selectionRange", SelectionRangeParams, Union{Vector{SelectionRange}, Nothing})
21+
const textDocument_documentLink_request_type = JSONRPC.RequestType("textDocument/documentLink", DocumentLinkParams, Union{Vector{DocumentLink}, Nothing})
2122

2223
const workspace_executeCommand_request_type = JSONRPC.RequestType("workspace/executeCommand", ExecuteCommandParams, Any)
2324
const workspace_symbol_request_type = JSONRPC.RequestType("workspace/symbol", WorkspaceSymbolParams, Union{Vector{SymbolInformation}, Nothing})

src/requests/actions.jl

Lines changed: 144 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,77 @@
11
struct ServerAction
2-
command::Command
2+
id::String
3+
desc::String
4+
kind::Union{CodeActionKind,Missing}
5+
preferred::Union{Bool,Missing}
36
when::Function
47
handler::Function
58
end
69

10+
function client_support_action_kind(s::LanguageServerInstance, _::CodeActionKind=CodeActionKinds.Empty)
11+
if s.clientCapabilities !== missing &&
12+
s.clientCapabilities.textDocument !== missing &&
13+
s.clientCapabilities.textDocument.codeAction !== missing &&
14+
s.clientCapabilities.textDocument.codeAction.codeActionLiteralSupport !== missing
15+
s.clientCapabilities.textDocument.codeAction.codeActionLiteralSupport.codeActionKind !== missing
16+
# From the spec of CodeActionKind (https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#codeActionClientCapabilities):
17+
#
18+
# The code action kind values the client supports. When this
19+
# property exists the client also guarantees that it will
20+
# handle values outside its set gracefully and falls back
21+
# to a default value when unknown.
22+
#
23+
# so we can always return true here(?).
24+
return true
25+
else
26+
return false
27+
end
28+
end
29+
30+
function client_preferred_support(s::LanguageServerInstance)::Bool
31+
if s.clientCapabilities !== missing &&
32+
s.clientCapabilities.textDocument !== missing &&
33+
s.clientCapabilities.textDocument.codeAction !== missing &&
34+
s.clientCapabilities.textDocument.codeAction.isPreferredSupport !== missing
35+
return s.clientCapabilities.textDocument.codeAction.isPreferredSupport
36+
else
37+
return false
38+
end
39+
end
40+
41+
# TODO: All currently supported CodeActions in LS.jl can be converted "losslessly" to
42+
# Commands but this might not be true in the future so unless the client support
43+
# literal code actions those need to be filtered out.
44+
function convert_to_command(ca::CodeAction)
45+
return ca.command
46+
end
47+
748
function textDocument_codeAction_request(params::CodeActionParams, server::LanguageServerInstance, conn)
8-
commands = Command[]
49+
actions = CodeAction[]
950
doc = getdocument(server, params.textDocument.uri)
10-
offset = get_offset(doc, params.range.start) # Should usef get_offset2?
51+
offset = get_offset2(doc, params.range.start)
1152
x = get_expr(getcst(doc), offset)
1253
arguments = Any[params.textDocument.uri, offset] # use the same arguments for all commands
1354
if x isa EXPR
1455
for (_, sa) in LSActions
1556
if sa.when(x, params)
16-
push!(commands, Command(sa.command.title, sa.command.command, arguments))
57+
action = CodeAction(
58+
sa.desc, # title
59+
sa.kind, # kind
60+
missing, # diagnostics
61+
client_preferred_support(server) ? sa.preferred : missing, # isPreferred
62+
missing, # edit
63+
Command(sa.desc, sa.id, arguments), # command
64+
)
65+
push!(actions, action)
1766
end
1867
end
1968
end
20-
return commands
69+
if client_support_action_kind(server)
70+
return actions
71+
else
72+
# TODO: Future CodeActions might have to be filtered here.
73+
return convert_to_command.(actions)
74+
end
2175
end
2276

2377
function workspace_executeCommand_request(params::ExecuteCommandParams, server::LanguageServerInstance, conn)
@@ -261,28 +315,97 @@ function remove_farg_name(x, server, conn)
261315
])
262316
else
263317
tde = TextDocumentEdit(VersionedTextDocumentIdentifier(file._uri, file._version), TextEdit[
264-
TextEdit(Range(file, offset .+ (0:x1.fullspan)), "::Any")
318+
TextEdit(Range(file, offset .+ (0:x1.fullspan)), "_")
265319
])
266320
end
267321
JSONRPC.send(conn, workspace_applyEdit_request_type, ApplyWorkspaceEditParams(missing, WorkspaceEdit(missing, TextDocumentEdit[tde])))
268322
end
269323

324+
function remove_unused_assignment_name(x, _, conn)
325+
x1 = StaticLint.get_parent_fexpr(x, x -> StaticLint.haserror(x) && StaticLint.errorof(x) == StaticLint.UnusedBinding && x isa EXPR && x.head === :IDENTIFIER)
326+
file, offset = get_file_loc(x1)
327+
tde = TextDocumentEdit(VersionedTextDocumentIdentifier(file._uri, file._version), TextEdit[
328+
TextEdit(Range(file, offset .+ (0:x1.span)), "_")
329+
])
330+
JSONRPC.send(conn, workspace_applyEdit_request_type, ApplyWorkspaceEditParams(missing, WorkspaceEdit(missing, TextDocumentEdit[tde])))
331+
end
332+
333+
function double_to_triple_equal(x, _, conn)
334+
x1 = StaticLint.get_parent_fexpr(x, y -> StaticLint.haserror(y) && StaticLint.errorof(y) in (StaticLint.NothingEquality, StaticLint.NothingNotEq))
335+
file, offset = get_file_loc(x1)
336+
tde = TextDocumentEdit(VersionedTextDocumentIdentifier(file._uri, file._version), TextEdit[
337+
TextEdit(Range(file, offset .+ (0:x1.span)), StaticLint.errorof(x1) == StaticLint.NothingEquality ? "===" : "!==")
338+
])
339+
JSONRPC.send(conn, workspace_applyEdit_request_type, ApplyWorkspaceEditParams(missing, WorkspaceEdit(missing, TextDocumentEdit[tde])))
340+
end
341+
270342
# Adding a CodeAction requires defining:
271-
# * a Command (title and description);
343+
# * a unique id
344+
# * a description
345+
# * an action kind (optionally)
272346
# * a function (.when) called on the currently selected expression and parameters of the CodeAction call;
273347
# * a function (.handler) called on three arguments (current expression, server and the jr connection) to implement the command.
274-
const LSActions = Dict(
275-
"ExplicitPackageVarImport" => ServerAction(Command("Explicitly import used package variables.", "ExplicitPackageVarImport", missing),
276-
(x, params) -> refof(x) isa StaticLint.Binding && refof(x).val isa SymbolServer.ModuleStore,
277-
explicitly_import_used_variables),
278-
"ExpandFunction" => ServerAction(Command("Expand function definition.", "ExpandFunction", missing),
279-
(x, params) -> is_in_fexpr(x, is_single_line_func),
280-
expand_inline_func),
281-
"FixMissingRef" => ServerAction(Command("Fix missing reference", "FixMissingRef", missing),
282-
(x, params) -> is_fixable_missing_ref(x, params.context),
283-
applymissingreffix),
284-
"ReexportModule" => ServerAction(Command("Re-export package variables.", "ReexportModule", missing),
285-
(x, params) -> StaticLint.is_in_fexpr(x, x -> headof(x) === :using || headof(x) === :import) && (refof(x) isa StaticLint.Binding && (refof(x).type === StaticLint.CoreTypes.Module || (refof(x).val isa StaticLint.Binding && refof(x).val.type === StaticLint.CoreTypes.Module) || refof(x).val isa SymbolServer.ModuleStore) || refof(x) isa SymbolServer.ModuleStore),
286-
reexport_package),
287-
"DeleteUnusedFunctionArgumentName" => ServerAction(Command("Delete name of unused function argument.", "DeleteUnusedFunctionArgumentName", missing), (x, params) -> StaticLint.is_in_fexpr(x, x -> StaticLint.haserror(x) && StaticLint.errorof(x) == StaticLint.UnusedFunctionArgument), remove_farg_name)
348+
const LSActions = Dict{String,ServerAction}()
349+
350+
LSActions["ExplicitPackageVarImport"] = ServerAction(
351+
"ExplicitPackageVarImport",
352+
"Explicitly import used package variables.",
353+
missing,
354+
missing,
355+
(x, params) -> refof(x) isa StaticLint.Binding && refof(x).val isa SymbolServer.ModuleStore,
356+
explicitly_import_used_variables
357+
)
358+
359+
LSActions["ExpandFunction"] = ServerAction(
360+
"ExpandFunction",
361+
"Expand function definition.",
362+
CodeActionKinds.Refactor,
363+
missing,
364+
(x, params) -> is_in_fexpr(x, is_single_line_func),
365+
expand_inline_func,
366+
)
367+
368+
LSActions["FixMissingRef"] = ServerAction(
369+
"FixMissingRef",
370+
"Fix missing reference",
371+
missing,
372+
missing,
373+
(x, params) -> is_fixable_missing_ref(x, params.context),
374+
applymissingreffix,
375+
)
376+
377+
LSActions["ReexportModule"] = ServerAction(
378+
"ReexportModule",
379+
"Re-export package variables.",
380+
missing,
381+
missing,
382+
(x, params) -> StaticLint.is_in_fexpr(x, x -> headof(x) === :using || headof(x) === :import) && (refof(x) isa StaticLint.Binding && (refof(x).type === StaticLint.CoreTypes.Module || (refof(x).val isa StaticLint.Binding && refof(x).val.type === StaticLint.CoreTypes.Module) || refof(x).val isa SymbolServer.ModuleStore) || refof(x) isa SymbolServer.ModuleStore),
383+
reexport_package,
384+
)
385+
386+
LSActions["DeleteUnusedFunctionArgumentName"] = ServerAction(
387+
"DeleteUnusedFunctionArgumentName",
388+
"Delete name of unused function argument.",
389+
CodeActionKinds.QuickFix,
390+
missing,
391+
(x, params) -> StaticLint.is_in_fexpr(x, x -> StaticLint.haserror(x) && StaticLint.errorof(x) == StaticLint.UnusedFunctionArgument),
392+
remove_farg_name,
393+
)
394+
395+
LSActions["ReplaceUnusedAssignmentName"] = ServerAction(
396+
"ReplaceUnusedAssignmentName",
397+
"Replace unused assignment name with _.",
398+
CodeActionKinds.QuickFix,
399+
missing,
400+
(x, params) -> StaticLint.is_in_fexpr(x, x -> StaticLint.haserror(x) && StaticLint.errorof(x) == StaticLint.UnusedBinding && x isa EXPR && x.head === :IDENTIFIER),
401+
remove_unused_assignment_name,
402+
)
403+
404+
LSActions["CompareNothingWithTripleEqual"] = ServerAction(
405+
"CompareNothingWithTripleEqual",
406+
"Change ==/!= to ===/!==.",
407+
CodeActionKinds.QuickFix,
408+
true,
409+
(x, _) -> StaticLint.is_in_fexpr(x, y -> StaticLint.haserror(y) && (StaticLint.errorof(y) in (StaticLint.NothingEquality, StaticLint.NothingNotEq))),
410+
double_to_triple_equal,
288411
)

0 commit comments

Comments
 (0)