From 41d20eeaa3d505065d84facfd78bf608ad5044d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BA=B7=E5=B0=8F=E5=B9=BF?= Date: Tue, 14 Jan 2020 03:30:30 -0800 Subject: [PATCH 01/40] Improve: a new divideCells function implementation 1. finds all lines of interests 2. generates cell construction directives 3. builds up cell in tree structure TODO: * test the efficiency with old implementation * cache the results --- src/WolframLanguageServer/TextDocument.wl | 430 ++++++++++++++++------ 1 file changed, 319 insertions(+), 111 deletions(-) diff --git a/src/WolframLanguageServer/TextDocument.wl b/src/WolframLanguageServer/TextDocument.wl index 6fd0897..4cf1860 100644 --- a/src/WolframLanguageServer/TextDocument.wl +++ b/src/WolframLanguageServer/TextDocument.wl @@ -140,157 +140,361 @@ DeclareType[CellNode, <| "level" -> _Integer | Infinity, "style" -> _String | _AdditionalStyle, "name" -> _String, - "range" -> {_Integer, _Integer | Infinity}, + "range" -> {_Integer, _Integer}, "selectionRange" -> _LspRange, - "codeRange" -> {{_Integer, _Integer | Infinity}...}, + "codeRange" -> {{_Integer, _Integer}...}, "children" -> {___CellNode} |>] +divideCells[doc_TextDocument] := Block[ + { + styleLineNo, titleLineNo, emptyLineNo + }, -divideCells[doc_TextDocument] := ( - - Table[ - Part[doc@"text", line] - // StringCases["(* "~~"::"~~Shortest[style___]~~"::"~~" *)" :> style] - // Replace[ - {"Package", ___} :> ( - {AdditionalStyle["Package"]} - (* If[If[ScriptFileQ[doc["uri"]], line == 2, line == 1], - {"Package"}, - {AdditionalStyle["Ignored"]} - ] *) - ) + styleLineNo = Position[ + doc["text"], + _?(StringContainsQ["(* " ~~ "::" ~~ Shortest[style___] ~~ "::" ~~ " *)"]), + {1}, Heads -> False + ] + // Flatten; + + titleLineNo = styleLineNo + (* selects style cells w/o a name *) + // Select[ + (Part[doc["text"], #]&) + /* StringCases["(* "~~"::"~~Shortest[style___]~~"::"~~" *)" :> style] + /* First + /* MemberQ[{"Package"}] + ] + (* excludes them when finding names *) + // (Complement[styleLineNo, #] + 1&) + // Select[(Part[doc["text"], #]&) + /* And[ + (* no style declared on name line *) + (Not @* StringContainsQ["(* " ~~ "::" ~~ style___ ~~ "::" ~~ " *)"]), + (* match a name *) + StringMatchQ[ + StartOfString ~~ (Whitespace | "") ~~ + "(*" ~~ Longest[title___] ~~ "*)" ~~ + (Whitespace | "") ~~ EndOfString + ] ] - // Replace[{ - {style_, ___} :> ( - style - // Replace[{ - (* "Package" :> {URLParse[doc["uri"], "Path"] // Last, (* name line: *) False}, *) - _String :> ( - Part[doc@"text", line + 1] - // Replace[( - (* if the line contains style definitions, discard it *) - _?(StringContainsQ["(* "~~"::"~~___~~"::"~~" *)"]) | - (* if the line contains strings outside the comment, discard it *) - _?(Not@*StringMatchQ[WhitespaceCharacter___~~"(*"~~___~~"*)"~~WhitespaceCharacter___]) - ) -> ""] - // StringCases["(*"~~Longest[name___]~~"*)" :> name] - // Replace[{ - {name_String, ___} :> {name, (* name line: *) True}, - {} -> {"", (* name line: *) False} - }] - ), - _AdditionalStyle -> {"", (* name line: *) False} - }] - // Apply[{name, nameLineQ} \[Function] CellNode[<| - "style" -> style, - "name" -> (name // Replace[ - "" -> "[unnamed]" - ]), - "range" -> {line, Infinity}, - style - // Replace[{ - _String :> ( - "selectionRange" -> ( - If[nameLineQ, - Part[doc@"text", line + 1] - // StringPosition["(*"<>name<>"*)"] - // First, - Part[doc@"text", line] - // StringPosition["(* "~~"::"<>style<>"::"~~" *)"] - // First - ] // Apply[{start, end} \[Function] LspRange[<| - "start" -> LspPosition[<| - "line" -> line, - "character" -> start - |>], - "end" -> LspPosition[<| - "line" -> line, - "character" -> end - |>] - |>]] - ) - ), - _AdditionalStyle -> ( - Nothing - ) - }], - "codeRange" -> {{ - If[nameLineQ, line + 2, line + 1], - Infinity - }} - |>]] - ), - {(* current line does not contain styles *)} -> Nothing + /* Through + ]; + + emptyLineNo = Position[doc["text"], "", {1}, Heads -> False] // Flatten; + + Join[ + (* styleLines *) + Thread[{ + "label" -> "style", + Thread["line" -> styleLineNo], + Thread["data" -> Block[ + { + styleLines = Part[doc["text"], styleLineNo] + }, + + styleLines + (* selectionRange *) + // StringPosition["(* " ~~ "::" ~~ Shortest[style___] ~~ "::" ~~ " *)"] + // Part[#, All, 1]& + // Apply[{#1 + 5, #2 - 5}&, #, {1}]& + // { + (* {("selectionRange" -> selectionRange)..} *) + Thread["selectionRange" -> #]&, + (* {("style" -> style)..} *) + Thread["style" -> ( + StringTake @@@ + Thread[{styleLines, #}] + )]& + } // Through + // Thread + // Map[Association] + ]] }], - {line, Length@doc@"text"} + + (* titleLines *) + Thread[{ + "label" -> "title", + Thread["line" -> titleLineNo], + Thread["data" -> Block[ + { + titleLines = Part[doc["text"], titleLineNo] + }, + + titleLines + // StringPosition["(*" ~~ Longest[title___] ~~ "*)"] + // Part[#, All, 1]& + // Apply[{#1 + 2, #2 - 2}&, #, {1}]& + // { + Thread["selectionRange" -> #]&, + Thread["title" -> ( + StringTake @@@ + Thread[{titleLines, #}] + )]& + } // Through + // Thread + // Map[Association] + ]] + }], + + (* emptyLines *) + Thread[{ + "label" -> "empty", + Thread["line" -> emptyLineNo] + }] ] + // Map[Association] + // SortBy[Key["line"]] + // Append[<|"label" -> "end", "line" -> (Length[doc["text"]] + 1)|>] + // LogDebug + // (divideCellImpl[#, + If[doc["text"] // First // StringStartsQ["#!"], 1, 0] + // <|"label" -> "start", "startLine" -> #, "endLine" -> #|>& + ] // Reap)& + // Last // First (* Get sown List*) // Prepend[CellNode[<| "style" -> AdditionalStyle["File"], "name" -> "", - "range" -> {1, Infinity}, - "codeRange" -> {{If[doc["text"] // First // StringStartsQ["#!"], 2, 1], Infinity}}, + "range" -> {1, 1}, + "codeRange" -> {}, "children" -> {} |>]] // Fold[InsertCell] - // Curry[TerminateCell][Length[doc["text"]]] + // TerminateCell +] -) +divideCellImpl[{}, state_Association] := Null +divideCellImpl[{lineState_Association, lineStates___}, state_Association] := divideCellImpl[{lineStates}, ( + Which[ + state["endLine"] + 1 == lineState["line"], + lineState["label"] + // Replace[{ + "empty" :> ( + state + // ReplacePart["endLine" -> lineState["line"]] + ), + "style" :> ( + state["label"] + // Replace[{ + "empty" :> ( + Sow[{ + "codeEnd", + If[state["startLine"] < state["endLine"], + state["startLine"] - 1, + state["endLine"] + ] + }] + ), + "style" :> ( + Sow[{"untitledStyle", state["startLine"], state["data"]}] + ) + (* do nothing: {"start"} | {"title",_,_} *) + }]; + <| + "label" -> "style", + "startLine" -> lineState["line"], + "endLine" -> lineState["line"], + "data" -> lineState["data"] + |> + ), + If[ + state["label"] == "style" && + state["startLine"] + 1 == lineState["line"] && + !MemberQ[{"Package"}, state["data"]["style"]], + "title" :> ( + Sow[{ + "titledStyle", + state["startLine"], + Join[state["data"], lineState["data"]] + }]; + <| + "label" -> "title", + "startLine" -> lineState["line"], + "endLine" -> lineState["line"], + "data" -> lineState["data"] + |> + ), + "title" :> ( + state + ) + ], + "end" :> ( + state["label"] + // Replace[{ + "empty" :> ( + Sow[{"codeEnd", lineState["line"] - 2}] + ) + }]; + <| + "label" -> "end", + "startLine" -> lineState["line"], + "endLine" -> lineState["line"] + |> + ) + }], -InsertCell[rootCell_CellNode, newCell_CellNode] := ( + state["endLine"] + 1 < lineState["line"], + (* any -> normal *) + state["label"] + // Replace[{ + "empty" :> ( + If[state["startLine"] < state["endLine"], + Sow[{"codeEnd", state["endLine"] - 2}] + ] + ), + "style" :> ( + Sow[{"untitledStyle", state["startLine"], state["data"]}] + ) + }]; + Sow[{"codeStart", state["endLine"] + 1}]; + (* normal -> any *) + lineState["label"] + // Replace[{ + "empty" :> ( + <| + "label" -> "empty", + "startLine" -> lineState["line"], + "endLine" -> lineState["line"] + |> + ), + "style" :> ( + Sow[{"codeEnd", lineState["line"]}]; + <| + "label" -> "style", + "startLine" -> lineState["line"], + "endLine" -> lineState["line"], + "data" -> lineState["data"] + |> + ), + "end" :> ( + Sow[{"codeEnd", lineState["line"] - 1}]; + <| + "label" -> "end", + "startLine" -> lineState["line"], + "endLine" -> lineState["line"] + |> + ) + }], + + (* state["endLine"] + 1 > lineState["line"], *) + True, + (* do nothing *) + state + ] +)] + + +InsertCell[rootCell_CellNode, directive:{"titledStyle"|"untitledStyle", line_Integer, data_Association}] := ( + If[!HeadingQ[data["style"]], + Return[rootCell] + ]; rootCell["children"] // Replace[{ _?MissingQ|{} :> ( rootCell - // ReplaceKey[{"codeRange", -1 ,-1} -> (First[newCell["range"]] - 1)] - // ReplaceKeyBy[{"codeRange", -1} -> Replace[{s_, e_} /; (s > e) -> Nothing]] - // If[HeadingQ[newCell["style"]], - ReplaceKey["children" -> {newCell}], - (* not a heading style, append its code range to its parent *) - ReplaceKeyBy["codeRange" -> (Join[#, newCell["codeRange"]]&)] - ] + // ReplaceKey["children" -> {cellDirectiveToCellNode[directive]}] ), {preCells___, lastCell_CellNode} :> ( - If[HeadingQ[newCell["style"]] \[Implies] (HeadingLevel[lastCell["style"]] < HeadingLevel[newCell["style"]]), + If[HeadingLevel[lastCell["style"]] < HeadingLevel[data["style"]], (* includes the new cell in the last child *) rootCell - // ReplaceKey["children" -> {preCells, InsertCell[lastCell, newCell]}], + // ReplaceKey["children" -> {preCells, InsertCell[lastCell, directive]}], (* append the new cell after the last child *) rootCell // ReplaceKey["children" -> { preCells, - TerminateCell[lastCell, First[newCell["range"]] - 1], - newCell + TerminateCell[lastCell], + cellDirectiveToCellNode[directive] }] ] ) }] - ) +cellDirectiveToCellNode[{label:("titledStyle"|"untitledStyle"), line_Integer, data_Association}] := ( + CellNode[<| + "style" -> data["style"], + "name" -> If[label == "titledStyle", data["title"], "[untitled]"], + "range" -> { + line, + line + If[label == "titledStyle", 1, 0] + }, + "selectionRange" -> LspRange[<| + "start" -> LspPosition[<| + "line" -> line - 1, + "character" -> Part[data["selectionRange"], 1] - 1 + |>], + "end" -> LspPosition[<| + "line" -> line - 1, + "character" -> Part[data["selectionRange"], 2] + |>] + |>], + "codeRange" -> {} + |>] +) -TerminateCell[cell_CellNode, endLine_Integer] := ( - cell - // ReplaceKey[{"range", -1} -> endLine] - // ReplaceKeyBy[{"codeRange", -1, -1} -> Replace[{ - Infinity -> endLine - }]] - // ReplaceKeyBy[{"codeRange", -1} -> Replace[{s_, e_} /; (s > e) -> Nothing]] - // ReplaceKeyBy[{"children"} -> Replace[{ - _?MissingQ :> {}, - {children___, lastChild_CellNode} :> { - children, - TerminateCell[lastChild, endLine] - } - }]] +InsertCell[rootCell_CellNode, directive:{label:("codeStart"|"codeEnd"), line_Integer}] := ( + rootCell["children"] + // Replace[{ + _?MissingQ|{} :> ( + rootCell + // If[label == "codeStart", + ReplaceKeyBy[ + "codeRange" -> Append[{line, line}] + ], + ReplaceKey[ + {"codeRange", -1, -1} -> line + ] + ] + ), + {preCells___, lastCell_CellNode} :> ( + rootCell + // ReplaceKey["children" -> {preCells, InsertCell[lastCell, directive]}] + ) + }] ) -CellContainsLine[indexLine_Integer][cell_CellNode] := ( - indexLine // Between[cell["range"]] -) +TerminateCell[cell_CellNode] := Block[ + { + newLastChild = cell["children"] + // Replace[{ + _?MissingQ|{} :> Nothing, + {___, lastChild_CellNode} :> ( + TerminateCell[lastChild] + ) + }] + }, + + cell + // ReplaceKey[{"range", -1} -> Max[{ + cell["range"] // Last, + If[Length[cell["codeRange"]] > 0, + cell["codeRange"] + // Last + // Last, + Nothing + ], + newLastChild + // Replace[ + _CellNode :> ( + newLastChild["range"] // Last + ) + ] + }]] + // ReplaceKeyBy["children" -> ( + Replace[{ + _?MissingQ -> {}, + {children___, _CellNode} :> { + children, + newLastChild + } + }] + )] +] HeadingCellQ[cellNode_CellNode] := HeadingQ[cellNode["style"]] @@ -330,6 +534,10 @@ CellToAST[doc_TextDocument, {startLine_, endLine_}] := ( ) +CellContainsLine[indexLine_Integer][cell_CellNode] := ( + indexLine // Between[cell["range"]] +) + GetCodeRangeAtPosition[doc_TextDocument, pos_LspPosition] := With[ { From a58d4a093a2ecfcde7d8341b1ec703722db67cc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BA=B7=E5=B0=8F=E5=B9=BF?= Date: Thu, 16 Jan 2020 00:11:21 -0800 Subject: [PATCH 02/40] Expose AssociationSameQ in `Test` context for testing --- external/Matypetica/src/DataType.wl | 10 ++++++++++ external/Matypetica/test/DataTypeTest.wl | 6 +++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/external/Matypetica/src/DataType.wl b/external/Matypetica/src/DataType.wl index 7c09346..d864ded 100644 --- a/external/Matypetica/src/DataType.wl +++ b/external/Matypetica/src/DataType.wl @@ -339,4 +339,14 @@ TypeUsage[typename_Symbol, usage_String] := ( End[] +Begin["`Test`"] + + +AssociationSameQ::usage = "AssociationSameQ[assoc1_Association, assoc2_Association] returns True if two associations are same except for key order." +AssociationSameQ = DataType`Private`AssociationSameQ + + +End[] + + EndPackage[] diff --git a/external/Matypetica/test/DataTypeTest.wl b/external/Matypetica/test/DataTypeTest.wl index 18f21fd..addbda2 100644 --- a/external/Matypetica/test/DataTypeTest.wl +++ b/external/Matypetica/test/DataTypeTest.wl @@ -16,7 +16,7 @@ Needs[TestingContext] { VerificationTest[ - AssociationSameQ @@@ { + DataType`Test`AssociationSameQ @@@ { {<|"a" -> 1, "b" -> 2|>, <|"b" -> 2, "a" -> 1 |>}, {<|"a" -> 1, "b" -> 2|>, <|"b" -> 1, "a" -> 2 |>}, {<|"a" -> 1, "b" -> 2|>, <|"a" -> 2 |>}, @@ -155,7 +155,7 @@ VerificationTest[ stu1["courses"], <|1-> "ECON101", 2->"COMP202", 3->"PHYS201"|>, TestID -> "Replace Association", - SameTest -> AssociationSameQ + SameTest -> DataType`Test`AssociationSameQ ], VerificationTest[ @@ -178,7 +178,7 @@ VerificationTest[ stu1["courses"], <|1-> "ECON101", 2->"COMP202", 3->"PHYS201"|>, TestID -> "Replace Association", - SameTest -> AssociationSameQ + SameTest -> DataType`Test`AssociationSameQ ], VerificationTest[ From 5de43878e491e32ed4b42723b9f3f8b3eefb1361 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BA=B7=E5=B0=8F=E5=B9=BF?= Date: Thu, 16 Jan 2020 03:09:25 -0800 Subject: [PATCH 03/40] Refine: divideCell implementation and add tests --- src/WolframLanguageServer/TextDocument.wl | 21 ++- .../WolframLanguageServer/TextDocumentTest.wl | 154 ++++++++++++++++++ 2 files changed, 167 insertions(+), 8 deletions(-) diff --git a/src/WolframLanguageServer/TextDocument.wl b/src/WolframLanguageServer/TextDocument.wl index 4cf1860..867c2f9 100644 --- a/src/WolframLanguageServer/TextDocument.wl +++ b/src/WolframLanguageServer/TextDocument.wl @@ -341,14 +341,18 @@ divideCellImpl[{lineState_Association, lineStates___}, state_Association] := div // Replace[{ "empty" :> ( If[state["startLine"] < state["endLine"], - Sow[{"codeEnd", state["endLine"] - 2}] + Sow[{"codeEnd", state["endLine"] - 2}]; + Sow[{"codeStart", state["endLine"] + 1}] ] ), "style" :> ( - Sow[{"untitledStyle", state["startLine"], state["data"]}] + Sow[{"untitledStyle", state["startLine"], state["data"]}]; + Sow[{"codeStart", state["endLine"] + 1}] + ), + "title" | "start" :> ( + Sow[{"codeStart", state["endLine"] + 1}] ) }]; - Sow[{"codeStart", state["endLine"] + 1}]; (* normal -> any *) lineState["label"] // Replace[{ @@ -360,7 +364,7 @@ divideCellImpl[{lineState_Association, lineStates___}, state_Association] := div |> ), "style" :> ( - Sow[{"codeEnd", lineState["line"]}]; + Sow[{"codeEnd", lineState["line"] - 1}]; <| "label" -> "style", "startLine" -> lineState["line"], @@ -417,7 +421,7 @@ InsertCell[rootCell_CellNode, directive:{"titledStyle"|"untitledStyle", line_Int cellDirectiveToCellNode[{label:("titledStyle"|"untitledStyle"), line_Integer, data_Association}] := ( CellNode[<| "style" -> data["style"], - "name" -> If[label == "titledStyle", data["title"], "[untitled]"], + "name" -> If[label == "titledStyle" && data["title"] != "", data["title"], "[untitled]"], "range" -> { line, line + If[label == "titledStyle", 1, 0] @@ -792,7 +796,7 @@ ToDocumentSymbolImpl[node_] := ( ) -ToLspRange[doc_TextDocument, {startLine_Integer, endLine_Integer}] := <| +ToLspRange[doc_TextDocument, {startLine_Integer, endLine_Integer}] := LspRange[<| "start" -> LspPosition[<| "line" -> startLine - 1, "character" -> 0 @@ -809,7 +813,7 @@ ToLspRange[doc_TextDocument, {startLine_Integer, endLine_Integer}] := <| |> ] ] -|> +|>] GetSymbolList[node_] := ( @@ -1078,7 +1082,8 @@ FindScopeOccurence[doc_TextDocument, pos_LspPosition, o:OptionsPattern[]] := Blo ), Missing["NotFound"], AstLevelspec["LeafNodeWithSource"] - ] // Replace[_?MissingQ :> Return[{{}, {}}]]; + ] + // Replace[_?MissingQ :> Return[{{}, {}}]]; LogDebug["Searching for " <> name]; diff --git a/test/WolframLanguageServer/TextDocumentTest.wl b/test/WolframLanguageServer/TextDocumentTest.wl index 61b3f9b..d034ca6 100644 --- a/test/WolframLanguageServer/TextDocumentTest.wl +++ b/test/WolframLanguageServer/TextDocumentTest.wl @@ -102,6 +102,160 @@ VerificationTest[ "" }, TestID -> "ChangeTextDocument2" +], + +VerificationTest[ + FindAllCodeRanges[TextDocument[<| + "text" -> { + "(* " ~~ "::Package::" ~~ " *)", + "(* code range with one line *)" + } + |>]], + {LspRange[<| + "start" -> LspPosition[<| + "line" -> 1, + "character" -> 0 + |>], + "end" -> LspPosition[<| + "line" -> 1, + "character" -> 30 + |>] + |>]}, + TestID -> "FindAllCodeRangePackage1" +], + +VerificationTest[ + FindAllCodeRanges[TextDocument[<| + "text" -> { + "(* " ~~ "::Package::" ~~ " *)", + "", + "(* code range with one line *)" + } + |>]], + {LspRange[<| + "start" -> LspPosition[<| + "line" -> 2, + "character" -> 0 + |>], + "end" -> LspPosition[<| + "line" -> 2, + "character" -> 30 + |>] + |>]}, + TestID -> "FindAllCodeRangePackage2" +], + +VerificationTest[ + FindAllCodeRanges[TextDocument[<| + "text" -> { + "(* " ~~ "::Section::" ~~ " *)", + "(*section name*)", + "", + "", + "(* code range with four lines *)", + "", + "(* code range with four lines *)", + "(* code range with four lines *)", + "", + "" + } + |>]], + { + LspRange[<| + "start" -> LspPosition[<| + "line" -> 4, + "character" -> 0 + |>], + "end" -> LspPosition[<| + "line" -> 9, + "character" -> 0 + |>] + |>] + }, + TestID -> "FindAllCodeRangeSection1" +], + +VerificationTest[ + FindAllCodeRanges[TextDocument[<| + "text" -> { + "(* " ~~ "::Section::" ~~ " *)", + "(*section name*)", + "", + "(* code range with four lines *)", + "", + "(* code range with four lines *)", + "(* code range with four lines *)", + "" + } + |>]], + { + LspRange[<| + "start" -> LspPosition[<| + "line" -> 3, + "character" -> 0 + |>], + "end" -> LspPosition[<| + "line" -> 7, + "character" -> 0 + |>] + |>] + }, + TestID -> "FindAllCodeRangeSection2" +], + +VerificationTest[ + FindAllCodeRanges[TextDocument[<| + "text" -> { + "(* " ~~ "::Section::" ~~ " *)", + "(*section name*)", + "(* code range with four lines *)", + "", + "(* code range with four lines *)", + "(* code range with four lines *)" + } + |>]], + { + LspRange[<| + "start" -> LspPosition[<| + "line" -> 2, + "character" -> 0 + |>], + "end" -> LspPosition[<| + "line" -> 5, + "character" -> 32 + |>] + |>] + }, + TestID -> "FindAllCodeRangeSection3" +], + +VerificationTest[ + FindAllCodeRanges[TextDocument[<| + "text" -> { + "(* " ~~ "::Section::" ~~ " *)", + "(*section name*)", + "", + "", + "(* code range with one line *)", + "(* " ~~ "::Section::" ~~ " *)", + "(*section name*)", + "", + "" + } + |>]], + { + LspRange[<| + "start" -> LspPosition[<| + "line" -> 4, + "character" -> 0 + |>], + "end" -> LspPosition[<| + "line" -> 5, + "character" -> 0 + |>] + |>] + }, + TestID -> "FindAllCodeRangeTwoSection1" ] } // Map@Curry[Sow]@CurrentContext From 75c0342ecbc3f8adbd6fbd0f5d4b9e869e9acce3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BA=B7=E5=B0=8F=E5=B9=BF?= Date: Wed, 5 Feb 2020 02:30:39 -0800 Subject: [PATCH 04/40] Add: cache to store some expensive requests. Known issues: * documentColor blocks if user stops for a certain period and starts to type. * cache may be outdated if the textDocument changes a lot in a short while. --- init.wls | 1 + src/WolframLanguageServer/Server.wl | 597 +++++++++++++--------- src/WolframLanguageServer/TextDocument.wl | 41 +- 3 files changed, 380 insertions(+), 259 deletions(-) diff --git a/init.wls b/init.wls index 6f6b7e1..857e146 100644 --- a/init.wls +++ b/init.wls @@ -212,6 +212,7 @@ Module[ DeclarePackage["WolframLanguageServer`Server`", {"WLServerStart", "WLServerVersion", "WLServerDebug"}]; *) << WolframLanguageServer`Server`; + WolframLanguageServer`CheckReturnTypeQ = True; LogDebug @ WolframLanguageServer`Server`WLServerStart[ "Stream" -> stream, "ClientPid" -> clientPid, diff --git a/src/WolframLanguageServer/Server.wl b/src/WolframLanguageServer/Server.wl index 96e0c7a..b9b718e 100644 --- a/src/WolframLanguageServer/Server.wl +++ b/src/WolframLanguageServer/Server.wl @@ -45,17 +45,35 @@ DeclareType[WorkState, <| "openedDocs" -> _Association, (* (_DocumentUri -> _DocumentText)... *) "client" -> (_SocketClient | _SocketObject | _NamedPipe | _StdioClient | "stdio" | Null), "clientCapabilities" -> _Association, - "config" -> _Association, - "scheduledTasks" -> {___ServerTask} -|>]; + "scheduledTasks" -> {___ServerTask}, + "caches" -> _Association, + "config" -> _Association +|>] + + +DeclareType[RequestCache, <| + "cachedTime" -> _DateObject, + "result" -> _, + "error" -> _Association +|>] + +initialCaches = <| + "textDocument/documentSymbol" -> <||>, + "textDocument/documentColor" -> <||>, + "textDocument/codeLens" -> <||>, + "textDocument/publishDiagnostics" -> <||> +|> InitialState = WorkState[<| "initialized" -> False, "openedDocs" -> <||>, "client" -> Null, - "config" -> loadConfig[], - "scheduledTasks" -> {} -|>]; + "scheduledTasks" -> {}, + "caches" -> initialCaches, + "config" -> <| + "configFileConfig" -> loadConfig[] + |> +|>] ServerCapabilities = <| "textDocumentSync" -> TextDocumentSyncKind["Full"], @@ -76,10 +94,28 @@ ServerCapabilities = <| |>, *) Nothing |> -(*Place where the temporary img would be stored, delete after usage.*) -(* tempImgPath = $TemporaryDirectory <> $PathnameSeparator <> "temp.svg"; *) -(* tempDirPath = WolframLanguageServer`Directory <> $PathnameSeparator <> "Cache"; *) -(* If[!FileExistsQ[tempImgPath], CreateFile[tempImgPath], ]; *) + +ServerConfig = <| + "updateCheckInterval" -> Quantity[7, "Days"], + (* cached results *) + "cachedRequests" -> { + "textDocument/documentSymbol", + "textDocument/documentColor" + (* "textDocument/codeLens" *) + }, + "delayedRequests" -> { + "textDocument/documentHighlight", + "textDocument/documentColor" + }, + (* default delays (seconds) *) + "requestDelays" -> <| + "textDocument/publishDiagnostics" -> 2.5, + "textDocument/signatrueHelp" -> 0.5, + "textDocument/documentSymbol" -> 4.0, + "textDocument/documentHighlight" -> 0.5, + "textDocument/documentColor" -> 5.0 + |> +|> (* ::Section:: *) @@ -111,7 +147,7 @@ WLServerStart[o:OptionsPattern[]] := Module[ (*Temporary cache.*) tempDirPath = FileNameJoin[{workingDir, "wlServerCache"}]; - If[DirectoryQ[tempDirPath], + If[DirectoryQ[tempDirPath], (*Clear the cache of last time usage.*) DeleteDirectory[tempDirPath, DeleteContents -> True], (*Create temporary file.*) @@ -276,7 +312,7 @@ SocketHandler[packet_Association] := Module[ { bytearray = packet["DataByteArray"], client = serverState["client"], - headerEndPosition, headertext, contentlength, + headerEndPosition, headertext, contentlength, consumelength, message, serverStatus }, @@ -353,16 +389,17 @@ TcpSocketHandler[state_WorkState] := Module[ }, If[SocketReadyQ[client], - handleMessageList[ReadMessages[client], state] - // Replace[{ - {"Continue", newstate_} :> newstate, - {stop_, newstate_} :> ( - {stop, newstate} - ), - {} :> state (* No message *) - }], + handleMessageList[ReadMessages[client], state], doNextScheduledTask[state] ] + // Replace[{ + {"Continue", newstate_} :> newstate, + {stop_, newstate_} :> ( + {stop, newstate} + ), + {} :> state, (* No message *) + err_ :> {LogError[err], state} + }] ] // TcpSocketHandler (* Put the recursive call out of the module to turn on the tail-recursion optimization *) @@ -647,25 +684,18 @@ CloseClient[client_NamedPipe] := With[ (*Handle Message*) -NotificationQ[msg_Association] := MissingQ[msg["id"]]; +NotificationQ = KeyExistsQ["id"] /* Not +(* NotificationQ[msg_Association] := MissingQ[msg["id"]] *) handleMessageList[msgs:{___Association}, state_WorkState] := ( FoldWhile[handleMessage[#2, Last[#1]]&, {"Continue", state}, msgs, MatchQ[{"Continue", _}]] ); -handleMessage[msg_Association, state_WorkState] := Module[ +handleMessage[msg_Association, state_WorkState] := With[ { - method, newState = state + method = msg["method"] }, - - method = msg["method"]; - Replace[method, { - "textDocument/didOpen" | "textDocument/didChange" :> ( - LogDebug @ Iconize[method] - ), - _ :> LogDebug @ Iconize[msg, method] - }]; - + Which[ (* wrong message before initialization *) !state["initialized"] && !MemberQ[{"initialize", "initialized", "exit"}, method], @@ -682,31 +712,78 @@ handleMessage[msg_Association, state_WorkState] := Module[ {"Continue", state}, (* notification*) NotificationQ[msg], - handleNotification[method, msg, newState], + handleNotification[method, msg, state], (* resquest *) True, - handleRequest[method, msg, newState] + Which[ + MemberQ[ServerConfig["cachedRequests"], method] && + cacheAvailableQ[method, msg, state], + LogInfo["Sending cached results of " <> ToString[method]]; + sendCachedResult[method, msg, state], + MemberQ[ServerConfig["delayedRequests"], method], + scheduleDelayedRequest[method, msg, state], + True, + handleRequest[method, msg, state] + ] ] ]; -(* ::Subsection:: *) -(*Send Response*) + +(* ::Section:: *) +(*Handle Requests*) + + +(* generic currying *) +cacheResponse[method_String, msg_][state_WorkState] = + cacheResponse[method, msg, state] -(* Both response and notification will call this function *) +sendCachedResult[method_String, msg_, state_WorkState] := Block[ + { + cache = getCache[method, msg, state] + }, + + sendResponse[state["client"], + <|"id" -> msg["id"]|> + // Append[ + If[!MissingQ[cache["result"]], + "result" -> cache["result"], + "error" -> cache["error"] + ] + ] + ]; + + {"Continue", state} +] + + +scheduleDelayedRequest[method_String, msg_, state_WorkState] := ( + { + "Continue", + state + // Curry[addScheduledTask][ServerTask[<| + "type" -> method, + "scheduledTime" -> DatePlus[Now, { + ServerConfig["requestDelays"][method], + "Second" + }], + "id" -> msg["id"], + "params" -> getScheduleTaskParameter[method, msg, state], + "callback" -> (handleRequest[method, msg, #1]&) + |>]] + } +) + + +(* response, notification and request will call this function *) sendResponse[client_, res_Association] := ( Prepend[res, <|"jsonrpc" -> "2.0"|>] - // LogDebug // constructRPCBytes // WriteMessage[client] ) -(* ::Section:: *) -(*Handle Requests*) - - (* ::Subsection:: *) (*initialize*) @@ -741,17 +818,75 @@ handleRequest["initialize", msg_, state_WorkState] := Module[ handleRequest["shutdown", msg_, state_] := Module[ { }, - - + sendResponse[state["client"], <| "id" -> msg["id"], "result" -> Null |>]; - + {"Continue", state} ]; +(* ::Subsection:: *) +(*workspace/executeCommand*) + + +handleRequest["workspace/executeCommand", msg_, state_] := With[ + { + command = msg["params"]["command"], + args = msg["params"]["arguments"] + }, + + Replace[command, { + "dap-wl.runfile" -> ( + LogInfo[StringJoin["executing ", command, "with arguments: ", ToString[args]]] + ) + }]; + + sendResponse[state["client"], <| + "id" -> msg["id"], + "result" -> Null + |>]; + + {"Continue", state} + +] + + +(* ::Subsection:: *) +(*textDocument/publishDiagnostics*) + + +handleRequest["textDocument/publishDiagnostics", uri_String, state_WorkState] := ( + sendResponse[state["client"], <| + "method" -> "textDocument/publishDiagnostics", + "params" -> <| + "uri" -> uri, + "diagnostics" -> ToAssociation[DiagnoseDoc[state["openedDocs"][uri]]] + |> + |>]; + {"Continue", state} +) + + +handleRequest["textDocument/clearDiagnostics", uri_String, state_WorkState] := ( + sendResponse[state["client"], <| + "method" -> "textDocument/publishDiagnostics", + "params" -> <| + "uri" -> uri, + "diagnostics" -> {} + |> + |>]; + {"Continue", state} +) + + +getScheduleTaskParameter[method:"textDocument/publishDiagnostics", uri_String, state_WorkState] := ( + uri +) + + (* ::Subsection:: *) (*textDocument/hover*) @@ -915,113 +1050,133 @@ handleRequest["textDocument/references", msg_, state_] := With[ (*textDocument/documentHighlight*) -handleRequest["textDocument/documentHighlight", msg_, state_] := With[ +handleRequest[method:"textDocument/documentHighlight", msg_, state_WorkState] := With[ { id = msg["id"], uri = msg["params"]["textDocument"]["uri"], - pos = LspPosition[msg["params"]["position"]], - scheduledTime = DatePlus[Now, { - state["config"]["documentHighlightDelay"], - "Second" - }] + pos = LspPosition[msg["params"]["position"]] }, - state - // Curry[addScheduledTask][ServerTask[<| - "type" -> "documentHighlight", - "scheduledTime" -> scheduledTime, + sendResponse[state["client"], <| "id" -> id, - "params" -> {uri, pos}, - "callback" -> (sendResponse[#1["client"], <| - "id" -> id, - "result" -> ( - { - #1["openedDocs"][uri], - pos - } - // Apply[FindDocumentHighlight] - // ToAssociation - ) - |>]&) - |>]] - // List - // Prepend["Continue"] + "result" -> ( + FindDocumentHighlight[state["openedDocs"][uri], pos] + // ToAssociation + ) + |>]; + {"Continue", state} ] +getScheduleTaskParameter["textDocument/documentHighlight", msg_, state_WorkState] := ( + msg +) + + (* ::Subsection:: *) (*textDocument/documentSymbol*) -handleRequest["textDocument/documentSymbol", msg_, state_] := With[ +handleRequest[method:"textDocument/documentSymbol", msg_, state_WorkState] := ( + state + // cacheResponse[method, msg] + // sendCachedResult[method, msg, #]& +) + + +cacheResponse[method:"textDocument/documentSymbol", msg_, state_WorkState] := With[ { - id = msg["id"], - uri = msg["params"]["textDocument"]["uri"], - scheduledTime = DatePlus[ - state["openedDocs"][msg["params"]["textDocument"]["uri"]]["lastUpdate"], - {state["config"]["documentSymbolDelay"], "Second"} - ] + uri = msg["params"]["textDocument"]["uri"] }, state - // Curry[addScheduledTask][ServerTask[<| - "type" -> "documentSymbol", - "scheduledTime" -> scheduledTime, - "id" -> id, - "params" -> uri, - "callback" -> (sendResponse[#1["client"], <| - "id" -> id, + // ReplaceKey[ + {"caches", method, uri} -> RequestCache[<| + "cachedTime" -> Now, "result" -> ( - #1["openedDocs"][uri] + state["openedDocs"][uri] // ToDocumentSymbol // ToAssociation ) - |>]&) - |>]] - // List - // Prepend["Continue"] + |>] + ] ] +cacheAvailableQ[method:"textDocument/documentSymbol", msg_, state_WorkState] := Block[ + { + cachedTime = getCache[method, msg, state]["cachedtime"] + }, + + !MissingQ[cachedTime] && + !( + cachedTime < + state["openedDocs"][ + msg["params"]["textDocument"]["uri"] + ]["lastUpdate"] < + (Now - Quantity[ServerConfig["requestDelays"][method], "Seconds"]) + ) +] + + +getCache[method:"textDocument/documentSymbol", msg_, state_WorkState] := ( + state["caches"][method][msg["params"]["textDocument"]["uri"]] +) + + (* ::Subsection:: *) (*textDocument/documentColor*) -handleRequest["textDocument/documentColor", msg_, state_] := With[ +handleRequest[method:"textDocument/documentColor", msg_, state_WorkState] := ( + state + // cacheResponse[method, msg] + // sendCachedResult[method, msg, #]& +) + + +cacheResponse[method:"textDocument/documentColor", msg_, state_WorkState] := With[ { - id = msg["id"], - uri = msg["params"]["textDocument"]["uri"], - scheduledTime = DatePlus[ - state["openedDocs"][msg["params"]["textDocument"]["uri"]]["lastUpdate"], - {state["config"]["documentColorDelay"], "Second"} - ] + uri = msg["params"]["textDocument"]["uri"] }, state - // Curry[addScheduledTask][ServerTask[<| - "type" -> "documentColor", - "scheduledTime" -> scheduledTime, - "id" -> id, - "params" -> uri, - "callback" -> (sendResponse[#1["client"], <| - "id" -> id, + // ReplaceKey[ + {"caches", method, uri} -> RequestCache[<| + "cachedTime" -> Now, "result" -> ( - #1["openedDocs"][uri] + state["openedDocs"][uri] // FindDocumentColor // ToAssociation ) - |>]&), - "duplicateFallback" -> (sendResponse[#1["client"], <| - "id" -> id, - "result" -> {} - |>]&) - |>]] - // List - // Prepend["Continue"] + |>] + ] ] +cacheAvailableQ[method:"textDocument/documentColor", msg_, state_WorkState] := Block[ + { + cachedTime = getCache[method, msg, state]["cachedTime"] + }, + + !MissingQ[cachedTime] && + (state["openedDocs"][ + msg["params"]["textDocument"]["uri"] + ]["lastUpdate"] < cachedTime) +] + + +getCache[method:"textDocument/documentColor", msg_, state_WorkState] := ( + state["caches"][method][msg["params"]["textDocument"]["uri"]] +) + + +getScheduleTaskParameter[method:"textDocument/documentColor", msg_, state_WorkState] := ( + msg["params"]["textDocument"]["uri"] +) + + (* ::Subsection:: *) (*textDocument/colorPresentation*) @@ -1158,10 +1313,21 @@ handleNotification["textDocument/didOpen", msg_, state_] := With[ // ReplaceKeyBy["openedDocs" -> Append[textDocumentItem["uri"] -> CreateTextDocument[textDocumentItem]] ] - // (newState \[Function] ( - publishDiagnostics[newState, textDocumentItem["uri"]]; - {"Continue", newState} - )) + // ReplaceKeyBy[{"caches", "textDocument/documentSymbol"} -> + Append[textDocumentItem["uri"] -> RequestCache[<||>]] + ] + // ReplaceKeyBy[{"caches", "textDocument/documentColor"} -> + Append[textDocumentItem["uri"] -> RequestCache[<||>]] + ] + // ReplaceKeyBy[{"caches", "textDocument/codeLens"} -> + Append[textDocumentItem["uri"] -> RequestCache[<||>]] + ] + // ReplaceKeyBy[{"caches", "textDocument/publishDiagnostics"} -> + Append[textDocumentItem["uri"] -> <| + "scheduledQ" -> False + |>] + ] + // handleRequest["textDocument/publishDiagnostics", textDocumentItem["uri"], #]& ] @@ -1173,11 +1339,15 @@ handleNotification["textDocument/didClose", msg_, state_] := With[ { uri = msg["params"]["textDocument"]["uri"] }, - + LogDebug @ ("Close Document " <> uri); - clearDiagnostics[state, uri]; - {"Continue", ReplaceKeyBy[state, {"openedDocs"} -> KeyDrop[uri]]} + state + // ReplaceKeyBy[{"openedDocs"} -> KeyDrop[uri]] + // ReplaceKeyBy[{"caches", "textDocument/documentSymbol"} -> KeyDrop[uri]] + // ReplaceKeyBy[{"caches", "textDocument/documentColor"} -> KeyDrop[uri]] + // ReplaceKeyBy[{"caches", "textDocument/codeLens"} -> KeyDrop[uri]] + // handleRequest["textDocument/clearDiagnostics", uri, #]& ] @@ -1185,10 +1355,14 @@ handleNotification["textDocument/didClose", msg_, state_] := With[ (*textSync/didChange*) -handleNotification["textDocument/didChange", msg_, state_] := With[ +handleNotification["textDocument/didChange", msg_, state_WorkState] := With[ { doc = msg["params"]["textDocument"], - uri = msg["params"]["textDocument"]["uri"] + uri = msg["params"]["textDocument"]["uri"], + (* lastUpdate = state["openedDocs"][msg["params"]["textDocument"]["uri"]]["lastUpdate"] *) + diagScheduledQ = state["caches"]["textDocument/publishDiagnostics"][ + msg["params"]["textDocument"]["uri"] + ]["scheduledQ"] }, (* Because of concurrency, we have to make sure the changed message brings a newer version. *) @@ -1214,14 +1388,7 @@ handleNotification["textDocument/didChange", msg_, state_] := With[ (* newState["openedDocs"][uri]["version"] = doc["version"]; *) LogDebug @ ("Change Document " <> uri); - (* Clean the diagnostics only if lastUpdate time is before delay *) - If[DatePlus[state["openedDocs"][uri]["lastUpdate"], { - state["config"]["diagnosticsDelay"], - "Second" - }] < Now, - clearDiagnostics[state, uri] - ]; state // ReplaceKey[{"openedDocs", uri} -> ( @@ -1237,19 +1404,22 @@ handleNotification["textDocument/didChange", msg_, state_] := With[ "type" -> "JustContinue", "scheduledTime" -> Now |>]] - (* Give diagnostics after delay *) - // Curry[addScheduledTask][ServerTask[<| - "type" -> "PublishDiagnostics", - "params" -> uri, - "scheduledTime" -> DatePlus[Now, { - state["config"]["diagnosticsDelay"], + // If[diagScheduledQ, + List + /* Prepend["Continue"], + ReplaceKey[{"caches", "textDocument/publishDiagnostics", uri, "scheduledQ"} -> True] + /* (scheduleDelayedRequest["textDocument/publishDiagnostics", uri, #]&) + ] + (* // If[DatePlus[lastUpdate, { + ServerConfig["requestDelays"]["textDocument/publishDiagnostics"], "Second" - }], - "callback" -> (publishDiagnostics[#1, uri]&) - |>]] - // List - // Prepend["Continue"] - + }] < Now, + handleRequest["textDocument/clearDiagnostics", uri, #]& + (* Give diagnostics after delay *) + /* Last + /* scheduleDelayedRequest["textDocument/publishDiagnostics", uri, #]& + /* Last, + ] *) ] @@ -1259,51 +1429,13 @@ handleNotification["textDocument/didChange", msg_, state_] := With[ handleNotification["textDocument/didSave", msg_, state_] := With[ { - uri = msg["params"]["textDocument"]["uri"] + (* uri = msg["params"]["textDocument"]["uri"] *) }, - - (* Give diagnostics after save *) - publishDiagnostics[state, uri]; - + (* do nothing *) {"Continue", state} ] -(* ::Subsection:: *) -(*textDocument/publishDiagnostics*) - - -diagnoseTextDocument[doc_TextDocument] := ( - <| - "uri" -> doc["uri"], - "diagnostics" -> ToAssociation/@DiagnoseDoc[doc["uri"], doc] - |> -) - - -publishDiagnostics[state_WorkState, uri_String] := ( - sendResponse[state["client"], <| - "method" -> "textDocument/publishDiagnostics", - "params" -> <| - "uri" -> uri, - "diagnostics" -> ToAssociation[DiagnoseDoc[state["openedDocs"][uri]]] - |> - |>] - (* Null *) -) - - -clearDiagnostics[state_WorkState, uri_String] := ( - sendResponse[state["client"], <| - "method" -> "textDocument/publishDiagnostics", - "params" -> <| - "uri" -> uri, - "diagnostics" -> {} - |> - |>] -) - - (* ::Subsection:: *) (*Invalid Notification*) @@ -1420,45 +1552,39 @@ doNextScheduledTask[state_WorkState] := ( // Replace[{ _?MissingQ :> ( Pause[0.001]; - state + {"Continue", state} ), task_ServerTask :> Block[ { newState = state // ReplaceKeyBy["scheduledTasks" -> Rest] }, - {task["type"], DateDifference[task["scheduledTime"], Now, "Second"] // InputForm} // LogDebug; + {task["type"], Chop[DateDifference[task["scheduledTime"], Now, "Second"], 10*^-6] // InputForm} // LogInfo; task["type"] // Replace[{ - "PublishDiagnostics" :> ( - newState = newState - (* delete all same tasks out of date *) - // ReplaceKeyBy["scheduledTasks" -> DeleteCases[_?(t \[Function] ( - t["type"] == "PublishDiagnostics" && - t["scheduledTime"] < Now && - t["params"] == task["params"] - ))]]; - - FirstPosition[ - newState["scheduledTasks"], - t_ /; ( - t["type"] == "PublishDiagnostics" && - t["params"] == task["params"] - ), - Missing["NotFound"], - {1} - ] // Replace[{ - (* if there will not be a same task in the future, do it now *) - _?MissingQ :> If[!MissingQ[task["callback"]], - task["callback"][newState, task["params"]] + method:"textDocument/publishDiagnostics" :> ( + newState + // If[DatePlus[ + newState["openedDocs"][task["params"]]["lastUpdate"], + {5, "Second"}] > Now, + (* Reschedule the task *) + scheduleDelayedRequest[method, task["params"], #]&, + ReplaceKey[ + { + "caches", + "textDocument/publishDiagnostics", + task["params"], + "scheduledQ" + } -> False ] - }] + /* (task["callback"][#, task["params"]]&) + ] ), "CheckUpgrades" :> ( - state - // checkUpgrades; + newState + // checkUpgrades ), - "JustContinue" -> Null, + "JustContinue" :> {"Continue", newState}, _ :> ( FirstPosition[ newState["scheduledTasks"], @@ -1475,15 +1601,16 @@ doNextScheduledTask[state_WorkState] := ( (* If the function is time constrained, than the there should not be a lot of lags. *) (* TimeConstrained[task["callback"][newState, task["params"]], 0.1, sendResponse[state["client"], <|"id" -> task["params"]["id"], "result" -> <||>|>]], *) task["callback"][newState, task["params"]] - (* // AbsoluteTiming - // Apply[(LogDebug[{task["type"], #1}];#2)&] *), + // AbsoluteTiming + // Apply[(LogInfo[{task["type"], #1}];#2)&], sendResponse[newState["client"], <| "id" -> task["id"], "error" -> ServerError[ "InternalError", "There is no callback function for this scheduled task." ] - |>] + |>]; + {"Continue", newState} ], (* find a recent duplicate request *) _ :> If[!MissingQ[task["duplicateFallback"]], @@ -1496,14 +1623,19 @@ doNextScheduledTask[state_WorkState] := ( "ContentModified", "There is a more recent duplicate request." ] - |>] + |>]; + {"Continue", newState} ] }] ) - }]; - newState + }] ] }] + // If[WolframLanguageServer`CheckReturnTypeQ, + Replace[err:Except[{_String, _WorkState}] :> LogError[err]], + Identity + ] + ) @@ -1542,13 +1674,7 @@ saveConfig[newConfig_Association] := With[ defaultConfig = <| - "lastCheckForUpgrade" -> DateString[Today], - (* default delays (seconds) *) - "diagnosticsDelay" -> 5.0, - "signatrueHelpDelay" -> 0.5, - "documentSymbolDelay" -> 3.0, - "documentHighlightDelay" -> 0.5, - "documentColorDelay" -> 5.0 + "lastCheckForUpgrade" -> DateString[Today] |> @@ -1556,28 +1682,25 @@ defaultConfig = <| (*check upgrades*) -checkUpgrades[state_WorkState] := With[ - { - checkInterval = Quantity[2, "Days"] - }, - - If[DateDifference[DateObject[state["config"]["lastCheckForUpgrade"]], Today] < checkInterval, +checkUpgrades[state_WorkState] := ( + If[ + DateDifference[ + DateObject[state["config"]["configFileConfig"]["lastCheckForUpgrade"]], + Today + ] < ServerConfig["updateCheckInterval"], logMessage[ "Upgrade not checked, only a few days after the last check.", "Log", state - ]; - Return[] + ], + (* check for upgrade if not checked for more than checkInterval days *) + checkGitRepo[state]; + checkDependencies[state]; + (* ReplaceKey[state["config"], "lastCheckForUpgrade" -> DateString[Today]] + // saveConfig *) ]; - - (* check for upgrade if not checked for more than checkInterval days *) - checkGitRepo[state]; - checkDependencies[state]; - - (* ReplaceKey[state["config"], "lastCheckForUpgrade" -> DateString[Today]] - // saveConfig *) - -] + {"Continue", state} +) checkGitRepo[state_WorkState] := ( Check[Needs["GitLink`"], diff --git a/src/WolframLanguageServer/TextDocument.wl b/src/WolframLanguageServer/TextDocument.wl index 867c2f9..d89305a 100644 --- a/src/WolframLanguageServer/TextDocument.wl +++ b/src/WolframLanguageServer/TextDocument.wl @@ -46,7 +46,6 @@ DeclareType[TextDocument, <| "uri" -> _DocumentUri, "text" -> {__String}, "version" -> _Integer, - "cell" -> _CellNode, "lastUpdate" -> _DateObject |>] @@ -188,11 +187,11 @@ divideCells[doc_TextDocument] := Block[ (* styleLines *) Thread[{ "label" -> "style", - Thread["line" -> styleLineNo], + Thread["line" -> styleLineNo], Thread["data" -> Block[ { styleLines = Part[doc["text"], styleLineNo] - }, + }, styleLines (* selectionRange *) @@ -201,10 +200,10 @@ divideCells[doc_TextDocument] := Block[ // Apply[{#1 + 5, #2 - 5}&, #, {1}]& // { (* {("selectionRange" -> selectionRange)..} *) - Thread["selectionRange" -> #]&, + Thread["selectionRange" -> #]&, (* {("style" -> style)..} *) Thread["style" -> ( - StringTake @@@ + StringTake @@@ Thread[{styleLines, #}] )]& } // Through @@ -216,11 +215,11 @@ divideCells[doc_TextDocument] := Block[ (* titleLines *) Thread[{ "label" -> "title", - Thread["line" -> titleLineNo], + Thread["line" -> titleLineNo], Thread["data" -> Block[ { titleLines = Part[doc["text"], titleLineNo] - }, + }, titleLines // StringPosition["(*" ~~ Longest[title___] ~~ "*)"] @@ -247,7 +246,6 @@ divideCells[doc_TextDocument] := Block[ // Map[Association] // SortBy[Key["line"]] // Append[<|"label" -> "end", "line" -> (Length[doc["text"]] + 1)|>] - // LogDebug // (divideCellImpl[#, If[doc["text"] // First // StringStartsQ["#!"], 1, 0] // <|"label" -> "start", "startLine" -> #, "endLine" -> #|>& @@ -280,8 +278,8 @@ divideCellImpl[{lineState_Association, lineStates___}, state_Association] := div // Replace[{ "empty" :> ( Sow[{ - "codeEnd", - If[state["startLine"] < state["endLine"], + "codeEnd", + If[state["startLine"] < state["endLine"], state["startLine"] - 1, state["endLine"] ] @@ -294,25 +292,25 @@ divideCellImpl[{lineState_Association, lineStates___}, state_Association] := div }]; <| "label" -> "style", - "startLine" -> lineState["line"], - "endLine" -> lineState["line"], + "startLine" -> lineState["line"], + "endLine" -> lineState["line"], "data" -> lineState["data"] |> ), If[ - state["label"] == "style" && + state["label"] == "style" && state["startLine"] + 1 == lineState["line"] && !MemberQ[{"Package"}, state["data"]["style"]], "title" :> ( Sow[{ "titledStyle", - state["startLine"], + state["startLine"], Join[state["data"], lineState["data"]] }]; <| "label" -> "title", - "startLine" -> lineState["line"], - "endLine" -> lineState["line"], + "startLine" -> lineState["line"], + "endLine" -> lineState["line"], "data" -> lineState["data"] |> ), @@ -329,7 +327,7 @@ divideCellImpl[{lineState_Association, lineStates___}, state_Association] := div }]; <| "label" -> "end", - "startLine" -> lineState["line"], + "startLine" -> lineState["line"], "endLine" -> lineState["line"] |> ) @@ -359,7 +357,7 @@ divideCellImpl[{lineState_Association, lineStates___}, state_Association] := div "empty" :> ( <| "label" -> "empty", - "startLine" -> lineState["line"], + "startLine" -> lineState["line"], "endLine" -> lineState["line"] |> ), @@ -367,8 +365,8 @@ divideCellImpl[{lineState_Association, lineStates___}, state_Association] := div Sow[{"codeEnd", lineState["line"] - 1}]; <| "label" -> "style", - "startLine" -> lineState["line"], - "endLine" -> lineState["line"], + "startLine" -> lineState["line"], + "endLine" -> lineState["line"], "data" -> lineState["data"] |> ), @@ -376,7 +374,7 @@ divideCellImpl[{lineState_Association, lineStates___}, state_Association] := div Sow[{"codeEnd", lineState["line"] - 1}]; <| "label" -> "end", - "startLine" -> lineState["line"], + "startLine" -> lineState["line"], "endLine" -> lineState["line"] |> ) @@ -561,7 +559,6 @@ GetCodeRangeAtPosition[doc_TextDocument, pos_LspPosition] := With[ (*AST utils*) - NodeDataContainsPosition[pos:{_Integer, _Integer}][data_] := NodeDataContainsPosition[data, pos] NodeDataContainsPosition[data_Association, pos:{_Integer, _Integer}] := ( CompareNodePosition[{data}, pos] === 0 From 4195f3f36103969e9262f3d49705cc8c0302b62a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BA=B7=E5=B0=8F=E5=B9=BF?= Date: Tue, 31 Dec 2019 18:12:12 -0800 Subject: [PATCH 05/40] :construction: add signatureHelp request handler * triggerred by "[" and "," * reuse Hover but without header and footer. Issue: * Cannot close the signatureHelp by keyboard. --- src/WolframLanguageServer/Server.wl | 40 +++++++ src/WolframLanguageServer/Specification.wl | 20 ++++ src/WolframLanguageServer/TextDocument.wl | 48 ++++++++ src/WolframLanguageServer/Token.wl | 130 +++++++++++++++++---- 4 files changed, 215 insertions(+), 23 deletions(-) diff --git a/src/WolframLanguageServer/Server.wl b/src/WolframLanguageServer/Server.wl index b9b718e..09496bb 100644 --- a/src/WolframLanguageServer/Server.wl +++ b/src/WolframLanguageServer/Server.wl @@ -78,6 +78,9 @@ InitialState = WorkState[<| ServerCapabilities = <| "textDocumentSync" -> TextDocumentSyncKind["Full"], "hoverProvider" -> True, + "signatureHelpProvider" -> <| + "triggerCharacters" -> {"[", ","} + |>, "completionProvider" -> <| "resolveProvider" -> True, "triggerCharacters" -> "\\" @@ -906,6 +909,43 @@ handleRequest["textDocument/hover", msg_, state_] := With[ ] +(* ::Subsection:: *) +(*textDocument/signatureHelp*) + + +handleRequest["textDocument/signatureHelp", msg_, state_] := With[ + { + id = msg["id"], + uri = msg["params"]["textDocument"]["uri"], + pos = LspPosition[msg["params"]["position"]] + }, + + state + // Curry[addScheduledTask][ServerTask[<| + "type" -> "signatureHelp", + "scheduledTime" -> DatePlus[Now, { + state["config"]["signatrueHelpDelay"], + "Second" + }], + "id" -> id, + "params" -> {uri, pos}, + "callback" -> (sendResponse[#1["client"], <| + "id" -> id, + "result" -> ( + { + #1["openedDocs"][uri], + pos + } + // Apply[GetSignatureHelp] + // ToAssociation + ) + |>]&) + |>]] + // List + // Prepend["Continue"] +] + + (* ::Subsection:: *) (*textDocument/completion*) diff --git a/src/WolframLanguageServer/Specification.wl b/src/WolframLanguageServer/Specification.wl index 61cffb2..5ea6b01 100644 --- a/src/WolframLanguageServer/Specification.wl +++ b/src/WolframLanguageServer/Specification.wl @@ -19,6 +19,9 @@ TextDocumentContentChangeEvent::usage = "is an event describing a change to a te the new text is considered to be the full content of the document." MarkupContent::usage = "is the type of MarkupContent interface in LSP." Hover::usage = "is the type of Hover interface in LSP." +SignatureHelp::usage = "is the type of SignatureHelp interface in LSP." +SignatureInformation::usage = "is the type of SignatureInformation interface in LSP." +ParameterInformation::usage = "is the type of ParameterInformation interface in LSP." DocumentSymbol::usage = "is the type of DocumentSymbol interface in LSP." Diagnostic::usage = "is the type of Diagnostic interface in LSP." DiagnosticRelatedInformation::usage = "is the type of DiagnosticRelatedInformation interface in LSP." @@ -211,6 +214,23 @@ DeclareType[Hover, <| "range" -> _LspRange |>] +DeclareType[SignatureHelp, <| + "signatures" -> {___SignatureInformation}, + "activeSignature" -> _Integer, + "activeParameter" -> _Integer +|>] + +DeclareType[SignatureInformation, <| + "label" -> _String, + "documentation" -> _String | _MarkupContent, + "parameters" -> {___ParameterInformation} +|>] + +DeclareType[ParameterInformation, <| + "label" -> _String | {_Integer, _Integer}, + "documentation" -> _String | _MarkupContent +|>] + DeclareType[DocumentSymbol, <| "name" -> _String, "detail" -> _String, diff --git a/src/WolframLanguageServer/TextDocument.wl b/src/WolframLanguageServer/TextDocument.wl index d89305a..9fed7a1 100644 --- a/src/WolframLanguageServer/TextDocument.wl +++ b/src/WolframLanguageServer/TextDocument.wl @@ -15,6 +15,7 @@ CreateTextDocument::usage = "CreateTextDocument[textDocumentItem_TextDocumentIte ChangeTextDocument::usage = "ChangeTextDocument[doc_TextDocument, change_TextDocumentContentChangeEvent] returns the changed doc from the input." HoverInfo::usage = "HoverInfo[hoverKind, {literal, docTag}] Basic information to generate a hover message." GetHoverInfo::usage = "GetHoverInfo[doc_TextDocument, pos_LspPosition] gives the HoverInfo and range at the given position." +GetFunctionName::usage = "GetFunctionName[doc_TextDocument, pos_LspPosition] gives the function being called at the position." GetTokenPrefix::usage = "GetTokenPrefix[doc_TextDocument, pos_LspPosition] gives the prefix of the token before the position." DiagnoseDoc::usage = "DiagnoseDoc[doc_TextDocument] gives diagnostic information of the doc." ToDocumentSymbol::usage = "ToDocumentSymbol[doc_TextDocument] gives the DocumentSymbol structure of a document." @@ -904,6 +905,53 @@ getHoverInfoImpl[ast_, {index_Integer, restIndices___}, res_] := ( ) +(* ::Section:: *) +(*GetFunctionName*) + + +GetFunctionName[doc_TextDocument, pos_LspPosition] := With[ + { + line = pos["line"] + 1, character = pos["character"] + 1 + }, + + GetCodeRangeAtPosition[doc, pos] + // Replace[lineRange:{_Integer, _Integer} :> ( + CellToAST[doc, lineRange] + // (ast \[Function] ( + FirstPosition[ + ast, + _Association?(NodeDataContainsPosition[{line, character}]), + Missing["NotFound", {}], + AstLevelspec["DataWithSource"], + Heads -> False + ] + // Most + // Replace[indices_List :> ( + getFunctionNameImpl[ast, indices] + )] + )) + )] +] + +getFunctionNameImpl[ast_, indices_] := ( + Extract[ast, indices // Replace[{} -> {All}]] + // Replace[{ + AstPattern["Function"][{functionName_}] :> ( + functionName + ), + _ :> ( + indices + // Replace[{ + {} -> Missing["NotFound"], + _ :> ( + getFunctionNameImpl[ast, indices // Most] + ) + }] + ) + }] +) + + (* ::Section:: *) (*GetTokenPrefix*) diff --git a/src/WolframLanguageServer/Token.wl b/src/WolframLanguageServer/Token.wl index e87544f..6c4185e 100644 --- a/src/WolframLanguageServer/Token.wl +++ b/src/WolframLanguageServer/Token.wl @@ -1,6 +1,6 @@ (* ::Package:: *) -(* Wolfram Language Server Documentation *) +(* Wolfram Language Server Token *) (* Author: kenkangxgwe , huxianglong *) @@ -15,6 +15,7 @@ TokenDocumentation::usage = "TokenDocumentation[token_String, tag_String, o] ret \"Format\" -> \"plaintext\" | \"markdown\" " GetHoverAtPosition::usage = "GetHoverAtPosition[doc_TextDocument, pos_LspPosition] gives the text to be shown when hover at the given position." +GetSignatureHelp::usage = "GetSignatureHelp[doc_TextDocument, pos_LspPosition] gives the signature help at the position." GetTokenCompletionAtPostion::usage = "GetTokenCompletionAtPostion[doc_TextDocument, pos_LspPosition] gives a list of suggestions for completion." GetTriggerKeys::usage = "GetTriggerKeys[] returns a list of characters that trigger a completion request when input." GetTriggerKeyCompletion::usage = "GetTriggerKeyCompletion[] returns a list of available leader keys." @@ -35,7 +36,8 @@ Needs["WolframLanguageServer`TextDocument`"] Options[TokenDocumentation] = { - "Format" -> MarkupKind["Markdown"] (* | MarkupKind["Plaintext"] *) + "Format" -> MarkupKind["Markdown"] (* | MarkupKind["Plaintext"] *), + "Header" -> True } TokenDocumentation[token_String, tag_String, o: OptionsPattern[]] := ( @@ -49,7 +51,10 @@ TokenDocumentation[token_String, tag_String, o: OptionsPattern[]] := ( tag // Replace[{ "usage" :> ( { - GenHeader[token, tag], + If[OptionValue["Header"], + GenHeader[token, tag, "Format" -> OptionValue["Format"]], + Nothing + ], boxText // If[(OptionValue["Format"]) === MarkupKind["Markdown"], splitUsage @@ -58,13 +63,16 @@ TokenDocumentation[token_String, tag_String, o: OptionsPattern[]] := ( /* Flatten /* DeleteCases[""], GenPlainText ], - GenFooter[token] + GenOptions[token, "Format" -> OptionValue["Format"]] } // Flatten // Curry[StringRiffle]["\n\n"] ), _(* other messages *) :> ( { - GenHeader[token, tag], + If[OptionValue["Header"], + GenHeader[token, tag, OptionValue["Format"]], + Nothing + ], boxText // If[OptionValue["Format"] === MarkupKind["Markdown"], GenMarkdownText, @@ -91,12 +99,23 @@ splitUsage[usageText_String] := ( ))] (* split header and content *) // Map[{ - StringCases[StartOfString ~~ (header:Shortest["\!\(\*"~~box__~~"\)"]) ~~ content__ ~~ EndOfString :> {header, content}], + StringCases[StartOfString ~~ + (header:( + Shortest[(* box: *) "\!\(\*"~~__~~"\)"] ~~ (( + (Whitespace|"") ~~ + (","|"or"|("," ~~ Whitespace ~~ "or")) ~~ + Whitespace ~~ + Shortest[(* box: *) "\!\(\*"~~__~~"\)"] + )...)) + ) ~~ + content__ ~~ + EndOfString :> {header, content} + ], Identity } /* Through /* Replace[{ {{{header_, content_}}, _} :> ( - {header, content} + {header, content} ), {{(* no matches *)}, origin_} :> ( (* use fallback method *) @@ -130,7 +149,11 @@ groupBalanceQ[text_String] := And[ -GenHeader[token_String, tag_String] := ( + +Options[GenHeader] = { + "Format" -> MarkupKind["Markdown"] (* | MarkupKind["Plaintext"] *) +} +GenHeader[token_String, tag_String, o: OptionsPattern[]] := ( tag // Replace[{ "usage" :> ( @@ -140,13 +163,23 @@ GenHeader[token_String, tag_String] := ( Curry[GetUri][tag], GenAttributes } // Through - // Apply[StringTemplate["**`1`** `2` (_`3`_)\n"]] + // Apply[ + If[OptionValue["Format"] == MarkupKind["Markdown"], + StringTemplate["**`1`** `2` (`3`)\n"], + StringTemplate["`1`\t(`3`)\n"] + ] + ] ), _ :> ( - StringJoin[ - "```mathematica\n", - token, "::", tag, "\n", - "```" + If[OptionValue["Format"] == MarkupKind["Markdown"], + StringJoin[ + token, "::", tag, "\n" + ], + StringJoin[ + "```mathematica\n", + token, "::", tag, "\n", + "```" + ] ] ) }] @@ -166,28 +199,40 @@ GetUri[token_String, tag_String] := ( }] ) - -GenAttributes[token_String] := ( +Options[GenAttributes] = { + "Format" -> MarkupKind["Markdown"] (* | MarkupKind["Plaintext"] *) +} +GenAttributes[token_String, o:OptionsPattern[]] := ( Attributes[token] // Replace[_Attributes -> {}] // Curry[StringRiffle][", "] ) -GenFooter[token_String] := ({ +Options[GenOptions] = { + "Format" -> MarkupKind["Markdown"] (* | MarkupKind["Plaintext"] *) +} +GenOptions[token_String, o:OptionsPattern[]] := ( token // StringTemplate["Options[``]"] // ToExpression // Replace[_Options -> {}] // Map[Curry[ToString][InputForm]] // Replace[{options__} :> ( - { - "__Options:__", - "``` mathematica", - options, - "```" - } // Curry[StringRiffle]["\n"] + If[OptionValue["Format"] == MarkupKind["Markdown"], + { + "__Options:__", + "``` mathematica", + options, + "```" + }, + { + "Options:", + options + } + ] )] -}) + // Curry[StringRiffle]["\n"] +) GenPlainText[boxText_String] := ( @@ -362,6 +407,45 @@ printHoverTextImpl[hoverInfo_HoverInfo] := ( ) +(* ::Section:: *) +(*SignatureHelp*) + + +GetSignatureHelp[doc_TextDocument, pos_LspPosition] := ( + GetFunctionName[doc, pos] + // Replace[{ + _?MissingQ -> Null, + functionName_ :> ( + printSignatureHelp[functionName] + ) + }] +) + + +printSignatureHelp[functionName_String] := ( + + TokenDocumentation[functionName, "usage", "Header" -> False] + // Replace[{ + "" -> Null, + text_String :> ( + SignatureHelp[<| + "signatures" -> { + SignatureInformation[<| + "label" -> functionName, + "documentation" -> MarkupContent[<| + "kind" -> MarkupKind["Markdown"], + "value" -> text + |>], + "parameters" -> {} + |>] + }, + "activeSignature" -> 0 + |>] + ) + }] +) + + (* ::Section:: *) (*Completion*) From f828558867c26f2b788f1517d88e3068ee7d66f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BA=B7=E5=B0=8F=E5=B9=BF?= Date: Thu, 6 Feb 2020 03:20:53 -0800 Subject: [PATCH 06/40] Improve: split and cache signatureHelp results. Bypass some trivial functions see FunctionPattern. --- src/WolframLanguageServer/AstPatterns.wl | 8 ++ src/WolframLanguageServer/Server.wl | 94 +++++++++++++---------- src/WolframLanguageServer/TextDocument.wl | 1 + src/WolframLanguageServer/Token.wl | 38 +++++---- 4 files changed, 85 insertions(+), 56 deletions(-) diff --git a/src/WolframLanguageServer/AstPatterns.wl b/src/WolframLanguageServer/AstPatterns.wl index c6fc13d..0538e05 100644 --- a/src/WolframLanguageServer/AstPatterns.wl +++ b/src/WolframLanguageServer/AstPatterns.wl @@ -80,6 +80,14 @@ FunctionPattern = <| "ColorDirective" -> ( "Opacity" | "Lighter" | "Darker" | "ColorNegate" + ), + + "NoSignatureHelp" -> ( + "List" | "Association" | "CompoundExpression" | + "Rule" | "RuleDelayed" | + "Set" | "SetDelayed" | "UpSet" | "UpSetDelayed" | + "TagSet" | "TagSetDelayed" | + "With" | "Block" | "Module" | "DynamicModule" ) |> diff --git a/src/WolframLanguageServer/Server.wl b/src/WolframLanguageServer/Server.wl index 09496bb..fd9a96c 100644 --- a/src/WolframLanguageServer/Server.wl +++ b/src/WolframLanguageServer/Server.wl @@ -58,6 +58,7 @@ DeclareType[RequestCache, <| |>] initialCaches = <| + "textDocument/signatureHelp" -> <||>, "textDocument/documentSymbol" -> <||>, "textDocument/documentColor" -> <||>, "textDocument/codeLens" -> <||>, @@ -102,6 +103,7 @@ ServerConfig = <| "updateCheckInterval" -> Quantity[7, "Days"], (* cached results *) "cachedRequests" -> { + "textDocument/signatureHelp", "textDocument/documentSymbol", "textDocument/documentColor" (* "textDocument/codeLens" *) @@ -113,7 +115,7 @@ ServerConfig = <| (* default delays (seconds) *) "requestDelays" -> <| "textDocument/publishDiagnostics" -> 2.5, - "textDocument/signatrueHelp" -> 0.5, + "textDocument/signatureHelp" -> 0.5, "textDocument/documentSymbol" -> 4.0, "textDocument/documentHighlight" -> 0.5, "textDocument/documentColor" -> 5.0 @@ -638,6 +640,7 @@ constructRPCBytes[msg_Association] := ( if the result is not able to convert to JSON, returns an error respond *) + LogError[msg]; ExportByteArray[ msg // KeyDrop["result"] @@ -913,39 +916,50 @@ handleRequest["textDocument/hover", msg_, state_] := With[ (*textDocument/signatureHelp*) -handleRequest["textDocument/signatureHelp", msg_, state_] := With[ +handleRequest[method:"textDocument/signatureHelp", msg_, state_] := ( + state + // cacheResponse[method, msg] + // sendCachedResult[method, msg, #]& + ) + + +cacheResponse[method:"textDocument/signatureHelp", msg_, state_WorkState] := With[ { - id = msg["id"], uri = msg["params"]["textDocument"]["uri"], pos = LspPosition[msg["params"]["position"]] + }, state - // Curry[addScheduledTask][ServerTask[<| - "type" -> "signatureHelp", - "scheduledTime" -> DatePlus[Now, { - state["config"]["signatrueHelpDelay"], - "Second" - }], - "id" -> id, - "params" -> {uri, pos}, - "callback" -> (sendResponse[#1["client"], <| - "id" -> id, + // ReplaceKey[ + {"caches", method, uri} -> RequestCache[<| + "cachedTime" -> Now, "result" -> ( - { - #1["openedDocs"][uri], - pos - } - // Apply[GetSignatureHelp] + GetSignatureHelp[state["openedDocs"][uri], pos] // ToAssociation ) - |>]&) - |>]] - // List - // Prepend["Continue"] + |>] + ] +] + + +cacheAvailableQ[method:"textDocument/signatureHelp", msg_, state_WorkState] := Block[ + { + cachedTime = getCache[method, msg, state]["cachedtime"] + }, + + !MissingQ[cachedTime] && ( + cachedTime + > DatePlus[Now, {-ServerConfig["requestDelays"][method], "Second"}] + ) ] +getCache[method:"textDocument/signatureHelp", msg_, state_WorkState] := ( + state["caches"][method][msg["params"]["textDocument"]["uri"]] +) + + (* ::Subsection:: *) (*textDocument/completion*) @@ -1155,7 +1169,7 @@ cacheAvailableQ[method:"textDocument/documentSymbol", msg_, state_WorkState] := state["openedDocs"][ msg["params"]["textDocument"]["uri"] ]["lastUpdate"] < - (Now - Quantity[ServerConfig["requestDelays"][method], "Seconds"]) + DateDifference[{ServerConfig["requestDelays"][method], "Second"}, Now] ) ] @@ -1353,20 +1367,18 @@ handleNotification["textDocument/didOpen", msg_, state_] := With[ // ReplaceKeyBy["openedDocs" -> Append[textDocumentItem["uri"] -> CreateTextDocument[textDocumentItem]] ] - // ReplaceKeyBy[{"caches", "textDocument/documentSymbol"} -> - Append[textDocumentItem["uri"] -> RequestCache[<||>]] - ] - // ReplaceKeyBy[{"caches", "textDocument/documentColor"} -> - Append[textDocumentItem["uri"] -> RequestCache[<||>]] - ] - // ReplaceKeyBy[{"caches", "textDocument/codeLens"} -> - Append[textDocumentItem["uri"] -> RequestCache[<||>]] - ] - // ReplaceKeyBy[{"caches", "textDocument/publishDiagnostics"} -> - Append[textDocumentItem["uri"] -> <| - "scheduledQ" -> False - |>] - ] + // ReplaceKeyBy["caches" -> (Fold[ReplaceKeyBy, #, { + "textDocument/signatureHelp" -> + Append[textDocumentItem["uri"] -> RequestCache[<||>]], + "textDocument/documentSymbol" -> + Append[textDocumentItem["uri"] -> RequestCache[<||>]], + "textDocument/documentColor" -> + Append[textDocumentItem["uri"] -> RequestCache[<||>]], + "textDocument/codeLens" -> + Append[textDocumentItem["uri"] -> RequestCache[<||>]], + "textDocument/publishDiagnostics" -> + Append[textDocumentItem["uri"] -> <|"scheduledQ" -> False|>] + }]&)] // handleRequest["textDocument/publishDiagnostics", textDocumentItem["uri"], #]& ] @@ -1384,9 +1396,11 @@ handleNotification["textDocument/didClose", msg_, state_] := With[ state // ReplaceKeyBy[{"openedDocs"} -> KeyDrop[uri]] - // ReplaceKeyBy[{"caches", "textDocument/documentSymbol"} -> KeyDrop[uri]] - // ReplaceKeyBy[{"caches", "textDocument/documentColor"} -> KeyDrop[uri]] - // ReplaceKeyBy[{"caches", "textDocument/codeLens"} -> KeyDrop[uri]] + // ReplaceKeyBy["caches" -> (Fold[ReplaceKeyBy, #, { + "textDocument/documentSymbol" -> KeyDrop[uri], + "textDocument/documentColor" -> KeyDrop[uri], + "textDocument/codeLens" -> KeyDrop[uri] + }]&)] // handleRequest["textDocument/clearDiagnostics", uri, #]& ] diff --git a/src/WolframLanguageServer/TextDocument.wl b/src/WolframLanguageServer/TextDocument.wl index 9fed7a1..1f058eb 100644 --- a/src/WolframLanguageServer/TextDocument.wl +++ b/src/WolframLanguageServer/TextDocument.wl @@ -938,6 +938,7 @@ getFunctionNameImpl[ast_, indices_] := ( // Replace[{ AstPattern["Function"][{functionName_}] :> ( functionName + // Replace[FunctionPattern["NoSignatureHelp"] -> Missing["NotFound"]] ), _ :> ( indices diff --git a/src/WolframLanguageServer/Token.wl b/src/WolframLanguageServer/Token.wl index 6c4185e..0af8d60 100644 --- a/src/WolframLanguageServer/Token.wl +++ b/src/WolframLanguageServer/Token.wl @@ -383,6 +383,8 @@ printHoverText[hoverInfo_List, range_LspRange:Automatic] := ( ) }] ) + + printHoverTextImpl[hoverInfo_HoverInfo] := ( hoverInfo // Replace[{ @@ -424,25 +426,29 @@ GetSignatureHelp[doc_TextDocument, pos_LspPosition] := ( printSignatureHelp[functionName_String] := ( - TokenDocumentation[functionName, "usage", "Header" -> False] + If[Names["System`"<>functionName] === {}, Return[Null]]; + + ToExpression[functionName<>"::"<>"usage"] // Replace[{ - "" -> Null, - text_String :> ( - SignatureHelp[<| - "signatures" -> { - SignatureInformation[<| - "label" -> functionName, - "documentation" -> MarkupContent[<| - "kind" -> MarkupKind["Markdown"], - "value" -> text - |>], - "parameters" -> {} - |>] - }, - "activeSignature" -> 0 - |>] + _MessageName -> {}, + usageText_String :> ( + splitUsage[usageText] + // MapAt[GenPlainText, {All, 1}] + // MapAt[GenMarkdownText, {All, 2}] ) }] + // Map[Apply[{label, documentation} \[Function] ( + SignatureInformation[<| + "label" -> label, + "documentation" -> MarkupContent[<| + "kind" -> MarkupKind["Markdown"], + "value" -> documentation + |>], + "parameters" -> {} + |>] + )]] + // SignatureHelp[<|"signatures" -> #|>]& + ) From 3faeaade41e1460f300d7666644c169748058832 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BA=B7=E5=B0=8F=E5=B9=BF?= Date: Thu, 6 Feb 2020 03:23:25 -0800 Subject: [PATCH 07/40] Improve: shrink range for GroupMissingCloser Also remove outdated "ImplicitTimes" severity. --- src/WolframLanguageServer/TextDocument.wl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/WolframLanguageServer/TextDocument.wl b/src/WolframLanguageServer/TextDocument.wl index 1f058eb..05f93f2 100644 --- a/src/WolframLanguageServer/TextDocument.wl +++ b/src/WolframLanguageServer/TextDocument.wl @@ -1001,12 +1001,16 @@ DiagnoseDoc[doc_TextDocument] := ( data // Key[AST`Source] // SourceToRange + // If[tag == "GroupMissingCloser", + ReplaceKey[#, "end" -> #["start"]]&, + Identity + ] ), "severity" -> ( severity // Replace[{ "Fatal" -> "Error", - "ImplicitTimes"|"Formatting"|"Remark" -> "Hint" + "Formatting"|"Remark" -> "Hint" }] // Replace[{ "Warning" :> ( From d2f673fa5ddf032df87bc1f2b4458f0fdb79386d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BA=B7=E5=B0=8F=E5=B9=BF?= Date: Thu, 6 Feb 2020 03:23:44 -0800 Subject: [PATCH 08/40] Add: Replace rule for \[ExponentialE] --- src/WolframLanguageServer/Token.wl | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/WolframLanguageServer/Token.wl b/src/WolframLanguageServer/Token.wl index 0af8d60..b480ada 100644 --- a/src/WolframLanguageServer/Token.wl +++ b/src/WolframLanguageServer/Token.wl @@ -348,9 +348,11 @@ PUACharactersReplaceRule = { "\[LeftAssociation]" -> "<|", "\[RightAssociation]" -> "|>", "\[InvisibleSpace]" -> " ", - "\[Null]" -> "" + "\[Null]" -> "", + "\[ExponentialE]" -> "\[ScriptE]" } + (* ::Section:: *) (*Hover*) @@ -358,7 +360,6 @@ PUACharactersReplaceRule = { GetHoverAtPosition[doc_TextDocument, pos_LspPosition] := ( GetHoverInfo[doc, pos] // Apply[printHoverText] - ) @@ -366,7 +367,7 @@ printHoverText[hoverInfo_List, range_LspRange:Automatic] := ( hoverInfo // Map[printHoverTextImpl] - // Curry[StringRiffle]["\n\n---\n\n"] + // StringRiffle[#, "\n\n---\n\n"]& // Replace[{ "" -> Null, text_String :> ( From 6a12b81cedaca010b626427f9be69d3228b206d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BA=B7=E5=B0=8F=E5=B9=BF?= Date: Thu, 6 Feb 2020 03:38:59 -0800 Subject: [PATCH 09/40] Update: AST and Lint version to 0.15.1 --- README.md | 2 +- src/WolframLanguageServer/Server.wl | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 9dd87d4..b5bd20a 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ client would certainly work too. git clone https://github.com/kenkangxgwe/lsp-wl.git ``` -2. Install the dependent paclets with the correct versions (currently 0.15) +2. Install the dependent paclets with the correct versions (currently 0.15.1) from the Wolfram kernel / Mathematica. (_This will cost some time for the first time_) : ``` mathematica diff --git a/src/WolframLanguageServer/Server.wl b/src/WolframLanguageServer/Server.wl index fd9a96c..2f7bf53 100644 --- a/src/WolframLanguageServer/Server.wl +++ b/src/WolframLanguageServer/Server.wl @@ -1799,8 +1799,8 @@ checkGitRepo[state_WorkState] := ( checkDependencies[state_WorkState] := With[ { dependencies = { - {"AST", "0.15"}, - {"Lint", "0.15"} + {"AST", "0.15.1"}, + {"Lint", "0.15.1"} } }, From 133ffd523cde000877e8c08faa5d2ea7d4ac55fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BA=B7=E5=B0=8F=E5=B9=BF?= Date: Fri, 7 Feb 2020 02:29:09 -0800 Subject: [PATCH 10/40] Change: suppress GitLink error if it is not found. --- README.md | 4 ++++ src/WolframLanguageServer/Server.wl | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index b5bd20a..0d98937 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,10 @@ from the Wolfram kernel / Mathematica. 3. Install the client. Currently, we provide the VS Code extension on [Visual Studio Marketplace: Wolfram Language Server](https://marketplace.visualstudio.com/items?itemName=lsp-wl.lsp-wl-client) +4. You may also want to install +[GitLink](https://github.com/WolframResearch/GitLink) packet in order to +check for updates. + ## Run the Server Clients can start the server by running the `init.wls` file from Wolfram diff --git a/src/WolframLanguageServer/Server.wl b/src/WolframLanguageServer/Server.wl index 2f7bf53..092d786 100644 --- a/src/WolframLanguageServer/Server.wl +++ b/src/WolframLanguageServer/Server.wl @@ -1764,7 +1764,7 @@ checkGitRepo[state_WorkState] := ( state ]; Return[] - ]; + ] // Quiet; If[!GitLink`GitRepoQ[WolframLanguageServer`RootDirectory], showMessage[ From 851694544c530f0f7e41667bf078449e99e7cf24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BA=B7=E5=B0=8F=E5=B9=BF?= Date: Sun, 9 Feb 2020 03:23:15 -0800 Subject: [PATCH 11/40] Fix: error message when textDocument is empty --- src/WolframLanguageServer/TextDocument.wl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/WolframLanguageServer/TextDocument.wl b/src/WolframLanguageServer/TextDocument.wl index 05f93f2..d31f39f 100644 --- a/src/WolframLanguageServer/TextDocument.wl +++ b/src/WolframLanguageServer/TextDocument.wl @@ -251,7 +251,8 @@ divideCells[doc_TextDocument] := Block[ If[doc["text"] // First // StringStartsQ["#!"], 1, 0] // <|"label" -> "start", "startLine" -> #, "endLine" -> #|>& ] // Reap)& - // Last // First (* Get sown List*) + (* Get the sown List if there is one. *) + // Last // Replace[{} :> {{}}] // First // Prepend[CellNode[<| "style" -> AdditionalStyle["File"], "name" -> "", From 060193d09975545e7019b38c41ed7d575d08e66b Mon Sep 17 00:00:00 2001 From: Daniel Nicolai Date: Sat, 21 Mar 2020 21:01:24 +0100 Subject: [PATCH 12/40] Fix: change triggerCharacters to a list. Original Commit Messaage: fix issue #25 I was able to fix this issue by changing the string to a list (cherry picked from commit 8f4287893d90bd7d7b54b26570b0ed27262694ad) --- src/WolframLanguageServer/Server.wl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/WolframLanguageServer/Server.wl b/src/WolframLanguageServer/Server.wl index 092d786..f3121ff 100644 --- a/src/WolframLanguageServer/Server.wl +++ b/src/WolframLanguageServer/Server.wl @@ -84,7 +84,7 @@ ServerCapabilities = <| |>, "completionProvider" -> <| "resolveProvider" -> True, - "triggerCharacters" -> "\\" + "triggerCharacters" -> {"\\"} |>, "definitionProvider" -> True, "referencesProvider" -> True, From 579c5e1f14b87712f0a2fcb0bfd715d6ab8baa88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BA=B7=E5=B0=8F=E5=B9=BF?= Date: Sat, 21 Mar 2020 15:51:47 -0700 Subject: [PATCH 13/40] Change: suppress the "duplicated requests" error. * Change its error code from "ContentModified" to "RequestCancelled". --- src/WolframLanguageServer/Server.wl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/WolframLanguageServer/Server.wl b/src/WolframLanguageServer/Server.wl index f3121ff..89eebcb 100644 --- a/src/WolframLanguageServer/Server.wl +++ b/src/WolframLanguageServer/Server.wl @@ -1674,7 +1674,7 @@ doNextScheduledTask[state_WorkState] := ( sendResponse[newState["client"], <| "id" -> task["id"], "error" -> ServerError[ - "ContentModified", + "RequestCancelled", "There is a more recent duplicate request." ] |>]; @@ -1693,7 +1693,6 @@ doNextScheduledTask[state_WorkState] := ( ) - (* ::Section:: *) (*Misc*) From 2d83004f5b4372c8739c3b89df5d1fdb6ca742eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BA=B7=E5=B0=8F=E5=B9=BF?= Date: Sun, 12 Apr 2020 23:02:30 -0700 Subject: [PATCH 14/40] Improve: the behavior of GetTriggerKeyCompletion * Reduce the number of completionItems sent from server when '\' triggers the completion. This is done by removing aliases that contain letters, which will be completed when a letter is input. * The second time "\" will only trigger "\"-prefixed aliases. * Update the UnicodeTable to Mathematica v12.1. * Use Set instead of SetDelayed to cache static results (NonLetterAliasCompletionItems). --- src/WolframLanguageServer/Server.wl | 2 +- src/WolframLanguageServer/Specification.wl | 2 +- src/WolframLanguageServer/TableGenerator.wl | 90 +++--- src/WolframLanguageServer/Token.wl | 83 ++++-- src/WolframLanguageServer/UnicodeTable.wl | 304 ++++++++++++-------- 5 files changed, 273 insertions(+), 208 deletions(-) diff --git a/src/WolframLanguageServer/Server.wl b/src/WolframLanguageServer/Server.wl index 89eebcb..c292eca 100644 --- a/src/WolframLanguageServer/Server.wl +++ b/src/WolframLanguageServer/Server.wl @@ -987,7 +987,7 @@ handleRequest["textDocument/completion", msg_, state_] := Module[ "result" -> <| "isIncomplete" -> True, "items" -> ( - GetTriggerKeyCompletion[] + GetTriggerKeyCompletion[doc, pos] // ToAssociation ) |> diff --git a/src/WolframLanguageServer/Specification.wl b/src/WolframLanguageServer/Specification.wl index 5ea6b01..6f405b3 100644 --- a/src/WolframLanguageServer/Specification.wl +++ b/src/WolframLanguageServer/Specification.wl @@ -261,7 +261,7 @@ DeclareType[CompletionItem, <| "kind" -> _Integer, "detail" -> _String, "documentation" -> _String | _MarkupContent, - "preselect" -> _String, + "preselect" -> _?BooleanQ, "filterText" -> _String, "insertText" -> _String, "insertTextFormat" -> _Integer, diff --git a/src/WolframLanguageServer/TableGenerator.wl b/src/WolframLanguageServer/TableGenerator.wl index e2ffee1..01a087c 100644 --- a/src/WolframLanguageServer/TableGenerator.wl +++ b/src/WolframLanguageServer/TableGenerator.wl @@ -47,19 +47,44 @@ GenerateUnicodeTable[file_] := Module[ ) :> (longName -> FromDigits[unicode, 16])] // Association; - Keys[AliasToLongName] - // Map[StringTake[#, 1]&] - // DeleteDuplicates - // DeleteCases[_?LetterQ] - // Prepend["["] - // DeleteDuplicates + (* aliases without A-Za-z. *) + NonLetterAliases = Keys[AliasToLongName] + // Cases[_?(StringMatchQ[Except[WordCharacter]..])]; + + (* + Non-letters which are only used as prefix of aliases that contains letters. + We know there is only one such leader, i.e. `$`, but we generate it here. + *) + NonLetterLeaders = Complement[ + (* all non-letter prefix *) + Keys[AliasToLongName] + // Map[StringTake[#, 1]&] + // Prepend["["] (* for long names `[` is a prefix *) + // DeleteDuplicates + // DeleteCases[_?LetterQ], + (* non-letter prefix *) + NonLetterAliases + // Map[StringTake[#, 1]&] + // DeleteDuplicates + ]; + + (* Use `Function` to do lexical replacement in `Unevaluated` *) + NonLetterLeaders // (leaders \[Function] ( - Write[file, Unevaluated[WolframLanguageServer`UnicodeTable`UnicodeLeaders = leaders]]; + Write[file, Unevaluated[WolframLanguageServer`UnicodeTable`NonLetterLeaders = leaders]]; leaders )); WriteLine[file, "\n"]; + NonLetterAliases + // (aliases \[Function] ( + Write[file, Unevaluated[WolframLanguageServer`UnicodeTable`NonLetterAliases = aliases]]; + aliases + )); + + WriteLine[file, "\n"]; + AliasToLongName // (assoc \[Function] ( Write[file, Unevaluated[WolframLanguageServer`UnicodeTable`AliasToLongName = assoc]]; @@ -74,62 +99,15 @@ GenerateUnicodeTable[file_] := Module[ assoc )); - (* Join[ - AliasToLongName - // KeyValueMap[{alias, longName} \[Function] ( - <| - "label" -> StringJoin[ - LongNameToUnicode[longName] - // Replace[{ - code_?(LessThan[16^^E000]) :> ( - FromCharacterCode[code] - ), - _ -> " " - }], "\t", - alias, "\t\t", - longName - ], - "kind" -> 1, - (* "detail" -> , *) - "filterText" -> alias, - "insertText" -> StringDrop[longName, 1], - "data" -> <|"type" -> "Alias"|> - |> - )], - LongNameToUnicode - // KeyValueMap[{longName, unicode} \[Function] ( - <| - "label" -> StringJoin[ - unicode - // Replace[{ - code_?(LessThan[16^^E000]) :> ( - FromCharacterCode[code] - ), - _ -> " " - }], "\t", - longName - ], - "kind" -> 1, - (* "detail" -> , *) - "filterText" -> StringDrop[longName, 1], - "insertText" -> StringDrop[longName, 1], - "data" -> <|"type" -> "LongName"|> - |> - )] - ] - // (items \[Function] ( - Write[file, Unevaluated[WolframLanguageServer`UnicodeTable`UnicodeCompletionItems = items]] - ));*) - WriteLine[file, "\n"]; WriteLine[file, "EndPackage[]"]; Close[file]; - + ] End[] -EndPackage[] \ No newline at end of file +EndPackage[] diff --git a/src/WolframLanguageServer/Token.wl b/src/WolframLanguageServer/Token.wl index b480ada..c0ac55f 100644 --- a/src/WolframLanguageServer/Token.wl +++ b/src/WolframLanguageServer/Token.wl @@ -510,36 +510,58 @@ GetTokenCompletionAtPostion[doc_TextDocument, pos_LspPosition] := With[ ] -GetTriggerKeys[] := UnicodeLeaders +(* SetDelayed is not needed. Cache it when define it. *) +GetTriggerKeyCompletion[doc_TextDocument, pos_LspPosition] := ( + If[GetTokenPrefix[doc, pos] == "\\\\", + (* double-triggered *) + GetAliasCompletion["\\", pos], + NonLetterAliasCompletionItems + ] +) -GetTriggerKeyCompletion[] := ( - AliasToLongName - // KeySelect[StringStartsQ[UnicodeLeaders]] - // KeyValueMap[{alias, longName} \[Function] With[ - { - unicode = LongNameToUnicode[longName] - }, +NonLetterAliasCompletionItems = ( + Join[ + AliasToLongName + // KeyTake[NonLetterAliases] + // KeyValueMap[{alias, longName} \[Function] With[ + { + unicode = LongNameToUnicode[longName] + }, - CompletionItem[<| - "label" -> StringJoin[ - If[unicode < 16^^E000, - FromCharacterCode[unicode], - "" - ], "\t", - alias , "\t\t", - "\\[", longName, "]" - ], - "kind" -> CompletionItemKind["Text"], - "detail" -> StringJoin["0x", StringPadLeft[IntegerString[unicode, 16] // ToUpperCase, 4, "0"]], - "filterText" -> alias, - "sortText" -> alias, - "insertText" -> "[" <> longName <> "]", - "data" -> <| - "type" -> "Alias" - |> - |>] - ]] + CompletionItem[<| + "label" -> StringJoin[ + If[unicode < 16^^E000, + FromCharacterCode[unicode], + "" + ], "\t", + alias // StringReplace[" " -> "\[SpaceIndicator]"] , "\t\t", + "\\[", longName, "]" + ], + "kind" -> CompletionItemKind["Text"], + "detail" -> StringJoin["0x", StringPadLeft[IntegerString[unicode, 16] // ToUpperCase, 4, "0"]], + "filterText" -> alias, + "sortText" -> alias, + "insertText" -> "[" <> longName <> "]", + "data" -> <| + "type" -> "Alias" + |> + |>] + ]], + Table[ + CompletionItem[<| + "label" -> leader <> "...", + "kind" -> CompletionItemKind["Text"], + "detail" -> "More input needed to show the completion.", + "filterText" -> leader, + "sortText" -> leader <> "...", + "insertText" -> leader, + "data" -> <| + "type" -> "Alias" + |> + |>], {leader, NonLetterLeaders} + ] + ] ) @@ -563,8 +585,8 @@ GetAliasCompletion[prefix_String, pos_LspPosition] := ( "kind" -> CompletionItemKind["Text"], "detail" -> StringJoin["0x", StringPadLeft[IntegerString[unicode, 16] // ToUpperCase, 4, "0"]], (* label has some extra information, thus cannot be used to sort, filter or insert *) - "sortText" -> StringDrop[alias, StringLength[prefix] - 1], - "filterText" -> StringDrop[alias, StringLength[prefix] - 1], + "sortText" -> alias (*StringDrop[alias, StringLength[prefix] - 1]*), + "filterText" -> alias (*StringDrop[alias, StringLength[prefix] - 1]*), "textEdit" -> TextEdit[<| "range" -> LspRange[<| "start" -> LspPosition[<| @@ -626,8 +648,7 @@ GetIncompleteCompletionAtPosition[doc_TextDocument, pos_LspPosition] := ( ), (* other aliases *) prefix_ :> ( - prefix - // Curry[GetAliasCompletion][pos] + GetAliasCompletion[prefix, pos] ) }] diff --git a/src/WolframLanguageServer/UnicodeTable.wl b/src/WolframLanguageServer/UnicodeTable.wl index 2fdd2c2..110c063 100644 --- a/src/WolframLanguageServer/UnicodeTable.wl +++ b/src/WolframLanguageServer/UnicodeTable.wl @@ -7,9 +7,20 @@ BeginPackage["WolframLanguageServer`UnicodeTable`"] ClearAll[Evaluate[Context[] <> "*"]] -WolframLanguageServer`UnicodeTable`UnicodeLeaders = - {"[", "&", "\\", "$", "-", ".", "'", "`", "!", "|", "=", "~", "<", ">", ":", - " ", "*", "+", "^", "@", "]", "_", "#", ","} +WolframLanguageServer`UnicodeTable`NonLetterLeaders = {"$"} + + +WolframLanguageServer`UnicodeTable`NonLetterAliases = + {"\\%", "\\&", "\\_", "\\-", "-", "--", "...", "'", "''", "`", "``", "|", + "=>", "~", "!~", "=~", "~=", "!~=", "~==", "~~", "!~~", "!=~", "!~==", + "<~", ">~", "!<~", "!>~", "!=", "!<", "!>", "<=", "!<=", ">=", "!>=", "/", "!>/", "===", "!===", ".=", ":", "==", " ||", "\\|", "!||", + "!", "*", "+-", "-+", ".", "\\", "^", "&&", "!&&", "||", " ->", "<-", + "<->", "@>", ":>", "->", " =>", " <=", "<=>", "-->", "<--", "<==", "==>", + "<-->", "<==>", "[[", "]]", "<", ">", "<|", "|>", "_", ":-(", ":-@", ":-|", + "#", "+", " ", " ", "\\,", " ", "\\>", "\\:", " ", "\\;", "- ", + "- ", "\\!", "- ", "- ", "['", "]'", "[\"", "]\"", "[", "]", "@", + ",", " |", "!|", "<-> "} WolframLanguageServer`UnicodeTable`AliasToLongName = @@ -334,62 +345,116 @@ WolframLanguageServer`UnicodeTable`AliasToLongName = "𝕎" -> "DoubleStruckCapitalW", "dsX" -> "DoubleStruckCapitalX", "𝕏" -> "DoubleStruckCapitalX", "dsY" -> "DoubleStruckCapitalY", "𝕐" -> "DoubleStruckCapitalY", "dsZ" -> "DoubleStruckCapitalZ", - "ℤ" -> "DoubleStruckCapitalZ", "$a" -> "FormalA", "$b" -> "FormalB", - "$c" -> "FormalC", "$d" -> "FormalD", "$e" -> "FormalE", "$f" -> "FormalF", - "$g" -> "FormalG", "$h" -> "FormalH", "$i" -> "FormalI", "$j" -> "FormalJ", - "$k" -> "FormalK", "$l" -> "FormalL", "$m" -> "FormalM", "$n" -> "FormalN", - "$o" -> "FormalO", "$p" -> "FormalP", "$q" -> "FormalQ", "$r" -> "FormalR", - "$s" -> "FormalS", "$t" -> "FormalT", "$u" -> "FormalU", "$v" -> "FormalV", - "$w" -> "FormalW", "$x" -> "FormalX", "$y" -> "FormalY", "$z" -> "FormalZ", - "$A" -> "FormalCapitalA", "$B" -> "FormalCapitalB", - "$C" -> "FormalCapitalC", "$D" -> "FormalCapitalD", - "$E" -> "FormalCapitalE", "$F" -> "FormalCapitalF", - "$G" -> "FormalCapitalG", "$H" -> "FormalCapitalH", - "$I" -> "FormalCapitalI", "$J" -> "FormalCapitalJ", - "$K" -> "FormalCapitalK", "$L" -> "FormalCapitalL", - "$M" -> "FormalCapitalM", "$N" -> "FormalCapitalN", - "$O" -> "FormalCapitalO", "$P" -> "FormalCapitalP", - "$Q" -> "FormalCapitalQ", "$R" -> "FormalCapitalR", - "$S" -> "FormalCapitalS", "$T" -> "FormalCapitalT", - "$U" -> "FormalCapitalU", "$V" -> "FormalCapitalV", - "$W" -> "FormalCapitalW", "$X" -> "FormalCapitalX", - "$Y" -> "FormalCapitalY", "$Z" -> "FormalCapitalZ", - "$CapitalAlpha" -> "FormalCapitalAlpha", "$CapitalBeta" -> - "FormalCapitalBeta", "$CapitalGamma" -> "FormalCapitalGamma", - "$CapitalDelta" -> "FormalCapitalDelta", "$CapitalEpsilon" -> - "FormalCapitalEpsilon", "$CapitalZeta" -> "FormalCapitalZeta", - "$CapitalEta" -> "FormalCapitalEta", "$CapitalTheta" -> - "FormalCapitalTheta", "$CapitalIota" -> "FormalCapitalIota", - "$CapitalKappa" -> "FormalCapitalKappa", "$CapitalLambda" -> - "FormalCapitalLambda", "$CapitalMu" -> "FormalCapitalMu", - "$CapitalNu" -> "FormalCapitalNu", "$CapitalXi" -> "FormalCapitalXi", + "ℤ" -> "DoubleStruckCapitalZ", "$a" -> "FormalA", ".a" -> "FormalA", + "$b" -> "FormalB", ".b" -> "FormalB", "$c" -> "FormalC", ".c" -> "FormalC", + "$d" -> "FormalD", ".d" -> "FormalD", "$e" -> "FormalE", ".e" -> "FormalE", + "$f" -> "FormalF", ".f" -> "FormalF", "$g" -> "FormalG", ".g" -> "FormalG", + "$h" -> "FormalH", ".h" -> "FormalH", "$i" -> "FormalI", ".i" -> "FormalI", + "$j" -> "FormalJ", ".j" -> "FormalJ", "$k" -> "FormalK", ".k" -> "FormalK", + "$l" -> "FormalL", ".l" -> "FormalL", "$m" -> "FormalM", ".m" -> "FormalM", + "$n" -> "FormalN", ".n" -> "FormalN", "$o" -> "FormalO", ".o" -> "FormalO", + "$p" -> "FormalP", ".p" -> "FormalP", "$q" -> "FormalQ", ".q" -> "FormalQ", + "$r" -> "FormalR", ".r" -> "FormalR", "$s" -> "FormalS", ".s" -> "FormalS", + "$t" -> "FormalT", ".t" -> "FormalT", "$u" -> "FormalU", ".u" -> "FormalU", + "$v" -> "FormalV", ".v" -> "FormalV", "$w" -> "FormalW", ".w" -> "FormalW", + "$x" -> "FormalX", ".x" -> "FormalX", "$y" -> "FormalY", ".y" -> "FormalY", + "$z" -> "FormalZ", ".z" -> "FormalZ", "$A" -> "FormalCapitalA", + ".A" -> "FormalCapitalA", "$B" -> "FormalCapitalB", + ".B" -> "FormalCapitalB", "$C" -> "FormalCapitalC", + ".C" -> "FormalCapitalC", "$D" -> "FormalCapitalD", + ".D" -> "FormalCapitalD", "$E" -> "FormalCapitalE", + ".E" -> "FormalCapitalE", "$F" -> "FormalCapitalF", + ".F" -> "FormalCapitalF", "$G" -> "FormalCapitalG", + ".G" -> "FormalCapitalG", "$H" -> "FormalCapitalH", + ".H" -> "FormalCapitalH", "$I" -> "FormalCapitalI", + ".I" -> "FormalCapitalI", "$J" -> "FormalCapitalJ", + ".J" -> "FormalCapitalJ", "$K" -> "FormalCapitalK", + ".K" -> "FormalCapitalK", "$L" -> "FormalCapitalL", + ".L" -> "FormalCapitalL", "$M" -> "FormalCapitalM", + ".M" -> "FormalCapitalM", "$N" -> "FormalCapitalN", + ".N" -> "FormalCapitalN", "$O" -> "FormalCapitalO", + ".O" -> "FormalCapitalO", "$P" -> "FormalCapitalP", + ".P" -> "FormalCapitalP", "$Q" -> "FormalCapitalQ", + ".Q" -> "FormalCapitalQ", "$R" -> "FormalCapitalR", + ".R" -> "FormalCapitalR", "$S" -> "FormalCapitalS", + ".S" -> "FormalCapitalS", "$T" -> "FormalCapitalT", + ".T" -> "FormalCapitalT", "$U" -> "FormalCapitalU", + ".U" -> "FormalCapitalU", "$V" -> "FormalCapitalV", + ".V" -> "FormalCapitalV", "$W" -> "FormalCapitalW", + ".W" -> "FormalCapitalW", "$X" -> "FormalCapitalX", + ".X" -> "FormalCapitalX", "$Y" -> "FormalCapitalY", + ".Y" -> "FormalCapitalY", "$Z" -> "FormalCapitalZ", + ".Z" -> "FormalCapitalZ", "$CapitalAlpha" -> "FormalCapitalAlpha", + ".CapitalAlpha" -> "FormalCapitalAlpha", "$CapitalBeta" -> + "FormalCapitalBeta", ".CapitalBeta" -> "FormalCapitalBeta", + "$CapitalGamma" -> "FormalCapitalGamma", ".CapitalGamma" -> + "FormalCapitalGamma", "$CapitalDelta" -> "FormalCapitalDelta", + ".CapitalDelta" -> "FormalCapitalDelta", "$CapitalEpsilon" -> + "FormalCapitalEpsilon", ".CapitalEpsilon" -> "FormalCapitalEpsilon", + "$CapitalZeta" -> "FormalCapitalZeta", ".CapitalZeta" -> + "FormalCapitalZeta", "$CapitalEta" -> "FormalCapitalEta", + ".CapitalEta" -> "FormalCapitalEta", "$CapitalTheta" -> + "FormalCapitalTheta", ".CapitalTheta" -> "FormalCapitalTheta", + "$CapitalIota" -> "FormalCapitalIota", ".CapitalIota" -> + "FormalCapitalIota", "$CapitalKappa" -> "FormalCapitalKappa", + ".CapitalKappa" -> "FormalCapitalKappa", "$CapitalLambda" -> + "FormalCapitalLambda", ".CapitalLambda" -> "FormalCapitalLambda", + "$CapitalMu" -> "FormalCapitalMu", ".CapitalMu" -> "FormalCapitalMu", + "$CapitalNu" -> "FormalCapitalNu", ".CapitalNu" -> "FormalCapitalNu", + "$CapitalXi" -> "FormalCapitalXi", ".CapitalXi" -> "FormalCapitalXi", "$CapitalOmicron" -> "FormalCapitalOmicron", - "$CapitalPi" -> "FormalCapitalPi", "$CapitalRho" -> "FormalCapitalRho", - "$CapitalSigma" -> "FormalCapitalSigma", - "$CapitalTau" -> "FormalCapitalTau", "$CapitalUpsilon" -> - "FormalCapitalUpsilon", "$CapitalPhi" -> "FormalCapitalPhi", - "$CapitalChi" -> "FormalCapitalChi", "$CapitalPsi" -> "FormalCapitalPsi", - "$CapitalOmega" -> "FormalCapitalOmega", "$Alpha" -> "FormalAlpha", - "$Beta" -> "FormalBeta", "$Gamma" -> "FormalGamma", - "$Delta" -> "FormalDelta", "$CurlyEpsilon" -> "FormalCurlyEpsilon", - "$Zeta" -> "FormalZeta", "$Eta" -> "FormalEta", "$Theta" -> "FormalTheta", - "$Iota" -> "FormalIota", "$Kappa" -> "FormalKappa", - "$Lambda" -> "FormalLambda", "$Mu" -> "FormalMu", "$Nu" -> "FormalNu", - "$Xi" -> "FormalXi", "$Omicron" -> "FormalOmicron", "$Pi" -> "FormalPi", - "$Rho" -> "FormalRho", "$FinalSigma" -> "FormalFinalSigma", - "$Sigma" -> "FormalSigma", "$Tau" -> "FormalTau", - "$Upsilon" -> "FormalUpsilon", "$CurlyPhi" -> "FormalCurlyPhi", - "$Chi" -> "FormalChi", "$Psi" -> "FormalPsi", "$Omega" -> "FormalOmega", - "$CurlyTheta" -> "FormalCurlyTheta", "$CurlyCapitalUpsilon" -> - "FormalCurlyCapitalUpsilon", "$Phi" -> "FormalPhi", - "$CurlyPi" -> "FormalCurlyPi", "$CapitalStigma" -> "FormalCapitalStigma", - "$Stigma" -> "FormalStigma", "$CapitalDigamma" -> "FormalCapitalDigamma", - "$Digamma" -> "FormalDigamma", "$CapitalKoppa" -> "FormalCapitalKoppa", - "$Koppa" -> "FormalKoppa", "$CapitalSampi" -> "FormalCapitalSampi", - "$Sampi" -> "FormalSampi", "$CurlyKappa" -> "FormalCurlyKappa", - "$CurlyRho" -> "FormalCurlyRho", "$Epsilon" -> "FormalEpsilon", - "wp" -> "WeierstrassP", "℘" -> "WeierstrassP", "\\wp" -> "WeierstrassP", - "al" -> "Aleph", "ℵ" -> "Aleph", "\\aleph" -> "Aleph", "be" -> "Bet", + ".CapitalOmicron" -> "FormalCapitalOmicron", + "$CapitalPi" -> "FormalCapitalPi", ".CapitalPi" -> "FormalCapitalPi", + "$CapitalRho" -> "FormalCapitalRho", ".CapitalRho" -> "FormalCapitalRho", + "$CapitalSigma" -> "FormalCapitalSigma", ".CapitalSigma" -> + "FormalCapitalSigma", "$CapitalTau" -> "FormalCapitalTau", + ".CapitalTau" -> "FormalCapitalTau", "$CapitalUpsilon" -> + "FormalCapitalUpsilon", ".CapitalUpsilon" -> "FormalCapitalUpsilon", + "$CapitalPhi" -> "FormalCapitalPhi", ".CapitalPhi" -> "FormalCapitalPhi", + "$CapitalChi" -> "FormalCapitalChi", ".CapitalChi" -> "FormalCapitalChi", + "$CapitalPsi" -> "FormalCapitalPsi", ".CapitalPsi" -> "FormalCapitalPsi", + "$CapitalOmega" -> "FormalCapitalOmega", ".CapitalOmega" -> + "FormalCapitalOmega", "$Alpha" -> "FormalAlpha", + ".Alpha" -> "FormalAlpha", "$Beta" -> "FormalBeta", + ".Beta" -> "FormalBeta", "$Gamma" -> "FormalGamma", + ".Gamma" -> "FormalGamma", "$Delta" -> "FormalDelta", + ".Delta" -> "FormalDelta", "$CurlyEpsilon" -> "FormalCurlyEpsilon", + ".CurlyEpsilon" -> "FormalCurlyEpsilon", "$Zeta" -> "FormalZeta", + ".Zeta" -> "FormalZeta", "$Eta" -> "FormalEta", ".Eta" -> "FormalEta", + "$Theta" -> "FormalTheta", ".Theta" -> "FormalTheta", + "$Iota" -> "FormalIota", ".Iota" -> "FormalIota", + "$Kappa" -> "FormalKappa", ".Kappa" -> "FormalKappa", + "$Lambda" -> "FormalLambda", ".Lambda" -> "FormalLambda", + "$Mu" -> "FormalMu", ".Mu" -> "FormalMu", "$Nu" -> "FormalNu", + ".Nu" -> "FormalNu", "$Xi" -> "FormalXi", ".Xi" -> "FormalXi", + "$Omicron" -> "FormalOmicron", ".Omicron" -> "FormalOmicron", + "$Pi" -> "FormalPi", ".Pi" -> "FormalPi", "$Rho" -> "FormalRho", + ".Rho" -> "FormalRho", "$FinalSigma" -> "FormalFinalSigma", + ".FinalSigma" -> "FormalFinalSigma", "$Sigma" -> "FormalSigma", + ".Sigma" -> "FormalSigma", "$Tau" -> "FormalTau", ".Tau" -> "FormalTau", + "$Upsilon" -> "FormalUpsilon", ".Upsilon" -> "FormalUpsilon", + "$CurlyPhi" -> "FormalCurlyPhi", ".CurlyPhi" -> "FormalCurlyPhi", + "$Chi" -> "FormalChi", ".Chi" -> "FormalChi", "$Psi" -> "FormalPsi", + ".Psi" -> "FormalPsi", "$Omega" -> "FormalOmega", + ".Omega" -> "FormalOmega", "$CurlyTheta" -> "FormalCurlyTheta", + ".CurlyTheta" -> "FormalCurlyTheta", "$CurlyCapitalUpsilon" -> + "FormalCurlyCapitalUpsilon", ".CurlyCapitalUpsilon" -> + "FormalCurlyCapitalUpsilon", "$Phi" -> "FormalPhi", ".Phi" -> "FormalPhi", + "$CurlyPi" -> "FormalCurlyPi", ".CurlyPi" -> "FormalCurlyPi", + "$CapitalStigma" -> "FormalCapitalStigma", ".CapitalStigma" -> + "FormalCapitalStigma", "$Stigma" -> "FormalStigma", + ".Stigma" -> "FormalStigma", "$CapitalDigamma" -> "FormalCapitalDigamma", + ".CapitalDigamma" -> "FormalCapitalDigamma", "$Digamma" -> "FormalDigamma", + ".Digamma" -> "FormalDigamma", "$CapitalKoppa" -> "FormalCapitalKoppa", + ".CapitalKoppa" -> "FormalCapitalKoppa", "$Koppa" -> "FormalKoppa", + ".Koppa" -> "FormalKoppa", "$CapitalSampi" -> "FormalCapitalSampi", + ".CapitalSampi" -> "FormalCapitalSampi", "$Sampi" -> "FormalSampi", + ".Sampi" -> "FormalSampi", "$CurlyKappa" -> "FormalCurlyKappa", + ".CurlyKappa" -> "FormalCurlyKappa", "$CurlyRho" -> "FormalCurlyRho", + ".CurlyRho" -> "FormalCurlyRho", "$Epsilon" -> "FormalEpsilon", + ".Epsilon" -> "FormalEpsilon", "wp" -> "WeierstrassP", + "℘" -> "WeierstrassP", "\\wp" -> "WeierstrassP", "al" -> "Aleph", + "ℵ" -> "Aleph", "\\aleph" -> "Aleph", "be" -> "Bet", "ℶ" -> "Bet", "\\beth" -> "Bet", "gi" -> "Gimel", "ℷ" -> "Gimel", "\\gimel" -> "Gimel", "da" -> "Dalet", "ℸ" -> "Dalet", "\\daleth" -> "Dalet", "d!" -> "DownExclamation", @@ -493,19 +558,20 @@ WolframLanguageServer`UnicodeTable`AliasToLongName = "≶" -> "LessGreater", "≷" -> "GreaterLess", "≸" -> "NotLessGreater", "≹" -> "NotGreaterLess", "⋚" -> "LessEqualGreater", - "⋛" -> "GreaterEqualLess", "lim" -> "Limit", - "\\lim" -> "Limit", "Mlim" -> "MaxLimit", "\\limsup" -> "MaxLimit", - "mlim" -> "MinLimit", "\\liminf" -> "MinLimit", "sub" -> "Subset", - "⊂" -> "Subset", "\\subset" -> "Subset", "sup" -> "Superset", - "⊃" -> "Superset", "\\supset" -> "Superset", - "!sub" -> "NotSubset", "⊂⃒" -> "NotSubset", - "!sup" -> "NotSuperset", "⊃⃒" -> "NotSuperset", - "sub=" -> "SubsetEqual", "⊆" -> "SubsetEqual", - "\\subseteq" -> "SubsetEqual", "sup=" -> "SupersetEqual", - "⊇" -> "SupersetEqual", "\\supseteq" -> "SupersetEqual", - "!sub=" -> "NotSubsetEqual", "⊈" -> "NotSubsetEqual", - "\\nsubseteq" -> "NotSubsetEqual", "!sup=" -> "NotSupersetEqual", - "⊉" -> "NotSupersetEqual", + "⋛" -> "GreaterEqualLess", "v>" -> "VectorGreater", + "v>=" -> "VectorGreaterEqual", "v<" -> "VectorLess", + "v<=" -> "VectorLessEqual", "lim" -> "Limit", "\\lim" -> "Limit", + "Mlim" -> "MaxLimit", "\\limsup" -> "MaxLimit", "mlim" -> "MinLimit", + "\\liminf" -> "MinLimit", "sub" -> "Subset", "⊂" -> "Subset", + "\\subset" -> "Subset", "sup" -> "Superset", "⊃" -> "Superset", + "\\supset" -> "Superset", "!sub" -> "NotSubset", + "⊂⃒" -> "NotSubset", "!sup" -> "NotSuperset", + "⊃⃒" -> "NotSuperset", "sub=" -> "SubsetEqual", + "⊆" -> "SubsetEqual", "\\subseteq" -> "SubsetEqual", + "sup=" -> "SupersetEqual", "⊇" -> "SupersetEqual", + "\\supseteq" -> "SupersetEqual", "!sub=" -> "NotSubsetEqual", + "⊈" -> "NotSubsetEqual", "\\nsubseteq" -> "NotSubsetEqual", + "!sup=" -> "NotSupersetEqual", "⊉" -> "NotSupersetEqual", "\\nsupseteq" -> "NotSupersetEqual", "⊏" -> "SquareSubset", "\\sqsubset" -> "SquareSubset", "⊐" -> "SquareSuperset", "\\sqsupset" -> "SquareSuperset", "⊑" -> @@ -529,30 +595,30 @@ WolframLanguageServer`UnicodeTable`AliasToLongName = "≽" -> "SucceedsSlantEqual", "⪰̸" -> "NotSucceedsEqual", "⋡" -> "NotSucceedsSlantEqual", "≿̸" -> "NotSucceedsTilde", - "uT" -> "UpTee", "⊥" -> "UpTee", "\\perp" -> "UpTee", - "\\bot" -> "UpTee", "perp" -> "Perpendicular", - "\\perpendicular" -> "Perpendicular", "prop" -> "Proportional", - "∝" -> "Proportional", "\\propto" -> "Proportional", - "∷" -> "Proportion", "===" -> "Congruent", - "≡" -> "Congruent", "\\equiv" -> "Congruent", - "!===" -> "NotCongruent", "≢" -> "NotCongruent", - "≍" -> "CupCap", "\\asymp" -> "CupCap", - "≭" -> "NotCupCap", "equi" -> "Equilibrium", - "⇌" -> "Equilibrium", "⇋" -> - "ReverseEquilibrium", "⥮" -> "UpEquilibrium", - "⥯" -> "ReverseUpEquilibrium", "rT" -> "RightTee", - "⊢" -> "RightTee", "\\vdash" -> "RightTee", "lT" -> "LeftTee", - "⊣" -> "LeftTee", "\\dashv" -> "LeftTee", "dT" -> "DownTee", - "⊤" -> "DownTee", "⊨" -> "DoubleRightTee", - "\\models" -> "DoubleRightTee", "⫤" -> "DoubleLeftTee", - "h=" -> "HumpEqual", "≏" -> "HumpEqual", - "\\bumpeq" -> "HumpEqual", "!h=" -> "NotHumpEqual", - "≏̸" -> "NotHumpEqual", "≎" -> "HumpDownHump", - "≎̸" -> "NotHumpDownHump", ".=" -> "DotEqual", - "≐" -> "DotEqual", "\\doteq" -> "DotEqual", ":" -> "Colon", - ":" -> "Colon", "==" -> "Equal", "⩵" -> "Equal", - "l=" -> "LongEqual", "sqrt" -> "Sqrt", "√" -> "Sqrt", - "\\surd" -> "Sqrt", "divides" -> "Divides", "&Divides;" -> "Divides", + "uT" -> "UpTee", "⊥" -> "UpTee", "\\bot" -> "UpTee", + "perp" -> "Perpendicular", "\\perp" -> "Perpendicular", + "prop" -> "Proportional", "∝" -> "Proportional", + "\\propto" -> "Proportional", "∷" -> "Proportion", + "===" -> "Congruent", "≡" -> "Congruent", + "\\equiv" -> "Congruent", "!===" -> "NotCongruent", + "≢" -> "NotCongruent", "≍" -> "CupCap", + "\\asymp" -> "CupCap", "≭" -> "NotCupCap", + "equi" -> "Equilibrium", "⇌" -> "Equilibrium", + "⇋" -> "ReverseEquilibrium", + "⥮" -> "UpEquilibrium", "⥯" -> + "ReverseUpEquilibrium", "rT" -> "RightTee", "⊢" -> "RightTee", + "\\vdash" -> "RightTee", "lT" -> "LeftTee", "⊣" -> "LeftTee", + "\\dashv" -> "LeftTee", "dT" -> "DownTee", "⊤" -> "DownTee", + "⊨" -> "DoubleRightTee", "\\models" -> "DoubleRightTee", + "⫤" -> "DoubleLeftTee", "h=" -> "HumpEqual", + "≏" -> "HumpEqual", "\\bumpeq" -> "HumpEqual", + "!h=" -> "NotHumpEqual", "≏̸" -> "NotHumpEqual", + "≎" -> "HumpDownHump", "≎̸" -> + "NotHumpDownHump", ".=" -> "DotEqual", "≐" -> "DotEqual", + "\\doteq" -> "DotEqual", ":" -> "Colon", ":" -> "Colon", + "==" -> "Equal", "⩵" -> "Equal", "l=" -> "LongEqual", + "sqrt" -> "Sqrt", "√" -> "Sqrt", "\\surd" -> "Sqrt", + "cbrti" -> "CubeRoot", "divides" -> "Divides", "&Divides;" -> "Divides", " ||" -> "DoubleVerticalBar", "∥" -> "DoubleVerticalBar", "\\Vert" -> "DoubleVerticalBar", "\\parallel" -> "DoubleVerticalBar", "\\|" -> "DoubleVerticalBar", "!||" -> "NotDoubleVerticalBar", @@ -575,18 +641,16 @@ WolframLanguageServer`UnicodeTable`AliasToLongName = "p*" -> "PermutationProduct", "t*" -> "TensorProduct", "t^" -> "TensorWedge", "star" -> "Star", "⋆" -> "Star", "sq" -> "Square", "□" -> "Square", "dia" -> "Diamond", - "⋄" -> "Diamond", "clap" -> "Laplacian", "del" -> "Del", - "∇" -> "Del", "\\nabla" -> "Del", "cdive" -> "Divergence", - "ccurl" -> "Curl", "prob" -> "ProbabilityPr", "expect" -> "ExpectationE", - "sha" -> "Shah", "\\" -> "Backslash", "∖" -> "Backslash", - "\\setminus" -> "Backslash", "⌢" -> "Cap", "\\frown" -> "Cap", - "⌣" -> "Cup", "\\smile" -> "Cup", "coprod" -> "Coproduct", - "∐" -> "Coproduct", "\\amalg" -> "Coproduct", - "int" -> "Integral", "∫" -> "Integral", "\\int" -> "Integral", - "\\intop" -> "Integral", "cint" -> "ContourIntegral", - "∮" -> "ContourIntegral", "\\oint" -> "ContourIntegral", - "∯" -> "DoubleContourIntegral", - "cccint" -> "CounterClockwiseContourIntegral", + "⋄" -> "Diamond", "del" -> "Del", "∇" -> "Del", + "\\nabla" -> "Del", "sha" -> "Shah", "\\" -> "Backslash", + "∖" -> "Backslash", "\\setminus" -> "Backslash", + "⌢" -> "Cap", "\\frown" -> "Cap", "⌣" -> "Cup", + "\\smile" -> "Cup", "coprod" -> "Coproduct", "∐" -> "Coproduct", + "\\amalg" -> "Coproduct", "int" -> "Integral", "∫" -> "Integral", + "\\int" -> "Integral", "\\intop" -> "Integral", + "cint" -> "ContourIntegral", "∮" -> "ContourIntegral", + "\\oint" -> "ContourIntegral", "∯" -> + "DoubleContourIntegral", "cccint" -> "CounterClockwiseContourIntegral", "∳" -> "CounterClockwiseContourIntegral", "ccint" -> "ClockwiseContourIntegral", "∲" -> "ClockwiseContourIntegral", "sum" -> "Sum", "∑" -> "Sum", @@ -609,7 +673,7 @@ WolframLanguageServer`UnicodeTable`AliasToLongName = "∂" -> "PartialD", "\\partial" -> "PartialD", "dd" -> "DifferentialD", "ⅆ" -> "DifferentialD", "DD" -> "CapitalDifferentialD", "ⅅ" -> - "CapitalDifferentialD", "cgrad" -> "Gradient", " ->" -> "RightArrow", + "CapitalDifferentialD", " ->" -> "RightArrow", "→" -> "RightArrow", "\\rightarrow" -> "RightArrow", "\\to" -> "RightArrow", "<-" -> "LeftArrow", "←" -> "LeftArrow", "\\leftarrow" -> "LeftArrow", "\\gets" -> "LeftArrow", @@ -1036,7 +1100,9 @@ WolframLanguageServer`UnicodeTable`LongNameToUnicode = "NotNestedGreaterGreater" -> 62504, "NotGreaterSlantEqual" -> 62505, "NotGreaterFullEqual" -> 8809, "LessGreater" -> 8822, "GreaterLess" -> 8823, "NotLessGreater" -> 8824, "NotGreaterLess" -> 8825, - "LessEqualGreater" -> 8922, "GreaterEqualLess" -> 8923, "Limit" -> 62520, + "LessEqualGreater" -> 8922, "GreaterEqualLess" -> 8923, + "VectorGreater" -> 62516, "VectorGreaterEqual" -> 62517, + "VectorLess" -> 62518, "VectorLessEqual" -> 62519, "Limit" -> 62520, "MaxLimit" -> 62521, "MinLimit" -> 62522, "Subset" -> 8834, "Superset" -> 8835, "NotSubset" -> 8836, "NotSuperset" -> 8837, "SubsetEqual" -> 8838, "SupersetEqual" -> 8839, "NotSubsetEqual" -> 8840, @@ -1060,16 +1126,16 @@ WolframLanguageServer`UnicodeTable`LongNameToUnicode = "DoubleRightTee" -> 8872, "DoubleLeftTee" -> 10980, "HumpEqual" -> 8783, "NotHumpEqual" -> 62465, "HumpDownHump" -> 8782, "NotHumpDownHump" -> 62466, "DotEqual" -> 8784, "Colon" -> 8758, - "Equal" -> 62513, "LongEqual" -> 63449, "Sqrt" -> 8730, "Divides" -> 8739, - "DoubleVerticalBar" -> 8741, "NotDoubleVerticalBar" -> 8742, "Not" -> 172, - "VerticalTilde" -> 8768, "Times" -> 215, "InvisibleTimes" -> 8290, - "" -> 8213, "PlusMinus" -> 177, "MinusPlus" -> 8723, "Minus" -> 8722, - "DivisionSlash" -> 8725, "Divide" -> 247, "CenterDot" -> 183, - "SmallCircle" -> 8728, "Cross" -> 62624, "CirclePlus" -> 8853, - "CircleMinus" -> 8854, "CircleTimes" -> 8855, "CircleDot" -> 8857, - "PermutationProduct" -> 62430, "TensorProduct" -> 62426, - "TensorWedge" -> 62427, "Star" -> 8902, "Square" -> 62752, - "Diamond" -> 8900, "Laplacian" -> 8710, "Del" -> 8711, + "Equal" -> 62513, "LongEqual" -> 63449, "Sqrt" -> 8730, "CubeRoot" -> 8731, + "Divides" -> 8739, "DoubleVerticalBar" -> 8741, + "NotDoubleVerticalBar" -> 8742, "Not" -> 172, "VerticalTilde" -> 8768, + "Times" -> 215, "InvisibleTimes" -> 8290, "" -> 62708, "PlusMinus" -> 177, + "MinusPlus" -> 8723, "Minus" -> 8722, "DivisionSlash" -> 8725, + "Divide" -> 247, "CenterDot" -> 183, "SmallCircle" -> 8728, + "Cross" -> 62624, "CirclePlus" -> 8853, "CircleMinus" -> 8854, + "CircleTimes" -> 8855, "CircleDot" -> 8857, "PermutationProduct" -> 62430, + "TensorProduct" -> 62426, "TensorWedge" -> 62427, "Star" -> 8902, + "Square" -> 62752, "Diamond" -> 8900, "Laplacian" -> 8710, "Del" -> 8711, "Divergence" -> 62423, "Curl" -> 62424, "ProbabilityPr" -> 62428, "ExpectationE" -> 62429, "Shah" -> 61725, "Backslash" -> 8726, "Cap" -> 8994, "Cup" -> 8995, "Coproduct" -> 8720, "Integral" -> 8747, From 39c6921943ef852f09b157657e785cfa5afaafe6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BA=B7=E5=B0=8F=E5=B9=BF?= Date: Mon, 13 Apr 2020 05:11:05 -0700 Subject: [PATCH 15/40] Update: dependencies of AST & Lint to CodeParser & CodeInspector * Rename AST` and Lint` to CodeParser` and CodeInspector` * Add a shim function to not abstract to context and package node. * Update the dependencies in README.md * Update version and install command * Add repo links --- README.md | 22 ++--- src/WolframLanguageServer/AstPatterns.wl | 66 +++++++-------- src/WolframLanguageServer/TextDocument.wl | 97 ++++++++++++++--------- 3 files changed, 104 insertions(+), 81 deletions(-) diff --git a/README.md b/README.md index 0d98937..0661f39 100644 --- a/README.md +++ b/README.md @@ -47,12 +47,12 @@ client would certainly work too. git clone https://github.com/kenkangxgwe/lsp-wl.git ``` -2. Install the dependent paclets with the correct versions (currently 0.15.1) +2. Install the dependent paclets with the correct versions (currently 1.0) from the Wolfram kernel / Mathematica. (_This will cost some time for the first time_) : ``` mathematica - PacletInstall["AST", "UpdateSites" -> True] - PacletInstall["Lint", "UpdateSites" -> True] + PacletInstall["CodeParser"] + PacletInstall["CodeInspector"] ``` 3. Install the client. Currently, we provide the VS Code extension on [Visual @@ -134,9 +134,10 @@ This is a good way to see the results from the unit tests. ![completion](images/completion.png) -- **Diagnostics:** Syntax error would be underlined. This feature is powered by - Brenton's `AST` and `Lint` paclets, thank you - [@bostick](https://github.com/bostick). +- **Diagnostics:** Syntax error would be underlined. This feature is powered + by [CodeParser](https://github.com/WolframResearch/codeparser) and + [CodeInspector](https://github.com/WolframResearch/codeinspector) paclets, + thank you [@bostick](https://github.com/bostick). ![diagnostics](images/diagnostics.png) @@ -195,10 +196,11 @@ It will be nice if you want to make a contribution to the following topic. create a pull request to add the clients in README.md once your client is released. -* Thanks to Brenton's `AST` and `Lint` paclets, we are able to parse the code - and extract useful information. If you have an idea about how to use these - fantastic syntax tools to help the language server add more features, please - send us issues or pull requests. +* Thanks to [CodeParser](https://github.com/WolframResearch/codeparser) and + [CodeInspector](https://github.com/WolframResearch/codeinspector) paclets, + we are able to parse the code and extract useful information. If you have + an idea about how to use these fantastic syntax tools to help the language + server add more features, please send us issues or pull requests. If you want to help us with this project, feel free to fork and create a pull request. Do not forget to add unit tests if possible. diff --git a/src/WolframLanguageServer/AstPatterns.wl b/src/WolframLanguageServer/AstPatterns.wl index 0538e05..e670b60 100644 --- a/src/WolframLanguageServer/AstPatterns.wl +++ b/src/WolframLanguageServer/AstPatterns.wl @@ -152,42 +152,42 @@ ExportPattern[pattern_, o:OptionsPattern[]] := With[ AstPattern = <| "Token" -> ( - AST`LeafNode[kind_Symbol, tokenString_String, data_Association] + CodeParser`LeafNode[kind_Symbol, tokenString_String, data_Association] ), "Symbol" -> ( - AST`LeafNode[Symbol, symbolName_String, data_Association] + CodeParser`LeafNode[Symbol, symbolName_String, data_Association] ), "Integer" -> ( - AST`LeafNode[Integer, integerLiteral_String, data_Association] + CodeParser`LeafNode[Integer, integerLiteral_String, data_Association] ), "Real" -> ( - AST`LeafNode[Real, realLiteral_String, data_Association] + CodeParser`LeafNode[Real, realLiteral_String, data_Association] ), "Function" -> ( - AST`CallNode[AST`LeafNode[Symbol, functionName_String, _], arguments_List, data_Association] + CodeParser`CallNode[CodeParser`LeafNode[Symbol, functionName_String, _], arguments_List, data_Association] ), "MessageName" -> ( - AST`CallNode[ - AST`LeafNode[Symbol, "MessageName", _], + CodeParser`CallNode[ + CodeParser`LeafNode[Symbol, "MessageName", _], { - AST`LeafNode[Symbol, symbolName_String, _], - message:AST`LeafNode[String, messageLiteral_String, _] + CodeParser`LeafNode[Symbol, symbolName_String, _], + message:CodeParser`LeafNode[String, messageLiteral_String, _] }, data_Association ] ), "Definable" -> ( - AST`CallNode[ - AST`LeafNode[Symbol, op:(FunctionPattern["BinarySet"]), _], + CodeParser`CallNode[ + CodeParser`LeafNode[Symbol, op:(FunctionPattern["BinarySet"]), _], { - head:AST`CallNode[AST`LeafNode[Symbol, func:FunctionPattern["Definable"], _], { - AST`LeafNode[Symbol, (key_), _], + head:CodeParser`CallNode[CodeParser`LeafNode[Symbol, func:FunctionPattern["Definable"], _], { + CodeParser`LeafNode[Symbol, (key_), _], ___ }, _], body_ @@ -197,10 +197,10 @@ AstPattern = <| ), "Set" -> ( - AST`CallNode[ - AST`LeafNode[Symbol, op:(FunctionPattern["BinarySet"] | FunctionPattern["TenarySet"]), _], + CodeParser`CallNode[ + CodeParser`LeafNode[Symbol, op:(FunctionPattern["BinarySet"] | FunctionPattern["TenarySet"]), _], { - Repeated[AST`LeafNode[Symbol, tag_String, _], {0, 1}], + Repeated[CodeParser`LeafNode[Symbol, tag_String, _], {0, 1}], head_?lhsQ, body_ }, @@ -209,8 +209,8 @@ AstPattern = <| ), "Scope" -> ( - AST`CallNode[ - AST`LeafNode[Symbol, op:(FunctionPattern["Scope"]), _], + CodeParser`CallNode[ + CodeParser`LeafNode[Symbol, op:(FunctionPattern["Scope"]), _], { head_, body_, @@ -222,10 +222,10 @@ AstPattern = <| ), "InscopeSet" -> ( - AST`CallNode[ - AST`LeafNode[Symbol, op:("Set" | "SetDelayed"), _], + CodeParser`CallNode[ + CodeParser`LeafNode[Symbol, op:("Set" | "SetDelayed"), _], { - AST`LeafNode[Symbol, symbolName_String, symbolData_Association], + CodeParser`LeafNode[Symbol, symbolName_String, symbolData_Association], value_ }, data_Association @@ -233,18 +233,18 @@ AstPattern = <| ), "Delayed" -> ( - AST`CallNode[ - AST`LeafNode[Symbol, op:(FunctionPattern["Delayed"]), _], + CodeParser`CallNode[ + CodeParser`LeafNode[Symbol, op:(FunctionPattern["Delayed"]), _], { (* Optional Tag: *) _:Null, head_, body_}, data_Association ] ), "DelayedPattern" -> ( - AST`CallNode[ - AST`LeafNode[Symbol, "Pattern", _], + CodeParser`CallNode[ + CodeParser`LeafNode[Symbol, "Pattern", _], { - AST`LeafNode[Symbol, patternName_String, patternData_Association], + CodeParser`LeafNode[Symbol, patternName_String, patternData_Association], patternObject_ }, data_Association @@ -252,22 +252,22 @@ AstPattern = <| ), "CompoundExpression" -> ( - AST`CallNode[ - AST`LeafNode[Symbol, op:("CompoundExpression"), _], + CodeParser`CallNode[ + CodeParser`LeafNode[Symbol, op:("CompoundExpression"), _], exprs_List, data_Association ] ), "NamedColor" -> ( - AST`LeafNode[Symbol, color:FunctionPattern["NamedColor"], data_Association] + CodeParser`LeafNode[Symbol, color:FunctionPattern["NamedColor"], data_Association] ), "ColorModel" -> ( - AST`CallNode[ - AST`LeafNode[Symbol, model:FunctionPattern["ColorModel"], _], + CodeParser`CallNode[ + CodeParser`LeafNode[Symbol, model:FunctionPattern["ColorModel"], _], params: { - (AST`LeafNode[Integer | Real | String, _, _]).. + (CodeParser`LeafNode[Integer | Real | String, _, _]).. }, data_Association ] @@ -288,7 +288,7 @@ AstLevelspec = <| lhsQ[node_] := ( - FreeQ[node, _AST`AbstractSyntaxErrorNode] && + FreeQ[node, _CodeParser`AbstractSyntaxErrorNode] && MatchQ[FirstPosition[node, Symbol], {(1)...}] ) diff --git a/src/WolframLanguageServer/TextDocument.wl b/src/WolframLanguageServer/TextDocument.wl index d31f39f..b88c6a6 100644 --- a/src/WolframLanguageServer/TextDocument.wl +++ b/src/WolframLanguageServer/TextDocument.wl @@ -31,14 +31,33 @@ ClearAll[Evaluate[Context[] <> "*"]] Needs["DataType`"] Needs["WolframLanguageServer`Logger`"] Needs["WolframLanguageServer`Specification`"] -Needs["AST`"] -Needs["Lint`"] -(* After AST 0.15: prevent AST parsed into PackageNode and ContextNode *) -AST`Abstract`Private`abstractTopLevel = List /* Append[{}] +Needs["CodeParser`"] +Needs["CodeInspector`"] Needs["WolframLanguageServer`AstPatterns`"] Needs["WolframLanguageServer`ColorTable`"] +(* ::Section:: *) +(*CodeParser Shims*) + + +(* + Prevents AST being parsed into PackageNode and ContextNode. + Only monitors top-level comma issues. +*) +CodeParser`Abstract`Private`abstractTopLevel = ( + Replace[ + #, + AstPattern["Function"][{functionName_, arguments_, data_}] + /; (functionName == "CodeParser`Comma") :> ( + CodeParser`AbstractSyntaxErrorNode[AbstractSyntaxError`CommaTopLevel, arguments, data] + ), + {1} + ]& + /* List + /* Append[{}] +) + (* ::Section:: *) (*TextDocument*) @@ -533,7 +552,7 @@ CellToAST[doc_TextDocument, {startLine_, endLine_}] := ( {StringRepeat::intp (* before 12.0 *)} ] // Quiet, #]& - // AST`ParseString + // CodeParser`CodeParse // Part[#, 2]& ) @@ -577,11 +596,11 @@ NodeContainsPosition[node_, pos:{_Integer, _Integer}] := ( -1, if node is before pos; 0, if node contains pos; +1, if node is after pos; - default, node does not have AST`Source information. + default, node does not have CodeParser`Source information. *) CompareNodePosition[node_, {line_Integer, col_Integer}, default_:Missing["NotFound"]] := With[ { - source = node // Last // Key[AST`Source] + source = node // Last // Key[CodeParser`Source] }, Which[ @@ -688,13 +707,13 @@ ToDocumentSymbolImpl[node_] := ( ), "range" -> ( data - // Key[AST`Source] + // Key[CodeParser`Source] // SourceToRange ), "selectionRange" -> ( head // Last - // Key[AST`Source] + // Key[CodeParser`Source] // SourceToRange ), "children" -> ({}) @@ -717,12 +736,12 @@ ToDocumentSymbolImpl[node_] := ( "kind" -> SymbolKind["Variable"], "range" -> ( data - // Key[AST`Source] + // Key[CodeParser`Source] // SourceToRange ), "selectionRange" -> ( symbolData - // Key[AST`Source] + // Key[CodeParser`Source] // SourceToRange ), "children" -> ({}) @@ -770,13 +789,13 @@ ToDocumentSymbolImpl[node_] := ( ), "range" -> ( data - // Key[AST`Source] + // Key[CodeParser`Source] // SourceToRange ), "selectionRange" -> ( head // Last - // Key[AST`Source] + // Key[CodeParser`Source] // SourceToRange ), "children" -> ({}) @@ -787,8 +806,8 @@ ToDocumentSymbolImpl[node_] := ( exprs // Map[ToDocumentSymbolImpl] ), - (* lhsNode[AST`CallNode[caller_, {callees__}, _]] :> ({}), - lhsNode[AST`LeafNode[Symbol, symbolName_String, _]] :> ({}), *) + (* lhsNode[CodeParser`CallNode[caller_, {callees__}, _]] :> ({}), + lhsNode[CodeParser`LeafNode[Symbol, symbolName_String, _]] :> ({}), *) _ -> Nothing }] @@ -855,13 +874,15 @@ GetHoverInfo[doc_TextDocument, pos_LspPosition] := With[ ] // Most // Replace[indices_List :> { - getHoverInfoImpl[ast, indices, {}] + getHoverInfoImpl[ast, indices] + // Reap + // Last // Last // DeleteDuplicates, (* get range *) ast // Extract[indices] // Last - // Key[AST`Source] + // Key[CodeParser`Source] // Replace[{ _?MissingQ -> Nothing, source_ :> SourceToRange[source] @@ -888,17 +909,17 @@ getHoverInfoImpl[ast_, {index_Integer, restIndices___}, res_] := ( HoverInfo["Message", {symbolName, "usage"}] ), integer:AstPattern["Integer"][{integerLiteral_}] :> ( - HoverInfo["Number", {integerLiteral, AST`FromNode[integer]}] + HoverInfo["Number", {integerLiteral, CodeParser`FromNode[integer]}] ), real:AstPattern["Real"][{realLiteral_}] :> ( - HoverInfo["Number", {realLiteral, AST`FromNode[real]}] + HoverInfo["Number", {realLiteral, CodeParser`FromNode[real]}] ), AstPattern["Function"][{functionName_}] /; Length[{restIndices}] == 0 :> ( HoverInfo["Operator", {functionName}] (* TODO(kenkangxgwe): to know whether the cursor is hovering on the operator *) ), AstPattern["MessageName"][{symbolName_, message_}] :> ( - HoverInfo["Message", {symbolName, AST`FromNode[message]}] + HoverInfo["Message", {symbolName, CodeParser`FromNode[message]}] ), _ :> Nothing }]] @@ -968,14 +989,14 @@ GetTokenPrefix[doc_TextDocument, pos_LspPosition] := With[ (* get token list *) Take[doc["text"], lineRange] // Curry[StringRiffle]["\n"] - // AST`TokenizeString + // CodeParser`CodeTokenize // SelectFirst[NodeContainsPosition[{ line - rangeStartLine + 1, pos["character"] }]] // Replace[ AstPattern["Token"][{tokenString_, data_}] :> ( - StringTake[tokenString, pos["character"] - Part[data[AST`Source], 1, 2] + 1] + StringTake[tokenString, pos["character"] - Part[data[CodeParser`Source], 1, 2] + 1] ) ] )] // Replace[ @@ -995,12 +1016,12 @@ DiagnoseDoc[doc_TextDocument] := ( // Replace[{_String?(StringStartsQ["#!"]), restLines___} :> ({"", restLines})] // Curry[StringRiffle]["\n"] // Replace[err:Except[_String] :> (LogError[doc]; "")] - // Lint`LintString + // CodeInspector`CodeInspect // Replace[_?FailureQ -> {}] - // ReplaceAll[Lint`Lint[tag_, description_, severity_, data_] :> Diagnostic[<| + // ReplaceAll[CodeInspector`InspectionObject[tag_, description_, severity_, data_] :> Diagnostic[<| "range" -> ( data - // Key[AST`Source] + // Key[CodeParser`Source] // SourceToRange // If[tag == "GroupMissingCloser", ReplaceKey[#, "end" -> #["start"]]&, @@ -1029,9 +1050,9 @@ DiagnoseDoc[doc_TextDocument] := ( StringJoin[ "[", tag, "] ", description - // ReplaceAll[{Lint`Format`LintMarkup[content_, ___] :> ( + (* // ReplaceAll[{CodeInspector`Format`LintMarkup[content_, ___] :> ( ToString[content] - )}] + )}] *) // StringReplace["``" -> "\""] ] ) @@ -1202,7 +1223,7 @@ ScopeHeadSymbolSource["With", head_, name_String] := ( Part[head, 2], AstPattern["InscopeSet"][{symbolName_, symbolData_}] /; (symbolName == name) :> ( - symbolData[AST`Source] + symbolData[CodeParser`Source] // Replace[ _?MissingQ :> ( LogDebug["With"]; @@ -1219,7 +1240,7 @@ ScopeHeadSymbolSource["Function", head_, name_String] := ( Replace[head, { AstPattern["Symbol"][{symbolName_, data_}] /; (symbolName == name) :> ( - data[AST`Source] + data[CodeParser`Source] // Replace[ _?MissingQ :> ( LogDebug["Function"]; @@ -1234,7 +1255,7 @@ ScopeHeadSymbolSource["Function", head_, name_String] := ( arguments, AstPattern["Symbol"][{symbolName_, data_}] /; (symbolName == name) :> ( - data[AST`Source] + data[CodeParser`Source] // Replace[ _?MissingQ :> ( LogDebug["Function"]; @@ -1260,7 +1281,7 @@ ScopeHeadSymbolSource["Block"|"Module"|"DynamicModule", head_, name_String] :=( AstPattern["InscopeSet"][{symbolName_, symbolData_}] | AstPattern["Symbol"][<|"symbolName" -> symbolName_, "data" -> symbolData_|>] /; (symbolName == name) :> ( - symbolData[AST`Source] + symbolData[CodeParser`Source] // Replace[ _?MissingQ :> ( LogDebug["Block"]; @@ -1283,7 +1304,7 @@ DelayedHeadPatternNameSource[head_, name_String] := ( Part[head, 2], AstPattern["DelayedPattern"][{patternName_, patternData_}] /; (patternName == name) :> ( - patternData[AST`Source] + patternData[CodeParser`Source] // Replace[ _?MissingQ :> ( LogDebug["Delayed"]; @@ -1312,7 +1333,7 @@ StaticLocalSource[node_, name_String] := ( node, AstPattern["Symbol"][{symbolName_, data_}] /; (symbolName == name) :> ( - data[AST`Source] + data[CodeParser`Source] (* happens when an operator is parsed as a symbol *) // Replace[_?MissingQ -> Nothing] ), @@ -1373,7 +1394,7 @@ FindTopLevelSymbols[node_, name_String] := ( // FirstCase[ AstPattern["Symbol"][{symbolName_, data_}] /; (functionName == name) :> ( - data[AST`Source] + data[CodeParser`Source] ) ] // ((symbolSource = #)&) @@ -1399,7 +1420,7 @@ FindTopLevelSymbols[node_, name_String] := ( // Replace[{ AstPattern["Symbol"][{symbolName_, data_}] /; (symbolName == name) :> ( - data[AST`Source] + data[CodeParser`Source] // Replace[{ source_ :> ( LogDebug[symbolName]; @@ -1443,7 +1464,7 @@ FindDocumentColor[doc_TextDocument] := With[ ColorInformation[<| "range" -> ( data - // Key[AST`Source] + // Key[CodeParser`Source] // SourceToRange ), "color" -> ( @@ -1461,7 +1482,7 @@ FindDocumentColor[doc_TextDocument] := With[ { color = ( params - // Map[AST`FromNode] + // Map[CodeParser`FromNode] // Apply[ToExpression[model]] ) }, @@ -1470,7 +1491,7 @@ FindDocumentColor[doc_TextDocument] := With[ ColorInformation[<| "range" -> ( data - // Key[AST`Source] + // Key[CodeParser`Source] // SourceToRange ), "color" -> ( From 558041d0ab6565f81904046b90dd0ae08ce773bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BA=B7=E5=B0=8F=E5=B9=BF?= Date: Mon, 13 Apr 2020 05:13:34 -0700 Subject: [PATCH 16/40] Improve: pattern matching in the signature of MyCurry * Add test that test extra arguments --- src/MyCurry.wl | 15 +++++++-------- test/MyCurryTest.wl | 14 +++++++++++++- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/src/MyCurry.wl b/src/MyCurry.wl index 9bdde6b..a0b2ef2 100644 --- a/src/MyCurry.wl +++ b/src/MyCurry.wl @@ -32,18 +32,17 @@ MyCurry[func_] := MyCurry[func, {2, 1}] CurriedFunction[func_, {arity_, index_Integer}, args_, slotMap_] /; (arity == index) := (func@@args) -CurriedFunction[func_, {arity_, index_Integer}, args_, slotMap_][firstArg_, restArgs___] := ( - CurriedFunction[func, {arity, index + 1}, ReplacePart[args, Position[slotMap, index] -> firstArg], slotMap] -) -(curriedFunction:CurriedFunction[func_, {arity_, index_Integer}, args_, slotMap_])[] := ( - curriedFunction -) CurriedFunction[func_, {arity_, index_Integer}, args_, slotMap_][firstArg_] := ( - CurriedFunction[func, {arity, index + 1}, ReplacePart[args, Position[slotMap, index + 1] -> firstArg], slotMap] + CurriedFunction[func, {arity, index + 1}, + ReplacePart[args, Position[slotMap, index + 1, {1}, Heads->False] -> firstArg], + slotMap] ) CurriedFunction[func_, {arity_, index_Integer}, args_, slotMap_][firstArg_, restArgs__] := ( - CurriedFunction[func, {arity, index + 1}, ReplacePart[args, Position[slotMap, index + 1] -> firstArg], slotMap][restArgs] + CurriedFunction[func, {arity, index + 1}, + ReplacePart[args, Position[slotMap, index + 1, {1}, Heads->False] -> firstArg], + slotMap][restArgs] ) +(curriedFunction:CurriedFunction[func_, {arity_, index_Integer}, args_, slotMap_])[] := curriedFunction End[] diff --git a/test/MyCurryTest.wl b/test/MyCurryTest.wl index 21b310f..e893a66 100644 --- a/test/MyCurryTest.wl +++ b/test/MyCurryTest.wl @@ -79,9 +79,21 @@ VerificationTest[ MyCurry[f, 6 -> {5,4,6,3,1}][e, f, d, b, a, c], f[a, b, c, d, e], TestID -> "Currying with a slot map and extended arity" +], + +VerificationTest[ + MyCurry[f, 3][a, b, c ,d], + f[a, b, c][d], + TestID -> "Currying with extra arguments 1" +], + +VerificationTest[ + MyCurry[f][a][b, c ,d], + f[b, a][c, d], + TestID -> "Currying with extra arguments 2" ] -} // Map@Curry[Sow]@CurrentContext +} // Map[Sow[#, CurrentContext]&] End[] From 21555a280af71f6bc31d8c86807f1b2bad96ddfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BA=B7=E5=B0=8F=E5=B9=BF?= Date: Mon, 13 Apr 2020 05:18:29 -0700 Subject: [PATCH 17/40] Fix: the MerssageName is not given when hovered * When a message name string is hovered it should return a HoverInfo, but currently the CodeParser does not give the message string a Source, which causes the replace rule of Operator conflicts with that of MessageName. Thus, split the two rules into two sets. * Use Sow/Reap instead of aggregation during recursion. * Add related tests. --- src/WolframLanguageServer/TextDocument.wl | 55 +++--- src/WolframLanguageServer/Token.wl | 17 +- .../WolframLanguageServer/TextDocumentTest.wl | 179 +++++++++++++++++- 3 files changed, 216 insertions(+), 35 deletions(-) diff --git a/src/WolframLanguageServer/TextDocument.wl b/src/WolframLanguageServer/TextDocument.wl index b88c6a6..4ea0238 100644 --- a/src/WolframLanguageServer/TextDocument.wl +++ b/src/WolframLanguageServer/TextDocument.wl @@ -898,32 +898,37 @@ GetHoverInfo[doc_TextDocument, pos_LspPosition] := With[ ] -getHoverInfoImpl[ast_, {}, res_List] := res -getHoverInfoImpl[ast_, {index_Integer, restIndices___}, res_] := ( +getHoverInfoImpl[ast_, {}] := Null +getHoverInfoImpl[ast_, {index_Integer, restIndices___}] := ( Part[ast, index] - // (node \[Function] getHoverInfoImpl[ - node, - {restIndices}, - Append[res, node // Replace[{ - AstPattern["Symbol"][{symbolName_}] :> ( - HoverInfo["Message", {symbolName, "usage"}] - ), - integer:AstPattern["Integer"][{integerLiteral_}] :> ( - HoverInfo["Number", {integerLiteral, CodeParser`FromNode[integer]}] - ), - real:AstPattern["Real"][{realLiteral_}] :> ( - HoverInfo["Number", {realLiteral, CodeParser`FromNode[real]}] - ), - AstPattern["Function"][{functionName_}] /; Length[{restIndices}] == 0 :> ( - HoverInfo["Operator", {functionName}] - (* TODO(kenkangxgwe): to know whether the cursor is hovering on the operator *) - ), - AstPattern["MessageName"][{symbolName_, message_}] :> ( - HoverInfo["Message", {symbolName, CodeParser`FromNode[message]}] - ), - _ :> Nothing - }]] - ]) + // (node \[Function] ( + node + // { + Replace[{ + AstPattern["Function"][{functionName_}] /; Length[{restIndices}] == 0 :> ( + HoverInfo["Operator", {functionName}] + (* TODO(kenkangxgwe): to know whether the cursor is hovering on the operator *) + ), + _ -> Nothing + }], + Replace[{ + AstPattern["Symbol"][{symbolName_}] :> ( + HoverInfo["Message", {symbolName, "usage"}] + ), + integer:AstPattern["Integer"][{integerLiteral_}] :> ( + HoverInfo["Number", {integerLiteral, CodeParser`FromNode[integer]}] + ), + real:AstPattern["Real"][{realLiteral_}] :> ( + HoverInfo["Number", {realLiteral, CodeParser`FromNode[real]}] + ), + AstPattern["MessageName"][{symbolName_, message_}] :> ( + HoverInfo["Message", {symbolName, CodeParser`FromNode[message]}] + ), + _ -> Nothing + }] + } // Through // Map[Sow]; + getHoverInfoImpl[node, {restIndices}] + )) ) diff --git a/src/WolframLanguageServer/Token.wl b/src/WolframLanguageServer/Token.wl index c0ac55f..b6827d8 100644 --- a/src/WolframLanguageServer/Token.wl +++ b/src/WolframLanguageServer/Token.wl @@ -70,7 +70,7 @@ TokenDocumentation[token_String, tag_String, o: OptionsPattern[]] := ( _(* other messages *) :> ( { If[OptionValue["Header"], - GenHeader[token, tag, OptionValue["Format"]], + GenHeader[token, tag, "Format" -> OptionValue["Format"]], Nothing ], boxText @@ -172,13 +172,13 @@ GenHeader[token_String, tag_String, o: OptionsPattern[]] := ( ), _ :> ( If[OptionValue["Format"] == MarkupKind["Markdown"], - StringJoin[ - token, "::", tag, "\n" - ], StringJoin[ "```mathematica\n", token, "::", tag, "\n", "```" + ], + StringJoin[ + token, "::", tag, "\n" ] ] ) @@ -366,7 +366,7 @@ GetHoverAtPosition[doc_TextDocument, pos_LspPosition] := ( printHoverText[hoverInfo_List, range_LspRange:Automatic] := ( hoverInfo - // Map[printHoverTextImpl] + // printHoverTextImpl // StringRiffle[#, "\n\n---\n\n"]& // Replace[{ "" -> Null, @@ -386,9 +386,8 @@ printHoverText[hoverInfo_List, range_LspRange:Automatic] := ( ) -printHoverTextImpl[hoverInfo_HoverInfo] := ( - hoverInfo - // Replace[{ +printHoverTextImpl[hoverInfo_List] := ( + Replace[hoverInfo, { HoverInfo["Operator", {symbolName_String}] :> ( TokenDocumentation[symbolName, "usage"] ), @@ -406,7 +405,7 @@ printHoverTextImpl[hoverInfo_HoverInfo] := ( ] }] ) - }] + }, {1}] ) diff --git a/test/WolframLanguageServer/TextDocumentTest.wl b/test/WolframLanguageServer/TextDocumentTest.wl index d034ca6..825de6c 100644 --- a/test/WolframLanguageServer/TextDocumentTest.wl +++ b/test/WolframLanguageServer/TextDocumentTest.wl @@ -256,9 +256,186 @@ VerificationTest[ |>] }, TestID -> "FindAllCodeRangeTwoSection1" +], + +VerificationTest[ + GetHoverInfo[ + TextDocument[<| + "text" -> { + "Replace[a, b]" + } + |>], + LspPosition[<| + "line" -> 0, + "character" -> 3 + |>] + ], + { + {HoverInfo["Message", {"Replace", "usage"}]}, + LspRange[<| + "start" -> LspPosition[<| + "line" -> 0, + "character" -> 0 + |>], + "end" -> LspPosition[<| + "line" -> 0, + "character" -> 7 + |>] + |>] + }, + TestID -> "HoverSymbol" +], + +VerificationTest[ + GetHoverInfo[ + TextDocument[<| + "text" -> { + "2^^110" + } + |>], + LspPosition[<| + "line" -> 0, + "character" -> 3 + |>] + ], + { + {HoverInfo["Number", {"2^^110", 6}]}, + LspRange[<| + "start" -> LspPosition[<| + "line" -> 0, + "character" -> 0 + |>], + "end" -> LspPosition[<| + "line" -> 0, + "character" -> 6 + |>] + |>] + }, + TestID -> "HoverNumericLiteral" +], + +VerificationTest[ + GetHoverInfo[ + TextDocument[<| + "text" -> { + "General::obspkg" + } + |>], + LspPosition[<| + "line" -> 0, + "character" -> 3 + |>] + ], + { + { + HoverInfo["Message", {"General", "obspkg"}], + HoverInfo["Message", {"General", "usage"}] + }, + LspRange[<| + "start" -> LspPosition[<| + "line" -> 0, + "character" -> 0 + |>], + "end" -> LspPosition[<| + "line" -> 0, + "character" -> 7 + |>] + |>] + }, + TestID -> "HoverMessageName 1" +], + +VerificationTest[ + GetHoverInfo[ + TextDocument[<| + "text" -> { + "General::obspkg" + } + |>], + LspPosition[<| + "line" -> 0, + "character" -> 8 + |>] + ], + { + { + HoverInfo["Operator", {"MessageName"}], + HoverInfo["Message", {"General", "obspkg"}] + }, + LspRange[<| + "start" -> LspPosition[<| + "line" -> 0, + "character" -> 0 + |>], + "end" -> LspPosition[<| + "line" -> 0, + "character" -> 15 + |>] + |>] + }, + TestID -> "HoverMessageName 2" +], + +VerificationTest[ + GetHoverInfo[ + TextDocument[<| + "text" -> { + "General::obspkg" + } + |>], + LspPosition[<| + "line" -> 0, + "character" -> 12 + |>] + ], + { + { + HoverInfo["Operator", {"MessageName"}], + HoverInfo["Message", {"General", "obspkg"}] + }, + LspRange[<| + "start" -> LspPosition[<| + "line" -> 0, + "character" -> 0 + |>], + "end" -> LspPosition[<| + "line" -> 0, + "character" -> 15 + |>] + |>] + }, + TestID -> "HoverMessageName 3" +], + +VerificationTest[ + GetHoverInfo[ + TextDocument[<| + "text" -> { + "f @@ a" + } + |>], + LspPosition[<| + "line" -> 0, + "character" -> 3 + |>] + ], + { + {HoverInfo["Operator", {"Apply"}]}, + LspRange[<| + "start" -> LspPosition[<| + "line" -> 0, + "character" -> 0 + |>], + "end" -> LspPosition[<| + "line" -> 0, + "character" -> 6 + |>] + |>] + }, + TestID -> "HoverOperator" ] -} // Map@Curry[Sow]@CurrentContext +} // Map[Sow[#, CurrentContext]&] End[] From 531327d182b6e18cc14016be9247a6a4781d3bb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BA=B7=E5=B0=8F=E5=B9=BF?= Date: Wed, 15 Apr 2020 23:50:17 -0700 Subject: [PATCH 18/40] Fix: backslash in doc should be escaped * Add TokenTest to test the documentation. * Remove GetTriggerKeys declaration. --- external/Matypetica/test/DataTypeTest.wl | 2 +- src/WolframLanguageServer/Token.wl | 4 +- test/RunTest.wl | 3 +- test/WolframLanguageServer/TokenTest.wl | 78 ++++++++++++++++++++++++ 4 files changed, 83 insertions(+), 4 deletions(-) create mode 100644 test/WolframLanguageServer/TokenTest.wl diff --git a/external/Matypetica/test/DataTypeTest.wl b/external/Matypetica/test/DataTypeTest.wl index addbda2..2eb206f 100644 --- a/external/Matypetica/test/DataTypeTest.wl +++ b/external/Matypetica/test/DataTypeTest.wl @@ -250,7 +250,7 @@ VerificationTest[ TestID -> "Type usage 2" ] -} // Map@Curry[Sow]@CurrentContext +} // Map[Sow[#, CurrentContext]&] End[] diff --git a/src/WolframLanguageServer/Token.wl b/src/WolframLanguageServer/Token.wl index b6827d8..84a8e40 100644 --- a/src/WolframLanguageServer/Token.wl +++ b/src/WolframLanguageServer/Token.wl @@ -17,7 +17,6 @@ TokenDocumentation::usage = "TokenDocumentation[token_String, tag_String, o] ret GetHoverAtPosition::usage = "GetHoverAtPosition[doc_TextDocument, pos_LspPosition] gives the text to be shown when hover at the given position." GetSignatureHelp::usage = "GetSignatureHelp[doc_TextDocument, pos_LspPosition] gives the signature help at the position." GetTokenCompletionAtPostion::usage = "GetTokenCompletionAtPostion[doc_TextDocument, pos_LspPosition] gives a list of suggestions for completion." -GetTriggerKeys::usage = "GetTriggerKeys[] returns a list of characters that trigger a completion request when input." GetTriggerKeyCompletion::usage = "GetTriggerKeyCompletion[] returns a list of available leader keys." GetIncompleteCompletionAtPosition::usage = "GetIncompleteCompletionAtPosition[doc_TextDocument, pos_LspPosition, leader_String] gives a list of completion items according to the leader key." @@ -323,7 +322,8 @@ BoxToText[input_, o:OptionsPattern[]] := Block[ StringReplace[{ "~" -> "\\~", "`" -> "\\`", - "*" -> "\\*" + "*" -> "\\*", + "\\" -> "\\\\" }], Identity ] diff --git a/test/RunTest.wl b/test/RunTest.wl index 74468b4..5c90c5b 100644 --- a/test/RunTest.wl +++ b/test/RunTest.wl @@ -7,7 +7,8 @@ ClearAll[Evaluate[Context[] <> "*"]] TestContexts = { "MyCurryTest`", "DataTypeTest`", - "WolframLanguageServer`TextDocumentTest`" + "WolframLanguageServer`TextDocumentTest`", + "WolframLanguageServer`TokenTest`" } TestRunContext::usage = "Run tests for given context." TestRunAll::usage = "Run tests for all the contexts below:\n\t" <> StringRiffle[TestContexts, "\n\t"] diff --git a/test/WolframLanguageServer/TokenTest.wl b/test/WolframLanguageServer/TokenTest.wl new file mode 100644 index 0000000..f57f32c --- /dev/null +++ b/test/WolframLanguageServer/TokenTest.wl @@ -0,0 +1,78 @@ +(* ::Package:: *) + +BeginPackage["WolframLanguageServer`TokenTest`"] +ClearAll[Evaluate[Context[] <> "*"]] + + +Begin["`Private`"] +ClearAll[Evaluate[Context[] <> "*"]] + + +TestingContext = "WolframLanguageServer`Token`" +CurrentContext = "WolframLanguageServer`TokenTest`" +Needs[TestingContext] +Needs["DataType`"] +Needs["WolframLanguageServer`Specification`"] +Needs["WolframLanguageServer`TextDocument`"] + + +{ + +VerificationTest[ + TokenDocumentation["BeginPackage", "usage"], + StringJoin[ + "**BeginPackage** [*reference*](https://reference.wolfram.com/language/ref/BeginPackage.html) (Protected)\n\n\n", + "```mathematica\n", + "BeginPackage[\"context`\"]\n", + "```\n\n", + " makes *context*\\` and System\\` the only active contexts. \n\n", + "```mathematica\n", + "BeginPackage[\"context`\",{\"need_1`\",\"need_2`\",\[Ellipsis]}]\n", + "```\n\n", + " calls Needs on the *need*\\_*i*. \n\n" + ], + TestID -> "KnownSymbolUsage 1" +], + +VerificationTest[ + TokenDocumentation["Replace", "usage"], + StringJoin[ + "**Replace** [*reference*](https://reference.wolfram.com/language/ref/Replace.html) (Protected)\n\n\n", + "```mathematica\n", + "Replace[expr,rules]\n", + "```\n\n", + " applies a rule or list of rules in an attempt to transform the entire expression *expr*. \n\n", + "```mathematica\n", + "Replace[expr,rules,levelspec]\n", + "```\n\n", + " applies rules to parts of *expr* specified by *levelspec*. \n\n", + "```mathematica\n", + "Replace[rules]\n", + "```\n\n", + " represents an operator form of Replace that can be applied to an expression.\n\n", + "__Options:__\n", + "``` mathematica\n", + "Heads -> False\n", + "```" + ], + TestID -> "KnownSymbolUsage 2" +], + +VerificationTest[ + TokenDocumentation["Syntax", "stresc"], + StringJoin[ + "```mathematica\n", + "Syntax::stresc\n", + "```\n", + "Unknown string escape \\\\\\`1\\`." + ], + TestID -> "KnownMessageName" +] + +} // Map[Sow[#, CurrentContext]&] + + +End[] + + +EndPackage[] From f3e25513ca59c3145af7b982557ff682986c5dba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BA=B7=E5=B0=8F=E5=B9=BF?= Date: Thu, 16 Apr 2020 00:08:53 -0700 Subject: [PATCH 19/40] Upgrade: check dependencies for new packages. --- src/WolframLanguageServer/Server.wl | 4 ++-- test/RunTest.wl | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/WolframLanguageServer/Server.wl b/src/WolframLanguageServer/Server.wl index c292eca..21df871 100644 --- a/src/WolframLanguageServer/Server.wl +++ b/src/WolframLanguageServer/Server.wl @@ -1798,8 +1798,8 @@ checkGitRepo[state_WorkState] := ( checkDependencies[state_WorkState] := With[ { dependencies = { - {"AST", "0.15.1"}, - {"Lint", "0.15.1"} + {"CodeParser", "1.0"}, + {"CodeInspector", "1.0"} } }, diff --git a/test/RunTest.wl b/test/RunTest.wl index 5c90c5b..ee92fa6 100644 --- a/test/RunTest.wl +++ b/test/RunTest.wl @@ -27,7 +27,7 @@ TestRunContext[context_String] := ( ) -ShowTestReport[report_TestReportObject, context_String] := +ShowTestReport[report_TestReportObject, context_String] := Column[{ TableForm[{ {"Test: ", context}, From 24ff91f79f33062d666f2d8bcebc59707c3a469b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BA=B7=E5=B0=8F=E5=B9=BF?= Date: Sun, 19 Apr 2020 19:00:20 -0700 Subject: [PATCH 20/40] Add: extract and rename ExportPattern to the PatternTemplate package * Add pattern injection to allow more patterns during application. * Refactor the lambda expression form to named functions. * Change the syntax to mimic TemplateObject. * Allow nested PatternTemplateObject. * Add tests. --- src/PatternTemplate.wl | 113 +++++++++++++ src/WolframLanguageServer/AstPatterns.wl | 63 +------- src/WolframLanguageServer/TextDocument.wl | 185 ++++++++++------------ test/PatternTemplateTest.wl | 96 +++++++++++ test/RunTest.wl | 1 + 5 files changed, 296 insertions(+), 162 deletions(-) create mode 100644 src/PatternTemplate.wl create mode 100644 test/PatternTemplateTest.wl diff --git a/src/PatternTemplate.wl b/src/PatternTemplate.wl new file mode 100644 index 0000000..915cbd3 --- /dev/null +++ b/src/PatternTemplate.wl @@ -0,0 +1,113 @@ +(* ::Package:: *) + +(* Pattern Template *) +(* Author: kenkangxgwe *) + + +BeginPackage["PatternTemplate`"] +ClearAll[Evaluate[Context[] <> "*"]] + + +PatternTemplate::usage = "PatternTemplate[pattern_, o:OptionsPattern[]] returns a pattern template that can be applied on a list or association, in which new pattern names and additional patterns are given." +PatternTemplateObject::usage = "PatternTemplateObject[...] represent a templated pattern can be applied on a list or association." + + +Begin["`Private`"] +ClearAll[Evaluate[Context[] <> "*"]] + + +Options[PatternTemplate] = { + "OverwritePostfix" -> True +} + + +Attributes[PatternTemplateSlot] = {HoldFirst} + + +PatternTemplate[pattern_, o:OptionsPattern[]] := ( + PatternTemplateObject[pattern /. {Verbatim[Pattern] -> PatternTemplateSlot}, o] +) + + +Options[PatternTemplateObject] = { + "OverwritePostfix" -> True +} + + +PatternTemplateObject[templatedPattern_, o:OptionsPattern[]][newPatterns___] := ( + {newPatterns} + // Cases[Verbatim[Pattern][newPatternSymbol_Symbol, newPatternObject_] :> ( + With[ + { + newPatternName = SymbolName[Unevaluated[newPatternSymbol]], + newPatternTuple = PatternTemplateSlot[newPatternSymbol, newPatternObject] + }, + + { + newPatternName -> newPatternTuple, + If[OptionValue["OverwritePostfix"] && StringEndsQ[newPatternName, "$"], + StringDrop[newPatternName, -1] -> newPatternTuple, + Nothing + ] + } + ] + )] + // Flatten + // Apply[Association] + // PatternTemplateObject[templatedPattern] +) + +PatternTemplateObject[templatedPattern_, o:OptionsPattern[]][newPatterns_Association] := Block[ + { + newPatternTuples = Replace[ + newPatterns, + newPattern_Pattern :> (PatternTemplateSlot @@ newPattern), + {1} + ] + }, + + templatedPattern + //. { + PatternTemplateSlot[oldPatternSymbol_Symbol, oldPatternObject_] :> With[ + { + oldPatternName = SymbolName[Unevaluated[oldPatternSymbol]] + }, + + (* Renamed function parameter *) + Quiet[ + newPatternTuples[oldPatternName] + // Replace[{ + (* fallbacks to use nameless old pattern*) + _?MissingQ -> oldPatternObject, + (* uses new name and combines two patterns *) + PatternTemplateSlot[newPatternSymbol_, newPatternObject_] :> ( + newPatternSymbol:combinePatterns[newPatternObject, oldPatternObject] + ), + (* otherwise, combines with new pattern without name *) + newPatternObject_ :> combinePatterns[newPatternObject, oldPatternObject] + }], + {RuleDelayed::rhs} + ] + ], + (* Don't replace nested PatternTemplateObjects *) + nestedObject_PatternTemplateObject :> nestedObject + } +] + + +Attributes[combinePatterns] = {Flat} + + +combinePatterns[patterns__] := ( + {patterns} + // DeleteDuplicates + // DeleteCases[Verbatim[_]] + // Replace[{} -> {_}] + // Fold[Except[Except[#1], #2]&] +) + + +End[] + + +EndPackage[] diff --git a/src/WolframLanguageServer/AstPatterns.wl b/src/WolframLanguageServer/AstPatterns.wl index e670b60..cfdeb21 100644 --- a/src/WolframLanguageServer/AstPatterns.wl +++ b/src/WolframLanguageServer/AstPatterns.wl @@ -6,7 +6,7 @@ *) BeginPackage["WolframLanguageServer`AstPatterns`"] -Construct[ClearAll, Context[] <> "*"] +ClearAll[Evaluate[Context[] <> "*"]] FunctionPattern::usage = "A set of function head patterns." @@ -16,6 +16,7 @@ AstLevelspec::usage = "A set of levelspec that is useful to specify when using C Begin["`Private`"] ClearAll[Evaluate[Context[] <> "*"]] +Needs["PatternTemplate`"] Needs["WolframLanguageServer`Logger`"] Needs["WolframLanguageServer`ColorTable`"] @@ -92,64 +93,6 @@ FunctionPattern = <| |> -Options[ExportPattern] = { - "OverwritePostfix" -> True -} - -ExportPattern[pattern_, o:OptionsPattern[]] := With[ - { - exportedPattern = pattern /. {Verbatim[Pattern] -> ExportedPattern} - }, - - Hold[patternNewNameAssocOrList \[Function] Block[ - { - patternNewNamesAssoc = ( - patternNewNameAssocOrList - //Replace[{ - _Association :> patternNewNameAssocOrList, - (* - convert list of patterns to association where pattern - names point to their pattern - *) - {Verbatim[Pattern][_Symbol, _]...} :> ( - patternNewNameAssocOrList - // Map[( - # - // First - // SymbolName - // (patternName \[Function] { - patternName -> #, - If[OptionValue["OverwritePostfix"] && StringEndsQ[patternName, "$"], - StringDrop[patternName, -1] -> #, - Nothing - ] - }) - )&] - // Flatten - // Apply[Association] - ), - (* otherwise, use empty association *) - _ -> <||> - }] - ) - }, - exportedPattern - ]] - //. {ExportedPattern[patternName_Symbol, patternObject_] :> ( - (* Renamed function parameter *) - patternNewNamesAssoc - // Key[SymbolName[patternName]] - // (patternObject - // If[MissingQ[#], - Identity, - Curry[Pattern, 2][# // First] - ] - )& - )} - // ReleaseHold -] - - AstPattern = <| "Token" -> ( CodeParser`LeafNode[kind_Symbol, tokenString_String, data_Association] @@ -273,7 +216,7 @@ AstPattern = <| ] ) -|> // Map[ExportPattern] +|> // Map[PatternTemplate] AstLevelspec = <| diff --git a/src/WolframLanguageServer/TextDocument.wl b/src/WolframLanguageServer/TextDocument.wl index 4ea0238..15eee19 100644 --- a/src/WolframLanguageServer/TextDocument.wl +++ b/src/WolframLanguageServer/TextDocument.wl @@ -48,8 +48,7 @@ Needs["WolframLanguageServer`ColorTable`"] CodeParser`Abstract`Private`abstractTopLevel = ( Replace[ #, - AstPattern["Function"][{functionName_, arguments_, data_}] - /; (functionName == "CodeParser`Comma") :> ( + AstPattern["Function"][functionName:"CodeParser`Comma", arguments_, data_] :> ( CodeParser`AbstractSyntaxErrorNode[AbstractSyntaxError`CommaTopLevel, arguments, data] ), {1} @@ -691,7 +690,7 @@ ToDocumentSymbolImpl[doc_TextDocument, node_] := ( ToDocumentSymbolImpl[node_] := ( node // Replace[{ - AstPattern["Definable"][{head_, func_, key_, data_}] :> ( + AstPattern["Definable"][head_, func_, key_, data_] :> ( DocumentSymbol[<| "name" -> ( key @@ -720,50 +719,44 @@ ToDocumentSymbolImpl[node_] := ( |>] ), - AstPattern["Set"][{head_, op_, data_}] :> Block[ + AstPattern["Set"][head_, op:"Set", data_] :> Block[ { symbolList }, - ( - symbolList - // Map[Replace[{ - AstPattern["Symbol"][<|"symbolName" -> symbolName_, "data" -> symbolData_|>] :> ( - DocumentSymbol[<| - "name" -> ( - symbolName - ), - "kind" -> SymbolKind["Variable"], - "range" -> ( - data - // Key[CodeParser`Source] - // SourceToRange - ), - "selectionRange" -> ( - symbolData - // Key[CodeParser`Source] - // SourceToRange - ), - "children" -> ({}) - |>] - ), - _ -> Nothing - }]] - ) + Replace[symbolList, { + AstPattern["Symbol"][<|"symbolName" -> symbolName_, "data" -> symbolData_|>] :> ( + DocumentSymbol[<| + "name" -> ( + symbolName + ), + "kind" -> SymbolKind["Variable"], + "range" -> ( + data + // Key[CodeParser`Source] + // SourceToRange + ), + "selectionRange" -> ( + symbolData + // Key[CodeParser`Source] + // SourceToRange + ), + "children" -> ({}) + |>] + ), + _ -> Nothing + }, {1}] /; ( - op == "Set" - && ( - head - // GetSymbolList - // ((symbolList = #)&) - // MissingQ - // Not - ) + head + // GetSymbolList + // ((symbolList = #)&) + // MissingQ + // Not ) ], - AstPattern["Set"][{head_, op_, tag_, data_}] :> ( + AstPattern["Set"][head_, op_, tag_, data_] :> ( DocumentSymbol[<| "name" -> ( FirstCase[ @@ -802,7 +795,7 @@ ToDocumentSymbolImpl[node_] := ( |>] ), - AstPattern["CompoundExpression"][{exprs_}] :> ( + AstPattern["CompoundExpression"][exprs_] :> ( exprs // Map[ToDocumentSymbolImpl] ), @@ -837,14 +830,13 @@ ToLspRange[doc_TextDocument, {startLine_Integer, endLine_Integer}] := LspRange[< GetSymbolList[node_] := ( node // Replace[{ - AstPattern["Function"][{functionName_, arguments_}] - /; (functionName == "List") :> ( + AstPattern["Function"][functionName:"List", arguments_] :> ( arguments // Map[GetSymbolList] // Catenate // Replace[_?(MemberQ[_?MissingQ]) -> Missing["NotSymbolList"]] ), - symbolNode:AstPattern["Symbol"][{}] :> ( + symbolNode:AstPattern["Symbol"][] :> ( {symbolNode} ), _ -> {Missing["NotSymbolList"]} @@ -876,7 +868,7 @@ GetHoverInfo[doc_TextDocument, pos_LspPosition] := With[ // Replace[indices_List :> { getHoverInfoImpl[ast, indices] // Reap - // Last // Last + // Last // Flatten // DeleteDuplicates, (* get range *) ast @@ -904,24 +896,27 @@ getHoverInfoImpl[ast_, {index_Integer, restIndices___}] := ( // (node \[Function] ( node // { + If[Length[{restIndices}] == 0, + Replace[{ + AstPattern["Function"][functionName_] :> ( + HoverInfo["Operator", {functionName}] + (* TODO(kenkangxgwe): to know whether the cursor is hovering on the operator *) + ), + _ -> Nothing + }], + Nothing + ], Replace[{ - AstPattern["Function"][{functionName_}] /; Length[{restIndices}] == 0 :> ( - HoverInfo["Operator", {functionName}] - (* TODO(kenkangxgwe): to know whether the cursor is hovering on the operator *) - ), - _ -> Nothing - }], - Replace[{ - AstPattern["Symbol"][{symbolName_}] :> ( + AstPattern["Symbol"][symbolName_] :> ( HoverInfo["Message", {symbolName, "usage"}] ), - integer:AstPattern["Integer"][{integerLiteral_}] :> ( + integer:AstPattern["Integer"][integerLiteral_] :> ( HoverInfo["Number", {integerLiteral, CodeParser`FromNode[integer]}] ), - real:AstPattern["Real"][{realLiteral_}] :> ( + real:AstPattern["Real"][realLiteral_] :> ( HoverInfo["Number", {realLiteral, CodeParser`FromNode[real]}] ), - AstPattern["MessageName"][{symbolName_, message_}] :> ( + AstPattern["MessageName"][symbolName_, message_] :> ( HoverInfo["Message", {symbolName, CodeParser`FromNode[message]}] ), _ -> Nothing @@ -963,7 +958,7 @@ GetFunctionName[doc_TextDocument, pos_LspPosition] := With[ getFunctionNameImpl[ast_, indices_] := ( Extract[ast, indices // Replace[{} -> {All}]] // Replace[{ - AstPattern["Function"][{functionName_}] :> ( + AstPattern["Function"][functionName_] :> ( functionName // Replace[FunctionPattern["NoSignatureHelp"] -> Missing["NotFound"]] ), @@ -1000,7 +995,7 @@ GetTokenPrefix[doc_TextDocument, pos_LspPosition] := With[ pos["character"] }]] // Replace[ - AstPattern["Token"][{tokenString_, data_}] :> ( + AstPattern["Token"][tokenString_, data_] :> ( StringTake[tokenString, pos["character"] - Part[data[CodeParser`Source], 1, 2] + 1] ) ] @@ -1153,7 +1148,7 @@ FindScopeOccurence[doc_TextDocument, pos_LspPosition, o:OptionsPattern[]] := Blo name = FirstCase[ ast, - AstPattern["Symbol"][{symbolName_}] + AstPattern["Symbol"][symbolName_] ?(NodeContainsPosition[{line, character}]) :> ( symbolName ), @@ -1167,9 +1162,9 @@ FindScopeOccurence[doc_TextDocument, pos_LspPosition, o:OptionsPattern[]] := Blo FirstCase[ ast, ( - AstPattern["Scope"][{head_, body_, op_}] + AstPattern["Scope"][head_, body_, op_] ?(NodeContainsPosition[{line, character}]) | - AstPattern["Delayed"][{head_, body_, op_}] + AstPattern["Delayed"][head_, body_, op_] ?(NodeContainsPosition[{line, character}]) ) :> Block[ { @@ -1226,8 +1221,7 @@ ScopeHeadSymbolSource["With", head_, name_String] := ( FirstCase[ (* elements in the list *) Part[head, 2], - AstPattern["InscopeSet"][{symbolName_, symbolData_}] - /; (symbolName == name) :> ( + AstPattern["InscopeSet"][symbolName:name, symbolData_] :> ( symbolData[CodeParser`Source] // Replace[ _?MissingQ :> ( @@ -1243,8 +1237,7 @@ ScopeHeadSymbolSource["With", head_, name_String] := ( ScopeHeadSymbolSource["Function", head_, name_String] := ( Replace[head, { - AstPattern["Symbol"][{symbolName_, data_}] - /; (symbolName == name) :> ( + AstPattern["Symbol"][symbolName:name, data_] :> ( data[CodeParser`Source] // Replace[ _?MissingQ :> ( @@ -1254,12 +1247,10 @@ ScopeHeadSymbolSource["Function", head_, name_String] := ( ) ] ), - AstPattern["Function"][{functionName_, arguments_}] - /; (functionName == "List") :> ( + AstPattern["Function"][functionName:"List", arguments_] :> ( FirstCase[ arguments, - AstPattern["Symbol"][{symbolName_, data_}] - /; (symbolName == name) :> ( + AstPattern["Symbol"][symbolName:name, data_] :> ( data[CodeParser`Source] // Replace[ _?MissingQ :> ( @@ -1279,13 +1270,11 @@ ScopeHeadSymbolSource["Function", head_, name_String] := ( ScopeHeadSymbolSource["Block"|"Module"|"DynamicModule", head_, name_String] :=( Replace[head, { - AstPattern["Function"][{functionName_, arguments_}] - /; (functionName == "List") :> ( + AstPattern["Function"][functionName:"List", arguments_] :> ( FirstCase[ arguments, - AstPattern["InscopeSet"][{symbolName_, symbolData_}] | - AstPattern["Symbol"][<|"symbolName" -> symbolName_, "data" -> symbolData_|>] - /; (symbolName == name) :> ( + AstPattern["InscopeSet"][symbolName:name, symbolData_] | + AstPattern["Symbol"][<|"symbolName" -> symbolName:name, "data" -> symbolData_|>] :> ( symbolData[CodeParser`Source] // Replace[ _?MissingQ :> ( @@ -1307,8 +1296,7 @@ DelayedHeadPatternNameSource[head_, name_String] := ( Join[ Cases[ Part[head, 2], - AstPattern["DelayedPattern"][{patternName_, patternData_}] - /; (patternName == name) :> ( + AstPattern["DelayedPattern"][patternName:name, patternData_] :> ( patternData[CodeParser`Source] // Replace[ _?MissingQ :> ( @@ -1321,8 +1309,7 @@ DelayedHeadPatternNameSource[head_, name_String] := ( {0, Infinity} ], Replace[head, { - AstPattern["Function"][{functionName_, arguments_}] - /; (functionName == "Condition") :> ( + AstPattern["Function"][functionName:"Condition", arguments_] :> ( arguments // Last // Curry[StaticLocalSource][name] @@ -1336,8 +1323,7 @@ DelayedHeadPatternNameSource[head_, name_String] := ( StaticLocalSource[node_, name_String] := ( Cases[ node, - AstPattern["Symbol"][{symbolName_, data_}] - /; (symbolName == name) :> ( + AstPattern["Symbol"][symbolName:name, data_] :> ( data[CodeParser`Source] (* happens when an operator is parsed as a symbol *) // Replace[_?MissingQ -> Nothing] @@ -1353,8 +1339,8 @@ DynamicLocalSource[node_, name_String] := ( StaticLocalSource[node, name], Cases[ node, - AstPattern["Scope"][{head_, body_, op_}] | - AstPattern["Delayed"][{head_, body_, op_}] :> Block[ + AstPattern["Scope"][head_, body_, op_] | + AstPattern["Delayed"][head_, body_, op_] :> Block[ { headSource }, @@ -1385,31 +1371,27 @@ DynamicLocalSource[node_, name_String] := ( FindTopLevelSymbols[node_, name_String] := ( node // Replace[{ - AstPattern["Set"][{head_, op_}] :> Block[ + AstPattern["Set"][head_, op:"Set"] :> Block[ { symbolSource }, {symbolSource} /; ( - op == "Set" - && ( - head - // GetSymbolList - // FirstCase[ - AstPattern["Symbol"][{symbolName_, data_}] - /; (functionName == name) :> ( - data[CodeParser`Source] - ) - ] - // ((symbolSource = #)&) - // MissingQ - // Not - ) + head + // GetSymbolList + // FirstCase[ + AstPattern["Symbol"][symbolName:name, data_] :> ( + data[CodeParser`Source] + ) + ] + // ((symbolSource = #)&) + // MissingQ + // Not ) ], - AstPattern["Set"][{head_}] :> Block[ + AstPattern["Set"][head_] :> Block[ { symbolSource }, @@ -1418,13 +1400,12 @@ FindTopLevelSymbols[node_, name_String] := ( /; ( FirstCase[ head, - AstPattern["Symbol"][{}], + AstPattern["Symbol"][], Missing["NotFound"], AstLevelspec["LeafNodeWithSource"] ] // Replace[{ - AstPattern["Symbol"][{symbolName_, data_}] - /; (symbolName == name) :> ( + AstPattern["Symbol"][symbolName:name, data_] :> ( data[CodeParser`Source] // Replace[{ source_ :> ( @@ -1442,7 +1423,7 @@ FindTopLevelSymbols[node_, name_String] := ( ) ], - AstPattern["CompoundExpression"][{exprs_}] :> ( + AstPattern["CompoundExpression"][exprs_] :> ( exprs // Map[Curry[FindTopLevelSymbols][name]] // Catenate @@ -1465,7 +1446,7 @@ FindDocumentColor[doc_TextDocument] := With[ Join[ Cases[ ast, - AstPattern["NamedColor"][{color_, data_}] :> ( + AstPattern["NamedColor"][color_, data_] :> ( ColorInformation[<| "range" -> ( data @@ -1483,7 +1464,7 @@ FindDocumentColor[doc_TextDocument] := With[ ], Cases[ ast, - AstPattern["ColorModel"][{model_, params_, data_}] :> With[ + AstPattern["ColorModel"][model_, params_, data_] :> With[ { color = ( params diff --git a/test/PatternTemplateTest.wl b/test/PatternTemplateTest.wl new file mode 100644 index 0000000..dd6a570 --- /dev/null +++ b/test/PatternTemplateTest.wl @@ -0,0 +1,96 @@ +(* ::Package:: *) + +BeginPackage["PatternTemplateTest`"] +ClearAll[Evaluate[Context[] <> "*"]] + + +Begin["`Private`"]; +ClearAll[Evaluate[Context[] <> "*"]] + + +TestingContext = "PatternTemplate`" +CurrentContext = "PatternTemplateTest`" +Needs[TestingContext] + + +{ + +VerificationTest[ + PatternTemplate[_][], + _, + TestID -> "Blank" +], + +VerificationTest[ + PatternTemplate[oldName_][<||>], + _, + TestID -> "BlankWithOldName" +], + +VerificationTest[ + PatternTemplate[oldName_][<|"oldName" -> newName_|>], + newName_, + TestID -> "BlankWithNewName" +], + +VerificationTest[ + PatternTemplate[oldName_][<|"oldName" -> _List|>], + _List, + TestID -> "BlankWithNewPattern" +], + +VerificationTest[ + PatternTemplate[oldName_][<|"oldName" -> newName_List|>], + newName_List, + TestID -> "BlankWithNewNameAndPattern" +], + +VerificationTest[ + PatternTemplate[{{oldName_List}}][oldName_], + {{oldName_List}}, + TestID -> "PatternWithList" +], + +VerificationTest[ + PatternTemplate[{{oldName:_List|_Association}}][oldName:_?((Length[#] == 3)&)], + {{oldName:Except[Except[_?((Length[#] == 3)&)], _List|_Association]}}, + TestID -> "PatternWithListNewPattern" +], + +VerificationTest[ + PatternTemplate[outer:{inner:{oldName:{___}}}][oldName_, inner_, outer_], + outer:{inner:{oldName:{___}}}, + TestID -> "NestedPatterns" +], + +VerificationTest[ + PatternTemplate[outer:{inner:{oldName:{other___}}}][<|"oldName" -> newName_, "inner" -> newInner_, "outer" -> newOuter_|>], + newOuter:{newInner:{newName:{___}}}, + TestID -> "NestedPatternWithNewNames" +], + +VerificationTest[ + PatternTemplate[outer:{inner:{oldName:{___}}}][oldName:PatternTemplate[symbol_List][{}], inner_, outer_], + outer:{inner:{oldName:Except[Except[_List], {___}]}}, + TestID -> "NestedPatternTemplatesApply" +], + +VerificationTest[ + PatternTemplate[outer:{inner:{PatternTemplate[oldName:{___}][<|"oldName" -> nestedOldName_|>]}}][nestedOldName_, inner_, outer_], + outer:{inner:{nestedOldName:{___}}}, + TestID -> "NestedPatternTemplates" +], + +VerificationTest[ + PatternTemplate[outer:{inner:{PatternTemplate[oldName:{___}]}}][inner_, outer_], + outer:{inner:{PatternTemplateObject[PatternTemplate`Private`PatternTemplateSlot[oldName, {___}]]}}, + TestID -> "NestedPatternTemplateObjects" +] + +} // Map[Sow[#, CurrentContext]&] + + +End[] + + +EndPackage[] diff --git a/test/RunTest.wl b/test/RunTest.wl index ee92fa6..a2aae24 100644 --- a/test/RunTest.wl +++ b/test/RunTest.wl @@ -6,6 +6,7 @@ ClearAll[Evaluate[Context[] <> "*"]] TestContexts = { "MyCurryTest`", + "PatternTemplateTest`", "DataTypeTest`", "WolframLanguageServer`TextDocumentTest`", "WolframLanguageServer`TokenTest`" From d0d7a5ee5f24606d413acb612513f95493d37a5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BA=B7=E5=B0=8F=E5=B9=BF?= Date: Sun, 19 Apr 2020 19:02:04 -0700 Subject: [PATCH 21/40] Clean: use the same ClearAll across all the packages. (cherry picked from commit 667927b25da592d1a29240722840ad78a290b092) --- src/WolframLanguageServer/ColorTable.wl | 2 +- src/WolframLanguageServer/Specification.wl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/WolframLanguageServer/ColorTable.wl b/src/WolframLanguageServer/ColorTable.wl index ed77820..ecce2a1 100644 --- a/src/WolframLanguageServer/ColorTable.wl +++ b/src/WolframLanguageServer/ColorTable.wl @@ -6,7 +6,7 @@ *) BeginPackage["WolframLanguageServer`ColorTable`"] -Construct[ClearAll, Context[] <> "*"] +ClearAll[Evaluate[Context[] <> "*"]] WolframLanguageServer`ColorTable`Colorspace = { "RGB", "CMYK", "HSB", "XYZ", "LAB", "LCH", "LUV", "GrayScale" diff --git a/src/WolframLanguageServer/Specification.wl b/src/WolframLanguageServer/Specification.wl index 6f405b3..8aa6156 100644 --- a/src/WolframLanguageServer/Specification.wl +++ b/src/WolframLanguageServer/Specification.wl @@ -7,7 +7,7 @@ BeginPackage["WolframLanguageServer`Specification`"] -Construct[ClearAll, Context[] <> "*"] +ClearAll[Evaluate[Context[] <> "*"]]; LspPosition::usage = "is type of Position interface in LSP." From 8b7f9c3482aa5dfa43f4569efdd6fd526048711e Mon Sep 17 00:00:00 2001 From: kenkangxgwe Date: Tue, 28 Jul 2020 02:40:50 -0700 Subject: [PATCH 22/40] Add Paclet check for version 12.1 * rename `checkUpdates` to `initialCheck`. * Use `PacletObject` to check if a paclet exists or not. --- src/WolframLanguageServer/Server.wl | 53 ++++++++++++++++------------- 1 file changed, 29 insertions(+), 24 deletions(-) diff --git a/src/WolframLanguageServer/Server.wl b/src/WolframLanguageServer/Server.wl index 21df871..7d4d76f 100644 --- a/src/WolframLanguageServer/Server.wl +++ b/src/WolframLanguageServer/Server.wl @@ -1,7 +1,7 @@ (* ::Package:: *) (* Wolfram Language Server *) -(* Author: kenkangxgwe , +(* Author: kenkangxgwe , huxianglong *) @@ -1294,7 +1294,7 @@ handleNotification["initialized", msg_, state_] := ( state // ReplaceKey["initialized" -> True] // Curry[addScheduledTask][ServerTask[<| - "type" -> "CheckUpgrades", + "type" -> "InitialCheck", "scheduledTime" -> Now |>]] } @@ -1634,9 +1634,9 @@ doNextScheduledTask[state_WorkState] := ( /* (task["callback"][#, task["params"]]&) ] ), - "CheckUpgrades" :> ( + "InitialCheck" :> ( newState - // checkUpgrades + // initialCheck ), "JustContinue" :> {"Continue", newState}, _ :> ( @@ -1735,7 +1735,8 @@ defaultConfig = <| (*check upgrades*) -checkUpgrades[state_WorkState] := ( +initialCheck[state_WorkState] := ( + checkDependencies[state]; If[ DateDifference[ DateObject[state["config"]["configFileConfig"]["lastCheckForUpgrade"]], @@ -1748,7 +1749,6 @@ checkUpgrades[state_WorkState] := ( ], (* check for upgrade if not checked for more than checkInterval days *) checkGitRepo[state]; - checkDependencies[state]; (* ReplaceKey[state["config"], "lastCheckForUpgrade" -> DateString[Today]] // saveConfig *) ]; @@ -1795,6 +1795,19 @@ checkGitRepo[state_WorkState] := ( ]; ) +If[$VersionNumber >= 12.1, + pacletInstalledQ[{name_String, version_String}] := ( + PacletObject[name -> version] + // LogDebug + // FailureQ // Not + ), + pacletInstalledQ[{name_String, version_String}] := ( + PacletManager`PacletInformation[{name, version}] + // LogDebug + // MatchQ[{}] // Not + ) +] + checkDependencies[state_WorkState] := With[ { dependencies = { @@ -1812,24 +1825,16 @@ checkDependencies[state_WorkState] := With[ Return[] ]; - Table[ - PacletManager`PacletInformation[depinfo] - // Replace[{ - {} :> (StringRiffle[depinfo, "-"]), - _ :> (Nothing) - }], - {depinfo, dependencies} - ] // Replace[{depInstalls__} :> ( - showMessage[ - StringJoin["These dependencies with correct versions need to be installed or upgraded: ", - StringRiffle[{depInstalls}, ", "], ", ", - "otherwise the server may malfunction. ", - "Please see the [Installation section](https://github.com/kenkangxgwe/lsp-wl/blob/master/README.md#installation) for details." - ], - "Warning", - state - ] - )]; + dependencies + // Select[pacletInstalledQ /* Not] + // StringRiffle[#, ", ", "-"]& + // LogDebug + // StringTemplate[StringJoin[ + "These dependencies with correct versions need to be installed or upgraded: ``, ", + "otherwise the server may malfunction. ", + "Please see the [Installation](https://github.com/kenkangxgwe/lsp-wl/blob/master/README.md#installation) section for details." + ]] + // showMessage[#, "Warning", state]& ] From 93f37e01634f8b71e9ddb7c51455f2c55bbad11f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BA=B7=E5=B0=8F=E5=B9=BF?= Date: Tue, 28 Jul 2020 05:46:08 -0700 Subject: [PATCH 23/40] Replace: Currys in favor of lambda expressions. * lambda expression explicitly shows how args are filled in the function and has batter performance in runtime. * remove MyCurry as well --- init.wls | 6 -- src/MyCurry.wl | 51 ----------- src/WolframLanguageServer/Server.wl | 22 +++-- src/WolframLanguageServer/TextDocument.wl | 24 ++--- src/WolframLanguageServer/Token.wl | 12 +-- test/MyCurryTest.wl | 102 ---------------------- test/RunTest.wl | 1 - 7 files changed, 28 insertions(+), 190 deletions(-) delete mode 100644 src/MyCurry.wl delete mode 100644 test/MyCurryTest.wl diff --git a/init.wls b/init.wls index 857e146..ebd9bd2 100644 --- a/init.wls +++ b/init.wls @@ -143,12 +143,6 @@ Running tests for all. Needs["WolframLanguageServer`Logger`"]; -If[$VersionNumber < 11.3, - (* load myCurry *) - Needs["MyCurry`"]; - System`Curry = MyCurry`MyCurry -] - Module[ { stream, loglevel, logstreams, clientPid, port, pipe, diff --git a/src/MyCurry.wl b/src/MyCurry.wl deleted file mode 100644 index a0b2ef2..0000000 --- a/src/MyCurry.wl +++ /dev/null @@ -1,51 +0,0 @@ -(* ::Package:: *) - - -(* - A handmade Curry for version before 11.3 -*) - -BeginPackage["MyCurry`"] -ClearAll[Evaluate[Context[] <> "*"]] - - -MyCurry::usage = "A handmade Curry for version before 11.3." -CurriedFunction::usage = "The intermediate state of a curried function." - - -Begin["`Private`"] -ClearAll[Evaluate[Context[] <> "*"]] - - -Attributes[Curry] = {NHoldRest} - -MyCurry[func_, (arity_Integer?NonNegative) -> slotMap:{___Integer?Positive}] /; (arity >= Max[slotMap, 0]) := ( - CurriedFunction[func, {arity, 0}, Table[Missing[], Length[slotMap]], slotMap] -) -MyCurry[func_, slotMap:{___Integer?Positive}] := ( - MyCurry[func, Max[slotMap, 0] -> slotMap] -) -MyCurry[func_, arity_Integer?NonNegative] := ( - MyCurry[func, Range[arity]] -) -MyCurry[func_] := MyCurry[func, {2, 1}] - - -CurriedFunction[func_, {arity_, index_Integer}, args_, slotMap_] /; (arity == index) := (func@@args) -CurriedFunction[func_, {arity_, index_Integer}, args_, slotMap_][firstArg_] := ( - CurriedFunction[func, {arity, index + 1}, - ReplacePart[args, Position[slotMap, index + 1, {1}, Heads->False] -> firstArg], - slotMap] -) -CurriedFunction[func_, {arity_, index_Integer}, args_, slotMap_][firstArg_, restArgs__] := ( - CurriedFunction[func, {arity, index + 1}, - ReplacePart[args, Position[slotMap, index + 1, {1}, Heads->False] -> firstArg], - slotMap][restArgs] -) -(curriedFunction:CurriedFunction[func_, {arity_, index_Integer}, args_, slotMap_])[] := curriedFunction - - -End[] - - -EndPackage[] \ No newline at end of file diff --git a/src/WolframLanguageServer/Server.wl b/src/WolframLanguageServer/Server.wl index 7d4d76f..514a0c3 100644 --- a/src/WolframLanguageServer/Server.wl +++ b/src/WolframLanguageServer/Server.wl @@ -767,8 +767,8 @@ sendCachedResult[method_String, msg_, state_WorkState] := Block[ scheduleDelayedRequest[method_String, msg_, state_WorkState] := ( { "Continue", - state - // Curry[addScheduledTask][ServerTask[<| + + addScheduledTask[state, ServerTask[<| "type" -> method, "scheduledTime" -> DatePlus[Now, { ServerConfig["requestDelays"][method], @@ -1021,7 +1021,7 @@ handleRequest["completionItem/resolve", msg_, state_] := With[ markupKind = ( state["clientCapabilities"]["textDocument"]["completion"]["completionItem"]["documentationFormat"] // First - // Replace[Except[_?(Curry[MemberQ, 2][Values[MarkupKind]])] -> MarkupKind["PlainText"]] + // Replace[Except[_?(MemberQ[Values[MarkupKind], #]&)] -> MarkupKind["PlainText"]] ) }, @@ -1051,8 +1051,7 @@ handleRequest["completionItem/resolve", msg_, state_] := With[ ) }]; - state - // Curry[addScheduledTask][ServerTask[<| + addScheduledTask[state, ServerTask[<| "type" -> "JustContinue", "scheduledTime" -> Now |>]] @@ -1252,8 +1251,7 @@ handleRequest["textDocument/colorPresentation", msg_, state_] := With[ { "Continue", - state - // Curry[addScheduledTask][ServerTask[<| + addScheduledTask[state, ServerTask[<| "type" -> "JustContinue", "scheduledTime" -> Now |>]] @@ -1293,10 +1291,10 @@ handleNotification["initialized", msg_, state_] := ( "Continue", state // ReplaceKey["initialized" -> True] - // Curry[addScheduledTask][ServerTask[<| + // addScheduledTask[#, ServerTask[<| "type" -> "InitialCheck", "scheduledTime" -> Now - |>]] + |>]]& } ) @@ -1333,7 +1331,7 @@ handleNotification["$/cancelRequest", msg_, state_] := With[ // Replace[{ {pos_} :> ( Part[state["scheduledTasks"], pos]["type"] - // Curry[StringJoin][" request is cancelled."] + // StringJoin[#, " request is cancelled."]& // LogDebug; sendResponse[state["client"], <| "id" -> id, @@ -1454,10 +1452,10 @@ handleNotification["textDocument/didChange", msg_, state_WorkState] := With[ ] // ReplaceKey["version" -> doc["version"]] )] - // Curry[addScheduledTask][ServerTask[<| + // addScheduledTask[#, ServerTask[<| "type" -> "JustContinue", "scheduledTime" -> Now - |>]] + |>]]& // If[diagScheduledQ, List /* Prepend["Continue"], diff --git a/src/WolframLanguageServer/TextDocument.wl b/src/WolframLanguageServer/TextDocument.wl index 15eee19..dad2f44 100644 --- a/src/WolframLanguageServer/TextDocument.wl +++ b/src/WolframLanguageServer/TextDocument.wl @@ -571,7 +571,7 @@ GetCodeRangeAtPosition[doc_TextDocument, pos_LspPosition] := With[ cell_CellNode?(CellContainsLine[line]) :> cell["codeRange"], {}, {0, Infinity} ] - // SelectFirst[Curry[Between, 2][line]] + // SelectFirst[Between[line, #]&] ] @@ -634,7 +634,7 @@ SourceToRange[{{startLine_, startCol_}, {endLine_, endCol_}}] := ( ToDocumentSymbol[doc_TextDocument] := ( doc // divideCells - // Curry[ToDocumentSymbolImpl, 2][doc] + // ToDocumentSymbolImpl[doc, #]& // Flatten ) @@ -655,14 +655,14 @@ ToDocumentSymbolImpl[doc_TextDocument, node_] := ( Join[ If[!MissingQ[node["codeRange"]], node["codeRange"] - // Map[Curry[CellToAST, 2][doc]] + // Map[CellToAST[doc ,#]&] // Flatten // Map[ToDocumentSymbolImpl], {} ], If[!MissingQ[node["children"]], node["children"] - // Map[Curry[ToDocumentSymbolImpl, 2][doc]], + // Map[ToDocumentSymbolImpl[doc, #]&], {} ] ] // Flatten @@ -672,14 +672,14 @@ ToDocumentSymbolImpl[doc_TextDocument, node_] := ( Join[ If[!MissingQ[node["codeRange"]], node["codeRange"] - // Map[Curry[CellToAST, 2][doc]] + // Map[CellToAST[doc, #]&] // Flatten // Map[ToDocumentSymbolImpl], {} ], If[!MissingQ[node["children"]], node["children"] - // Map[Curry[ToDocumentSymbolImpl, 2][doc]], + // Map[ToDocumentSymbolImpl[doc, #]&], {} ] ] // Flatten @@ -988,7 +988,7 @@ GetTokenPrefix[doc_TextDocument, pos_LspPosition] := With[ // Replace[lineRange:{rangeStartLine_Integer, _Integer} :> ( (* get token list *) Take[doc["text"], lineRange] - // Curry[StringRiffle]["\n"] + // StringRiffle[#, "\n"]& // CodeParser`CodeTokenize // SelectFirst[NodeContainsPosition[{ line - rangeStartLine + 1, @@ -1014,7 +1014,7 @@ DiagnoseDoc[doc_TextDocument] := ( doc["text"] // Replace[{_String?(StringStartsQ["#!"]), restLines___} :> ({"", restLines})] - // Curry[StringRiffle]["\n"] + // StringRiffle[#, "\n"]& // Replace[err:Except[_String] :> (LogError[doc]; "")] // CodeInspector`CodeInspect // Replace[_?FailureQ -> {}] @@ -1206,7 +1206,7 @@ FindScopeOccurence[doc_TextDocument, pos_LspPosition, o:OptionsPattern[]] := Blo ], "TopLevelOnly" :> ( CellToAST[doc, {1, doc["text"] // Length}] - // Map[Curry[FindTopLevelSymbols][name]] + // Map[FindTopLevelSymbols[#, name]&] // Catenate ), _ -> {} @@ -1312,7 +1312,7 @@ DelayedHeadPatternNameSource[head_, name_String] := ( AstPattern["Function"][functionName:"Condition", arguments_] :> ( arguments // Last - // Curry[StaticLocalSource][name] + // StaticLocalSource[#, name]& ), _ :> {} }] @@ -1425,7 +1425,7 @@ FindTopLevelSymbols[node_, name_String] := ( AstPattern["CompoundExpression"][exprs_] :> ( exprs - // Map[Curry[FindTopLevelSymbols][name]] + // Map[FindTopLevelSymbols[#, name]&] // Catenate ), @@ -1512,7 +1512,7 @@ GetColorPresentation[doc_TextDocument, color_LspColor, range_LspRange] := With[ ColorPresentation[<| "label" -> ( rgbColor - // Curry[ColorConvert][colorSpace] + // ColorConvert[#, colorSpace]& // InputForm // ToString ) diff --git a/src/WolframLanguageServer/Token.wl b/src/WolframLanguageServer/Token.wl index 84a8e40..3b2e153 100644 --- a/src/WolframLanguageServer/Token.wl +++ b/src/WolframLanguageServer/Token.wl @@ -64,7 +64,7 @@ TokenDocumentation[token_String, tag_String, o: OptionsPattern[]] := ( ], GenOptions[token, "Format" -> OptionValue["Format"]] } // Flatten - // Curry[StringRiffle]["\n\n"] + // StringRiffle[#, "\n\n"]& ), _(* other messages *) :> ( { @@ -78,7 +78,7 @@ TokenDocumentation[token_String, tag_String, o: OptionsPattern[]] := ( GenPlainText ] } // Flatten - // Curry[StringRiffle]["\n"] + // StringRiffle[#, "\n"]& ) }] ) @@ -159,7 +159,7 @@ GenHeader[token_String, tag_String, o: OptionsPattern[]] := ( token // { Identity, - Curry[GetUri][tag], + GetUri[#, tag]&, GenAttributes } // Through // Apply[ @@ -204,7 +204,7 @@ Options[GenAttributes] = { GenAttributes[token_String, o:OptionsPattern[]] := ( Attributes[token] // Replace[_Attributes -> {}] - // Curry[StringRiffle][", "] + // StringRiffle[#, ", "]& ) Options[GenOptions] = { @@ -215,7 +215,7 @@ GenOptions[token_String, o:OptionsPattern[]] := ( // StringTemplate["Options[``]"] // ToExpression // Replace[_Options -> {}] - // Map[Curry[ToString][InputForm]] + // Map[ToString[#, InputForm]&] // Replace[{options__} :> ( If[OptionValue["Format"] == MarkupKind["Markdown"], { @@ -230,7 +230,7 @@ GenOptions[token_String, o:OptionsPattern[]] := ( } ] )] - // Curry[StringRiffle]["\n"] + // StringRiffle[#, "\n"]& ) diff --git a/test/MyCurryTest.wl b/test/MyCurryTest.wl deleted file mode 100644 index e893a66..0000000 --- a/test/MyCurryTest.wl +++ /dev/null @@ -1,102 +0,0 @@ -(* ::Package:: *) - -BeginPackage["MyCurryTest`"] -ClearAll[Evaluate[Context[] <> "*"]] - - -Begin["`Private`"]; -ClearAll[Evaluate[Context[] <> "*"]] - - -TestingContext = "MyCurry`" -CurrentContext = "MyCurryTest`" -Needs[TestingContext] - - -{ - -VerificationTest[ - MyCurry[f][y][x], - f[x, y], - TestID -> "Currying a binary function 1" -], - -VerificationTest[ - MyCurry[f][y, x], - f[x, y], - TestID -> "Currying a binary function 2" -], - -VerificationTest[ - MyCurry[f, 2][x, y], - f[x, y], - TestID -> "Currying a binary function 3" -], - -VerificationTest[ - MyCurry[f, 2][x][y], - f[x, y], - TestID -> "Currying a binary function 4" -], - -VerificationTest[ - MyCurry[f, 0], - f[], - TestID -> "Currying a nullary function 1" -], - -VerificationTest[ - MyCurry[f, 0][x], - f[][x], - TestID -> "Currying a nullary function 2" -], - -VerificationTest[ - MyCurry[f, {}][x], - f[][x], - TestID -> "Currying a nullary function 3" -], - -VerificationTest[ - MyCurry[f, 2 -> {}][x][y], - f[], - TestID -> "Currying a nullary function using a binary operator" -], - -VerificationTest[ - MyCurry[f, 3][x][][y][z, a], - f[x, y, z][a], - TestID -> "Currying with multiple applications" -], - -VerificationTest[ - MyCurry[f, {5,4,2,3,1}][e, c, d, b, a], - f[a, b, c, d, e], - TestID -> "Currying with a slot map" -], - -VerificationTest[ - MyCurry[f, 6 -> {5,4,6,3,1}][e, f, d, b, a, c], - f[a, b, c, d, e], - TestID -> "Currying with a slot map and extended arity" -], - -VerificationTest[ - MyCurry[f, 3][a, b, c ,d], - f[a, b, c][d], - TestID -> "Currying with extra arguments 1" -], - -VerificationTest[ - MyCurry[f][a][b, c ,d], - f[b, a][c, d], - TestID -> "Currying with extra arguments 2" -] - -} // Map[Sow[#, CurrentContext]&] - - -End[] - - -EndPackage[] diff --git a/test/RunTest.wl b/test/RunTest.wl index a2aae24..9cc6771 100644 --- a/test/RunTest.wl +++ b/test/RunTest.wl @@ -5,7 +5,6 @@ ClearAll[Evaluate[Context[] <> "*"]] TestContexts = { - "MyCurryTest`", "PatternTemplateTest`", "DataTypeTest`", "WolframLanguageServer`TextDocumentTest`", From 00584f87ebf1eb723c33e89a808ba2b06a626f27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BA=B7=E5=B0=8F=E5=B9=BF?= Date: Fri, 31 Jul 2020 04:10:26 -0700 Subject: [PATCH 24/40] Fix: Allow token pattern to match ErrorNode * This fixes the error when a backslash is input and being tokenized. --- src/WolframLanguageServer/AstPatterns.wl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/WolframLanguageServer/AstPatterns.wl b/src/WolframLanguageServer/AstPatterns.wl index cfdeb21..f92d408 100644 --- a/src/WolframLanguageServer/AstPatterns.wl +++ b/src/WolframLanguageServer/AstPatterns.wl @@ -95,7 +95,7 @@ FunctionPattern = <| AstPattern = <| "Token" -> ( - CodeParser`LeafNode[kind_Symbol, tokenString_String, data_Association] + (CodeParser`LeafNode|CodeParser`ErrorNode)[kind_Symbol, tokenString_String, data_Association] ), "Symbol" -> ( From da12935d13ad9defa90b074f160945e04ddab4ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BA=B7=E5=B0=8F=E5=B9=BF?= Date: Fri, 31 Jul 2020 04:11:05 -0700 Subject: [PATCH 25/40] Add more tests --- test/PatternTemplateTest.wl | 14 +++++++- .../WolframLanguageServer/TextDocumentTest.wl | 33 +++++++++++++++++-- test/WolframLanguageServer/TokenTest.wl | 6 ++++ 3 files changed, 49 insertions(+), 4 deletions(-) diff --git a/test/PatternTemplateTest.wl b/test/PatternTemplateTest.wl index dd6a570..9518659 100644 --- a/test/PatternTemplateTest.wl +++ b/test/PatternTemplateTest.wl @@ -54,7 +54,19 @@ VerificationTest[ VerificationTest[ PatternTemplate[{{oldName:_List|_Association}}][oldName:_?((Length[#] == 3)&)], {{oldName:Except[Except[_?((Length[#] == 3)&)], _List|_Association]}}, - TestID -> "PatternWithListNewPattern" + TestID -> "PatternWithListNewPattern 1" +], + +VerificationTest[ + PatternTemplate[{{oldName_?((Length[#] == 3)&)}}][oldName:_List|_Association], + {{(oldName:_List|_Association)?((Length[#] == 3)&)}}, + TestID -> "PatternWithListNewPattern 2" +], + +VerificationTest[ + PatternTemplate[{{oldName_:0}}][oldName:_List], + {{oldName:_List:0}}, + TestID -> "PatternWithOptional" ], VerificationTest[ diff --git a/test/WolframLanguageServer/TextDocumentTest.wl b/test/WolframLanguageServer/TextDocumentTest.wl index 825de6c..ecfbd81 100644 --- a/test/WolframLanguageServer/TextDocumentTest.wl +++ b/test/WolframLanguageServer/TextDocumentTest.wl @@ -390,13 +390,12 @@ VerificationTest[ ], { { - HoverInfo["Operator", {"MessageName"}], HoverInfo["Message", {"General", "obspkg"}] }, LspRange[<| "start" -> LspPosition[<| "line" -> 0, - "character" -> 0 + "character" -> 9 |>], "end" -> LspPosition[<| "line" -> 0, @@ -432,7 +431,35 @@ VerificationTest[ |>] |>] }, - TestID -> "HoverOperator" + TestID -> "HoverOperator 1" +], + +VerificationTest[ + GetHoverInfo[ + TextDocument[<| + "text" -> { + "{##&@@#}&" + } + |>], + LspPosition[<| + "line" -> 0, + "character" -> 2 + |>] + ], + { + {HoverInfo["Operator", {"SlotSequence"}]}, + LspRange[<| + "start" -> LspPosition[<| + "line" -> 0, + "character" -> 1 + |>], + "end" -> LspPosition[<| + "line" -> 0, + "character" -> 3 + |>] + |>] + }, + TestID -> "HoverOperator 2" ] } // Map[Sow[#, CurrentContext]&] diff --git a/test/WolframLanguageServer/TokenTest.wl b/test/WolframLanguageServer/TokenTest.wl index f57f32c..736876d 100644 --- a/test/WolframLanguageServer/TokenTest.wl +++ b/test/WolframLanguageServer/TokenTest.wl @@ -58,6 +58,12 @@ VerificationTest[ TestID -> "KnownSymbolUsage 2" ], +VerificationTest[ + TokenDocumentation["SlotSequence", "usage"], + StringJoin[""], + TestID -> "KnownSymbolUsage 3" +], + VerificationTest[ TokenDocumentation["Syntax", "stresc"], StringJoin[ From 658da5e29a4721de09fc2d143981fa64bfa520fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BA=B7=E5=B0=8F=E5=B9=BF?= Date: Fri, 31 Jul 2020 04:58:44 -0700 Subject: [PATCH 26/40] Change: divideCell logic Previously, we regard double empty lines as a cell separator but this is not true. Thus `(* ::style:: *)` comments are the only reliable pattern to divide the file now. --- src/WolframLanguageServer/TextDocument.wl | 524 +++++++--------------- 1 file changed, 155 insertions(+), 369 deletions(-) diff --git a/src/WolframLanguageServer/TextDocument.wl b/src/WolframLanguageServer/TextDocument.wl index dad2f44..51d9b02 100644 --- a/src/WolframLanguageServer/TextDocument.wl +++ b/src/WolframLanguageServer/TextDocument.wl @@ -1,7 +1,7 @@ (* ::Package:: *) (* Wolfram Language Server TextDocument *) -(* Author: kenkangxgwe , +(* Author: kenkangxgwe , huxianglong *) @@ -63,7 +63,7 @@ CodeParser`Abstract`Private`abstractTopLevel = ( DeclareType[TextDocument, <| "uri" -> _DocumentUri, - "text" -> {__String}, + "text" -> {___String}, "version" -> _Integer, "lastUpdate" -> _DateObject |>] @@ -164,362 +164,145 @@ DeclareType[CellNode, <| "children" -> {___CellNode} |>] -divideCells[doc_TextDocument] := Block[ - { - styleLineNo, titleLineNo, emptyLineNo - }, - - styleLineNo = Position[ +divideCells[doc_TextDocument] := ( + Position[ doc["text"], + (* matches style line *) _?(StringContainsQ["(* " ~~ "::" ~~ Shortest[style___] ~~ "::" ~~ " *)"]), {1}, Heads -> False ] - // Flatten; - - titleLineNo = styleLineNo - (* selects style cells w/o a name *) - // Select[ - (Part[doc["text"], #]&) - /* StringCases["(* "~~"::"~~Shortest[style___]~~"::"~~" *)" :> style] - /* First - /* MemberQ[{"Package"}] - ] - (* excludes them when finding names *) - // (Complement[styleLineNo, #] + 1&) - // Select[(Part[doc["text"], #]&) - /* And[ - (* no style declared on name line *) - (Not @* StringContainsQ["(* " ~~ "::" ~~ style___ ~~ "::" ~~ " *)"]), - (* match a name *) - StringMatchQ[ - StartOfString ~~ (Whitespace | "") ~~ - "(*" ~~ Longest[title___] ~~ "*)" ~~ - (Whitespace | "") ~~ EndOfString - ] - ] - /* Through - ]; - - emptyLineNo = Position[doc["text"], "", {1}, Heads -> False] // Flatten; - - Join[ - (* styleLines *) - Thread[{ - "label" -> "style", - Thread["line" -> styleLineNo], - Thread["data" -> Block[ - { - styleLines = Part[doc["text"], styleLineNo] - }, - - styleLines - (* selectionRange *) - // StringPosition["(* " ~~ "::" ~~ Shortest[style___] ~~ "::" ~~ " *)"] - // Part[#, All, 1]& - // Apply[{#1 + 5, #2 - 5}&, #, {1}]& - // { - (* {("selectionRange" -> selectionRange)..} *) - Thread["selectionRange" -> #]&, - (* {("style" -> style)..} *) - Thread["style" -> ( - StringTake @@@ - Thread[{styleLines, #}] - )]& - } // Through - // Thread - // Map[Association] - ]] - }], - - (* titleLines *) - Thread[{ - "label" -> "title", - Thread["line" -> titleLineNo], - Thread["data" -> Block[ - { - titleLines = Part[doc["text"], titleLineNo] - }, - - titleLines - // StringPosition["(*" ~~ Longest[title___] ~~ "*)"] - // Part[#, All, 1]& - // Apply[{#1 + 2, #2 - 2}&, #, {1}]& - // { - Thread["selectionRange" -> #]&, - Thread["title" -> ( - StringTake @@@ - Thread[{titleLines, #}] - )]& - } // Through - // Thread - // Map[Association] - ]] - }], - - (* emptyLines *) - Thread[{ - "label" -> "empty", - Thread["line" -> emptyLineNo] - }] - ] - // Map[Association] - // SortBy[Key["line"]] - // Append[<|"label" -> "end", "line" -> (Length[doc["text"]] + 1)|>] - // (divideCellImpl[#, - If[doc["text"] // First // StringStartsQ["#!"], 1, 0] - // <|"label" -> "start", "startLine" -> #, "endLine" -> #|>& - ] // Reap)& - (* Get the sown List if there is one. *) - // Last // Replace[{} :> {{}}] // First - // Prepend[CellNode[<| - "style" -> AdditionalStyle["File"], - "name" -> "", - "range" -> {1, 1}, - "codeRange" -> {}, - "children" -> {} - |>]] + // Flatten + // Append[Length[doc["text"]] + 1] + // Prepend[0] + // BlockMap[Apply[constructCellNode[doc, #1, #2]&], #, 2, 1]& // Fold[InsertCell] // TerminateCell -] - - -divideCellImpl[{}, state_Association] := Null -divideCellImpl[{lineState_Association, lineStates___}, state_Association] := divideCellImpl[{lineStates}, ( - Which[ - state["endLine"] + 1 == lineState["line"], - lineState["label"] - // Replace[{ - "empty" :> ( - state - // ReplacePart["endLine" -> lineState["line"]] - ), - "style" :> ( - state["label"] - // Replace[{ - "empty" :> ( - Sow[{ - "codeEnd", - If[state["startLine"] < state["endLine"], - state["startLine"] - 1, - state["endLine"] - ] - }] - ), - "style" :> ( - Sow[{"untitledStyle", state["startLine"], state["data"]}] - ) - (* do nothing: {"start"} | {"title",_,_} *) - }]; - <| - "label" -> "style", - "startLine" -> lineState["line"], - "endLine" -> lineState["line"], - "data" -> lineState["data"] - |> - ), - If[ - state["label"] == "style" && - state["startLine"] + 1 == lineState["line"] && - !MemberQ[{"Package"}, state["data"]["style"]], - "title" :> ( - Sow[{ - "titledStyle", - state["startLine"], - Join[state["data"], lineState["data"]] - }]; - <| - "label" -> "title", - "startLine" -> lineState["line"], - "endLine" -> lineState["line"], - "data" -> lineState["data"] - |> - ), - "title" :> ( - state - ) - ], - "end" :> ( - state["label"] - // Replace[{ - "empty" :> ( - Sow[{"codeEnd", lineState["line"] - 2}] - ) - }]; - <| - "label" -> "end", - "startLine" -> lineState["line"], - "endLine" -> lineState["line"] - |> - ) - }], - - state["endLine"] + 1 < lineState["line"], - (* any -> normal *) - state["label"] - // Replace[{ - "empty" :> ( - If[state["startLine"] < state["endLine"], - Sow[{"codeEnd", state["endLine"] - 2}]; - Sow[{"codeStart", state["endLine"] + 1}] - ] - ), - "style" :> ( - Sow[{"untitledStyle", state["startLine"], state["data"]}]; - Sow[{"codeStart", state["endLine"] + 1}] - ), - "title" | "start" :> ( - Sow[{"codeStart", state["endLine"] + 1}] - ) - }]; - (* normal -> any *) - lineState["label"] - // Replace[{ - "empty" :> ( - <| - "label" -> "empty", - "startLine" -> lineState["line"], - "endLine" -> lineState["line"] - |> - ), - "style" :> ( - Sow[{"codeEnd", lineState["line"] - 1}]; - <| - "label" -> "style", - "startLine" -> lineState["line"], - "endLine" -> lineState["line"], - "data" -> lineState["data"] - |> - ), - "end" :> ( - Sow[{"codeEnd", lineState["line"] - 1}]; - <| - "label" -> "end", - "startLine" -> lineState["line"], - "endLine" -> lineState["line"] - |> - ) - }], + // Replace[err:Except[_CellNode] :> ( + LogError["The result of devideCells is not a CellNode " <> ToString[err]] + )] + // LogDebug +) - (* state["endLine"] + 1 > lineState["line"], *) - True, - (* do nothing *) - state - ] -)] +constructCellNode[doc_TextDocument, styleLine_Integer, endLine_Integer] := Block[ + { + style, title = Missing["Untitled"], codeStart + }, -InsertCell[rootCell_CellNode, directive:{"titledStyle"|"untitledStyle", line_Integer, data_Association}] := ( - If[!HeadingQ[data["style"]], - Return[rootCell] + style = If[styleLine == 0, + AdditionalStyle["File"], + Part[doc["text"], styleLine] + // StringCases["(* "~~"::"~~Shortest[style___]~~"::"~~" *)" :> style] + // First + // Replace["" -> "[empty]"] ]; - rootCell["children"] - // Replace[{ - _?MissingQ|{} :> ( - rootCell - // ReplaceKey["children" -> {cellDirectiveToCellNode[directive]}] - ), - {preCells___, lastCell_CellNode} :> ( - If[HeadingLevel[lastCell["style"]] < HeadingLevel[data["style"]], - (* includes the new cell in the last child *) - rootCell - // ReplaceKey["children" -> {preCells, InsertCell[lastCell, directive]}], - (* append the new cell after the last child *) - rootCell - // ReplaceKey["children" -> { - preCells, - TerminateCell[lastCell], - cellDirectiveToCellNode[directive] - }] - ] - ) - }] -) + If[!AnonymousStyleQ[style] && + (styleLine + 1 != endLine), + (Part[doc["text"], styleLine + 1] + // StringCases[ + StartOfString ~~ (Whitespace | "") ~~ + "(*" ~~ Longest[t___] ~~ "*)" ~~ + (Whitespace | "") ~~ EndOfString :> t + ] + // Replace[ + {t_, ___} :> (title = t) + ]); + codeStart = findCodeLine[doc, styleLine + 2], + codeStart = findCodeLine[doc, styleLine + 1] + ]; -cellDirectiveToCellNode[{label:("titledStyle"|"untitledStyle"), line_Integer, data_Association}] := ( CellNode[<| - "style" -> data["style"], - "name" -> If[label == "titledStyle" && data["title"] != "", data["title"], "[untitled]"], - "range" -> { - line, - line + If[label == "titledStyle", 1, 0] - }, - "selectionRange" -> LspRange[<| - "start" -> LspPosition[<| - "line" -> line - 1, - "character" -> Part[data["selectionRange"], 1] - 1 - |>], - "end" -> LspPosition[<| - "line" -> line - 1, - "character" -> Part[data["selectionRange"], 2] + "level" -> If[HeadingQ[style], HeadingLevel[style], Infinity], + "style" -> style, + "name" -> (title // Replace[(_?MissingQ|"") :> ""]), + "range" -> {styleLine, endLine}, + "selectionRange" -> If[!MissingQ[title], + Part[doc["text"], styleLine + 1] + // StringPosition[title] + // First + // Apply[{startPos, endPos} \[Function] ( + LspRange[<| + "start" -> LspPosition[<| + "line" -> styleLine, + "character" -> startPos - 1 + |>], + "end" -> LspPosition[<| + "line" -> styleLine, + "character" -> endPos + |>] + |>] + )], + LspRange[<| + "start" -> LspPosition[<| + "line" -> styleLine - 1, + "character" -> 0 + |>], + "end" -> LspPosition[<| + "line" -> styleLine, + "character" -> 0 + |>] |>] - |>], - "codeRange" -> {} + ], + "codeRange" -> If[codeStart < endLine, {{codeStart, endLine - 1}}, {}], + "children" -> {} |>] -) -InsertCell[rootCell_CellNode, directive:{label:("codeStart"|"codeEnd"), line_Integer}] := ( - rootCell["children"] - // Replace[{ - _?MissingQ|{} :> ( - rootCell - // If[label == "codeStart", - ReplaceKeyBy[ - "codeRange" -> Append[{line, line}] - ], - ReplaceKey[ - {"codeRange", -1, -1} -> line - ] - ] - ), - {preCells___, lastCell_CellNode} :> ( - rootCell - // ReplaceKey["children" -> {preCells, InsertCell[lastCell, directive]}] - ) - }] +] + +findCodeLine[doc_TextDocument, currentLine_Integer] := ( + If[currentLine <= Length[doc["text"]] && + Part[doc["text"], currentLine] === "", + findCodeLine[doc, currentLine + 1], + currentLine + ] ) -TerminateCell[cell_CellNode] := Block[ - { - newLastChild = cell["children"] - // Replace[{ - _?MissingQ|{} :> Nothing, - {___, lastChild_CellNode} :> ( - TerminateCell[lastChild] - ) - }] - }, - - cell - // ReplaceKey[{"range", -1} -> Max[{ - cell["range"] // Last, - If[Length[cell["codeRange"]] > 0, - cell["codeRange"] - // Last - // Last, - Nothing - ], - newLastChild - // Replace[ - _CellNode :> ( - newLastChild["range"] // Last - ) +InsertCell[rootCell_CellNode, nextCell_CellNode] := ( + If[Length[rootCell["children"]] > 0 && + Last[rootCell["children"]]["level"] < nextCell["level"], + (* includes the new cell in the last child *) + rootCell + // ReplaceKeyBy[{"children", -1} -> (InsertCell[#, nextCell]&)], + rootCell + // If[nextCell["level"] == Infinity, + (* Joins the codeRange with root *) + ReplaceKeyBy["codeRange" -> (Join[#, nextCell["codeRange"]]&)], + Identity ] - }]] - // ReplaceKeyBy["children" -> ( - Replace[{ - _?MissingQ -> {}, - {children___, _CellNode} :> { - children, - newLastChild - } - }] - )] -] + // If[Length[rootCell["children"]] > 0, + ReplaceKeyBy[{"children", -1} -> TerminateCell], + Identity + ] + (* appends the new cell in the children list *) + // ReplaceKeyBy["children" -> Append[ + nextCell + // If[nextCell["level"] == Infinity, + (* removes codeRange *) + ReplaceKey["codeRange" -> {}], + Identity + ] + ]] + ] +) +TerminateCell[rootcell_CellNode] := ( + If[Length[rootcell["children"]] > 0, + rootcell + // ReplaceKeyBy[{"children", -1} -> TerminateCell] + // (newRootCell \[Function] ( + newRootCell + // ReplaceKey[{"range", -1} -> ( + Last[Last[newRootCell["children"]]["range"]] + )] + )), + rootcell + ] +) -HeadingCellQ[cellNode_CellNode] := HeadingQ[cellNode["style"]] + +AnonymousStyleQ[style:(_String|_AdditionalStyle)] := MatchQ[style, "Package" | _AdditionalStyle] HeadingQ[style:(_String|_AdditionalStyle)] := KeyMemberQ[HeadingLevel, style] HeadingLevel = <| "Title" -> 1, @@ -642,32 +425,34 @@ ToDocumentSymbol[doc_TextDocument] := ( ToDocumentSymbolImpl[doc_TextDocument, node_] := ( node // Replace[{ - _CellNode?HeadingCellQ :> (DocumentSymbol[<| - "name" -> node["name"], - "detail" -> node["style"], - "kind" -> If[node["style"] == "Package", - SymbolKind["Package"], - SymbolKind["String"] - ], - "range" -> ToLspRange[doc, node["range"]], - "selectionRange" -> node["selectionRange"], - "children" -> ( - Join[ - If[!MissingQ[node["codeRange"]], - node["codeRange"] - // Map[CellToAST[doc ,#]&] - // Flatten - // Map[ToDocumentSymbolImpl], - {} - ], - If[!MissingQ[node["children"]], - node["children"] - // Map[ToDocumentSymbolImpl[doc, #]&], - {} - ] - ] // Flatten - ) - |>]), + _CellNode?(Key["style"] /* AnonymousStyleQ /* Not) :> ( + DocumentSymbol[<| + "name" -> node["name"], + "detail" -> node["style"], + "kind" -> If[node["style"] == "Package", + SymbolKind["Package"], + SymbolKind["String"] + ], + "range" -> ToLspRange[doc, node["range"]], + "selectionRange" -> node["selectionRange"], + "children" -> ( + Join[ + If[!MissingQ[node["codeRange"]], + node["codeRange"] + // Map[CellToAST[doc ,#]&] + // Flatten + // Map[ToDocumentSymbolImpl], + {} + ], + If[!MissingQ[node["children"]], + node["children"] + // Map[ToDocumentSymbolImpl[doc, #]&], + {} + ] + ] // Flatten + ) + |>] + ), _CellNode :> ( Join[ If[!MissingQ[node["codeRange"]], @@ -762,7 +547,7 @@ ToDocumentSymbolImpl[node_] := ( FirstCase[ head, AstPattern["Symbol"][<|"symbolName" -> rootSymbol_|>] :> rootSymbol, - "[unnamed]", + "", AstLevelspec["LeafNodeWithSource"] ] ), @@ -852,7 +637,7 @@ GetHoverInfo[doc_TextDocument, pos_LspPosition] := With[ { line = pos["line"] + 1, character = pos["character"] + 1 }, - + GetCodeRangeAtPosition[doc, pos] // Replace[lineRange:{_Integer, _Integer} :> ( CellToAST[doc, lineRange] @@ -935,7 +720,7 @@ GetFunctionName[doc_TextDocument, pos_LspPosition] := With[ { line = pos["line"] + 1, character = pos["character"] + 1 }, - + GetCodeRangeAtPosition[doc, pos] // Replace[lineRange:{_Integer, _Integer} :> ( CellToAST[doc, lineRange] @@ -994,11 +779,12 @@ GetTokenPrefix[doc_TextDocument, pos_LspPosition] := With[ line - rangeStartLine + 1, pos["character"] }]] - // Replace[ + // Replace[{ AstPattern["Token"][tokenString_, data_] :> ( StringTake[tokenString, pos["character"] - Part[data[CodeParser`Source], 1, 2] + 1] - ) - ] + ), + err_ :> (LogError["Unknown token node " <> ToString[err]]; "") + }] )] // Replace[ (* this happens when line is not in codeRange or character == 0 *) _?MissingQ -> "" @@ -1129,7 +915,7 @@ FindDocumentHighlight[doc_TextDocument, pos_LspPosition] := ( Options[FindScopeOccurence] = { - (* only document-wide search, not pooject-wide currently *) + (* only document-wide search, not project-wide currently *) "GlobalSearch" -> True, "BodySearch" -> True } From 6ed2d7efc7a0796f37a3727b4dcc1e5e5a38cbda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BA=B7=E5=B0=8F=E5=B9=BF?= Date: Fri, 31 Jul 2020 05:09:15 -0700 Subject: [PATCH 27/40] Fix: DelayedPattern should be found in head --- src/WolframLanguageServer/TextDocument.wl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/WolframLanguageServer/TextDocument.wl b/src/WolframLanguageServer/TextDocument.wl index 51d9b02..4b52008 100644 --- a/src/WolframLanguageServer/TextDocument.wl +++ b/src/WolframLanguageServer/TextDocument.wl @@ -1081,7 +1081,7 @@ ScopeHeadSymbolSource["Block"|"Module"|"DynamicModule", head_, name_String] :=( DelayedHeadPatternNameSource[head_, name_String] := ( Join[ Cases[ - Part[head, 2], + head, AstPattern["DelayedPattern"][patternName:name, patternData_] :> ( patternData[CodeParser`Source] // Replace[ From a9e525ea4686597ddc7de302c3b55507ddc2ed36 Mon Sep 17 00:00:00 2001 From: kenkangxgwe Date: Sat, 1 Aug 2020 02:03:14 -0700 Subject: [PATCH 28/40] Fix: wrong (code)range when the child is merged to the parent. * range should be maximum of current range, coderange and child's range. * range should be close interval --- src/WolframLanguageServer/TextDocument.wl | 40 +++++++++++++++-------- 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/src/WolframLanguageServer/TextDocument.wl b/src/WolframLanguageServer/TextDocument.wl index 4b52008..5533c5a 100644 --- a/src/WolframLanguageServer/TextDocument.wl +++ b/src/WolframLanguageServer/TextDocument.wl @@ -180,7 +180,6 @@ divideCells[doc_TextDocument] := ( // Replace[err:Except[_CellNode] :> ( LogError["The result of devideCells is not a CellNode " <> ToString[err]] )] - // LogDebug ) @@ -216,7 +215,7 @@ constructCellNode[doc_TextDocument, styleLine_Integer, endLine_Integer] := Block "level" -> If[HeadingQ[style], HeadingLevel[style], Infinity], "style" -> style, "name" -> (title // Replace[(_?MissingQ|"") :> ""]), - "range" -> {styleLine, endLine}, + "range" -> {styleLine, endLine - 1}, "selectionRange" -> If[!MissingQ[title], Part[doc["text"], styleLine + 1] // StringPosition[title] @@ -280,7 +279,10 @@ InsertCell[rootCell_CellNode, nextCell_CellNode] := ( nextCell // If[nextCell["level"] == Infinity, (* removes codeRange *) - ReplaceKey["codeRange" -> {}], + ReplaceKey[{"range", -1} -> ( + First[First[nextCell["codeRange"]]] - 1 + )] + /* ReplaceKey["codeRange" -> {}], Identity ] ]] @@ -288,17 +290,29 @@ InsertCell[rootCell_CellNode, nextCell_CellNode] := ( ) TerminateCell[rootcell_CellNode] := ( - If[Length[rootcell["children"]] > 0, - rootcell - // ReplaceKeyBy[{"children", -1} -> TerminateCell] - // (newRootCell \[Function] ( - newRootCell - // ReplaceKey[{"range", -1} -> ( - Last[Last[newRootCell["children"]]["range"]] - )] - )), - rootcell + rootcell + // If[Length[rootcell["children"]] > 0, + ReplaceKeyBy[{"children", -1} -> TerminateCell], + Identity ] + // (newRootCell \[Function] ( + newRootCell + // ReplaceKey[{"range", -1} -> ( + Max[ + Last[newRootCell["range"]], + newRootCell["children"] + // Replace[{ + {___, lastChild_} :> Last[lastChild["range"]], + _ -> -Infinity + }], + newRootCell["codeRange"] + // Replace[{ + {___, {_, last_}} :> last, + _ -> -Infinity + }] + ] + )] + )) ) From c6a88c3a7e4810c92b0de75ce95223cef80d9a3d Mon Sep 17 00:00:00 2001 From: kenkangxgwe Date: Fri, 31 Jul 2020 07:45:39 -0700 Subject: [PATCH 29/40] Add FindAllCodeRanges This is useful for both testing and dap --- src/WolframLanguageServer/TextDocument.wl | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/WolframLanguageServer/TextDocument.wl b/src/WolframLanguageServer/TextDocument.wl index 5533c5a..0c7b59f 100644 --- a/src/WolframLanguageServer/TextDocument.wl +++ b/src/WolframLanguageServer/TextDocument.wl @@ -22,6 +22,7 @@ ToDocumentSymbol::usage = "ToDocumentSymbol[doc_TextDocument] gives the Document FindDefinitions::usage = "FindDefinitions[doc_TextDocument, pos_LspPosition] gives the definitions of the symbol at the position in the Top level." FindReferences::usage = "FindReferences[doc_TextDocument, pos_LspPosition, o:OptionsPattern[]] gives the references of the symbol at the position." FindDocumentHighlight::usage = "FindDocumentHighlight[doc_TextDocument, pos_LspPosition] gives a list of DocumentHighlight." +FindAllCodeRanges::usage = "FindAllCodeRanges[doc_TextDocument] returns a list of LspRange which locate all the code ranges (cells) in the given doc." FindDocumentColor::usage = "FindDocumentColor[doc_TextDocument] gives a list of colors in the text document." GetColorPresentation::usage = "GetColorPresentation[doc_TextDocument, color_LspColor, range_LspRange] gives the RGBColor presentation of the color." @@ -372,6 +373,18 @@ GetCodeRangeAtPosition[doc_TextDocument, pos_LspPosition] := With[ ] +FindAllCodeRanges[doc_TextDocument] := ( + + Cases[ + divideCells[doc], + node_CellNode :> node["codeRange"], + {0, Infinity} + ] + // Catenate + // Map[ToLspRange[doc, #]&] +) + + (* ::Section:: *) (*AST utils*) From d24770ed7f42e07bb6e90b091e20da859eda6e4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BA=B7=E5=B0=8F=E5=B9=BF?= Date: Sat, 21 Mar 2020 15:36:10 -0700 Subject: [PATCH 30/40] Add test for FindAllCodeRanges --- test/WolframLanguageServer/TextDocumentTest.wl | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/WolframLanguageServer/TextDocumentTest.wl b/test/WolframLanguageServer/TextDocumentTest.wl index ecfbd81..4368953 100644 --- a/test/WolframLanguageServer/TextDocumentTest.wl +++ b/test/WolframLanguageServer/TextDocumentTest.wl @@ -258,6 +258,12 @@ VerificationTest[ TestID -> "FindAllCodeRangeTwoSection1" ], +VerificationTest[ + FindAllCodeRanges[TextDocument[<|"text" -> {}|>]], + {}, + TestID -> "FindAllCodeRangeEmptyDoc" +], + VerificationTest[ GetHoverInfo[ TextDocument[<| From dcafe973db024e5ab77db24f0aeb70c6f012f52b Mon Sep 17 00:00:00 2001 From: kenkangxgwe Date: Thu, 30 Jul 2020 03:02:21 -0700 Subject: [PATCH 31/40] Don't show warning message if no dependencies are missing. --- src/WolframLanguageServer/Server.wl | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/WolframLanguageServer/Server.wl b/src/WolframLanguageServer/Server.wl index 514a0c3..07e8220 100644 --- a/src/WolframLanguageServer/Server.wl +++ b/src/WolframLanguageServer/Server.wl @@ -1796,12 +1796,10 @@ checkGitRepo[state_WorkState] := ( If[$VersionNumber >= 12.1, pacletInstalledQ[{name_String, version_String}] := ( PacletObject[name -> version] - // LogDebug // FailureQ // Not ), pacletInstalledQ[{name_String, version_String}] := ( PacletManager`PacletInformation[{name, version}] - // LogDebug // MatchQ[{}] // Not ) ] @@ -1825,14 +1823,17 @@ checkDependencies[state_WorkState] := With[ dependencies // Select[pacletInstalledQ /* Not] - // StringRiffle[#, ", ", "-"]& - // LogDebug - // StringTemplate[StringJoin[ - "These dependencies with correct versions need to be installed or upgraded: ``, ", - "otherwise the server may malfunction. ", - "Please see the [Installation](https://github.com/kenkangxgwe/lsp-wl/blob/master/README.md#installation) section for details." - ]] - // showMessage[#, "Warning", state]& + // Replace[ + missingDeps:Except[{}] :> ( + StringRiffle[missingDeps, ", ", "-"] + // StringTemplate[StringJoin[ + "These dependencies with correct versions need to be installed or upgraded: ``, ", + "otherwise the server may malfunction. ", + "Please see the [Installation](https://github.com/kenkangxgwe/lsp-wl/blob/master/README.md#installation) section for details." + ]] + // showMessage[#, "Warning", state]& + ) + ] ] From 7cec2a0abf95c8da3ca35d19670fa7330d175549 Mon Sep 17 00:00:00 2001 From: kenkangxgwe Date: Sat, 1 Aug 2020 02:41:44 -0700 Subject: [PATCH 32/40] Modify: tests according to version of wolfram and dependencies * `HoverMessageName 3` will be disabled temporarily. * `KnowSymbolUsage 3` will have different behaviors for different versions. --- .../WolframLanguageServer/TextDocumentTest.wl | 9 +++++-- test/WolframLanguageServer/TokenTest.wl | 26 ++++++++++++++++--- 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/test/WolframLanguageServer/TextDocumentTest.wl b/test/WolframLanguageServer/TextDocumentTest.wl index 4368953..a6fc74c 100644 --- a/test/WolframLanguageServer/TextDocumentTest.wl +++ b/test/WolframLanguageServer/TextDocumentTest.wl @@ -382,7 +382,12 @@ VerificationTest[ TestID -> "HoverMessageName 2" ], -VerificationTest[ +(* + TODO: There's an unreleased commit on CodeParser's master to reveal the + source of the message name in Symbol::name form. Enable this test after + that is release. +*) +(* VerificationTest[ GetHoverInfo[ TextDocument[<| "text" -> { @@ -410,7 +415,7 @@ VerificationTest[ |>] }, TestID -> "HoverMessageName 3" -], +], *) VerificationTest[ GetHoverInfo[ diff --git a/test/WolframLanguageServer/TokenTest.wl b/test/WolframLanguageServer/TokenTest.wl index 736876d..6c4f350 100644 --- a/test/WolframLanguageServer/TokenTest.wl +++ b/test/WolframLanguageServer/TokenTest.wl @@ -58,10 +58,28 @@ VerificationTest[ TestID -> "KnownSymbolUsage 2" ], -VerificationTest[ - TokenDocumentation["SlotSequence", "usage"], - StringJoin[""], - TestID -> "KnownSymbolUsage 3" +If[$VersionNumber >= 12.0, + VerificationTest[ + TokenDocumentation["SlotSequence", "usage"], + StringJoin[ + "**SlotSequence** [*reference*](https://reference.wolfram.com/language/ref/SlotSequence.html) (NHoldAll, Protected)\n\n\n", + "```mathematica\n", + "## \n", + "```\n\n", + "represents the sequence of arguments supplied to a pure function. \n\n", + "```mathematica\n", + "##n \n", + "```\n\n", + "represents the sequence of arguments supplied to a pure function, starting with the *n*-th argument. \n\n" + ], + TestID -> "KnownSymbolUsage 3" + ], + (* before 12.0, SlotSequence::usage is empty *) + VerificationTest[ + TokenDocumentation["SlotSequence", "usage"], + "", + TestID -> "KnownSymbolUsage 3" + ] ], VerificationTest[ From ec2554e2377e31e520e11fdbab5fa954f9391a92 Mon Sep 17 00:00:00 2001 From: kenkangxgwe Date: Sat, 1 Aug 2020 03:11:48 -0700 Subject: [PATCH 33/40] Format: Test results in command line * Set `$Output` to OutputForm * Return 0 or 1 if all tests pass or not. --- init.wls | 16 ++++++++++------ test/RunTest.wl | 29 +++++++++++++++++++---------- 2 files changed, 29 insertions(+), 16 deletions(-) diff --git a/init.wls b/init.wls index ebd9bd2..8880158 100644 --- a/init.wls +++ b/init.wls @@ -14,6 +14,8 @@ Please see the help info below. *) +(* For windows, this should be set for Print to correctly format outputs. *) +SetOptions[$Output, FormatType -> OutputForm] (* ::Subsection:: *) (*RootDirectory*) @@ -127,14 +129,16 @@ Wolfram Language " <> $Version <> "\n"]; If[MemberQ[WolframLanguageServer`CommandLine, "-t" | "--test"], - Print[" -Running tests for all. -"]; + Print["\nRunning tests for all.\n"]; + Off[General::shdw]; << RunTest`; - Print[RunTest`TestRunAll[]]; - Quit[]; -]; + RunTest`TestRunAll[] + // Replace[{testAllPass_, testResult_} :> ( + Print[testResult]; + Exit[If[testAllPass, 0, 1]] + )] +] (* ::Subsection:: *) diff --git a/test/RunTest.wl b/test/RunTest.wl index 9cc6771..f629c94 100644 --- a/test/RunTest.wl +++ b/test/RunTest.wl @@ -18,7 +18,14 @@ Begin["`Private`"] ClearAll[Evaluate[Context[] <> "*"]] -TestRunAll[] := Column[TestRunContext /@ TestContexts] +TestRunAll[] := ( + TestContexts + // Map[TestRunContext] + // Transpose + // MapAt[Apply[And], 1] + // MapAt[Column, 2] +) + TestRunContext[context_String] := ( @@ -27,15 +34,17 @@ TestRunContext[context_String] := ( ) -ShowTestReport[report_TestReportObject, context_String] := -Column[{ - TableForm[{ - {"Test: ", context}, - {"Test passed: ", {{report["TestsSucceededCount"], "/", report["TestsSucceededCount"] + report["TestsFailedCount"]}}}, - {"Time Elapsed: ", report["TimeElapsed"]} - }], - Column[ShowTestResult /@ Cases[report["TestsFailed"], _TestResultObject, Infinity]] -}] +ShowTestReport[report_TestReportObject, context_String] := { + report["AllTestsSucceeded"], + Column[{ + Grid[{ + {"Test: ", context}, + {"Test passed: ", {{report["TestsSucceededCount"], "/", report["TestsSucceededCount"] + report["TestsFailedCount"]}}}, + {"Time Elapsed: ", report["TimeElapsed"]} + }], + Column[ShowTestResult /@ Cases[report["TestsFailed"], _TestResultObject, Infinity]] + }] +} ShowTestResult[result_TestResultObject] := ( From b4e582b446b847d74f43d3bb28d79a9492f45594 Mon Sep 17 00:00:00 2001 From: kenkangxgwe Date: Sat, 1 Aug 2020 03:18:20 -0700 Subject: [PATCH 34/40] [Action] add tests to GitHub Actions Run `wolframscript -file init.wls --test` in action. --- .github/workflows/workflow.yml | 60 ++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 .github/workflows/workflow.yml diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml new file mode 100644 index 0000000..b8067e7 --- /dev/null +++ b/.github/workflows/workflow.yml @@ -0,0 +1,60 @@ +# This is a basic workflow to help you get started with Actions + +name: CI + +# Controls when the action will run. Triggers the workflow on push or pull request +# events but only for the master branch +on: + push: + branches: + - master + - develop + pull_request: + branches: + - master + +env: + WOLFRAM_ID: ${{ secrets.WOLFRAM_ID }} + WOLFRAM_PW: ${{ secrets.WOLFRAM_PW }} + +# A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: + # This workflow contains a single job called "build" + build: + name: "Build Code Parser" + # The type of runner that the job will run on + runs-on: ubuntu-latest + # Steps represent a sequence of tasks that will be executed as part of the job + steps: + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it + - uses: actions/checkout@v2 + + - name: Install Dependencies + run: | + sudo apt update + sudo apt install -y wget cmake + - name: Install Wolfram Engine + run: | + wget https://account.wolfram.com/download/public/wolfram-engine/desktop/LINUX + sudo bash LINUX -- -auto -verbose + rm LINUX + - name: Activate Wolfram Engine + run: | + /usr/bin/wolframscript -authenticate $WOLFRAM_ID $WOLFRAM_PW + /usr/bin/wolframscript -activate + - name: Build CodeParser + run: | + mkdir build + cd build + cmake .. -DMATHEMATICA_INSTALL_DIR="/usr/local/Wolfram/WolframEngine/12.1" + cmake --build . --target paclet + - name: Install Paclet + # TODO: find a way to specify the name of the paclet file instead of hardcoding. + run: | + ls build + /usr/bin/wolframscript -code 'PacletInstall["./build/paclet/CodeParser-1.1.paclet"];Exit[]' + + - name: Run Tests + run: | + pwd + /usr/bin/wolframscript -file init.wls --test \ No newline at end of file From 9062f88d3f333eee49203ea70df2425d3767e359 Mon Sep 17 00:00:00 2001 From: kenkangxgwe Date: Sat, 1 Aug 2020 03:43:22 -0700 Subject: [PATCH 35/40] [Action] remove extra steps * remove old steps for CodeParser compilation --- .github/workflows/workflow.yml | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index b8067e7..60df39b 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -21,7 +21,7 @@ env: jobs: # This workflow contains a single job called "build" build: - name: "Build Code Parser" + name: "Run lsp-wl tests" # The type of runner that the job will run on runs-on: ubuntu-latest # Steps represent a sequence of tasks that will be executed as part of the job @@ -29,10 +29,6 @@ jobs: # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - uses: actions/checkout@v2 - - name: Install Dependencies - run: | - sudo apt update - sudo apt install -y wget cmake - name: Install Wolfram Engine run: | wget https://account.wolfram.com/download/public/wolfram-engine/desktop/LINUX @@ -42,19 +38,10 @@ jobs: run: | /usr/bin/wolframscript -authenticate $WOLFRAM_ID $WOLFRAM_PW /usr/bin/wolframscript -activate - - name: Build CodeParser - run: | - mkdir build - cd build - cmake .. -DMATHEMATICA_INSTALL_DIR="/usr/local/Wolfram/WolframEngine/12.1" - cmake --build . --target paclet - - name: Install Paclet - # TODO: find a way to specify the name of the paclet file instead of hardcoding. + - name: Install Paclets run: | ls build - /usr/bin/wolframscript -code 'PacletInstall["./build/paclet/CodeParser-1.1.paclet"];Exit[]' - + /usr/bin/wolframscript -code 'PacletInstall["CodeParser"];PacletInstall["CodeInspector"];Exit[]' - name: Run Tests run: | - pwd /usr/bin/wolframscript -file init.wls --test \ No newline at end of file From f58ba5d32ea5f2c8f173a04ba9f202803a6953e8 Mon Sep 17 00:00:00 2001 From: kenkangxgwe Date: Sat, 1 Aug 2020 03:48:15 -0700 Subject: [PATCH 36/40] [Action] remove unless step --- .github/workflows/workflow.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index 60df39b..1a9da55 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -40,7 +40,6 @@ jobs: /usr/bin/wolframscript -activate - name: Install Paclets run: | - ls build /usr/bin/wolframscript -code 'PacletInstall["CodeParser"];PacletInstall["CodeInspector"];Exit[]' - name: Run Tests run: | From 364051777ad0d313e3876847ea390a07c6f15c9d Mon Sep 17 00:00:00 2001 From: kenkangxgwe Date: Sat, 1 Aug 2020 03:58:00 -0700 Subject: [PATCH 37/40] Format: concatenate numbers to string in "Tests Passed" --- test/RunTest.wl | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/test/RunTest.wl b/test/RunTest.wl index f629c94..1de0b08 100644 --- a/test/RunTest.wl +++ b/test/RunTest.wl @@ -38,8 +38,15 @@ ShowTestReport[report_TestReportObject, context_String] := { report["AllTestsSucceeded"], Column[{ Grid[{ - {"Test: ", context}, - {"Test passed: ", {{report["TestsSucceededCount"], "/", report["TestsSucceededCount"] + report["TestsFailedCount"]}}}, + {"Test Context: ", context}, + { + "Tests Passed: ", + { + report["TestsSucceededCount"], + " / ", + report["TestsSucceededCount"] + report["TestsFailedCount"] + } // Map[ToString] // StringJoin + }, {"Time Elapsed: ", report["TimeElapsed"]} }], Column[ShowTestResult /@ Cases[report["TestsFailed"], _TestResultObject, Infinity]] From 7ed08575955823b82c96a39e7c3e0e936dc5c519 Mon Sep 17 00:00:00 2001 From: kenkangxgwe Date: Fri, 31 Jul 2020 07:54:23 -0700 Subject: [PATCH 38/40] Fix: patterns for LSP specifications * Add Head to those missing ones. --- src/WolframLanguageServer/Specification.wl | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/WolframLanguageServer/Specification.wl b/src/WolframLanguageServer/Specification.wl index 8aa6156..5e134d0 100644 --- a/src/WolframLanguageServer/Specification.wl +++ b/src/WolframLanguageServer/Specification.wl @@ -269,20 +269,19 @@ DeclareType[CompletionItem, <| "commitCharacters" -> {___String} |>] - DeclareType[Location, <| "uri" -> DocumentUri, - "range" -> LspRange + "range" -> _LspRange |>] DeclareType[DocumentHighlight, <| - "range" -> LspRange, + "range" -> _LspRange, "kind" -> _Integer |>] DeclareType[ColorInformation, <| - "range" -> LspRange, - "color" -> LspColor + "range" -> _LspRange, + "color" -> _LspColor |>] DeclareType[LspColor, <| From ba892bce922853ffcc062ae9d118a46bcb156549 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BA=B7=E5=B0=8F=E5=B9=BF?= Date: Sat, 21 Mar 2020 16:11:48 -0700 Subject: [PATCH 39/40] :memo: update CHANGLOG for release 0.2.2 --- CHANGELOG.md | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 37473d4..f0fb0a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,14 +4,31 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.2.2] - TBD + +### Added + +- SignatureHelp is available for functions when `[` and `,` is input. + +### Changed + +- The alias completion has been improved by reducing the number of +`completionItem`s, and returning `\`-prefixed aliases when triggered twice. + +### Fixed + +- `triggerCharacters` should be a list (thanks to +[@dalanicolai](https://github.com/dalanicolai)). +- The `duplicate requests` +error will not popup in Output window in VSCode (reported by +[@GiovanniBordiga](https://github.com/GiovanniBordiga)). + ## [0.2.1] - 2020-01-24 🏮🐀 ### Added - Definition / References are available to show in scopes / the whole file. - - DocumentHighlight is available to show the scoped variables. - - DocumentColor / ColorRepresentation are available to show the color names and models. ### Changed From 78a8523a13244adfc4e2a8506dfb955de70be4a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BA=B7=E5=B0=8F=E5=B9=BF?= Date: Sat, 1 Aug 2020 20:53:47 -0700 Subject: [PATCH 40/40] Bump version to 0.2.2 --- .github/workflows/workflow.yml | 1 + CHANGELOG.md | 5 ++++- init.wls | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index 1a9da55..529a0df 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -12,6 +12,7 @@ on: pull_request: branches: - master + - develop env: WOLFRAM_ID: ${{ secrets.WOLFRAM_ID }} diff --git a/CHANGELOG.md b/CHANGELOG.md index f0fb0a5..896b9d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [0.2.2] - TBD +## [0.2.2] - 2020-08-01 🐱‍🏍 ### Added @@ -12,6 +12,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- Change the dependencies from `AST` and `Lint` to `CodeParser` and +`CodeInspector`. - The alias completion has been improved by reducing the number of `completionItem`s, and returning `\`-prefixed aliases when triggered twice. @@ -22,6 +24,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - The `duplicate requests` error will not popup in Output window in VSCode (reported by [@GiovanniBordiga](https://github.com/GiovanniBordiga)). +- MessageName should be shown when hovered ## [0.2.1] - 2020-01-24 🏮🐀 diff --git a/init.wls b/init.wls index 8880158..1e383f1 100644 --- a/init.wls +++ b/init.wls @@ -115,7 +115,7 @@ Options: (*Version*) -WolframLanguageServer`Version = "0.2.1"; +WolframLanguageServer`Version = "0.2.2" If[MemberQ[WolframLanguageServer`CommandLine, "-v" | "--version"], Print[" Wolfram Language Server " <> WolframLanguageServer`Version <> " running on